Code前端首页关于Code前端联系我们

我如何理解何时在匿名函数中使用 PHP 闭包?

terry 2年前 (2023-09-25) 阅读数 48 #后端开发
什么是闭包?

1)。 PHP5.3 中引入了闭包和匿名函数。
2)。闭包是指一个函数在创建时封装了该函数周围的状态。即使闭包所在的环境不再存在,闭包封装的状态仍然存在。这与Javascript的闭包特性非常相似。
3)。匿名函数是没有名称的函数。匿名函数可以分配给变量并像任何其他 PHP 对象一样传递。匿名函数和闭包可以被认为是同一个概念。
4)。需要注意的是,闭包使用的语法与普通函数相同,但它实际上是一个伪装成函数的对象,也是 Closure 类的实例。闭包是一流的值类型,就像字符串或整数一样。

创建闭包
$closure = function ($name) {
  return sprintf('hello %s', $name);
};

echo $closure('Josh');复制代码

我们之所以可以调用$closure变量是因为这个变量的值是一个闭包,而闭包对象只要实现了magic方法就实现了__invoke()。接下来是 (),PHP 会查找 __invoke() 方法。这里简单解释一下这个魔术方法:

class testClass
{
    public function __invoke
    {
        print "hello world";
    }
}
$n = new testClass;
$n();复制代码

PHP 从 5.3 版本开始添加了一个新的魔术方法,称为 __invoke()。使用此方法,您可以在创建实例后立即调用该对象。

什么时候使用?

我们通常使用PHP闭包作为函数和方法的回调。许多 PHP 函数都使用回调函数,例如 array_map()preg_replace_callback()

$numbersPlusOne = array_map(function($number) {
    return $number + 1;
}, [1, 2, 3]);复制代码
如何理解附加状态?

1)。请注意,PHP 闭包不会像 JS 那样自动封装应用程序状态。在 PHP 中,必须调用闭包对象的 bindTo 方法或使用 use 关键字将状态附加到 PHP 闭包。让我们看一个示例

function enclosePerson($name)
{
    return function ($doCommand) use ($name) {
        return sprintf('%s , %s', $name, $doCommand);
   };
}
//把字符串“Clay”封装在闭包中
$clay = enclosePerson('Clay');
//传入参数,调用闭包
echo $clay('get me sweat tea!'); // Clay, get me sweat tea!复制代码

在这个示例中,函数 enclosePerson() 有一个 $name 参数。该函数返回一个闭包对象。这个闭包封装了 $name参数,即使返回的对象跳出enclosePerson()函数的范围,它也会记住Name $参数的值,因为 $name 变量仍在闭包中。
2)。使用 use 关键字将多个关键字传递给闭包。这时,就像PHP函数或方法的参数一样,使用逗号来分隔不同的参数。
3).PHP闭包仍然是对象,你可以使用$this关键字来获取闭包的内部状态。闭包的默认状态有一个 __invoke() 魔术方法和 bindTo() 方法。
4).bindTo()方法为闭包添加了一些有趣的东西。我们可以使用这个方法将 Closure 对象的内部状态绑定到其他对象。 bindTo()方法的第二个参数可以指定绑定闭包的对象所属的PHP类,因此我们可以访问该类的protected和private成员变量。请看下面的代码示例:

class App
{
    protected $route = array();
    protected $responseStatus = '200 OK';
    protected $responseContentType = 'text/html';
    protected $responseBody = 'Hello world';

    public function addRoute($routePath, $routeCallback)
    {
        $this->routes[$routePath] = $routeCallback->bindTo($this, __CLASS__);
    }

    public function dispatch($currentPath)
    {
        foreach($this->routes as $routePath => $callback) {
            if ($routePath === $currentPath) {
                 $callback();
            }
        }
        header('HTTP/1.1' . $this->responseStatus);
        header('Content-type: ' . $this->responseContentType);
        header('Content-length: ' . mb_strlen($this->responseBody));
        echo $this->responseBody;
    }
}复制代码

我们将路由回调绑定到当前的app实例上,这样就可以在回调函数中处理app实例的状态。

$app = new App();
$app->addRoute('/users/xiaoxiao', function () {
    $this->responseContentType = 'application/json;charset=utf8';
    $this->responseBody = '{"name" : "xiaoxiao"}';
});
$app->dispatch('/users/xiaoxiao');

作者:潇潇
链接:https://juejin.im/post/598c25f4518825486b16e363
来源:掘金❀ 商业转载请联系求作者授权。非商业转载请来源。

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门