PHP 5.4 – Closures the right way!

Citește postarea în română

Share on:

The concept of closure was introduced in PHP 5.3, with the new “more traditional” syntax for anonymous functions.

PHP 5.3

In PHP 5.3, a closure will rely on the term “use”, which was passing the variables to the anonymous function, making it a closure.

The problem is that the anonymous function will only be able to access the variables that have been passed with “use”. When it comes to objects, there are passed by reference by default, but scalar variables (int, string, etc.) are passed by value, as this is the default behavior 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

Another problem is that you cannot pass $this when the anonymous function is declared inside an object, so only the public method and properties can be accessed inside the closure.

PHP 5.4

In PHP 5.4 the keyword “use” is optional, and the entire environment where the function was created is available inside the function.

The advantage is that when the anonymous function is created inside another function or method, the anonymous function has access to the environment where it was created, even after the execution of the environment is over. The objects from this environment will be unset, only after the last reference to the closure will be unset:

 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         * A method that returns the 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         * Method that displays a private property
27         */
28        public function showChangeableVar() {
29                echo 'Private property in method: ' . $this->changeableVar.PHP_EOL;
30        }
31
32}
33
34// Memory befor the objects is created
35echo "Memory: " . memory_get_usage() . PHP_EOL; // Memory: 229896
36
37// Create object
38$testObj = new testClass();
39
40// Create closure
41$closure = $testObj->closure();
42
43// Execute closure
44$closure(); // Private property: 1
45
46// Displaying the current value of the private property
47$testObj->showChangeableVar(); // Private property in method: 2
48
49// Memory befor object will be unset
50echo "Memory: ". memory_get_usage() . PHP_EOL; // Memory: 266240
51
52// Unset the object
53unset($testObj);
54
55// Memory after the object was distroyed, there is no big difference in memory
56echo "Memory: ". memory_get_usage() . PHP_EOL; // Memory: 266152
57
58// Run closure after the object in which it was created was unset
59echo $closure(); // Private property: 2
60
61// Unset closure and with it the object environment
62unset($closure);
63
64// Memotry after the las reference to the object (closure) is unset
65echo "Memory: " . memory_get_usage() . PHP_EOL; // Memory: 230416

Callable type hinting

Another new feature introduced in PHP 5.4 regarding closures is the new “type hint”: “callable”. Actually callable is referring to any anonymous function, and even to a new way of calling a method of an object:

 1<?php
 2
 3// A function that uses type hinting
 4function typeHinting(callable $a) {
 5     echo $a() . PHP_EOL;
 6}
 7
 8// A closure
 9$closure = function () {
10     return __FUNCTION__;
11};
12
13// Call the type hinting function with the closure
14typeHinting($closure); // {closure}
15
16class testClass {
17     public function testMethod() {
18          return __METHOD__;
19     }
20}
21
22// A mock object
23$testObj = new testClass();
24
25// The new way of calling object methods
26$objCallable = array($testObj, 'testMethod');
27
28// Call type hinting function with the new method calling way
29typeHinting($objCallable); // testClass::testMethod

I believe that only now we can really say that PHP supports closures, the right way!