可调用和闭包


Callable and closures

我创建了不太复杂的测试代码(在PHP 5.5.12中测试):

<?php
class Test
{
    private $cached = null;
    public function __construct()
    {
        $this->cached = [];
        $this->cached[0] = 12;
    }
    function wrap($function, $index)
    {
        if (isset($this->cached[$index])) {
            return $this->cached[$index];
        }
        $result = call_user_func($function);
        return $result;
    }
}
class B
{
    public function run()
    {
        $x = 6;
        $obj = new Test();
        $value = $obj->wrap(
            function () use ($x) {
                return $this->test($x);
            },
            1
        );
        echo $value."<br />";
    }

    protected function test($x)
    {
        echo "I'm running ";
        return $x * $x;
    }
}
class C extends B
{
    public function run()
    {
        $x = 6;
        $obj = new Test();
        $myFunc = function () use ($x) {
        return $this->test($x);
        };
        $value = $obj->wrap($myFunc, 1);
        echo $value."<br />";
    }
}
class D extends B
{
    public function run()
    {
        $x = 6;
        $obj = new Test();
        $value = $obj->wrap(array($this, 'test'), 1);
        echo $value."<br />";
    }
}

$b = new B();
$b->run();
$c = new C();
$c->run();
$d = new D();
$d->run();

可能有些部分的代码你可以说可以做得更好,但重点是闭包函数和可调用的。这些类以一种非常简单的方式模拟缓存系统。如果数据在缓存中,则从缓存中返回数据,否则调用获取数据的函数(当然这个缓存系统不工作,因为它不需要工作-这只是一个示例代码)。

问题:

1)为什么当使用对象$d时,我得到以下警告:

call_user_func() expects parameter 1 to be a valid callback, cannot access protected method D::test() 

是否可以从父类启动protected方法?当我将这个方法从protected更改为public时,它可以毫无问题地启动

2)正如你可能注意到的,我想使用一些参数的函数,我使用call_user_sync调用。不幸的是,当我调用call_user_func时,我不知道这些参数,所以在B类和C类中,我使用闭包,在那里我可以使用/传递额外的参数。我还有两个问题:

  • 这是闭包有用和常用的方式吗?

  • 是否可以使用对象$d传递参数到测试方法,而不使用闭包,但不是在调用call_user_sync时,而是在类D内?

在执行时注意范围是很重要的。您正在在正确的范围内创建回调,但是您正在在另一个无法访问protected方法的对象中执行回调(回调在class Test中执行,而不是在class B的父或子对象中执行)。

我在编写自己的调度程序类时遇到了这个问题。一种选择是在调度程序上设置"父",并将调度程序作为回调的参数之一传递。然后,回调检查与Dispatcher关联的=== $this的"父",然后知道它具有访问权限并进入城镇。

你必须自己做访问检查,这是关键。