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").



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow