可以动态地向/扩展一个类添加代码吗?

4
我希望为我的代码编写一种“插件/模块”系统,如果我能在定义类之后“添加”东西到一个类中,那将会使这个过程更加容易。例如,像这样的东西:
class foo {
  public function a() {
     return 'b';
  }
}

这是一个类。现在我想在它定义之后向其添加另一个函数/变量/常量。

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


1
您能告诉我您所谓的“定义”是什么意思吗?您是指该文件已被包含但未初始化对象,还是指对象已被初始化? - Matt Asbury
我想我在irc.freenode.net上见过你,你注册了吗?还是有人盗用了你在freenode的在线别名? - JamesM-SiteGen
@JamesM-SiteGen 是的,那应该是我。你好。 - Savetheinternet
你喜欢我的回答吗?简单而且明了。 :) - JamesM-SiteGen
请参考此处类似问题,并查看指向组合模式的好的解决方案/讨论。 - Peter Krauss
7个回答

6

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

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

这个解决方案相当有限,但也许对你有用。

这个有什么限制吗?我所能看到的唯一限制可能是不能添加受保护的方法。 - B T

3

要在运行时添加/更改类的行为,应使用装饰器和/或策略模式。这是优选的面向对象方法,而不是采用任何神奇的方法或猴子补丁。

装饰器包装一个类的实例,并提供与该实例相同的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?中提供的更完整的示例。

更多细节和示例代码可在以下链接找到:


1

我有几个方法供你尝试。:) 祝你编程愉快。

仅适用于一个类的方法:

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.

注意:

我目前正在使用第一种方法(我只有一个类,mod是例外)在我自己从头开始制作的 CMS(http://sitegen.com.au)中,并且它运行良好。我需要这个的原因是,我有一个 main_class,在我要求./mods-enabled/* 中的所有 mod 之后生成,创建函数并更改其他函数的工作方式。我还会回来,提供一个解决方案,使两个 mod 都可以更改一个函数,而不是先运行的那个获胜。我把我的插件分成了两部分:在每个站点上运行的 mod 和具有站点设置并且甚至可能未启用的插件。
祝编程愉快。


0
你可以扩展这个类。
class foo {
  public function a() {
     return 'b';
  }
}

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

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

1
但是那样我就得在我的类中使用woo类而不是foo类,对吗? - Savetheinternet
你需要通过woo而不是foo来使用你添加的功能。基本上,你保持foo类不变,但通过从其他不同的类继承它来添加不同的功能。如果不了解你想让代码做什么,很难给出有用的确切示例。 - Sondre

0

这是完全可能的。例如:

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

我更多地考虑添加函数和常量之类的内容。我应该在我的原始帖子中说过这个... - Savetheinternet

0

你可以使用 PHP 的 魔术功能 来提供在编译时未定义的方法上执行操作。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接