我正在尝试使用一些DI技术来删除我的应用程序中的服务定位器/注册表的使用。这个应用程序是一个学习练习,而不是生产就绪。
我目前正在使用Pimple来保存对类对象的新实例的引用。家长班'正确填充属性。
这里有一些代码:
abstract class Parent
{
protected $dataService:
protected $registry;
public function __construct()
{
$constructorArgs = func_get_args();
foreach ($constructorArgs as $arg){
if ($arg instanceof Container){
$this->registry = $arg;
}
}
$this->dataService = $this->registry['DataService'];
}
}
class Child extends Parent
{
protected $dependency;
public function __construct()
{
$constructorArgs = func_get_args();
parent::__construct($constructorArgs);
foreach ($constructorArgs as $i => $arg){
if ($arg instanceof Dependency){
$this->dependency = $arg;
}
}
}
}
现在,在Child
内,如果我尝试通过getter(来自Parent)访问$this->dataService
,我将返回null。
我已经检查过,看起来它是在父级内设置$dataService
,但是Child
无法看到它?
答案 0 :(得分:1)
首先,func_get_args()
返回传递给给定函数或方法的数组。这意味着在您的Child类中实例化$child = new Child(12, 'x');
时,您将在array(12, 'x')
变量中获得$constructorArgs
。它没什么特别的用途和合法的用法。但是你用这个变量调用父构造函数并再次用func_get_args()
读取所有参数,这意味着你有效地获得了一个元素的数组 - 你在子构造函数中得到的数组。因此在父构造函数$constructorArgs
中等于array(array(12, 'x'))
。你在脑海中仍在循环第一个数组,但实际上它包含一个元素,这些元素是请求参数的数组。所以你有两个解决方案:
$ConstructorArgs[0]
,因为通过此类实施,它会包含所请求的值,parent::__construct($constructorArgs)
,您可以致电call_user_func_array(array($this, 'parent::__construct'), $constructorArgs)
,这会像自然呼叫那样扩展您的论点而您不必更改父构造函数因为所有的论点都会像Child's那样传递给它。作为旁注,如果您正在尝试学习依赖注入,那么将参数显式传递给具有适当类型提示的构造函数会更好,因此您和PHP解释器都会在您眼前拥有所有信息,不需要遍历一些可以包含任何内容的奇怪数组。
答案 1 :(得分:1)
问题是您没有正确转发构造函数调用。你这样做:
$constructorArgs = func_get_args();
parent::__construct($constructorArgs);
func_get_args
将参数作为数组获取。这意味着parent::__construct
将始终只接收一个参数:一个数组,其中包含调用子项时使用的所有参数。然后,您在func_get_args()
中再次拨打parent::__construct
,这意味着您的结构如下:
array(
array(
Container,
otherData
)
)
当您遍历第一个数组时,代码会检查每个项是否是Container
的实例。它们都不是,因为只有一个参数传递给Parent::__construct
并且它始终是一个数组。
您希望转发调用,以便将参数作为参数传递,而不是作为数组传递。您可以使用call_user_func_array
。
call_user_func_array(array($this, 'parent::__construct'), $constructorArgs);