💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# 扩展行为 ### 介绍 行为增加了类具有*私有特征*的能力,也称为行为。这些与[本机PHP特性](http://php.net/manual/en/language.oop5.traits.php)相似,但它们具有一些明显的优点: 1. 行为具有自己的构造函数。 2. 行为可以具有私有或受保护的方法。 3. 方法和属性名称可以安全地冲突。 4. 可以通过行为动态扩展类。 ### [](https://octobercms.com/docs/services/behaviors#compare-traits)性状比较 在哪里可以使用如下所示的PHP特性: ~~~ class MyClass { use \October\Rain\UtilityFunctions; use \October\Rain\DeferredBinding; } ~~~ 行为以类似的方式使用: ~~~ class MyClass extends \October\Rain\Extension\Extendable { public $implement = [ 'October.Rain.UtilityFunctions', 'October.Rain.DeferredBinding', ]; } ~~~ 您可以在其中定义如下特征: ~~~ trait UtilityFunctions { public function sayHello() { echo "Hello from " . get_class($this); } } ~~~ 行为定义如下: ~~~ class UtilityFunctions extends \October\Rain\Extension\ExtensionBase { protected $parent; public function __construct($parent) { $this->parent = $parent; } public function sayHello() { echo "Hello from " . get_class($this->parent); } } ~~~ 扩展对象始终作为第一个参数传递给Behavior的构造函数。 总结一下: * 扩展\\ October \\ Rain \\ Extension \\ ExtensionBase以将您的类声明为“行为” * 想要实现该行为的类需要扩展\\ October \\ Rain \\ Extension \\ Extendable > **注意**:请参阅[使用特征而不是基类](https://octobercms.com/docs/services/behaviors#using-traits) ### [](https://octobercms.com/docs/services/behaviors#constructor-extension)扩展构造函数 任何使用`Extendable`或的类都可以使用`ExtendableTrait`静态`extend`方法扩展其构造函数。该参数应传递一个闭包,该闭包将作为类构造函数的一部分被调用。 ~~~ MyNamespace\Controller::extend(function($controller) { // }); ~~~ #### 动态声明属性 可以通过调用`addDynamicProperty`并传递属性名称和值来在可扩展对象上声明属性。 ~~~ Post::extend(function($model) { $model->addDynamicProperty('tagsCache', null); }); ~~~ > **注意**:尝试通过常规方法(`$this->foo = 'bar';`)在实现**October \\ Rain \\ Extension \\ ExtendableTrait**的对象上设置未声明的属性将不起作用。它不会引发异常,但也不会自动声明该属性。`addDynamicProperty`必须调用才能在可扩展对象上设置先前未声明的属性。 #### 检索动态属性 可以使用从ExtendableTrait继承的getDynamicProperties函数检索动态创建的属性。 因此,检索所有动态属性将如下所示: ~~~ $model->getDynamicProperties(); ~~~ 这将返回一个关联数组\[key => value\],其中key为动态属性名称,value为属性值。 如果知道我们想要什么属性,我们可以简单地将键(属性名称)附加到函数中: ~~~ $model->getDynamicProperties()[$key]; ~~~ #### 动态创建方法 通过调用`addDynamicMethod`并传递方法名称和可调用对象(例如),可以将方法创建为可扩展对象`Closure`。 ~~~ Post::extend(function($model) { $model->addDynamicProperty('tagsCache', null); $model->addDynamicMethod('getTagsAttribute', function() use ($model) { if ($this->tagsCache) { return $this->tagsCache; } else { return $this->tagsCache = $model->tags()->lists('name'); } }); }); ~~~ #### 检查方法的存在 您可以在检查的方法是否存在`Extendable`使用类`methodExists`类似PHP -方法`method_exists()`的功能。这将检测通过`addDynamicMethod`调用添加的标准方法和动态方法。`methodExists`接受一个参数:方法名称的字符串以检查其存在。 ~~~ Post::extend(function($model) { $model->addDynamicMethod('getTagsAttribute', function () use ($model) { return $this->tagsCache; }); }); $post = new Post; $post->methodExists('getTagsAttribute'); // true $post->methodExists('missingMethod'); // false ~~~ #### 列出所有可用的方法 要检索`Extendable`类中所有可用方法的列表,可以使用该`getClassMethods`方法。此方法的操作类似于PHP`get_class_methods()`函数,因为它返回一个类中的可用方法数组,但是除了该类中已定义的方法外,它还将列出扩展或通过`addDynamicMethod`调用提供的任何方法。 ~~~ Post::extend(function($model) { $model->addDynamicMethod('getTagsAttribute', function () use ($model) { return $this->tagsCache; }); }); $post = new Post; $methods = $post->getClassMethods(); /** * $methods = [ * 0 => '__construct', * 1 => 'extend', * 2 => 'getTagsAttribute', * ... * ]; */ ~~~ #### 动态实施行为 这种扩展构造函数的独特能力允许动态地执行行为,例如: ~~~ /** * Extend the RainLab.Users controller to include the RelationController behavior too */ RainLab\Users\Controllers\Users::extend(function($controller) { // Implement the list controller behavior dynamically $controller->implement[] = 'Backend.Behaviors.RelationController'; // Declare the relationConfig property dynamically for the RelationController behavior to use $controller->addDynamicProperty('relationConfig', '$/myvendor/myplugin/controllers/users/config_relation.yaml'); }); ~~~ ### [](https://octobercms.com/docs/services/behaviors#usage-example)使用范例 #### 行为/扩展类 ~~~ <?php namespace MyNamespace\Behaviors; class FormController extends \October\Rain\Extension\ExtensionBase { /** * @var Reference to the extended object. */ protected $controller; /** * Constructor */ public function __construct($controller) { $this->controller = $controller; } public function someMethod() { return "I come from the FormController Behavior!"; } public function otherMethod() { return "You might not see me..."; } } ~~~ #### 延伸课程 此类`Controller`将实现`FormController`行为,然后这些方法将可用于(混合)该类。我们将重写该`otherMethod`方法。 ~~~ <?php namespace MyNamespace; class Controller extends \October\Rain\Extension\Extendable { /** * Implement the FormController behavior */ public $implement = [ 'MyNamespace.Behaviors.FormController' ]; public function otherMethod() { return "I come from the main Controller!"; } } ~~~ #### 使用扩展名 ~~~ $controller = new MyNamespace\Controller; // Prints: I come from the FormController Behavior! echo $controller->someMethod(); // Prints: I come from the main Controller! echo $controller->otherMethod(); // Prints: You might not see me... echo $controller->asExtension('FormController')->otherMethod(); ~~~ #### 检测利用的扩展 要检查对象是否已通过行为扩展,可以`isClassExtendedWith`在对象上使用方法。 ~~~ $controller->isClassExtendedWith('Backend.Behaviors.RelationController'); ~~~ 下面是`UsersController`使用此方法动态扩展第三方插件的示例,以避免避免其他插件也扩展上述第三方插件。 ~~~ UsersController::extend(function($controller) { // Implement behavior if not already implemented if (!$controller->isClassExtendedWith('Backend.Behaviors.RelationController')) { $controller->implement[] = 'Backend.Behaviors.RelationController'; } // Define property if not already defined if (!isset($controller->relationConfig)) { $controller->addDynamicProperty('relationConfig'); } // Splice in configuration safely $myConfigPath = '$/myvendor/myplugin/controllers/users/config_relation.yaml'; $controller->relationConfig = $controller->mergeConfig( $controller->relationConfig, $myConfigPath ); } ~~~ ### 软定义 如果不存在行为类(如特征),则将引发“*找不到类”*错误。在某些情况下,如果系统中存在某种行为,您可能希望抑制此错误,以便有条件地实施。您可以通过`@`在类名的开头放置一个符号来实现。 ~~~ class User extends \October\Rain\Extension\Extendable { public $implement = ['@RainLab.Translate.Behaviors.TranslatableModel']; } ~~~ 如果类名`RainLab\Translate\Behaviors\TranslatableModel`不存在,则不会引发任何错误。这等效于以下代码: ~~~ class User extends \October\Rain\Extension\Extendable { public $implement = []; public function __construct() { if (class_exists('RainLab\Translate\Behaviors\TranslatableModel')) { $this->implement[] = 'RainLab.Translate.Behaviors.TranslatableModel'; } parent::__construct(); } } ~~~ ### [](https://octobercms.com/docs/services/behaviors#using-traits)使用特征而不是基类 对于那些您可能不想扩展`ExtensionBase`或`Extendable`类的情况,可以改用traits。您的类必须按以下方式实现: 首先,让我们创建将充当行为的类,即。可以由其他类实现。 ~~~ <?php namespace MyNamespace\Behaviours; class WaveBehaviour { use \October\Rain\Extension\ExtensionTrait; /** * When using the Extensiontrait, your behaviour also has to implement this method * @see \October\Rain\Extension\ExtensionBase */ public static function extend(callable $callback) { self::extensionExtendCallback($callback); } public function wave() { echo "*waves*<br>"; } } ~~~ 现在,让我们创建一个能够使用ExtendableTrait实现行为的类。 ~~~ class AI { use \October\Rain\Extension\ExtendableTrait; /** * @var array Extensions implemented by this class. */ public $implement; /** * Constructor */ public function __construct() { $this->extendableConstruct(); } public function __get($name) { return $this->extendableGet($name); } public function __set($name, $value) { $this->extendableSet($name, $value); } public function __call($name, $params) { return $this->extendableCall($name, $params); } public static function __callStatic($name, $params) { return self::extendableCallStatic($name, $params); } public static function extend(callable $callback) { self::extendableExtendCallback($callback); } public function youGotBrains() { echo "I've got an AI!<br>"; } } ~~~ AI类现在可以使用行为。让我们对其进行扩展,并让该类实现WaveBehaviour。 ~~~ <?php namespace MyNamespace\Classes; class Robot extends AI { public $implement = [ 'MyNamespace.Behaviours.WaveBehaviour' ]; public function identify() { echo "I'm a Robot<br>"; echo $this->youGotBrains(); echo $this->wave(); } } ~~~ 现在,您可以按以下方式使用机器人: ~~~ $robot = new Robot(); $robot->identify(); ~~~ 将输出: ~~~ I'm a Robot I've got an AI! *waves* ~~~ 记得: * 使用`ExtensionTrait`方法时,`ExtensionBase`应将from方法应用于类。 * 使用`ExtendableTrait`方法时,`Extendable`应将from方法应用于类。