按顺序加载特征依赖项
Trait dependency loading in sequence
我目前正在研究 trait 自动初始化程序,它一切正常,但很遗憾,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();
}
}
可以在构造函数中对数组进行排序,以确保设置在缓存或数据库之后进行索引。然而棘手的部分来了,最好的方法是什么?
由于大多数 classes 只使用少量特征,定义一个冗长的数组作为排序的基础似乎是不够的。由于该系统是 运行 CMS 的核心,因此我宁愿按特征对特征进行排序。
traits 的问题是我不能两次定义相同的东西,也不能在 class 中定义,所以我想将范围污染保持在最低限度。
我找到了一个没有太多冗余代码的可行解决方案。
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.
}
}
}
如果大家有其他建议,请补充回答。
我目前正在研究 trait 自动初始化程序,它一切正常,但很遗憾,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();
}
}
可以在构造函数中对数组进行排序,以确保设置在缓存或数据库之后进行索引。然而棘手的部分来了,最好的方法是什么?
由于大多数 classes 只使用少量特征,定义一个冗长的数组作为排序的基础似乎是不够的。由于该系统是 运行 CMS 的核心,因此我宁愿按特征对特征进行排序。
traits 的问题是我不能两次定义相同的东西,也不能在 class 中定义,所以我想将范围污染保持在最低限度。
我找到了一个没有太多冗余代码的可行解决方案。
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.
}
}
}
如果大家有其他建议,请补充回答。