PHP
Sluiting
Zoeken…
Basisgebruik van een sluiting
Een sluiting is het PHP-equivalent van een anonieme functie, bijvoorbeeld. een functie die geen naam heeft. Zelfs als dat technisch niet correct is, blijft het gedrag van een sluiting hetzelfde als dat van een functie, met een paar extra functies.
Een sluiting is niets anders dan een object van de klasse Sluiting dat wordt gemaakt door een functie zonder naam aan te geven. Bijvoorbeeld:
<?php
$myClosure = function() {
echo 'Hello world!';
};
$myClosure(); // Shows "Hello world!"
Houd er rekening mee dat $myClosure
een exemplaar van Closure
zodat u weet wat u er echt mee kunt doen (zie http://fr2.php.net/manual/en/class.closure.php )
Het klassieke geval dat u een sluiting nodig zou hebben, is wanneer u een callable
functie moet geven, bijvoorbeeld usort .
Hier is een voorbeeld waarbij een array wordt gesorteerd op het aantal broers en zussen van elke persoon:
<?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
Externe variabelen gebruiken
Het is mogelijk in een sluiting, een externe variabele gebruiken met de speciaal sleutelwoord gebruik. Bijvoorbeeld:
<?php
$quantity = 1;
$calculator = function($number) use($quantity) {
return $number + $quantity;
};
var_dump($calculator(2)); // Shows "3"
U kunt verder gaan door "dynamische" sluitingen te maken. Het is mogelijk om een functie te maken die een specifieke rekenmachine retourneert, afhankelijk van de hoeveelheid die u wilt toevoegen. Bijvoorbeeld:
<?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"
Basic sluiting bindend
Zoals eerder gezien, is een sluiting niets anders dan een instantie van de klasse Closure en kunnen verschillende methoden worden gebruikt. Een ervan is bindTo
, dat bij een afsluiting een nieuwe retourneert die aan een bepaald object is gebonden. Bijvoorbeeld:
<?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!"
Sluiting bindend en toepassingsgebied
Laten we dit voorbeeld eens bekijken:
<?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!"
Probeer de zichtbaarheid van de property
te wijzigen in protected
of private
. U krijgt een fatale fout die aangeeft dat u geen toegang hebt tot deze eigenschap. Zelfs als de sluiting aan het object is gebonden, is de reikwijdte waarin de sluiting wordt ingeroepen niet degene die die toegang nodig had. Daar is het tweede argument van bindTo
voor.
De enige manier om toegang te krijgen tot een eigenschap als deze private
is, is dat deze wordt geopend vanuit een scope die het toestaat, dat wil zeggen. het bereik van de klas. In het vorige codevoorbeeld is de scope niet gespecificeerd, wat betekent dat de afsluiting is ingeroepen in dezelfde scope als die is gebruikt waar de afsluiting is gemaakt. Laten we dat veranderen:
<?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!"
Zoals gezegd, als deze tweede parameter niet wordt gebruikt, wordt de sluiting aangeroepen in dezelfde context als die waarin de sluiting is gemaakt. Een sluiting die bijvoorbeeld in de klasse van een methode is gemaakt en die in een objectcontext wordt aangeroepen, heeft hetzelfde bereik als de 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!"
Een sluiting bindend voor één oproep
Sinds PHP7 is het mogelijk om een sluiting te binden voor slechts één oproep, dankzij de call
. Bijvoorbeeld:
<?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!"
In tegenstelling tot de bindTo
methode is er geen reden om u zorgen te maken. Het bereik dat voor deze aanroep wordt gebruikt, is hetzelfde als het bereik dat wordt gebruikt bij het openen of oproepen van een eigenschap van $myInstance
.
Gebruik sluitingen om een waarnemerspatroon te implementeren
Over het algemeen is een waarnemer een klasse met een specifieke methode die wordt aangeroepen wanneer een actie op het waargenomen object plaatsvindt. In bepaalde situaties kunnen sluitingen voldoende zijn om het ontwerppatroon van de waarnemer te implementeren.
Hier is een gedetailleerd voorbeeld van een dergelijke implementatie. Laten we eerst een klasse verklaren die tot doel heeft waarnemers op de hoogte te stellen wanneer de eigenschap wordt gewijzigd.
<?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();
}
}
Laten we vervolgens de klasse verklaren die de verschillende waarnemers zal vertegenwoordigen.
<?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);
}
}
Laten we dit eindelijk 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!
Merk op dat dit voorbeeld werkt omdat de waarnemers dezelfde aard hebben (ze zijn allebei "benoemde waarnemers").