【问题标题】:Trait dependency loading in sequence按顺序加载特征依赖项
【发布时间】:2016-03-22 20:33:18
【问题描述】:

我目前正在开发一个 trait 自动初始化器,它的一切工作都很好而且很漂亮,但遗憾的是,trait 有一些严重的限制。

我遇到的一个问题如下:

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 的核心运行,我宁愿按特征排序。

trait 的问题是我不能在课堂上定义相同的东西两次,所以我想将范围污染保持在最低限度。

【问题讨论】:

  • 有趣。我们一直在遇到特性(和/或加载它们的客户端类)的依赖问题,并决定避免使用它们,而是使用我们可以依赖注入的适当类。然而,在某些情况下,这与其说是一个好的解决方案不如说是一个坏事。只是想知道,您的目标是哪个 PHP 版本?我问是因为 5.4 和 5.6 之间有一些变化,也可能在 5 和 7 之间。
  • 我目前正在专门为 PHP 7 进行重写,但是用于实现这一奇迹的代码与 5.4 非常兼容。

标签: php sorting oop traits


【解决方案1】:

我找到了一个可行的解决方案,无需过多的冗余代码。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-22
    相关资源
    最近更新 更多