LAMPlights Personal anecdotes from my experiences using the LAMP stack

15Mar/103

PHP 5.3 is the new JavaScript (almost)

In my last post, I argued that the best way to start developing functional PHP applications was to code review some JavaScript projects.  I think this is a good place to start as most web developers have written some JavaScript at one point during their career.  I briefly mentioned that the array is pretty similar to the JavaScript object too.  However, if you start hacking away at PHP based on JavaScript's functional syntax, you will quickly run into some problems.

One major difference between PHP and JavaScript style closures.  Here is a typical JavaScript closure:

// start counting from some number
function counter(n) {
    return function() {
        return n++;
    }
}
 
countFrom3 = counter(3);
console.log(countFrom3());
console.log(countFrom3());
console.log(countFrom3());
console.log(countFrom3());

We can write this same function in PHP.  The main difference is the "use" identifier required for closure:

<?php
function counter($n) {
    return function() use($n) {
        return $n++;
    };
}
 
$countFrom3 = counter(3);
echo $countFrom3(), PHP_EOL;
echo $countFrom3(), PHP_EOL;
echo $countFrom3(), PHP_EOL;
echo $countFrom3(), PHP_EOL;

If you run this code, it will not work as expected: every number will be 3.  Why?  Well, the closure RFC for PHP requires that variables are only explicitly passed by reference.  That means we need to use modify the function signature:

<?php
function counter($n) {
    return function() use(&$n) { // <--- note the pass by reference syntax
        return $n++;
    };
}

Now JavaScript does not have traditional classes like PHP.  Instead, we define an object with a set of variables.  Object methods are simply variables that reference an anonymous function.  Something like this:

obj = {};
obj.add = function(l, r) {
    return l + r;
};
 
console.log(obj.add(1, 2));

Let's write that same function in PHP.  It has very similar syntax:

<?php
$obj = new stdClass;
$obj->add = function($l, $r) {
    return $l + $r;
};
 
echo $obj->add(1, 2), PHP_EOL;

If you run this you will notice that PHP complains that the object does not have that method.  In PHP, functions are not quite first class citizens.  The variable function is completely ignored and only explicitly declared method's are allowed to be called this way.  We need to modify our example to make this work:

<?php
$obj = new stdClass;
$obj->add = function($l, $r) {
    return $l + $r;
};
 
$add = $obj->add;
echo $add(1, 2), PHP_EOL;

With these nuances out of the way, you have can have a productive functional hacking session.  Have fun!

Comments (3) Trackbacks (1)
  1. A small correction on “Now JavaScript does not have traditional classes like PHP”. Javascript does have objects you can instantiate and save state with, but most of the time its not needed and we use static objects instead (var obj = {}). Example:

    function Object(param) {
    function Object(param) {
    // Private constructor
    };

    this.publicVar = param;
    this.publicMethod: function() {};

    var privateVar;
    function privateMethod() {};
    }

    var obj = new Object(1);

    However back onto the topic at hand. The use() in the closure really irks me sometimes as I really wish it would just inherit the current scope, but oh well.
    Twitter:

  2. Ah, you are right. I have not used that method of creating objects in a long time and completely forgot about it. Thanks.

    One thing I do like about the “use” syntax is that it is easier for the parser to see what variables are being used in the closure. I know JavaScript minification programs have a lot of bugs that stem from the fact that it is hard to tell what the closure is using sometimes.
    Twitter:

  3. The use() clause in PHP closures might seem annoying at first, but it actually helps enforce referential transparency in your functions.


Leave a comment