PHP中的类插件?
我在学习
PHP时遇到了一些问题,php是否实现了任何内置的插件系统?
所以插件将能够改变核心组件的行为. 例如这样的工作: include 'core.class.php'; include 'plugin1.class.php'; include 'plugin2.class.php'; new plugin2; 哪里 class core { public function coremethod1(){ echo 'coremethod1'; } public function coremethod2(){ echo 'coremethod2'; } } plugin1.class.php包含 class plugin1 extends core { public function coremethod1(){ echo 'plugin1method1'; } } plugin2.class.php包含 class plugin2 extends plugin1 { public function coremethod2(){ echo 'plugin2method2'; } } 如果不是因为现在插件彼此可靠并且删除其中一个插件的问题,这将是理想的: include 'core.class.php'; //include 'plugin1.class.php'; include 'plugin2.class.php'; new plugin2; 打破了整个事情…… 有没有适当的方法来做到这一点? 谢谢你的帮助. 编辑: core.class.php包含任何内容…… plugin1.class.php包含任何内容…… plugin2.class.php包含任何内容…… include 'core.class.php'; include 'plugin1.class.php'; include 'plugin2.class.php'; $core = new core; $core->coremethod1();//outputs plugin2method1 然而: include 'core.class.php'; include 'plugin2.class.php'; $core = new core; $core->coremethod1();//outputs plugin1method1 我对任何实现感兴趣,即使是不涉及类的实现 include 'core.php'; //does core stuff include 'core.php'; include 'plugin1'; //does extended core stuff include 'core.php'; include 'plugin2'; //does extended core stuff include 'core.php'; include 'plugin2'; include 'plugin1'; //does very extended core stuff 包含文件需要更改应用程序行为.因为它有任何意义. 我也不知道这叫什么,所以如果有的话,请指出正确的命名. 解决方法
如果您唯一担心的是不包括plugin1会产生错误,那么您可以使用自动加载来自动加载plugin2加载插件1:
来自PHP Manual on spl_autoload的评论 // Your custom class dir define('CLASS_DIR','class/') // Add your class dir to include path set_include_path(get_include_path().PATH_SEPARATOR.CLASS_DIR); // You can use this trick to make autoloader look // for commonly used "My.class.php" type filenames spl_autoload_extensions('.class.php'); // Use default autoload implementation spl_autoload_register(); 但是,如果您正在寻找类似特征/ mixin的功能,那么答案就是否定. PHP目前不支持此功能.至少not without patching the core或诉诸于these two API,您不希望在生产代码中使用它们. 更改对象在运行时的行为方式的正确方法是使用Decorators: $class = new BasicCache( new BasicValidators ( new Basic ) ); 或Strategy模式: $class = new Basic; $class->setStrategy(function() { return 'foo'} ); echo $class->callStrategy(); // foo $class->setStrategy(function() { return 'bar'} ); echo $class->callStrategy(); // bar 有关最常见的模式,请参阅http://sourcemaking.com/design_patterns. 编辑以下是如何使用装饰器创建插件的示例.假设,我们有一种类型的游戏,其中一些非玩家角色在虚拟空间中四处走动并不时地迎接主角.这就是他们现在所做的一切.我们想要他们如何问候一些变化,这就是我们在这种情况下需要我们的插件/装饰器的原因. 首先我们create an interface定义了一些能够问候的对象应该有的方法.我们不关心在特定对象上调用这些方法时它的作用.我们只是想确保方法可用,并且使用明确定义的输入调用它们: interface GreetInterface { public function greet($name); public function setGreeting($greeting); } 接口基本上是任何实现对象必须满足的合同.在我们的案例中,合同说,如果你是一个可以迎接的对象,你必须有两种方法.以您喜欢的方式实现它们,但有这些方法. 现在让我们构建我们的非玩家角色类,实现这个界面 class Dude implements GreetInterface { protected $greeting = 'hello'; public function greet($name) { return sprintf('%s %s',$this->greeting,$name); } public function setGreeting($greeting) { $this->greeting = $greeting; return $this; } } 我猜这是非常直截了当的. Dude类只是从接口定义了两个方法.当调用greet()时,它将获取存储在greeting中的字符串,并将前传到传递给greet方法的param. setGreeting方法允许我们在运行时更改问候语.注意:你也可以添加一个getter(我只是懒惰) 现在来看插件.我们将创建一个抽象的GreetPlugin类来包含一些shared boilerplate code,因为我们不想在实际的插件中复制代码.抽象插件类将实现GreetInterface,因此我们可以确保所有子类也实现接口. 由于Dude已经实现了接口,我们可以让插件扩展Dude,但这在概念上是错误的,因为扩展创建了一个is-a关系,但插件不是Dude. abstract class GreetPluginAbstract implements GreetInterface { protected $inner; public function __construct(GreetInterface $inner) { $this->inner = $inner; } public function setGreeting($greeting) { $this->inner->setGreeting($greeting); return $this; } public function greet($name) { return $this->inner->greet($name); } } 初始化时,插件类接受一个参数:任何实现GreetInterface的类. TypeHint确保,班级履行合同.这是必需的,因为正如你在代码中看到的那样,我们的插件需要在通过构造函数传递的类的接口中调用方法.如果我们从Dude延伸出来,我们现在可以将家伙包装成男人,这有点奇怪.不这样做的另一个原因. 现在转到第一个插件.我们希望我们的一些家伙能说一种华丽的法国口音,这意味着他们会在整个地方使用,但是不能说出合适的h.免责声明:是的,我知道这是陈词滥调.请忍受我的例子 class FrenchPlugin extends GreetPluginAbstract { public function greet($name) { return str_replace(array('h','e'),array('','é'),$this->inner->greet($name)); } } 由于插件扩展了抽象插件,我们现在可以专注于修改常规家伙如何进行问候的实际代码.当调用greet()时,我们在包装元素上调用greet(),然后删除所有h字符并将所有es转换为és.其他一切都是未经修改的抽象行为. 在另一个插件中,我们想要改变问候语的措辞,所以我们有一些人说Heya,而不仅仅是Hello.只是添加一些变化. class EasyGoingPlugin extends GreetPluginAbstract { protected $inner; public function __construct(GreetInterface $inner) { $this->inner = $inner->setGreeting('heya'); parent::__construct($inner); } } 这样我们只重写构造函数,因为greet方法应该只返回它将会是什么.所以我们在传递给这个插件的对象上调用setGreeting方法.因为对象必须实现GreetInterface,所以我们可以确定这是有效的. 请注意,我将setGreeting的返回值指定为内部对象.这是可能的,因为每当调用setMethod时我都返回$this.这不能通过界面强制执行,因此您不能依赖此表单的界面.我刚刚添加它以显示另一种技术:method chaining. 完成两个插件后,我们觉得我们有足够的变化.现在我们只需要一种方便的方法来创建Dudes.为此我们创建一个这样的小类: class DudeBuilder { public static function build() { $dude = new Dude(); $decorators = func_get_args(); foreach($decorators as $decorator) { $decorator .= "Plugin"; // require_once $decorator; $dude = new $decorator($dude); } return $dude; } } 注意:我总是混淆Builder和AbstractFactory,所以如果上面是Factory,那么它就是一个工厂.看看我之前给出的设计模式链接;) 所有这些构建器都是,创建一个普通的家伙,然后将它包装/装饰到/我们告诉它使用的任何插件,而不是返回它.因为构建器不封装自己的状态,所以我们使构建方法保持静态. 对于这个例子,我假设您使用了我在右上方给出的自动加载代码.如果没有,您可以在foreach循环中包含插件文件.只有在需要时才加载它们会使加载时间缩短几微秒,而不是将它们全部放在最上面.希望这也解释了我在各种评论中的意思,当时我认为行为不应该由文件包含控制.文件包含只是必需品.您不能使用PHP不了解的类.但实际使用该类,仅由我们的代码控制,通过将插件名称传递给构建方法. 我们现在就这样做 $regularDude = DudeBuilder::build(); $frenchDude = DudeBuilder::build('French'); $easygoingDude = DudeBuilder::build('EasyGoing'); $frenchEasyGoingDude = DudeBuilder::build('French','EasyGoing'); 这实际上与: $regularDude = new Dude; $frenchDude = new FrenchPlugin(new Dude); $easygoingDude = new EasyGoingPlugin(new Dude); $frenchEasyGoingDude = new FrenchPlugin(new EasyGoingPlugin(new Dude)); 只需两个插件,我们现在可以创建三种类型的Dudes.让他们问候你: echo $regularDude->greet('Yuri'),PHP_EOL,$frenchDude->greet('Yuri'),$easygoingDude->greet('Yuri'),$frenchEasyGoingDude->greet('Yuri'),PHP_EOL; // gives hello Yuri éllo Yuri heya Yuri éya Yuri 我们现在可以创建额外的插件来装饰我们的基本类.如果由于某种原因,你决定你的游戏也应该有说话的马或汽车,你也可以创建一个类Car或Horse并让它实现greet接口并为它们添加一个Builder.然后,您可以重复使用插件来创建法国EasyGoing汽车或马匹. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |