PHP 5.4 – Closures asa cum ar trebui!

Read this post in English

Share on:

Notiunea de Closure, a fost introdusa in PHP 5.3, o data cu o noua sintaxa, “mai traditionala” pentru functiile anonime.

PHP 5.3

In 5.3, un closure se baza pe termenul “use”, care transmite anumite variabile functiei anonime, transformand-o intr-un closure.

Problema este ca functia anonima nu va avea acces decat la variabilele trimise folosind “use”. In cazul obiectelor ele sunt trimise prin referinta, dar in cazul variabilelor scalare (int, string, etc.) acestea se transmit prin valoare, asa cum face implicit in PHP 5+:

 1$scalar = 5;
 2
 3$closure = function () use ($scalar) {
 4     return 'Scalar: ' . $scalar . PHP_EOL;
 5};
 6
 7echo $closure(); // Scalar: 5
 8
 9$scalar = 7;
10
11echo $closure(); // Scalar: 5

O alta problema este ca nu se pot trimite $this in cadrul obiectelor, deci doar proprietatile sau metodele care sunt publice nu pot fi accesate de closure.

PHP 5.4

In PHP 5.4 folosirea cuvantului “use” este optionala, iar tot mediul in care functia anonima a fost creata este disponibil in interiorul functiei.

Avantajul este ca atunci cand functia anonima este generata in interiorul unei alte functii, sau a unei metode, functia anonima va putea accesa mediul in care aceasta a fost creata chiar si dupa terminarea executiei acestuia. Obiectele din mediul creat se vor dezaloca de abea dupa ce se ultima referinta catre closure se va sterge:

 1class testClass {
 2
 3        private $changeableVar = 1;
 4        private $bigVar;
 5
 6        public function __construct() {
 7                // Allocate a big variable so we can see the changes in memory
 8                $this->bigVar = str_repeat("BigWord", 5000);
 9        }
10
11        /**
12         * O metoda care intoarce un closure
13         */
14        public function closure() {
15
16                return function () {
17                        // Display the value of a private property of the object
18                        echo 'Private property: ' . $this->changeableVar.PHP_EOL;
19
20                        // Change the value of a private property of the object
21                        $this->changeableVar = 2;
22                };
23        }
24
25        /**
26         * O metoda care afisaza o variabila privata
27         */
28        public function showChangeableVar() {
29                echo 'Private property in method: ' . $this->changeableVar.PHP_EOL;
30        }
31
32}
33
34// Memoria inainte sa fie alocat obiectul
35echo "Memory: " . memory_get_usage() . PHP_EOL; // Memory: 229896
36
37// Creare obiect
38$testObj = new testClass();
39
40// Creare closure
41$closure = $testObj->closure();
42
43// Executie closure
44$closure(); // Private property: 1
45
46// Afisare valoare curenta a proprietatii private
47$testObj->showChangeableVar(); // Private property in method: 2
48
49// Memoria inainte sa fie desalocat obiectul
50echo "Memory: ". memory_get_usage() . PHP_EOL; // Memory: 266240
51
52// Dezalocare obiect
53unset($testObj);
54
55// Memoria dupa ce a fost dezalocat obiectul, nu este o diferenta mare in memorie
56echo "Memory: ". memory_get_usage() . PHP_EOL; // Memory: 266152
57
58// Executie closure dupa ce obiectul in care a fost creata a fost dezalocat
59echo $closure(); // Private property: 2
60
61// Dezalocat closure si o data cu el mediul in care a fost generat
62unset($closure);
63
64// Memoria dupa ce a fost stearsa ultima referinta catre obiect
65echo "Memory: " . memory_get_usage() . PHP_EOL; // Memory: 230416

Callable type hinting

Inca un nou lucru introdus legat de closures introdus in PHP 5.4 este un nou “type hint”: “callable”. De fapt “callable” se refera la orice functie anonima, chiar si la un nou mod de a apela o metoda a unui obiect:

 1<?php
 2// O functie care foloseste type hinting
 3function typeHinting(callable $a) {
 4     echo $a() . PHP_EOL;
 5}
 6
 7// Un closure
 8$closure = function () {
 9     return __FUNCTION__;
10};
11
12// Apelare functie cu type hinting cu un closure ca argument
13typeHinting($closure); // {closure}
14
15class testClass {
16     public function testMethod() {
17          return __METHOD__;
18     }
19}
20
21// Un obiect de test
22$testObj = new testClass();
23
24// Noua forma de apelare a unei metode dintr-un obiect
25$objCallable = array($testObj, 'testMethod');
26
27// Apelare functie type hinting cu noua forma de apelare ca argument
28typeHinting($objCallable); // testClass::testMethod

Cred ca de abea acum este momentul sa spunem ca PHP suporta closures cu adevarat!