特征依赖性依次加载

时间:2016-03-22 20:33:19

标签: php sorting oop traits

我目前正在研究一种特质自动初始化器,它的工作正常和花花公子,但遗憾的是有一些严重的特性限制。

我遇到的一个问题如下:

trait constructor{}
trait cache{}
trait database{}
trait settings{} # requires database or cache.

现在我的自动加载系统工作了,我必须按照正确的顺序定义用法:

class t{
  use constructor, cache, settings;
}

如果我会这样做:

class t{
  use constructor, settings, cache;
}

将在缓存初始值设定项之前调用设置特征初始值设定项,因此缓存特征中的$cache变量为空。

现在一个简单的解决方案是将use cache, database;添加到设置特征中,但如果被必须包含的另一个特征使用,则可能导致已定义方法的冲突。

另一个解决方案是检查属性$cache是否已定义,然后检查它是否已设置,但这会产生大量冗余代码而我不想为每个代码编写代码和每一个特质。

现在代码的逻辑如下:

trait constructor{
  function init(){
    echo 'I am called first';
    print_r(class_uses(self::class))/*[
      'constructor' => 'constructor',
      'cache' => 'cache',
      'database' => 'database',
      'settings' => 'settings'

      // Sorted in sequence as defined in the class.
    ]*/

    # now call other trait initializers.
  }
}

trait settings{
  function settings(){
    echo 'I am called last, but before __construct()';
  }
}

class t{
  use constructor; // required by all traits
  use cache;       // requires constructor.
  use database;    // requires constructor.
  use settings;    // requires constructor, cache || database.

  function __construct(){
    echo 'I am called after init()';
    $this->db->prepare(...)->execute();
  }
}

可以在构造函数中对数组进行排序,以确保在缓存或数据库之后对设置进行索引。然而,这里有一个棘手的部分,最佳方法是做什么的?

由于大多数类仅使用少量特征,因此定义用作排序基础的冗长数组似乎不足。由于这个系统是在CMS的核心运行,我宁愿按特质排序。

特质的问题在于我不能两次定义相同的东西,在课堂上也是如此,所以我想将范围污染降到最低。

1 个答案:

答案 0 :(得分:0)

我找到了一个可行的解决方案,没有太多的冗余代码。

trait constructor{
  private $traits;

  function loaded(array $list){
    foreach($list as $trait){
      if(isset($this->_traits[$trait])){ // class_uses returns array of ['trait' => 'trait'] while I append numeric.
        $this->_traits[] = $this->_current; // append the current looping method (of init) to be loaded last.
        return false;
      }
    }
    return true;
  }

  function init(){
    $traits = class_uses(self::class);
    while(list(,$method) = each($this->traits)){
      unset($this->_traits[$method]); // avoid infinite loop.
      // This while loop will loop trough newly added values added by 'loaded' 
      call_user_func([$this, $method]);
    }
  }
}

trait settings{
  function settings(){
    if($this->loaded(['cache', 'database'])){
      # This method will queue the 'settings' method for later regardless if the traits are already loaded.
      # So it will first load the traits that don't have dependency's.
      # The order is not important because the __construct method is called last anyways.
    }
  }
}

如果有人有任何其他建议,只需添加答案。