489 Stimmen

Was ist in PHP ein Closure und warum wird der Bezeichner "use" verwendet?

Ich schaue mir gerade einige PHP 5.3.0 Funktionen und stieß auf der Website auf einen Code, der ziemlich lustig aussieht:

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

als eines der Beispiele auf anonyme Funktionen .

Weiß jemand etwas darüber? Gibt es eine Dokumentation? Und sieht es böse aus, sollte es jemals verwendet werden?

571voto

zupa Punkte 11846

Eine einfachere Antwort.

function ($quantity) use ($tax, &$total) { .. };

  1. Die Schließung ist eine Funktion, die einer Variablen zugewiesen ist, so dass Sie sie weitergeben können
  2. Ein Closure ist ein separater Namensraum, normalerweise kann man nicht auf Variablen zugreifen, die außerhalb dieses Namensraumes definiert sind. Hier kommt die verwenden. Stichwort:
  3. verwenden. ermöglicht Ihnen den Zugriff (die Verwendung) auf die nachfolgenden Variablen innerhalb der Schließung.
  4. verwenden. ist früh verbindlich. Das bedeutet, dass die Variablenwerte beim DEFINIEREN der Schließung kopiert werden. Das Ändern von $tax innerhalb der Schließung hat keine Außenwirkung, es sei denn, es handelt sich um einen Zeiger, wie bei einem Objekt.
  5. Sie können Variablen als Zeiger übergeben, wie im Fall von &$total . Auf diese Weise wird die Änderung des Wertes von $total eine externe Wirkung hat, ändert sich der Wert der ursprünglichen Variablen.
  6. Auf Variablen, die innerhalb der Schließung definiert sind, kann auch von außerhalb der Schließung nicht zugegriffen werden.
  7. Closures und Funktionen haben die gleiche Geschwindigkeit. Ja, Sie können sie überall in Ihren Skripten verwenden.

Wie @Mytskine hervorgehoben Die wahrscheinlich beste und ausführlichste Erklärung ist die RFC für Verschlüsse . (Geben Sie ihm dafür eine höhere Bewertung.)

400voto

Andrew Hare Punkte 332190

So drückt PHP eine Verschluss . Das ist überhaupt nicht böse und in der Tat ist es ziemlich mächtig und nützlich.

Grundsätzlich bedeutet dies, dass Sie der anonymen Funktion erlauben, lokale Variablen zu "fangen" (in diesem Fall, $tax und einen Verweis auf $total ) außerhalb ihres Geltungsbereichs und bewahren ihre Werte (oder im Fall von $total den Verweis auf $total selbst) als Zustand innerhalb der anonymen Funktion selbst.

146voto

Steely Wing Punkte 14161

El function () use () {} ist wie ein Abschluss für PHP.

Ohne use Funktion kann nicht auf die Variable des übergeordneten Bereichs zugreifen

$s = "hello";
$f = function () {
    echo $s;
};

$f(); // Notice: Undefined variable: s

$s = "hello";
$f = function () use ($s) {
    echo $s;
};

$f(); // hello

El use der Wert der Variablen stammt vom Zeitpunkt der Definition der Funktion, nicht vom Zeitpunkt des Aufrufs

$s = "hello";
$f = function () use ($s) {
    echo $s;
};

$s = "how are you?";
$f(); // hello

use Variable durch Verweis mit &

$s = "hello";
$f = function () use (&$s) {
    echo $s;
};

$s = "how are you?";
$f(); // how are you?

57voto

stefs Punkte 17901

Closures sind wunderbar! Sie lösen viele Probleme, die mit anonymen Funktionen einhergehen, und ermöglichen wirklich eleganten Code (zumindest solange wir über PHP sprechen).

Javascript-Programmierer benutzen ständig Closures, manchmal sogar ohne es zu wissen, weil gebundene Variablen nicht explizit definiert sind - dafür gibt es in php "use".

Es gibt bessere Beispiele aus der Praxis als das obige. Nehmen wir an, Sie müssen ein mehrdimensionales Array nach einem Unterwert sortieren, aber der Schlüssel ändert sich.

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

warnung: ungetesteter code (ich habe php5.3 nicht installiert), aber es sollte in etwa so aussehen.

Es gibt einen Nachteil: viele php-Entwickler sind vielleicht etwas hilflos, wenn man sie mit Closures konfrontiert.

um die nettigkeit von closures besser zu verstehen, gebe ich ihnen ein weiteres beispiel - diesmal in javascript. eines der probleme ist das scoping und die dem browser innewohnende asynchronität. besonders, wenn es um window.setTimeout(); (oder -interval). Sie übergeben also eine Funktion an setTimeout, können aber keine Parameter angeben, da die Angabe von Parametern den Code ausführt!

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 gibt eine Funktion mit einer Art vordefiniertem Parameter zurück!

Um ehrlich zu sein, mag ich php seit 5.3 und anonymen Funktionen/Verschlüssen viel mehr. namespaces sind vielleicht wichtiger, aber sie sind viel weniger sexy .

19voto

joronimo Punkte 544

Zupa hat eine großartige Arbeit geleistet, indem er Schließungen mit "use" und den Unterschied zwischen EarlyBinding und Referenzierung der Variablen, die "verwendet" werden, erklärt hat.

Also habe ich ein Codebeispiel mit frühem Binden einer Variablen (= Kopieren) erstellt:

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

?>

Beispiel mit Verweis auf eine Variable (beachten Sie das '&'-Zeichen vor der Variable);

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

?>

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X