Mauro Bringolf

Currently geeking out as WordPress developer at WebKinder and student of computer science at ETH.

Anonymous functions and object context in older PHP versions

April 20, 2017
,

PHP Fatal error: Using $this when not in object context

This was an error message I ran into while working on my footnotes plugin for this site 1. The error message came from an array_map call that was using a closure. That is what anonymous functions are called in PHP. I only discovered them recently and have not seen them a whole lot in a WordPress context. I think that is because backwards compatibility is a big deal in WordPress and closures were only added in PHP version 5.3 2. In fact, the error did not occur on my local development environment but only when Travis CI 3 ran the test suite of the plugin. It failed on PHP 5.3, but passed on PHP 5.6. A bit of research shows that the behavior of closures was slightly modified in release 5.4 of PHP.

Automatic $this binding within anonymous functions inside classes

The part of my plugin that failed the tests had the same structure as the example below. It uses an anonymous function to map over an array inside a class method. The important thing is, that the anonymous function then calls another class method using the implicit $this reference available inside class methods. This reference is also available inside the closure without explicitly being passed to the anonymous function. In general anonymous functions will not be able to access variables from lexical scope that were not explicitly passed in with the use primitive. The $this reference is an exception handled by the language itself.

These semantics are what I intuitively assumed. Fortunately, Travis CI helped me discover the difference in PHP versions lower than 5.4. The scope of anonymous functions is more strict and does not inherit $this from lexical scope. That means when anonymous functions are used within object context, they have no implicit $this reference:

<?php

class A {
  public function f() {
    $a = array( 1, 2, 3 );
    array_map( function( $n ) {
      // >= 5.4: $this is available here
      // <  5.4: $this is empty
      $this->g( $n );
    }, $a );
  }
  public function g( $n ) {
    echo $n;
  }
}


$a = new A();

// >= 5.4: 123
// <  5.4: PHP Fatal error:  Using $this when not in object context
$a->f();


My ugly patch for now (probably forever)

What I ended up doing was simply manually passing a $this reference into the closure with the use primitive. Not pretty, but it works all the way back to PHP 5.3. I am not using anonymous functions a whole lot in PHP anyways, but recently began embracing the general concept of maps, reduces and filters. I find it handy to be able to use these in WordPress development as well, so I will have to be aware of this nuance ( or Travis CI will remind me ). Having a continuous integration service running your code in different environments is extremely valuable. This is especially true for a WordPress plugin, where you have very minimal knowledge about the environment it will run on and backwards compatibility is usually expected. So I should probably add some more WordPress and PHP versions into my Travis CI testing matrix!

References

  1. https://github.com/maurobringolf/minimal-footnotes
  2. http://php.net/releases/5_3_0.php
  3. https://travis-ci.org/