Maison >développement back-end >tutoriel php >Analyser les fonctions des fermetures PHP et des méthodes de classe Clourse

Analyser les fonctions des fermetures PHP et des méthodes de classe Clourse

藏色散人
藏色散人avant
2022-02-05 04:00:324715parcourir

PHP Clourse (classe de fermeture) Une brève analyse

0x00 Préface

Une fermeture fait référence à une fonction qui encapsule l'état environnant lors de sa création. Même si l’environnement dans lequel se situe la fermeture n’existe plus, l’état encapsulé dans la fermeture existe toujours.

Toutes les fermetures en PHP sont des objets instanciés par la classe Clourse, ce qui signifie que les fermetures ne sont pas différentes des autres objets PHP. Un objet doit avoir ses méthodes et ses propriétés. Cet article résumera l'utilisation de base des fermetures en PHP et le rôle de la méthode de classe Clourse. [Recommandé : Tutoriel vidéo PHP]

0x01 Utilisation de base des fermetures

Jetons un coup d'œil à l'utilisation la plus élémentaire des fermetures :

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

Hé, la sensation la plus intuitive de ce code est d'attribuer une fonction à $hello variable puis appelez-la directement via $hello. Mais cette fermeture n'hérite pas des variables de la portée parent (c'est-à-dire qu'elle encapsule l'état environnant). Nous pouvons hériter des variables de la portée parent de la fermeture via le mot-clé use. Les exemples sont les suivants :

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

À partir de PHP 7.1, use ne peut pas transmettre de telles variables : superglobales, $this ou avoir le même nom en tant que paramètre.

De plus, lors de l'utilisation du mot-clé use, les variables de la portée parent sont transmises dans la fermeture par valeur. C'est-à-dire qu'une fois la fermeture créée, même si les variables externes sont modifiées, cela n'affectera pas la valeur passée dans la fermeture (c'est-à-dire que même si l'environnement dans lequel se trouve la fermeture n'existe plus, l'état encapsulé dans la fermeture existe toujours). Un exemple est le suivant :

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

Passer une référence à une variable permet à la fermeture de modifier la valeur d'une variable externe. Un exemple est le suivant :

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

Remarque : Lors du passage d'un objet en PHP, la valeur par défaut est de passer. par référence, donc lors de l'utilisation de l'objet passé par utilisation dans la fermeture, une attention particulière est requise. Un exemple est le suivant :

<?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 Classe de fermeture

Prouve que la fermeture est juste un objet de classe Closure

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

Le code ci-dessus affichera Closure prouvant que la fermeture est juste un objet de classe Closure ordinaire.

Résumé de la classe Clourse

Nous pouvons voir les informations pertinentes de la classe de fermeture dans le manuel officiel PHP Ce qui suit est le résumé de la classe Clourse que j'ai consulté dans la documentation locale de 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) {}
}

Tout d'abord, la classe Clourse est une classe finale, ce qui signifie qu'elle ne peut pas être héritée. Deuxièmement, son constructeur __construct est défini sur private, ce qui signifie que l'objet de fermeture ne peut pas être instancié via ces deux points. assurez-vous que la fermeture uniquement Il peut être instancié via la fonction de syntaxe (...) use(...) {...}.

Pourquoi les fermetures peuvent-elles être exécutées en tant que fonctions ?

D'après le résumé de la classe ci-dessus, nous voyons que la classe Clourse implémente la méthode __invoke. La méthode est expliquée comme suit dans le manuel PHP officiel :

Lorsque vous essayez d'appeler un objet en appelant une fonction, la méthode __invoke(). le testament est automatiquement appelé.

C'est pourquoi les fermetures peuvent être exécutées en tant que fonctions.

Liez l'objet $this et la portée de classe spécifiés

Dans les frameworks qui permettent l'utilisation du routage de fermeture (tels que : Slim), nous pouvons voir l'écriture suivante :

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

Peut-on réellement utiliser $ dans une fermeture this ? Vers quel objet $this pointe-t-il ?

La fonction de liaison de $this à la portée de la classe peut être réalisée via les méthodes bindTo et bind L'exemple est le suivant :

<?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

L'exemple ci-dessus lie l'objet spécifié au $this de la fermeture, mais nous ne le faisons pas. spécifiez la classe Scope. Donc, si vous réécrivez la propriété $num de la classe Pandas en protected ou private, une erreur fatale sera générée !

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

Lorsque nous devons accéder aux propriétés ou méthodes non publiques de l'objet lié, nous devons spécifier la portée de la classe. L'exemple est le suivant :

<?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

Ici, nous voyons que les méthodes bindTo et bind spécifient le $newscope. Le paramètre $newscope est par défaut statique, c'est-à-dire que la portée de la classe n'est pas modifiée. Le paramètre $newscope accepte un nom de classe ou un objet et modifie la portée de classe de la fermeture par la portée de classe spécifiée. À ce stade, la propriété $num de la classe Pandas est accessible par la fermeture.

Liez $cet objet et cette portée de classe en même temps et exécutez (PHP7)

Les méthodes bindTo et bind doivent copier la fermeture d'origine et renvoyer la nouvelle fermeture chaque fois qu'un nouvel objet et une nouvelle portée de classe sont spécifiés, cela devient un package. fastidieux lorsque l'objet lié doit être modifié plusieurs fois, PHP7 fournit donc un nouvel appel de méthode, qui peut temporairement lier la fermeture à un objet (la portée de la classe est également modifiée en fonction de l'objet auquel il appartient) et s'exécuter. L'exemple est le suivant :

<?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)

En PHP7.1, la classe Closure a une méthode fromCallable qui peut convertir la valeur du type appelable en fermeture. L'exemple est le suivant :

<?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;);

Cette façon d'écrire C'est plutôt cool. Après tout, appeler via une fermeture est bien plus amusant que d'appeler avec la fonction call_user_func ^_^.

Résumé 0x03

Pour plus de contenu connexe, veuillez consulter la classe Closure et les fonctions anonymes. Étant donné que la version chinoise de la classe Closure dans le manuel officiel PHP n'a pas été mise à jour, il n'y a aucun contenu sur l'appel et les méthodes fromCallable. . Il est recommandé de lire la version anglaise (ㄒoㄒ ).

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer