Recherche…


Utilisation de base d'une fermeture

Une fermeture est l'équivalent PHP d'une fonction anonyme, par exemple. une fonction qui n'a pas de nom. Même si cela n'est pas techniquement correct, le comportement d'une fermeture reste le même que celui d'une fonction, avec quelques fonctionnalités supplémentaires.

Une fermeture n'est rien d'autre qu'un objet de la classe Closure qui est créé en déclarant une fonction sans nom. Par exemple:

<?php

$myClosure = function() {
    echo 'Hello world!';
};

$myClosure(); // Shows "Hello world!"

Gardez à l'esprit que $myClosure est une instance de Closure qui vous permet de savoir ce que vous pouvez réellement en faire (cf. http://fr2.php.net/manual/en/class.closure.php )

Le cas classique où vous auriez besoin d'une fermeture, c'est lorsque vous devez donner un callable à une fonction, par exemple usort .

Voici un exemple où un tableau est trié en fonction du nombre de frères et sœurs de chaque personne:

<?php

$data = [
    [
        'name' => 'John',
        'nbrOfSiblings' => 2,
    ],
    [
        'name' => 'Stan',
        'nbrOfSiblings' => 1,
    ],
    [
        'name' => 'Tom',
        'nbrOfSiblings' => 3,
    ]
];

usort($data, function($e1, $e2) {
    if ($e1['nbrOfSiblings'] == $e2['nbrOfSiblings']) {
        return 0;
    }
    
    return $e1['nbrOfSiblings'] < $e2['nbrOfSiblings'] ? -1 : 1;
});

var_dump($data); // Will show Stan first, then John and finally Tom

Utilisation de variables externes

Il est possible, dans une fermeture, d'utiliser une variable externe avec le mot-clé spécial utilisé . Par exemple:

<?php

$quantity = 1;

$calculator = function($number) use($quantity) {
    return $number + $quantity;
};

var_dump($calculator(2)); // Shows "3"

Vous pouvez aller plus loin en créant des fermetures "dynamiques". Il est possible de créer une fonction qui renvoie une calculatrice spécifique, en fonction de la quantité à ajouter. Par exemple:

<?php

function createCalculator($quantity) {
    return function($number) use($quantity) {
        return $number + $quantity;
    };
}

$calculator1 = createCalculator(1);
$calculator2 = createCalculator(2);

var_dump($calculator1(2)); // Shows "3"
var_dump($calculator2(2)); // Shows "4"

Fixation de base

Comme vu précédemment, une fermeture n'est rien d'autre qu'une instance de la classe Closure, et différentes méthodes peuvent y être appelées. L'un d'eux est bindTo , qui, à la fermeture, retournera un nouveau lié à un objet donné. Par exemple:

<?php

$myClosure = function() {
    echo $this->property;
};

class MyClass
{
    public $property;

    public function __construct($propertyValue)
    {
        $this->property = $propertyValue;
    }
}

$myInstance = new MyClass('Hello world!');
$myBoundClosure = $myClosure->bindTo($myInstance);

$myBoundClosure(); // Shows "Hello world!"

Fermeture et lunette de fermeture

Considérons cet exemple:

<?php

$myClosure = function() {
    echo $this->property;
};

class MyClass
{
    public $property;

    public function __construct($propertyValue)
    {
        $this->property = $propertyValue;
    }
}

$myInstance = new MyClass('Hello world!');
$myBoundClosure = $myClosure->bindTo($myInstance);

$myBoundClosure(); // Shows "Hello world!"

Essayez de modifier la visibilité de la property pour qu'elle soit protected ou private . Vous obtenez une erreur fatale indiquant que vous n'avez pas accès à cette propriété. En effet, même si la fermeture a été liée à l'objet, la portée dans laquelle la fermeture est appelée n'est pas celle nécessaire pour avoir cet accès. C'est à cela que sert le second argument de bindTo .

La seule façon d'accéder à une propriété si elle est private est qu'elle est accessible à partir d'une étendue qui le permet, c'est-à-dire. la portée de la classe. Dans l'exemple de code précédent, la portée n'a pas été spécifiée, ce qui signifie que la fermeture a été appelée dans la même portée que celle utilisée lorsque la fermeture a été créée. Changeons cela:

