【发布时间】:2023-03-13 04:50:01
【问题描述】:
我正在构建一个 API 类来扩展供应商类的功能。供应商类预计会被扩展,并将检查是否存在这样的方法:
if (method_exists($this, 'block'.$CurrentBlock['type']))
{
$CurrentBlock = $this->{'block'.$CurrentBlock['type']}($CurrentBlock);
}
因此,由于我的 API 也是一个供应商文件,我想我会做一些聪明的事情,并尝试让人们将闭包传递给我的 API 并扩展类。
public function extendBlock($blockName, Closure $closure)
{
$methodName = camel_case("block_{$blockName}");
$this->{$methodName} = $closure;
return method_exists($this, $methodName);
}
理论上这会绑定闭包,这样我的第一个代码块中的调用就会成功......但这不会发生。它不被视为一种方法,而是一种包含闭包的属性。不仅method_exist 失败,而且尝试调用该方法也会失败。
这是一个修改后的版本,我试图找出问题所在。
public function extendBlock($blockName, Closure $closure)
{
$methodName = camel_case("block_{$blockName}");
$newClosure = clone $closure;
$newClosure = $newClosure->bindTo($this);
$this->{$methodName} = $newClosure;
$this->{$methodName}();
return method_exists($this, $methodName);
}
这些都不起作用。该属性已明确设置,$closure 中的$this 的范围当前指向该方法的$this。
如果我改为运行它,则闭包会正确执行。
$this->{$methodName} = $newClosure;
//$this->{$methodName}();
$foobar = $this->{$methodName};
$foobar();
是的。我真的希望有一种很好、整洁的方式来满足我的第一个代码块中的检查,而不需要用户继承我的类并直接编写它们,但我认为这是不可能的。
编辑:这与Storing a Closure Function in a Class Property in PHP 略有不同——而提供的__call 解决方案非常优秀,如果您对将闭包绑定到类,此方法不会欺骗method_exists 检查。
【问题讨论】:
-
Storing a Closure Function in a Class Property in PHP 的可能副本。见this answer particularly。我会重新考虑这种方法,继承是 做你想做的事的正确方法。或者,查看traits。
-
@RandomSeed Traits 在编译后无法绑定,虽然您提供的那个问题非常有趣并且是一个很好的解决方案,但我的问题的限制意味着我还必须欺骗
method_exists,它的设置__call不行。 -
你真的需要
method_exists()来返回true吗?在另一个答案中,is_callable()完成了这项工作。 -
是的,不幸的是,确实如此。如果不接触图书馆,我就无法绕过它。不过,我会说你链接到的问题会回答 99% 的人问同样的问题。我想我别无选择,只能与项目所有者打交道。
-
好的,我看到你已经意识到这是一个丑陋的黑客攻击。请帮我打一下项目所有者 :) PS:
clone似乎是多余的,因为Closure::bindTo()已经“复制带有新绑定对象和类范围的闭包”