Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
PHP does Meta Programming too! (Requires PHP 5.4)
<?php
namespace CHH;
trait MetaObject
{
protected static $__metaClass;
static function setMetaClass(MetaClass $metaClass)
{
static::$__metaClass = $metaClass;
}
static function getMetaClass()
{
if (null === static::$__metaClass) {
static::$__metaClass = new MetaClass;
}
return static::$__metaClass;
}
function __call($method, array $argv = array())
{
$metaClass = static::getMetaClass();
if (!$metaClass->respondsTo($method)) {
throw new \BadMethodCallException(sprintf(
'Call to undefined method %s', $method
));
}
return $metaClass->send($method, $argv, $this);
}
function __get($property)
{
$metaClass = static::getMetaClass();
if (property_exists($metaClass, $property)) {
return $this->$property = $metaClass->$property;
}
}
function __isset($property)
{
return property_exists(static::getMetaClass(), $property);
}
}
class MetaClass
{
protected $methods = array();
function extend($methods)
{
if ($methods instanceof MetaClass) {
$methods = $methods->getMethods();
}
foreach ($methods as $method => $body) {
$this->method($method, $body);
}
return $this;
}
function getMethods()
{
return $this->methods;
}
function method($name, \Closure $body)
{
$this->methods[$name] = $body;
return $this;
}
function property($name, $default = null)
{
$this->{$name} = $default;
return $this;
}
function respondsTo($method)
{
return isset($this->methods[$method]);
}
function send($method, array $argv = array(), $context = null)
{
if (!$this->respondsTo($method)) {
throw new \BadMethodCallException("Call to undefined Method $method");
}
$body = $this->methods[$method];
if (null !== $context) {
$body = $body->bindTo($context, get_class($context));
}
return call_user_func_array($body, $argv);
}
}
<?php
namespace CHH\Test;
require_once __DIR__ . "/MetaObject.php";
use CHH\MetaObject;
class Animal
{
use MetaObject;
}
class Dog extends Animal
{
public $name;
protected $favouriteToy = "Stick";
function __construct($name = null)
{
$this->name = $name;
}
}
class MetaClassTest extends \PHPUnit_Framework_TestCase
{
/**
* Registers an Instance Meta Property at runtime and initializes it
* with a default value.
*/
function testRegisterMetaProperty()
{
$beethoven = new Dog;
Dog::getMetaClass()->property('no');
$this->assertTrue(isset($beethoven->no));
}
function testRegisterMetaMethod()
{
$beethoven = new Dog("Beethoven");
Dog::getMetaClass()->method('speak', function() {
return "Hello I'm {$this->name}";
});
$this->assertTrue(is_callable(array($beethoven, 'speak')));
$this->assertEquals("Hello I'm Beethoven", $beethoven->speak());
}
function testExtendWithArray()
{
$beethoven = new Dog("Beethoven");
Dog::getMetaClass()->extend([
"bark" => function() {
return "Woof! Woof!";
},
"isNamed" => function($name) {
return $this->name === $name;
}
]);
$this->assertFalse($beethoven->isNamed('Johnny'));
$this->assertEquals('Woof! Woof!', $beethoven->bark());
}
function testProtectedMemberAccess()
{
$beethoven = new Dog("Beethoven");
Dog::getMetaClass()->method("getFavouriteToy", function() {
return $this->favouriteToy;
});
$this->assertEquals("Stick", $beethoven->getFavouriteToy());
}
// I've to figure out this later. It's a quirk by PHP's
// behaviour with static inheritance.
function testInheritanceAndMetaClass()
{
$beethoven = new Dog("Beethoven");
Animal::getMetaClass()->method("foo", function() {
return "bar";
});
Dog::getMetaClass()->method("bar", function() {
return "bar";
});
$this->assertFalse(Animal::getMetaClass()->respondsTo("bar"));
$this->assertFalse(is_callable(array($beethoven, 'foo')));
$this->assertFalse(Animal::getMetaClass() === Dog::getMetaClass());
}
}
@HallofFamer

This comment has been minimized.

Copy link

commented Aug 27, 2013

Interesting, its good to know that PHP is smarter than we thought. XD With proper work, PHP can do a lot of things that are supposedly only achievable in more object oriented languages like Java, C# and Ruby.

@solankin1576

This comment has been minimized.

Copy link

commented Apr 14, 2016

Thanks for Contribute it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.