<?php

$myClosure = function() {
    echo $this->property;
};

class MyClass
{
    private $property; // $property is now private

    public function __construct($propertyValue)
    {
        $this->property = $propertyValue;
    }
}

$myInstance = new MyClass('Hello world!');
$myBoundClosure = $myClosure->bindTo($myInstance, MyClass::class);

$myBoundClosure(); // Shows "Hello world!"

Comme nous venons de le dire, si ce second paramètre n'est pas utilisé, la fermeture est appelée dans le même contexte que celui utilisé lors de la création de la fermeture. Par exemple, une fermeture créée à l'intérieur d'une classe de méthode appelée dans un contexte d'objet aura la même portée que celle de la méthode:

<?php

class MyClass
{
    private $property;

    public function __construct($propertyValue)
    {
        $this->property = $propertyValue;
    }

    public function getDisplayer()
      {
        return function() {
              echo $this->property;
        };
      }
}

$myInstance = new MyClass('Hello world!');

$displayer = $myInstance->getDisplayer();
$displayer(); // Shows "Hello world!"

Relier une fermeture pour un appel

Depuis PHP7 , il est possible de lier une fermeture pour un seul appel, grâce à la méthode d' call . Par exemple:

<?php

class MyClass
{
    private $property;

    public function __construct($propertyValue)
    {
        $this->property = $propertyValue;
    }
}

$myClosure = function() {
    echo $this->property;
};

$myInstance = new MyClass('Hello world!');

$myClosure->call($myInstance); // Shows "Hello world!"

Contrairement à la méthode bindTo , il n’ya pas de bindTo de s’inquiéter. La portée utilisée pour cet appel est la même que celle utilisée lors de l'accès ou de l'invocation d'une propriété de $myInstance .

Utiliser des fermetures pour implémenter un motif d'observateur

En général, un observateur est une classe avec une méthode spécifique appelée lorsqu'une action sur l'objet observé se produit. Dans certaines situations, les fermetures peuvent être suffisantes pour mettre en œuvre le modèle de conception de l'observateur.

Voici un exemple détaillé d'une telle implémentation. Déclarons d'abord une classe dont le but est d'informer les observateurs lorsque sa propriété est modifiée.

<?php

class ObservedStuff implements SplSubject
{
    protected $property;
    protected $observers = [];

    public function attach(SplObserver $observer)
    {
        $this->observers[] = $observer;
        return $this;
    }

    public function detach(SplObserver $observer)
    {
        if (false !== $key = array_search($observer, $this->observers, true)) {
            unset($this->observers[$key]);
        }
    }

    public function notify()
    {
        foreach ($this->observers as $observer) {
            $observer->update($this);
        }
    }

    public function getProperty()
    {
        return $this->property;
    }

    public function setProperty($property)
    {
        $this->property = $property;
        $this->notify();
    }
}

Ensuite, déclarons la classe qui représentera les différents observateurs.

<?php

class NamedObserver implements SplObserver
{
    protected $name;
    protected $closure;

    public function __construct(Closure $closure, $name)
    {
        $this->closure = $closure->bindTo($this, $this);
        $this->name = $name;
    }

    public function update(SplSubject $subject)
    {
        $closure = $this->closure;
        $closure($subject);
    }
}

Testons enfin ceci:

<?php

$o = new ObservedStuff;

$observer1 = function(SplSubject $subject) {
    echo $this->name, ' has been notified! New property value: ', $subject->getProperty(), "\n";
};

$observer2 = function(SplSubject $subject) {
    echo $this->name, ' has been notified! New property value: ', $subject->getProperty(), "\n";
};

$o->attach(new NamedObserver($observer1, 'Observer1'))
  ->attach(new NamedObserver($observer2, 'Observer2'));

$o->setProperty('Hello world!');
// Shows:
// Observer1 has been notified! New property value: Hello world!
// Observer2 has been notified! New property value: Hello world!

Notez que cet exemple fonctionne car les observateurs partagent la même nature (ils sont tous deux "observateurs nommés").



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow