PHP
Schließung
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").