加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长学院 > PHP教程 > 正文

PHP中的类插件?

发布时间:2020-12-13 22:07:13 所属栏目:PHP教程 来源:网络整理
导读:我在学习 PHP时遇到了一些问题,php是否实现了任何内置的插件系统? 所以插件将能够改变核心组件的行为. 例如这样的工作: include 'core.class.php';include 'plugin1.class.php';include 'plugin2.class.php';new plugin2; 哪里 core.class.php包含 class c
我在学习 PHP时遇到了一些问题,php是否实现了任何内置的插件系统?

所以插件将能够改变核心组件的行为.

例如这样的工作:

include 'core.class.php';
include 'plugin1.class.php';
include 'plugin2.class.php';
new plugin2;

哪里
core.class.php包含

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汽车或马匹.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读