W PHP czym jest zamknięcie i dlaczego używa identyfikatora "use"?

Sprawdzam niektóre funkcje PHP 5.3.0 i natknąłem się na jakiś kod na stronie, który wygląda dość śmiesznie:

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

Jako jeden z przykładów na funkcje anonimowe.

Czy ktoś o tym wie? Jakieś dokumenty? I wygląda źle, czy powinno się go kiedyś używać?
 431
Author: Andrey Belykh, 2009-06-30

6 answers

W ten sposób PHP wyraża zamknięcie . To wcale nie jest złe i w rzeczywistości jest dość potężne i użyteczne.

Zasadniczo oznacza to, że pozwalasz funkcji anonimowej "przechwytywać" zmienne lokalne (w tym przypadku $tax i odniesienie do $total) poza jej zakresem i zachowujesz ich wartości (lub w przypadku $total odniesienie do $total) jako stan wewnątrz samej funkcji anonimowej.

 381
Author: Andrew Hare,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-30 18:24:10

Prostsza odpowiedź.

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

  1. zamknięcie jest funkcją przypisaną do zmiennej, więc można ją przekazać
  2. zamknięcie jest oddzielną przestrzenią nazw, zwykle nie można uzyskać dostępu do zmiennych zdefiniowanych poza tą przestrzenią nazw. Nadchodzi użycie słowo kluczowe:
  3. użycie umożliwia dostęp (użycie) do kolejnych zmiennych wewnątrz zamknięcia.
  4. użycie jest wcześnie wiążąca. Oznacza to, że wartości zmiennych są kopiowane podczas definiowania zamknięcia. Tak więc modyfikowanie $tax wewnątrz zamknięcia nie ma żadnego efektu zewnętrznego, chyba że jest wskaźnikiem, jak obiekt.
  5. możesz przekazać zmienne jako wskaźniki, jak w przypadku &$total. W ten sposób modyfikowanie wartości $total ma efekt zewnętrzny, zmienia się wartość oryginalnej zmiennej.
  6. zmienne zdefiniowane wewnątrz zamknięcia również nie są dostępne poza zamknięciem.
  7. zamknięcia i funkcje mają tę samą prędkość. Tak., możesz ich używać we wszystkich skryptach.

Jak zauważył @Mytskine prawdopodobnie najlepszym szczegółowym wyjaśnieniem jestRFC dla zamknięć . / Align = "left" / )

 496
Author: zupa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:00

function () use () {} jest jak zamknięcie dla PHP.

Bez use, funkcja nie może uzyskać dostępu do nadrzędnej zmiennej scope

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

$f(); // Notice: Undefined variable: s
$s = "hello";
$f = function () use ($s) {
    echo $s;
};

$f(); // hello

Wartość zmiennej use pochodzi od momentu zdefiniowania funkcji, a nie od momentu wywołania

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

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

use zmienna by-reference with &

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

$s = "how are you?";
$f(); // how are you?
 88
Author: Steely Wing,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-10-14 02:44:44

Zamknięcia są piękne! rozwiązują one wiele problemów związanych z funkcjami anonimowymi i umożliwiają naprawdę elegancki kod (przynajmniej tak długo, jak mówimy o php).

Programiści Javascript używają zamknięć cały czas, czasami nawet nie wiedząc o tym, ponieważ powiązane zmienne nie są wyraźnie zdefiniowane - po to jest" use " w php.

Istnieją lepsze rzeczywiste przykłady niż powyższe. powiedzmy, że trzeba posortować wielowymiarową tablicę według pod-wartości, ale najważniejsze zmiany.

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

Warning: untested code( nie mam zainstalowanego PHP5. 3 atm), ale powinien wyglądać jak coś w tym stylu.

Jest jeden minus: wielu programistów php może być trochę bezradnych, jeśli skonfrontujesz ich z zamknięciami.

Aby lepiej zrozumieć milość zamknięć, podam kolejny przykład - tym razem w javascript. jednym z problemów jest zasięg i asynchroniczność związana z przeglądarką. szczególnie, jeśli chodzi o window.setTimeout(); (or-interval). więc ty przekaż funkcję do setTimeout, ale tak naprawdę nie możesz podać żadnych parametrów, ponieważ podanie parametrów powoduje wykonanie kodu!

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

Mojafunction zwraca funkcję z predefiniowanym parametrem!

Szczerze mówiąc, lubię php dużo bardziej od 5.3 i anonimowych funkcji/zamknięć. przestrzenie nazw mogą być ważniejsze, , ale są o wiele mniej seksowne .

 54
Author: stefs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-07 14:54:41

Zupa wykonała świetną robotę wyjaśniając zamknięcia za pomocą " use "i różnicy między początkowymi a odwołującymi się do zmiennych, które są "używane".

Więc zrobiłem przykład kodu z wczesnym wiązaniem zmiennej (=kopiowanie):

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

?>

Przykład z odwołaniem do zmiennej (zwróć uwagę na znak ' & ' przed zmienną);

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

?>
 17
Author: joronimo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-17 01:21:17

Do ostatnich lat PHP zdefiniował swój AST, a interpreter PHP odizolował parser od części ewaluacyjnej. Podczas wprowadzania zamknięcia, parser PHP jest wysoce połączony z ewaluacją.

Dlatego, gdy closure zostało po raz pierwszy wprowadzone do PHP, interpreter nie ma metody, aby wiedzieć, które zmienne będą używane w closure, ponieważ nie jest jeszcze przetwarzane. Więc użytkownik musi zadowolić silnik zend poprzez explicit import, odrabiając zadanie domowe, które zend powinien to zrobić.

Jest to tzw. prosty sposób w PHP.

 3
Author: Zhu Jinxuan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-07-10 14:03:30