Sök…


Grundläggande användning av en stängning

En stängning är PHP-ekvivalenten för en anonym funktion, t.ex. en funktion som inte har ett namn. Även om det tekniskt inte är korrekt, förblir beteendet för en stängning detsamma som en funktion, med några extra funktioner.

En stängning är inget annat än ett objekt i Closure-klassen som skapas genom att deklarera en funktion utan namn. Till exempel:

<?php

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

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

Kom ihåg att $myClosure är ett exempel på Closure så att du är medveten om vad du verkligen kan göra med det (se http://fr2.php.net/manual/en/class.closure.php )

Det klassiska fallet du skulle behöva en stängning är när du måste ge en callable till en funktion, till exempel usort .

Här är ett exempel där en matris sorteras efter antalet syskon till varje person:

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

Använda externa variabler

Det är möjligt, i en stängning, för att använda en extern variabel med den speciella nyckelordet användning. Till exempel:

<?php

$quantity = 1;

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

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

Du kan gå längre genom att skapa "dynamiska" stängningar. Det är möjligt att skapa en funktion som returnerar en specifik kalkylator, beroende på den mängd du vill lägga till. Till exempel:

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

Grundläggande stängningsbindning

Som vi tidigare sett är en stängning inget annat än ett exempel på klassen, och olika metoder kan påkallas på dem. En av dem är bindTo , som med tanke på en stängning kommer att returnera en ny som är bunden till ett givet objekt. Till exempel:

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

Stängningsbindande och omfattning

Låt oss överväga detta exempel:

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

Försök att ändra property synlighet till antingen protected eller private . Du får ett dödligt fel som indikerar att du inte har tillgång till den här egenskapen. Även om stängningen har varit bunden till föremålet, är omfattningen i vilken stängningen åberopas inte den som behövs för att ha den åtkomsten. Det är vad det andra argumentet för bindTo är för.

Det enda sättet att få tillgång till en fastighet om den är private är att den nås från en räckvidd som tillåter den, dvs. klassens omfattning. I det just föregående kodexemplet har omfattningen inte specificerats, vilket innebär att stängningen har åberopats i samma omfattning som den som användes där stängningen har skapats. Låt oss ändra det:

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

Som just sagt, om denna andra parameter inte används, åberopas stängningen i samma sammanhang som den som användes där stängningen har skapats. Till exempel kommer en stängning som skapas i en metods klass som åberopas i ett objektsammanhang att ha samma räckvidd som metodens:

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

Binder en stängning för ett samtal

Sedan PHP7 är det möjligt att binda en stängning bara för ett samtal tack vare call . Till exempel:

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

I motsats till bindTo metoden finns det inget utrymme att oroa sig för. Räckvidden som används för det här samtalet är detsamma som det som används vid åtkomst till eller åkallande av en egendom på $myInstance .

Använd stängningar för att implementera observatörsmönster

I allmänhet är en iakttagare en klass med en specifik metod som kallas när en handling på det observerade objektet inträffar. I vissa situationer kan stängningar vara tillräckliga för att implementera observatörens designmönster.

Här är ett detaljerat exempel på en sådan implementering. Låt oss först förklara en klass vars syfte är att meddela observatörer när dess egendom ändras.

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

Låt oss sedan förklara klassen som kommer att representera de olika observatörerna.

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

Låt oss äntligen testa detta:

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

Observera att detta exempel fungerar eftersom observatörerna delar samma karaktär (de är båda "namngivna observatörer.")



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow