有没有办法在调用构造函数之前设置属性?

时间:2014-05-02 12:51:58

标签: php constructor code-injection

是的,我知道应该将依赖项传递给构造函数。我不是在询问编码风格,也不是在做什么,也不要问。

我的应用程序中的许多类都绑定到数据库驱动程序类的实例。为此,我使用PHP的后期静态绑定创建了一个抽象的Factory类。此类中唯一的成员是保存驱动程序引用的属性。它看起来像这样:

abstract class Factory {

    static private $__instances;
    static private $__default_driver;

    protected $_driver;

    static public function getInstance ( \Database\Driver $driver = null ) {
        if ( ! isset($driver) ) {
            if ( ! isset(self::$__default_driver) ) {
                require ( \Core\Options::$database_driver[ 'path' ] );
                self::$__default_driver = new \Core\Options::$database_driver[ 'class' ]( \Core\Options::$database_options );
            }
            $driver = self::$__default_driver;
        }

        $schema = $driver->getDatabase();
        $class  = get_called_class();

        if ( ! isset(self::$__instances[ $schema ][ $class ]) ) {
            self::$__instances[ $schema ][ $class ] = new $class( $driver );
            self::$__instances[ $schema ][ $class ]->_driver = $driver;
        }

        return self::$__instances[ $schema ][ $class ];
    }
}

如您所见,当我创建派生类的实例时,我将驱动程序的实例传递给它的构造函数。然后设置属性。如果可能的话,我想通过首先设置属性然后调用构造函数来反转它。这样,派生类不需要实现构造函数,也不用担心调用父方法。

我已经查看Reflection API来执行此操作,但我似乎无法找到任何可行的方法。我发现的大多数Dependency Injection链接实际上都使用了构造函数。这需要在PHP 5.3上运行。

对于那些无法做到这一点的人,It can easily be done in PHP 5.4使用ReflectionClass::newInstanceWithoutConstructor()

3 个答案:

答案 0 :(得分:2)

class Foo {
    public function __construct() {
        echo 'foo';
    }
}

$r = new ReflectionClass('Foo');
$o = $r->newInstanceWithoutConstructor();
$o->bar = 'baz';
$o->__construct();

这可以从PHP 5.4开始。

但是真的:没有。就是不行。不要。在实例化对象时,应始终调用对象的构造函数。不久(显然),不久之后。构造函数和对象其余部分的适当封装可确保对象始终处于一致状态。一旦你开始使用ReflectionClass拆开它,就不能再保证对象的状态了,理智和类型安全就会消失。

因为你在5.3,你很幸运,那里没有办法做到这一点。如果你发现自己被这样的角落所支持,你只是做错了。编写了非常复杂的程序,无需欺骗PHP的对象模型来延迟构造函数调用。

答案 1 :(得分:0)

“我知道依赖项应该传递给构造函数”,而不是真的,如果依赖项被重写的构造函数继承,使用它是一个坏主意。

在这些情况下,类甚至可能不使用构造函数,这意味着类所拥有的唯一状态是它的依赖项,并且这些方法在对象的生命周期中被视为静态,之后是无状态的。

这在Symfony2中非常常见,其中通过DI加载的某些服务不包含传递给构造函数的参数,或者如果它们是,则扩展必须兼容。

答案 2 :(得分:0)

你不能设置一个尚不存在的对象的值,你需要实例化你的对象,然后通过将必要的参数传递给构造函数来初始化它,你必须使用参数来初始化你的对象