PHP Static Binding

PHP 5.3 was really great for the comunity. It came packed with gifts like namespaces, closures and late static binding.
This last feature allows us to build very functiona abstract classes. I am currenty developing software in 5.5.30 and 7.0.1 versions of PHP in my local environment.
However not everything is sunshines and rainbows when it comes to production environments. I ran into a very easy to fix bug a couple of days ago. The getAll method in WPExpress BaseModel brought down the new release of the theme I am currently working on.
I came across the Cannot instantiate abstract class error. Much to my suprise cause I could not see it at the very begining –Silly me–.
But before I talk about the solution a little bit of background if you are not familiar with WPExpress.
WPExpress is a Framework for WordPress Developers. Not it has no GUI, no, it is not a plugin, and no you cannot download it from the WordPress.org repository.
It is a Framework for Developers. People who actually know what they’re doing not just dragginf and dropping and pressing buttons here and there. I built it so others people who works with modern PHP Frameworks can get to write meaningful code faster.
The WPExpress module contains an abstract class called BaseModel. This class allows you to create Custom Post Types for WordPress under 30 seconds.
Your Custom Post Types extend this class, and that’s it. The BaseModel takes care of almost everything on the WordPress side.
One of those nice features I added was the ability to call static methods like this one
MyPostType::getAll();
The getAll, method uses the static keyword to instantiate a list of objects of the child class.
I am using array_map to parse the $posts list and keep me from writing a foreach method. Everything is sunshine and rainbows until it goes to production and breaks.
Why o why I wondered, since, well it works in my environment, it works on several other production environments, and it breaks here.
After banging my head for a while I remembered that closures, did not inherit the ability to manage static bindings.
So I wrote this piece of code and ran it with 3val.org
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// Late Static Binding in PHP are great. | |
// However they are not supported in every version of PHP | |
// Use the code below in a new 3val.org window to test support | |
// for Late Static Bindings inside closures. | |
abstract class Demo | |
{ | |
protected $item; | |
protected $calledClass; | |
public function __construct($bean) | |
{ | |
if( !empty($bean) ){ | |
$this->item = $bean; | |
} | |
$this->calledClass = get_called_class(); | |
// Some other code here | |
} | |
public function getCalledClass() | |
{ | |
return $this->calledClass; | |
} | |
public static function parseArray($items) | |
{ | |
return array_map(function($item){ | |
return new static($item); | |
}, $items); | |
} | |
} | |
final class Proto extends Demo | |
{ | |
} | |
$list = [1,2,3,4]; | |
print_r( array_map(function($i){ | |
if( $i instanceof \Demo || $i instanceof \Proto ){ | |
return $i->getCalledClass(); | |
} | |
return 'Is null'; | |
}, Proto::parseArray($list) ) ); |
And surprise surprise. It breaks on PHP 5.5.1 to 5.5.13 around the same time when they added support for it in PHP 5.4.30 as well.
With this in hand I was tempted to bump my PHP version to 5.5.14 but it would mean loosing a significant part of the market share of PHP in production environments. So for the time bein I think I’ll change that array_map back to a foreach statment, and then build a 2.0 version which is only compatible with PHP 7 and HHVM 3.14
Considering all this varibales can make an impact in the future of my framework –If there is any–. Even if the experience was an annoyance, I think it gifter me with a glimpse into the real hard work behind real PHP Frameworks such as Symfony and Laravel.