简体   繁体   English

是否可以动态地向/扩展类添加代码?

[英]Is it possible to dynamically add code to/extend a class?

I want to write a sort of "plugin/module" system for my code, and it would make it much easier if I could "add" stuff into a class after it's been defined. 我想为我的代码编写一种“插件/模块”系统,如果我可以在定义之后将其“添加”到类中,它会更容易。

For example, something like this: 例如,像这样:

class foo {
  public function a() {
     return 'b';
  }
}

There's the class. 这是班级。 Now I want to add another function/variable/const to it, after it's defined. 现在我想在定义后添加另一个函数/变量/ const。

I realize that this is probably not possible, but I need confirmation. 我意识到这可能是不可能的,但我需要确认。

No, you cannot add methods to an already defined class at runtime. 不,您无法在运行时向已定义的类添加方法。

But you can create similar functionality using __call/__callStatic magic methods. 但是您可以使用__call/__callStatic魔术方法创建类似的功能。

Class Extendable  {
    private $handlers = array();
    public function registerHandler($handler) {
        $this->handlers[] = $handler;
    }

    public function __call($method, $arguments) {
        foreach ($this->handlers as $handler) {
            if (method_exists($handler, $method)) {
                return call_user_func_array(
                    array($handler, $method),
                    $arguments
                );
            }
        }
    }
}

Class myclass extends Extendable {
    public function foo() {
        echo 'foo';
    }
}

CLass myclass2 {
    public function bar() {
        echo 'bar';
    }
}

$myclass = new myclass();
$myclass->registerHandler(new myclass2());

$myclass->foo(); // prints 'foo'
echo "\n";

$myclass->bar(); // prints 'bar'
echo "\n";

This solution is quite limited but maybe it will work for you 这个解决方案非常有限,但它可能适合您

To add/change how classes behave at runtime, you should use Decorators and/or Strategies. 要添加/更改类在运行时的行为方式,您应该使用装饰器和/或策略。 This is the prefered OO approach over resorting to any magic approaches or monkey patching. 这是首选的OO方法,而不是采用任何魔术方法或猴子修补。

A Decorator wraps an instance of a class and provides the same API as that instance. 装饰器包装类的实例并提供与该实例相同的API。 Any calls are delegated to the wrapped instance and results are modified where needed. 任何调用都委托给包装实例,并在需要时修改结果。

class Decorator 
{
    // …

    public function __construct($decoratedInstance)
    {
        $this->_decoratedInstace = $decoratedInstance;    
    }
    public function someMethod()
    {
        // call original method
        $result = $this->_decoratedInstance->someMethod();
        // decorate and return
        return $result * 10;
    }
    // …
}

For Strategy, see my more complete example at Can I include code into a PHP class? 对于策略,请参阅我的更完整的示例我可以将代码包含到PHP类中吗?

More details and example code can be found at 更多细节和示例代码可以在

I have a few methods for you to try. 我有几种方法供您尝试。 :) Have fun coding. :)玩得开心编码。

Method for only one class: 只有一个类的方法:

class main_class {
    private $_MODS = array(),...;
    public ...;
    public function __construct(...) {
        ...
        global $MODS_ENABLED;
        $this -> $_MODS = $MODS_ENABLED;
    }
    ...
    public function __get( $var ) {
        foreach ( $this->_MODS as $mod ) 
            if ( property_exists( $mod, $var ) )
                return $mod -> $var;
    }
    public function __call( $method, $args ) {
        foreach ( $this->_MODS as $mod )
            if ( method_exists( $mod, $method ) )
                return call_user_method_array( $method, $mod, $args );
    }
}

Method for when you want to deal with more than one class: 想要处理多个类的方法:

class modMe {
    private $_MODS = array();
    public function __construct__() {
        global $MODS_ENABLED;
        $this -> $_MODS = $MODS_ENABLED;
    }
    public function __get( $var ) {
        foreach ( $this->_MODS as $mod ) 
            if ( property_exists( $mod, $var ) )
                return $mod -> $var;
    }
    public function __call( $method, $args ) {
        foreach ( $this->_MODS as $mod )
            if ( method_exists( $mod, $method ) )
                return call_user_method_array( $method, $mod, $args );
    }
}
class mainClass extends modMe {
    function __construct(...){
        $this -> __construct__();
    }
}

Now lets try to use them: 现在让我们尝试使用它们:

$MODS_ENABLED = array();
$MODS_ENABLED[] = new mod_mail();
$myObj = new main_class(...);
$myObj -> mail("me@me.me","you@you.you","subject","message/body","Extra:Headers;More:Headers");
# Hey look, my mail class was just added into my main_class for later use.

Note: 注意:

I am currently using the first method (I only have one class, the mods are exceptions) in my own CMS that I have made from scratch (http://sitegen.com.au), and it works great, my reason on needing this is because I have my main_class that is getting generated after I have required all mods in ./mods-enabled/* creating functions and changing how other functions work, I will also come back here another time with a solution for two mods to both change a function without one winning as it ran first. 我目前正在使用第一种方法(我只有一个类,mods是例外)在我自己的CMS中,我从头开始(http://sitegen.com.au),它工作得很好,我需要的理由这是因为我已经生成了我的main_class,在我需要./mods-enabled / *创建函数和改变其他函数的工作方式的所有mod之后,我还会再次回到这里,为两个mod提供两个mod的解决方案在没有获胜的情况下更改功能,因为它首先运行。 I have split my plugins in two, mods that run on every site, and plugins that have settings for a site and may not even be enabled. 我将我的插件拆分为两个,在每个站点上运行的mod,以及具有站点设置的插件,甚至可能都没有启用。
Have fun programming. 玩得开心。

If it is not enough magic for you, you can use dynamic objects. 如果它不够神奇,您可以使用动态对象。 The common idea is here: https://github.com/ptrofimov/jslikeobject 常见的想法是: https//github.com/ptrofimov/jslikeobject

You can extend the class 你可以扩展课程

class foo {
  public function a() {
     return 'b';
  }
}

class woo extends foo {
  public function newStuff() {
     $var = $this->a();
     echo $var;
  }
}

By extending foo from the woo class the functionality in foo is usable while you can also create new methods in woo. 通过从woo类扩展foo,foo中的功能可用,同时您还可以在woo中创建新方法。 That's the easiest way to add new functionality to a class. 这是向类添加新功能的最简单方法。

您可以使用PHP的魔术功能来提供对编译时未定义的方法的操作。

It is actually possible. 这实际上是可能的。 For instance: 例如:

<?php
class Test {

  function set($set, $val) {
    $this->$set = $val;
  }

  function get($get) {
    return $this->$get;
  } 
}

$t = new Test();
$t->set('hello', 'world');
echo $t->get('hello');
exit; 
?>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM