从静态方法中获取父类数据


Fetch parent class data from static method

在现有的代码库中,我有一个返回实例的静态构建器方法。下面是一个简单的例子:

class Grandparent{
}
class Parent extends Grandparent{
}
class Child extends Parent{
    public static fetchChildById($id){
        // ...
        return new Child;
    }
}

在实际代码中,我有一个单一的Grandparent类和几个子类类似于ParentChild(不仅仅是ParentChild)。

我现在需要在Grandparent实现一个在fetchChildById()使用的新方法。这种方法需要利用同一父节点的所有子节点共有的某些数据。因为我还没有一个类实例,我被迫使一切都是静态的,但是,当然,这将不能正常工作,因为它是不可能覆盖静态属性和方法:

class Grandparent{
    protected static $data = array(
        'default',
    );
    protected static function filter(){
        foreach(self::$data as $i){ // <!-- Will always be `default'
            // ...
        }
    }
}
class Parent extends Grandparent{
    protected static $data = array(
        'one',
        'two',
    );
}
class Child extends Parent{
    public static fetchChildById($id){
        self::filter();
        // ...
        return new Child;
    }
}

我相信这是一个用于后期静态绑定的用例,但是代码需要在PHP/5.2.0上运行:(

我不太喜欢我想过的明显的变通方法:

  • 创建一个单独的构建器类建议进行更多的重构,而不是我目前所能承受的:

    $builder = new ChildBuilder;
    $bart = $builder->fetchChildById(1);
    
  • 创建额外的实例看起来很难看(也意味着许多更改):

    $builder = new Child;
    $bart = $builder->fetchChildById(1);
    
  • 全局变量……哦,好吧,我还没那么绝望。

我是否忽略了一些明显的机制来定制$data ?

这里有一个使用反射的替代方法。这将需要修改所有的fetchChildById实现,但是用全局查找/替换:

来完成它是非常简单的。
self::filter(__CLASS__); // this is the modification

filter将变成:

protected static function filter($className){
    $reflect = new ReflectionClass($className);
    $data = $reflect->getStaticPropertyValue('data');
    foreach($data as $i){
        // ...
    }
}

更新:属性$data需要公开以上工作(抱歉-我在探索期间编写了public)。但是有一个等价的版本没有这个要求:

$reflect = new ReflectionProperty($className, 'data');
$reflect->setAccessible(true);
$data = $reflect->getValue();