Ricerca…


Uso di base di una chiusura

Una chiusura è l'equivalente PHP di una funzione anonima, ad es. una funzione che non ha un nome. Anche se tecnicamente non è corretto, il comportamento di una chiusura rimane lo stesso di quello di una funzione, con alcune caratteristiche extra.

Una chiusura non è altro che un oggetto della classe Closure che viene creata dichiarando una funzione senza un nome. Per esempio:

<?php

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

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

Tieni presente che $myClosure è un'istanza di Closure modo che tu sia a conoscenza di ciò che puoi veramente fare con esso (cfr. Http://fr2.php.net/manual/en/class.closure.php )

Il caso classico che avresti bisogno di una chiusura è quando devi dare un callable a una funzione, ad esempio usort .

Ecco un esempio in cui un array è ordinato per il numero di fratelli di ogni persona:

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

Utilizzo di variabili esterne

È possibile, all'interno di una chiusura, utilizzare una variabile esterna con la parola chiave speciale use . Per esempio:

<?php

$quantity = 1;

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

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

Puoi andare oltre creando chiusure "dinamiche". È possibile creare una funzione che restituisce una calcolatrice specifica, a seconda della quantità che si desidera aggiungere. Per esempio:

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

Chiusura di base vincolante

Come visto in precedenza, una chiusura non è altro che un'istanza della classe Closure e su di essi possono essere invocati metodi diversi. Uno di questi è bindTo , che, data una chiusura, restituirà uno nuovo associato a un dato oggetto. Per esempio:

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

Vincolo e scopo di chiusura

Consideriamo questo esempio:

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

Prova a modificare la visibilità della property su protected o private . Si verifica un errore irreversibile che indica che non si ha accesso a questa proprietà. Infatti, anche se la chiusura è stata vincolata all'oggetto, l'ambito in cui viene invocata la chiusura non è quello necessario per avere quell'accesso. Questo è il secondo argomento di bindTo .

L'unico modo per accedere a una proprietà se è private è che è accessibile da un ambito che lo consente, ad es. l'ambito della classe. Nell'esempio di codice precedente, l'ambito non è stato specificato, il che significa che la chiusura è stata invocata nello stesso ambito di quella utilizzata dove è stata creata la chiusura. Cambiamo questo:

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

Come appena detto, se questo secondo parametro non viene utilizzato, la chiusura viene invocata nello stesso contesto di quello utilizzato dove è stata creata la chiusura. Ad esempio, una chiusura creata all'interno della classe di un metodo invocata in un contesto di oggetto avrà lo stesso ambito di quello del metodo:

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

Vincolare una chiusura per una chiamata

Dal momento che PHP7 , è possibile associare una chiusura solo per una chiamata, grazie al metodo di call . Per esempio:

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

Al contrario del metodo bindTo - bindTo , non c'è spazio per preoccuparsi. L'ambito utilizzato per questa chiamata è uguale a quello utilizzato quando si accede o si richiama una proprietà di $myInstance .

Utilizzare chiusure per implementare il modello di osservatore

In generale, un osservatore è una classe con un metodo specifico chiamato quando si verifica un'azione sull'oggetto osservato. In determinate situazioni, le chiusure possono essere sufficienti per implementare il modello di progettazione dell'osservatore.

Ecco un esempio dettagliato di tale implementazione. Prima dichiariamo una classe il cui scopo è di notificare agli osservatori quando la sua proprietà è cambiata.

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

Quindi, dichiariamo la classe che rappresenterà i diversi osservatori.

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

Proviamo finalmente questo:

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

Nota che questo esempio funziona perché gli osservatori condividono la stessa natura (sono entrambi "osservatori nominati".)



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow