Javascript style PHP with PHP 5.3.0
Hi guys, as you probably know, PHP 5.3.0 has great chances of bringing anonymous functions and closures to PHP. So I decided to play a little bit with this and inspired by the Javascript language I've tried to implement some kind of prototypal functionality. Although on the RFC page it is listed as a misconception the thing that I've done, I felt that I should try it.
So here's what I've done so far:
Code PHP:
<?php
class Object
{
/**
* Holds functions assigned to PROTOTYPE
*/
protected static $_classMethods = array();
/**
* Holds properties assigned to PROTOTYPE
*/
protected static $_classProperties = array();
/**
* Holds functions assigned to THIS instance
*/
protected $_instanceMethods = array();
/**
* Holds properties assigned to THIS instance
*/
protected $_instanceProperties = array();
/**
* Flag that tells whether to assign to PROTOTYPE or to INSTANCE
*/
protected $_usePrototype = false;
public function __get($property)
{
if ($property === 'prototype') {
$this->_usePrototype = true;
return $this;
} else {
if (array_key_exists($property, $this->_instanceMethods)) {
return $this->_instanceMethods[$property];
}
if (array_key_exists($property, $this->_instanceProperties)) {
return $this->_instanceProperties[$property];
}
if (array_key_exists($property, self::$_classMethods)) {
return self::$_classMethods[$property];
}
if (array_key_exists($property, self::$_classProperties)) {
return self::$_classProperties[$property];
}
return $this->$property;
}
}
public function __set($property, $value)
{
if ($property === 'prototype') {
throw new Exception('Cannot assign to prototype');
}
if ($this->_usePrototype) {
if ($value instanceof Closure) {
self::$_classMethods[$property] = $value;
} else {
self::$_classProperties[$property] = $value;
}
$this->_usePrototype = false;
} else {
if ($value instanceof Closure) {
$this->_instanceMethods[$property] = $value;
} else {
$this->_instanceProperties[$property] = $value;
}
}
}
public function __unset($property)
{
unset($this->_instanceMethods[$property]);
unset($this->_instanceProperties[$property]);
unset(self::$_classMethods[$property]);
unset(self::$_classProperties[$property]);
}
public function __call($method, $args)
{
$args = array($this) + $args;
if (array_key_exists($method, $this->_instanceMethods)) {
return call_user_func_array($this->_instanceMethods[$method], $args);
}
if (array_key_exists($method, self::$_classMethods)) {
return call_user_func_array(self::$_classMethods[$method], $args);
}
return call_user_func_array(array($this, $method), $args);
}
}
Examples:
Code PHP:
<?php
$A = new Object;
$A->prototype->foo = function($this) {
return 'foo in prototype';
};
$A->prototype->bar = 'bar in prototype';
$A->foo = function($this) {
return 'This should only be in $A';
};
echo $A->foo();
echo '<br>';
echo $A->bar;
echo '<br><br><br>';
$B = new Object;
echo $B->foo();
echo '<br>';
echo $B->bar;
echo '<br>';
$B->foo = function() {
return '<b>foo of instance B</b>';
};
$B->bar = '<b>bar of instance B</b>';
echo $B->bar;
echo '<br>';
echo $B->foo();
echo '<br><br><br>';
echo $A->foo();
echo '<br>';
print_r($A->foo);
$B->prototype->baz = function() {
return 'Hello, I\'m baz';
};
$A->foo;
echo '<br><br><br>';
echo $A->baz();
echo '<br>';
echo $B->baz();
One WTF factor of the above is that the instance variable "$this" is passed as a first argument to the functions and not already available. It's a little bit Pythonic I think.
So, what do you think? I hope you have a snapshot of PHP 5.3.0 from http://spans.php/net so that you can play a bit with these, they're great and we may provide some valuable feedback to the devs. I can't wait for this version to go mainstream. Closures + namespaces + phar will totally change the way we do PHP. Yet, one question bothers me... who needs the dollar anymore?! :)