在忽略参数类型提示的情况下调用一个PHP方法。

6 浏览
0 Comments

在忽略参数类型提示的情况下调用一个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注意:是的,这是奇怪的东西,我期待有关此事的评论,但请将“答案”留给实际问题的答案。 这不是用于生产代码。

0
0 Comments

问题的出现原因是在调用`$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`的实例)。

0
0 Comments

问题出现的原因是:需要调用一个PHP方法,但是需要忽略参数的类型限制。

解决方法是:使用反射来忽略私有/受保护的修饰符,从而直接访问属性。如果这不是一个选项,那么可能需要使用eval函数。代码中创建了一个名为EvilProxy的类,继承自Proxy,并实现了Bar接口。然后使用该类的实例来调用方法。

以上是解决方法的代码示例。不幸的是,这种方法可能需要"sell my soul"(出卖灵魂)给eval函数。另外,如果Bar是一个类,则需要扩展它,并重写其所有方法。

这是一个通过绕过参数类型限制来调用PHP方法的解决方法。

0
0 Comments

问题:如何在调用PHP方法时忽略参数的类型提示?

在上述内容中,提到了一种解决方法——使用ReflectionProperty::setAccessible来将私有属性设置为公共属性。通过使用反射类,可以修改私有属性的值,就像它是一个公共属性一样。虽然实际上并没有将其转换为公共属性,但通过反射类可以修改它。

然而,上述方法只适用于直接设置私有属性的情况,并不能解决调用方法的问题。如果需要调用一个方法,并且不能确定该方法是否是一个setter方法,以及参数的名称是否与属性名称相同,那么上述方法无法解决该问题。

要解决这个问题,一种方法是使用字符串创建临时类,然后使用eval()函数执行该类。这就是模拟框架所做的。通过动态创建临时类并在其中调用方法,可以绕过类型提示,实现对私有属性的修改。

要解决忽略类型提示的问题,可以使用ReflectionProperty::setAccessible方法将私有属性设置为公共属性;对于调用方法的情况,可以使用字符串创建临时类并使用eval()函数执行该类。这些方法可以绕过类型提示,实现对私有属性的修改。

0