Home  >  Article  >  Backend Development  >  Analyze the functions of PHP closures and Clourse class methods

Analyze the functions of PHP closures and Clourse class methods

藏色散人
藏色散人forward
2022-02-05 04:00:324704browse

PHP Clourse (closure class) Brief analysis

0x00 Preface

A closure refers to a function that encapsulates the surrounding state when it is created. Even if the environment in which the closure is located no longer exists, the state encapsulated in the closure still exists.

All closures in PHP are objects instantiated by the Clourse class, which means closures are no different from other PHP objects. An object must have its methods and properties. This article will summarize the basic usage of closures in PHP and the role of the Clourse class method. [Recommended: PHP video tutorial]

0x01 Basic usage of closures

Let’s take a look at the most basic usage of closures:

<?php
$hello = function ($word) {
    return &#39;hello &#39; . $word;
};
echo $hello(&#39;world&#39;);
// 输出 hello world

Hey, the most intuitive feeling of this code is to assign a function to the $hello variable and then call it directly through $hello. But this closure does not inherit variables from the parent scope (that is, it encapsulates the surrounding state). We can inherit variables from the closure's parent scope through the use keyword. Examples are as follows:

<?php
$name = &#39;panda&#39;;
$hello = function () use ($name) {
    return &#39;hello &#39; . $name;
};
echo $hello();
// 输出 hello panda

Starting from PHP 7.1, use cannot pass in such variables: superglobals, $this or have the same name as a parameter.

In addition, when using the use keyword, the variables of the parent scope are passed into the closure by value. That is to say, once the closure is created, even if the external variables are modified, it will not affect the value passed into the closure (that is, even if the environment where the closure is located no longer exists, the state encapsulated in the closure still exists). An example is as follows:

<?php
$name = &#39;panda&#39;;
$hello = function () use ($name) {
    return &#39;hello &#39; . $name;
};
$name = &#39;cat&#39;;
echo $hello();
// 输出 hello panda

Passing a reference to a variable allows the closure to modify the value of an external variable. An example is as follows:

<?php
$name = &#39;panda&#39;;
$changeName = function () use (&$name) {
    $name = &#39;cat&#39;;
};
$changeName();
echo $name;
// 输出 cat

Note: When passing an object in PHP, the default is to pass it by reference, so in the closure Special attention needs to be paid when operating objects passed by use within the package. The example is as follows:

<?php
class Dog {
    public $name = &#39;Wang Cai&#39;;
}
$dog = new Dog();
$changeName = function () use ($dog) {
    $dog->name = &#39;Lai Fu&#39;;
};
$changeName();
echo $dog->name;
// 输出 Lai Fu

0x02 Closure class

Prove that the closure is just the Closure class object

<?php
$clourse = function () {
    echo &#39;hello clourse&#39;;
};
if (is_object($clourse)) {
    echo get_class($clourse);
}
// 输出 Closure

The above code will output Closure, proving that the closure is just An ordinary Closure class object.

Clourse class summary

We can see the relevant information of the closure class from the PHP official manual. The following is a summary of the Clourse class that I viewed in the local documentation of PhpStorm.

/**
 * Class used to represent anonymous functions.
 * <p>Anonymous functions, implemented in PHP 5.3, yield objects of this type.
 * This fact used to be considered an implementation detail, but it can now be relied upon.
 * Starting with PHP 5.4, this class has methods that allow further control of the anonymous function after it has been created.
 * <p>Besides the methods listed here, this class also has an __invoke method.
 * This is for consistency with other classes that implement calling magic, as this method is not used for calling the function.
 * @link http://www.php.net/manual/en/class.closure.php
 */
final class Closure {
    /**
     * This method exists only to disallow instantiation of the Closure class.
     * Objects of this class are created in the fashion described on the anonymous functions page.
     * @link http://www.php.net/manual/en/closure.construct.php
     */
    private function __construct() { }
    /**
     * This is for consistency with other classes that implement calling magic,
     * as this method is not used for calling the function.
     * @param mixed $_ [optional]
     * @return mixed
     * @link http://www.php.net/manual/en/class.closure.php
     */
    public function __invoke(...$_) { }
    /**
     * Duplicates the closure with a new bound object and class scope
     * @link http://www.php.net/manual/en/closure.bindto.php
     * @param object $newthis The object to which the given anonymous function should be bound, or NULL for the closure to be unbound.
     * @param mixed $newscope The class scope to which associate the closure is to be associated, or &#39;static&#39; to keep the current one.
     * If an object is given, the type of the object will be used instead.
     * This determines the visibility of protected and private methods of the bound object.
     * @return Closure Returns the newly created Closure object or FALSE on failure
     */
    function bindTo($newthis, $newscope = &#39;static&#39;) { }
    /**
     * This method is a static version of Closure::bindTo().
     * See the documentation of that method for more information.
     * @static
     * @link http://www.php.net/manual/en/closure.bind.php
     * @param Closure $closure The anonymous functions to bind.
     * @param object $newthis The object to which the given anonymous function should be bound, or NULL for the closure to be unbound.
     * @param mixed $newscope The class scope to which associate the closure is to be associated, or &#39;static&#39; to keep the current one.
     * If an object is given, the type of the object will be used instead.
     * This determines the visibility of protected and private methods of the bound object.
     * @return Closure Returns the newly created Closure object or FALSE on failure
     */
    static function bind(Closure $closure, $newthis, $newscope = &#39;static&#39;) { }
    /**
     * Temporarily binds the closure to newthis, and calls it with any given parameters.
     * @link http://php.net/manual/en/closure.call.php
     * @param object $newThis The object to bind the closure to for the duration of the call.
     * @param mixed $parameters [optional] Zero or more parameters, which will be given as parameters to the closure.
     * @return mixed
     * @since 7.0
     */
    function call ($newThis, ...$parameters) {}
    
    /**
     * @param callable $callable
     * @return Closure
     * @since 7.1
     */
    public static function fromCallable (callable $callable) {}
}

First of all, the Clourse class is a final class, which means that it cannot be inherited. Secondly, its constructor __construct is set to private, which means that the closure object cannot be instantiated through the new keyword. These two guarantees Closures can only be instantiated through the syntax function (...) use(...) {...}.

Why can closures be executed as functions?

From the above class summary, we can see that the Clourse class implements the __invoke method. This method is explained in the PHP official manual as follows:

When trying to call a function When an object is called, the __invoke() method is automatically called.

This is why closures can be executed as functions.

Bind the specified $this object and class scope

In frameworks that allow the use of closure routing (such as: Slim), we can see the following writing:

$app->get(&#39;/test&#39;, function () {
    echo $this->request->getMethod();
});

Can I use $this in a closure? Which object does this $this point to?

The function of binding $this and class scope can be achieved through the bindTo and bind methods. The example is as follows:

<?php
class Pandas {
    public $num = 1;
}
$pandas = new Pandas();
$add = function () {
    echo ++$this->num . PHP_EOL;
};
$newAdd1 = $add->bindTo($pandas);
$newAdd1();
// 输出 2
$newAdd2 = Closure::bind($add, $pandas);
$newAdd2();
// 输出 3

The above example binds the specified object as a closure $ this, but we didn't specify class scope. So if you rewrite the $num property of the Pandas class to protected or private, a fatal error will be thrown!

Fatal error: Uncaught Error: Cannot access protected property Pandas::$num

When we need to access non-public properties or methods of the bound object, we need to specify the class scope. The example is as follows:

<?php
class Pandas {
    protected $num = 1;
}
$pandas = new Pandas();
$add = function () {
    echo ++$this->num . PHP_EOL;
};
$newAdd1 = $add->bindTo($pandas, $pandas);
$newAdd1();
// 输出 2
$newAdd2 = Closure::bind($add, $pandas, &#39;Pandas&#39;);
$newAdd2();
// 输出 3

Here we see that both the bindTo and bind methods specify $newscope. Parameters, the $newscope parameter defaults to static, which means it does not change the class scope. The $newscope parameter accepts a class name or object and changes the class scope of the closure to the specified class scope. At this time, the $num property of the Pandas class can be accessed by the closure.

Bind $this object and class scope once and execute (PHP7)

The bindTo and bind methods are executed each time a new object and class scope are specified. To copy the original closure and then return the new closure, it becomes cumbersome when the bound object needs to be modified multiple times, so PHP7 provides a new method call, which can temporarily bind the closure to an object. (the class scope is also modified to the class to which the object belongs) and executed. An example is as follows:

<?php
class Pandas {
    protected $num = 1;
}
$pandas = new Pandas();
$add = function ($num) {
    $this->num += $num;
    echo $this->num . PHP_EOL;
};
$add->call($pandas, 5);
// 输出 6

Callable to Closure (PHP7.1)

In PHP7.1, the Closure class has a fromCallable method that can convert callable type values ​​into Closure, the example is as follows:

<?php
class Foo
{
    protected $num = 1;
    public static function hello(string $bar)
    {
        echo &#39;hello &#39; . $bar;
    }
}
$hello = Closure::fromCallable([&#39;Foo&#39;, &#39;hello&#39;]);
$hello(&#39;world&#39;);

This way of writing is quite cool. After all, calling through closure is much more fun than calling with call_user_func function^_^.

0x03 Summary

For more related information, please see the Closure class and anonymous functions. Because the Chinese version of the Closure class in the PHP official manual has not been updated, there are no call and fromCallable methods. For the content, I recommend everyone to read the English version (ㄒoㄒ).

The above is the detailed content of Analyze the functions of PHP closures and Clourse class methods. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:juejin.im. If there is any infringement, please contact admin@php.cn delete