PHP będzie obsługiwać zamknięcia natywnie w wersji 5.3. Zamknięcie jest dobre, gdy potrzebujesz funkcji lokalnej, która jest używana tylko do jakiegoś małego, konkretnego celu. Dokument RFC dla domknięć daje dobry przykład:
function replace_spaces ($text) {
$replacement = function ($matches) {
return str_replace ($matches[1], ' ', ' ').' ';
};
return preg_replace_callback ('/( +) /', $replacement, $text);
}
Pozwala to zdefiniować replacement
funkcję lokalnie wewnątrz replace_spaces()
, aby nie była:
1) Zaśmiecanie globalnej przestrzeni nazw
2 ) Sprawianie, że ludzie trzy lata później zastanawiają się, dlaczego istnieje funkcja zdefiniowana globalnie, która jest używana tylko w jednej innej funkcji
Utrzymuje porządek. Zwróć uwagę, że sama funkcja nie ma nazwy, po prostu jest zdefiniowana i przypisana jako odniesienie $replacement
.
Ale pamiętaj, na PHP 5.3 trzeba poczekać :)
Możesz również uzyskać dostęp do zmiennych spoza jego zakresu w zamknięciu za pomocą słowa kluczowego use
. Rozważmy ten przykład.
$multiplier = 3;
$numbers = array(1,2,3,4);
array_walk($numbers, function($number) use($multiplier){
echo $number * $multiplier;
});
Doskonałe wyjaśnienie podano tutaj. Co to są lambdy i domknięcia php
Kiedy w przyszłości będziesz potrzebować funkcji, która będzie wykonywać zadanie, o którym teraz zdecydowałeś.
Na przykład, jeśli czytasz plik konfiguracyjny i jeden z parametrów mówi ci, że
hash_method
dla twojego algorytmu jestmultiply
raczej niżsquare
, możesz utworzyć zamknięcie, które będzie używane wszędzie tam, gdzie będziesz musiał coś zaszyfrować.Zamknięcie można utworzyć w (na przykład)
config_parser()
; tworzy funkcję o nazwiedo_hash_method()
using variable local toconfig_parser()
(z pliku konfiguracyjnego). Za każdym razem, gdydo_hash_method()
jest wywoływana, ma dostęp do zmiennych w zakresie lokalnym,config_parser()
nawet jeśli nie jest wywoływana w tym zakresie.Miejmy nadzieję, że dobry hipotetyczny przykład:
function config_parser() { // Do some code here // $hash_method is in config_parser() local scope $hash_method = 'multiply'; if ($hashing_enabled) { function do_hash_method($var) { // $hash_method is from the parent's local scope if ($hash_method == 'multiply') return $var * $var; else return $var ^ $var; } } } function hashme($val) { // do_hash_method still knows about $hash_method // even though it's not in the local scope anymore $val = do_hash_method($val) }
źródło
Oprócz szczegółów technicznych, zamknięcia są podstawowym warunkiem wstępnym stylu programowania znanego jako programowanie zorientowane na funkcje. Zamknięcie jest z grubsza używane do tego samego, co obiekt w programowaniu obiektowym; Wiąże dane (zmienne) razem z jakimś kodem (funkcją), który można następnie przekazać gdzie indziej. W związku z tym wpływają na sposób, w jaki piszesz programy lub - jeśli nie zmienisz sposobu pisania programów - nie mają żadnego wpływu.
W kontekście PHP są one trochę dziwne, ponieważ PHP jest już mocno oparte na klasowym, obiektowym paradygmacie, jak również na starszym paradygmacie proceduralnym. Zazwyczaj języki, które mają domknięcia, mają pełny zakres leksykalny. Aby zachować kompatybilność wsteczną, PHP tego nie otrzyma, więc oznacza to, że zamknięcia będą tutaj trochę inne niż w innych językach. Myślę, że jeszcze nie widzieliśmy dokładnie, jak będą używane.
źródło
Podoba mi się kontekst dostarczony przez post troelskn. Kiedy chcę zrobić coś takiego jak przykład Dana Udeya w PHP, używam wzorca strategii OO. Moim zdaniem jest to znacznie lepsze niż wprowadzenie nowej funkcji globalnej, której zachowanie jest określane w czasie wykonywania.
http://en.wikipedia.org/wiki/Strategy_pattern
Możesz także wywoływać funkcje i metody używając zmiennej przechowującej nazwę metody w PHP, co jest świetne. więc innym podejściem do przykładu Dana byłoby coś takiego:
class ConfigurableEncoder{ private $algorithm = 'multiply'; //default is multiply public function encode($x){ return call_user_func(array($this,$this->algorithm),$x); } public function multiply($x){ return $x * 5; } public function add($x){ return $x + 5; } public function setAlgorithm($algName){ switch(strtolower($algName)){ case 'add': $this->algorithm = 'add'; break; case 'multiply': //fall through default: //default is multiply $this->algorithm = 'multiply'; break; } } } $raw = 5; $encoder = new ConfigurableEncoder(); // set to multiply echo "raw: $raw\n"; // 5 echo "multiply: " . $encoder->encode($raw) . "\n"; // 25 $encoder->setAlgorithm('add'); echo "add: " . $encoder->encode($raw) . "\n"; // 10
oczywiście, jeśli chcesz, aby był dostępny wszędzie, możesz po prostu ustawić wszystko statycznie ...
źródło
Zamknięcie to w zasadzie funkcja, dla której piszesz definicję w jednym kontekście, ale uruchamiasz w innym kontekście. Javascript bardzo mi pomógł w ich zrozumieniu, ponieważ są wszędzie używane w JavaScript.
W PHP są mniej efektywne niż w JavaScript ze względu na różnice w zakresie i dostępności zmiennych „globalnych” (lub „zewnętrznych”) z poziomu funkcji. Jednak począwszy od PHP 5.4, zamknięcia mogą uzyskać dostęp do obiektu $ this, gdy są uruchamiane wewnątrz obiektu, co czyni je o wiele bardziej efektywnymi.
Na tym polega zamknięcie i powinno wystarczyć, aby zrozumieć, co jest napisane powyżej.
Oznacza to, że powinno być możliwe zapisanie gdzieś definicji funkcji i użycie zmiennej $ this wewnątrz definicji funkcji, następnie przypisanie definicji funkcji do zmiennej (inni podali przykłady składni), a następnie przekazanie tej zmiennej do obiektu i wywołać go w kontekście obiektu, funkcja może wtedy uzyskać dostęp do obiektu i manipulować nim za pomocą $ this tak, jakby była to tylko kolejna z jej metod, podczas gdy w rzeczywistości nie jest zdefiniowana w definicji klasy tego obiektu, ale gdzie indziej.
Jeśli nie jest to zbyt jasne, nie martw się, stanie się jasne, gdy zaczniesz ich używać.
źródło
Zasadniczo Closure to funkcje wewnętrzne, które mają dostęp do zmiennych zewnętrznych i są używane jako funkcja zwrotna do anonimowej funkcji (funkcji, które nie mają żadnej nazwy).
<?php $param='ironman'; function sayhello(){ $param='captain'; $func=function () use ($param){ $param='spiderman'; }; $func(); echo $param; } sayhello(); ?> //output captain //and if we pass variable as a reference as(&$param) then output would be spider man;
źródło
$param='captain'
in funcsayhello()
jest lokalną zmienną funcsayhello()
.$param='ironman'
powyżejsayhello()
jest zmienną globalną. Jeśli chcesz zrobić tylko jedną zmienną $ param w swoim skrypcie, powinieneś zadzwonić:global $param;
withinsayhello()
funcOto przykłady zamknięć w php
// Author: [email protected] // Publish on: 2017-08-28 class users { private $users = null; private $i = 5; function __construct(){ // Get users from database $this->users = array('a', 'b', 'c', 'd', 'e', 'f'); } function displayUsers($callback){ for($n=0; $n<=$this->i; $n++){ echo $callback($this->users[$n], $n); } } function showUsers($callback){ return $callback($this->users); } function getUserByID($id, $callback){ $user = isset($this->users[$id]) ? $this->users[$id] : null; return $callback($user); } } $u = new users(); $u->displayUsers(function($username, $userID){ echo "$userID -> $username<br>"; }); $u->showUsers(function($users){ foreach($users as $user){ echo strtoupper($user).' '; } }); $x = $u->getUserByID(2, function($user){ return "<h1>$user</h1>"; }); echo ($x);
Wynik:
0 -> a 1 -> b 2 -> c 3 -> d 4 -> e 5 -> f A B C D E F c
źródło
Domknięcia:
MDN ma najlepsze wyjaśnienie IMO:
tj. zamknięcie to funkcja z dostępem do zmiennych znajdujących się w zakresie nadrzędnym. Zamknięcie pozwala nam wygodnie tworzyć funkcje w locie, ponieważ w niektórych sytuacjach funkcje są potrzebne tylko w jednym miejscu (wywołania zwrotne, argumenty wywoływalne).
Przykład:
$arr = [1,2,3,3]; $outersScopeNr = 2; // The second arg in array_filter is a closure // It would be inconvenient to have this function in global namespace // The use keyword lets us access a variable in an outer scope $newArr = array_filter($arr, function ($el) use ($outersScopeNr) { return $el === 3 || $el === $outersScopeNr; }); var_dump($newArr); // array (size=3) // 1 => int 2 // 2 => int 3 // 3 => int 3
źródło