Suche…


Grundlegende Verwendung eines Verschlusses

Ein Abschluss ist das PHP-Äquivalent einer anonymen Funktion, z. eine Funktion, die keinen Namen hat. Auch wenn dies technisch nicht korrekt ist, bleibt das Verhalten eines Verschlusses mit einigen Funktionen identisch wie bei einer Funktion.

Ein Abschluss ist nichts anderes als ein Objekt der Abschlussklasse, das durch Deklaration einer Funktion ohne Namen erstellt wird. Zum Beispiel:

<?php

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

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

$myClosure Sie, dass $myClosure eine Instanz von Closure damit Sie wissen, was Sie wirklich damit tun können (vgl. Http://fr2.php.net/manual/en/class.closure.php

Der klassische Fall , dass Sie eine Schließung benötigen würde , ist , wenn Sie ein geben callable auf eine Funktion, zum Beispiel usort .

Hier ist ein Beispiel, in dem ein Array nach der Anzahl der Geschwister jeder Person sortiert wird:

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

Verwendung externer Variablen

Innerhalb eines Abschlusses ist es möglich, eine externe Variable mit dem speziellen Schlüsselwort use zu verwenden . Zum Beispiel:

<?php

$quantity = 1;

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

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

Sie können noch weiter gehen, indem Sie "dynamische" Schließungen erstellen. Es ist möglich, eine Funktion zu erstellen, die einen bestimmten Rechner zurückgibt, abhängig von der Menge, die Sie hinzufügen möchten. Zum Beispiel:

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

Grundverschlußbindung

Wie zuvor gesehen, ist ein Abschluss nichts anderes als eine Instanz der Closure-Klasse, und für sie können verschiedene Methoden aufgerufen werden. Eines davon ist bindTo , das bei einer Schließung eine neue bindTo , die an ein bestimmtes Objekt gebunden ist. Zum Beispiel:

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

Abschlussbindung und Geltungsbereich

Betrachten wir dieses Beispiel:

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

Versuchen Sie, die Sichtbarkeit der property in protected oder private zu ändern. Sie erhalten einen schwerwiegenden Fehler, der darauf hinweist, dass Sie keinen Zugriff auf diese Eigenschaft haben. Selbst wenn die Schließung an das Objekt gebunden wurde, ist der Bereich, in dem die Schließung aufgerufen wird, nicht derjenige, der für diesen Zugriff erforderlich ist. bindTo gibt es das zweite Argument von bindTo .

Die einzige Möglichkeit, auf eine Eigenschaft zuzugreifen, wenn sie private ist, besteht darin, dass auf sie aus einem Bereich zugegriffen wird, der es erlaubt, z. der Umfang der Klasse. Im vorherigen Codebeispiel wurde der Gültigkeitsbereich nicht angegeben. Dies bedeutet, dass die Schließung in demselben Gültigkeitsbereich wie derjenige aufgerufen wurde, in dem die Schließung erstellt wurde. Ändern wir das:

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

Wie gesagt, wenn dieser zweite Parameter nicht verwendet wird, wird der Abschluss in demselben Kontext aufgerufen wie der, in dem der Abschluss erstellt wurde. Beispielsweise hat ein innerhalb einer Klasse einer Methode erstellter Abschluss, der in einem Objektkontext aufgerufen wird, denselben Gültigkeitsbereich wie die Methode der Methode:

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

Eine Schließung für einen Anruf binden

Da PHP7 ist es möglich , einen Verschluss nur für einen Anruf zu binden, dank der call - Methode. Zum Beispiel:

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

Im Gegensatz zur bindTo Methode besteht kein bindTo zur Sorge. Der für diesen Aufruf verwendete Bereich ist derselbe wie beim Zugriff auf eine Eigenschaft von $myInstance oder deren Aufruf.

Verwenden Sie Verschlüsse, um ein Beobachtermuster zu implementieren

Im Allgemeinen ist ein Beobachter eine Klasse, bei der eine bestimmte Methode aufgerufen wird, wenn eine Aktion für das beobachtete Objekt ausgeführt wird. In bestimmten Situationen können Verschlüsse ausreichen, um das Beobachter-Entwurfsmuster zu implementieren.

Hier ist ein detailliertes Beispiel für eine solche Implementierung. Lassen Sie uns zunächst eine Klasse deklarieren, die dazu dient, Beobachter zu benachrichtigen, wenn ihre Eigenschaft geändert wird.

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

Dann erklären wir die Klasse, die die verschiedenen Beobachter repräsentieren wird.

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

Lassen Sie uns das endlich testen:

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

Beachten Sie, dass dieses Beispiel funktioniert, weil die Beobachter dieselbe Natur haben (sie sind beide "benannte Beobachter").



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow