method - php function in variable




In PHP, cos'è una chiusura e perché usa l'identificativo "usa"? (4)

La function () use () {} è la chiusura per PHP, è necessario utilizzare l' use per includere la variabile della function genitore.

<?php
$message = "hello\n";


$example = function () {
    echo $message;
};
// Notice: Undefined variable: message
$example();


$example = function () use ($message) {
    echo $message;
};
// "hello"
$example();


// Inherited variable's value is from when the function is defined, not when called
$message = "world\n";
// "hello"
$example();


// Inherit by-reference
$message = "hello\n";
$example = function () use (&$message) {
    echo $message;
};
// "hello"
$example();
// The changed value in the parent scope is reflected inside the function call
$message = "world\n";
// "world"
$example();


// Closures can also accept regular arguments
$example = function ($arg) use ($message) {
    echo $arg . ' ' . $message;
};
// "hello world"
$example("hello");

Sto verificando alcune funzionalità di PHP 5.3.0 e ho trovato del codice sul sito che sembra abbastanza divertente:

public function getTotal($tax)
{
    $total = 0.00;

    $callback =
        /* This line here: */
        function ($quantity, $product) use ($tax, &$total)
        {
            $pricePerItem = constant(__CLASS__ . "::PRICE_" .
                strtoupper($product));
            $total += ($pricePerItem * $quantity) * ($tax + 1.0);
        };

    array_walk($this->products, $callback);
    return round($total, 2);
}

come uno degli esempi sulle funzioni anonime .

Qualcuno sa di questo? Qualche documentazione? E sembra malvagio, dovrebbe mai essere usato?


Questo è il modo in cui PHP esprime una closure . Questo non è affatto male e infatti è abbastanza potente e utile.

Fondamentalmente ciò significa che stai permettendo alla funzione anonima di "catturare" le variabili locali (in questo caso, $tax e un riferimento a $total ) al di fuori del suo ambito e preservare i loro valori (o nel caso di $total il riferimento a $total ) come stato all'interno della funzione anonima stessa.


Zupa ha fatto un ottimo lavoro spiegando chiusure con "uso" e la differenza tra EarlyBinding e Referencing le variabili che sono "usate".

Così ho creato un esempio di codice con l'associazione anticipata di una variabile (= copia):

<?php

$a = 1;
$b = 2;

$closureExampleEarlyBinding = function() use ($a, $b){
    $a++;
    $b++;
    echo "Inside \$closureExampleEarlyBinding() \$a = ".$a."<br />";
    echo "Inside \$closureExampleEarlyBinding() \$b = ".$b."<br />";    
};

echo "Before executing \$closureExampleEarlyBinding() \$a = ".$a."<br />";
echo "Before executing \$closureExampleEarlyBinding() \$b = ".$b."<br />";  

$closureExampleEarlyBinding();

echo "After executing \$closureExampleEarlyBinding() \$a = ".$a."<br />";
echo "After executing \$closureExampleEarlyBinding() \$b = ".$b."<br />";

/* this will output:
Before executing $closureExampleEarlyBinding() $a = 1
Before executing $closureExampleEarlyBinding() $b = 2
Inside $closureExampleEarlyBinding() $a = 2
Inside $closureExampleEarlyBinding() $b = 3
After executing $closureExampleEarlyBinding() $a = 1
After executing $closureExampleEarlyBinding() $b = 2
*/

?>

Esempio con riferimento a una variabile (notare il carattere '&' prima della variabile);

<?php

$a = 1;
$b = 2;

$closureExampleReferencing = function() use (&$a, &$b){
    $a++;
    $b++;
    echo "Inside \$closureExampleReferencing() \$a = ".$a."<br />";
    echo "Inside \$closureExampleReferencing() \$b = ".$b."<br />"; 
};

echo "Before executing \$closureExampleReferencing() \$a = ".$a."<br />";
echo "Before executing \$closureExampleReferencing() \$b = ".$b."<br />";   

$closureExampleReferencing();

echo "After executing \$closureExampleReferencing() \$a = ".$a."<br />";
echo "After executing \$closureExampleReferencing() \$b = ".$b."<br />";    

/* this will output:
Before executing $closureExampleReferencing() $a = 1
Before executing $closureExampleReferencing() $b = 2
Inside $closureExampleReferencing() $a = 2
Inside $closureExampleReferencing() $b = 3
After executing $closureExampleReferencing() $a = 2
After executing $closureExampleReferencing() $b = 3
*/

?>

le chiusure sono belle! risolvono un sacco di problemi che derivano da funzioni anonime e rendono possibile un codice veramente elegante (almeno finché parliamo di php).

i programmatori javascript usano sempre le chiusure, a volte anche senza saperlo, perché le variabili associate non sono definite in modo esplicito - questo è il significato di "use" in php.

ci sono esempi migliori del mondo reale di quelli precedenti. diciamo che devi ordinare una matrice multidimensionale per un valore secondario, ma la chiave cambia.

<?php
    function generateComparisonFunctionForKey($key) {
        return function ($left, $right) use ($key) {
            if ($left[$key] == $right[$key])
                return 0;
            else
                return ($left[$key] < $right[$key]) ? -1 : 1;
        };
    }

    $myArray = array(
        array('name' => 'Alex', 'age' => 70),
        array('name' => 'Enrico', 'age' => 25)
    );

    $sortByName = generateComparisonFunctionForKey('name');
    $sortByAge  = generateComparisonFunctionForKey('age');

    usort($myArray, $sortByName);

    usort($myArray, $sortByAge);
?>

avviso: codice non testato (non ho php5.3 installato atm), ma dovrebbe assomigliare a qualcosa del genere.

c'è uno svantaggio: molti sviluppatori di PHP potrebbero essere un po 'impotenti se li affrontate con chiusure.

per capire meglio la simpatia delle chiusure, ti darò un altro esempio - questa volta in javascript. uno dei problemi è l'ambito e l'asincronia intrinseca del browser. in particolare, se si tratta di window.setTimeout(); (o -intervallo). quindi, si passa una funzione a setTimeout, ma non si può davvero dare alcun parametro, perché fornire parametri esegue il codice!

function getFunctionTextInASecond(value) {
    return function () {
        document.getElementsByName('body')[0].innerHTML = value; // "value" is the bound variable!
    }
}

var textToDisplay = prompt('text to show in a second', 'foo bar');

// this returns a function that sets the bodys innerHTML to the prompted value
var myFunction = getFunctionTextInASecond(textToDisplay);

window.setTimeout(myFunction, 1000);

myFunction restituisce una funzione con un tipo di parametro predefinito!

ad essere onesti, mi piace molto di più dal php 5.3 e funzioni / chiusure anonime. gli spazi dei nomi potrebbero essere più importanti, ma sono molto meno sexy .





closures