【问题标题】:Is it possible to dynamically add code to/extend a class?是否可以动态地将代码添加到/扩展一个类?
【发布时间】:2011-06-07 19:36:40
【问题描述】:

我想为我的代码编写一种“插件/模块”系统,如果我可以在定义后将内容“添加”到一个类中,这将变得更加容易。

例如,像这样的:

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

上课了。现在我想在它定义后添加另一个函数/变量/常量。

我意识到这可能是不可能的,但我需要确认。

【问题讨论】:

  • 你能告诉我你所说的定义是什么意思吗?你的意思是文件已经被包含,但是没有对象被初始化或者对象已经被初始化?
  • 我想我在 irc.freenode.net 上见过你,你注册了吗?还是有人盗用了你的 freenode 在线别名?
  • @JamesM-SiteGen 是的。那将是我。你好。
  • 你喜欢我的回答吗?小而简单。 :)
  • similar question here,好的解决方案/讨论指向Composite pattern

标签: php


【解决方案1】:

不,您不能在运行时向已定义的类添加方法。

但是您可以使用__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";

这个解决方案非常有限,但也许对你有用

【讨论】:

  • 这在什么方面受到限制?我可以看到这受到限制的唯一方法可能是无法添加受保护的方法。
【解决方案2】:

要添加/更改类在运行时的行为方式,您应该使用装饰器和/或策略。这是首选的 OO 方法,而不是求助于任何魔术方法或猴子补丁。

装饰器包装类的实例并提供与该实例相同的 API。任何调用都被委托给包装的实例,并在需要时修改结果。

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;
    }
    // …
}

对于策略,请参阅Can I include code into a PHP class? 上的更完整示例

更多细节和示例代码可以在

找到

【讨论】:

    【解决方案3】:

    我有一些方法供您尝试。 :) 玩得开心编码。

    只有一个类的方法:

    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 );
        }
    }
    

    当你想处理多个类时的方法:

    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__();
        }
    }
    

    现在让我们尝试使用它们:

    $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.
    

    注意:

    我目前在我自己从头开始制作的 CMS (http://sitegen.com.au) 中使用第一种方法(我只有一个类,mod 是例外),效果很好,我的需要这个的原因是因为我的 main_class 是在我需要 ./mods-enabled/* 中的所有模块创建函数并更改其他函数的工作方式之后生成的,我还将再次回到这里,提供两个解决方案mods 都可以更改功能而没有一个获胜,因为它首先运行。我将我的插件一分为二,在每个站点上运行的模块,以及具有站点设置但甚至可能未启用的插件。
    享受编程的乐趣。

    【讨论】:

      【解决方案4】:

      你可以扩展类

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

      通过从 woo 类扩展 foo,可以使用 foo 中的功能,同时您还可以在 woo 中创建新方法。这是向类添加新功能的最简单方法。

      【讨论】:

      • 但是我必须在课堂上使用 woo 而不是 foo,对吗?
      • 是的,您必须通过 woo 而不是 foo 才能使用您添加的功能。基本上你保持 foo 类不变,但是通过从不同的其他类扩展它来添加不同的功能。在不了解您希望代码做什么的情况下,很难给出可能对您有用的确切示例
      【解决方案5】:

      您可以使用 PHP 的magic functionality 对未在编译时定义的方法提供操作。

      【讨论】:

        【解决方案6】:

        其实是可以的。例如:

        <?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; 
        ?>
        

        【讨论】:

        • 我更多地考虑添加诸如函数和常量之类的东西。我应该在我的 OP 中这么说...
        【解决方案7】:

        如果对你来说还不够神奇,你可以使用动态对象。共同的想法在这里:https://github.com/ptrofimov/jslikeobject

        【讨论】:

          猜你喜欢
          • 2010-12-05
          • 1970-01-01
          • 2016-07-01
          • 2017-11-02
          • 2012-08-05
          • 2015-01-18
          • 2018-10-15
          • 2013-04-28
          相关资源
          最近更新 更多