在忽略参数类型提示的情况下调用一个PHP方法。
在忽略参数类型提示的情况下调用一个PHP方法。
给定:\n
class Foo { private $bar; public function setBar(Bar $bar) { $this->bar = $bar; } }
\n有任何方法可以使用不是Bar
实例的参数调用setBar()
吗?\n
$proxy = new Proxy(); $foo = new Foo(); $foo->setBar($proxy); // with class Proxy { }
\n想法是注入一个代理对象而不是Bar
实例。 Proxy
类是通用的(在依赖注入框架中),不能使其实现/扩展Bar
。\n
\n反射API似乎没有提供使这种可能性成为可能的任何内容。 我希望在不使用PHP扩展(标准PHP安装> 5.3)的情况下实现这一点(如果可能,我尽量避免使用eval)。\n注意:是的,这是奇怪的东西,我期待有关此事的评论,但请将“答案”留给实际问题的答案。 这不是用于生产代码。
问题的出现原因是在调用`$foo->setBar($proxy)`时,由于参数类型不匹配,会触发`Fatal error`。如果可以接受`Strict standards`警告,可以通过重写函数并使用反射来修改父类的私有属性来解决该问题。
解决方法是创建一个`TestFoo`类,继承自`Foo`类,并重写`setBar`方法。在重写的方法中,通过反射来获取父类的私有属性,并修改其值。具体代码如下:
class TestFoo extends Foo { public function setBar(Proxy $bar) { $this->setPrivateParentProperty('bar', $bar); } private function setPrivateParentProperty($name, $value) { $refl = new ReflectionObject($this); $property = $refl->getParentClass()->getProperty($name); $property->setAccessible(true); $property->setValue($this, $value); } }
然后,可以使用`TestFoo`类型来替代`Foo`类型进行调用(前提是没有接口、抽象类或者`final`修饰阻止了这个替代)。具体代码如下:
$foo = new TestFoo(); $foo->setBar($proxy);
然而,正如`Strict standards`警告所显示的,这种方法并不是很好。更详细的信息可以参考为什么PHP允许“不兼容”的构造函数?。
问题是我无法保证我要调用的方法是一个setter方法。它可能是一个执行其他操作的方法,所以我不能只是更新父类的属性,我需要调用它。至于“strict standards”警告,确实不太好,因为这是一个将被重用的库。
我得出的结论是我需要使用一个继承自原始类的代理类来调用该方法,因为即使我找到了解决原始问题的方法,我也会在`instanceof`调用中遇到问题(代理对象将不是`Bar`的实例)。
问题:如何在调用PHP方法时忽略参数的类型提示?
在上述内容中,提到了一种解决方法——使用ReflectionProperty::setAccessible来将私有属性设置为公共属性。通过使用反射类,可以修改私有属性的值,就像它是一个公共属性一样。虽然实际上并没有将其转换为公共属性,但通过反射类可以修改它。
然而,上述方法只适用于直接设置私有属性的情况,并不能解决调用方法的问题。如果需要调用一个方法,并且不能确定该方法是否是一个setter方法,以及参数的名称是否与属性名称相同,那么上述方法无法解决该问题。
要解决这个问题,一种方法是使用字符串创建临时类,然后使用eval()函数执行该类。这就是模拟框架所做的。通过动态创建临时类并在其中调用方法,可以绕过类型提示,实现对私有属性的修改。
要解决忽略类型提示的问题,可以使用ReflectionProperty::setAccessible方法将私有属性设置为公共属性;对于调用方法的情况,可以使用字符串创建临时类并使用eval()函数执行该类。这些方法可以绕过类型提示,实现对私有属性的修改。