Created
August 29, 2011 10:59
-
-
Save nakamuray/1178178 to your computer and use it in GitHub Desktop.
python like object system on php
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 | |
class Type extends Object { | |
public $__name__; | |
public $__base__; | |
public $__bases__; | |
public $__mro__; | |
function __construct() { | |
// FIXME: construct with Type class itself | |
parent::__construct(Null); | |
$arguments = func_get_args(); | |
// first argument is class name | |
$this->__name__ = array_shift($arguments); | |
// last argument is class constructer | |
$__new__ = array_pop($arguments); | |
// and others are base classes | |
if ($arguments) { | |
$this->__base__ = $arguments[0]; | |
} | |
$this->__bases__ = $arguments; | |
$this->__mro__ = Type::__calculate_mro($this); | |
// initialize this class | |
$this->__dict__ = new __dict__(); | |
$__new__($this->__dict__); | |
} | |
function __get($name) { | |
foreach ($this->__mro__ as $class) { | |
if (isset($class->__dict__->$name)) { | |
return $class->__dict__->$name; | |
} | |
} | |
throw new AttributeError("Type object '{$this->__name__}' has no attribute '{$name}'"); | |
} | |
function __isset($name) { | |
foreach ($this->__mro__ as $class) { | |
if (isset($class->__dict__->$name)) { | |
return True; | |
} | |
} | |
return False; | |
} | |
function __invoke() { | |
// create class instance and initialize it | |
$object = new Object($this); | |
if (isset($object->__init__)) { | |
call_user_func_array($object->__init__, func_get_args()); | |
} | |
return $object; | |
} | |
function __toString() { | |
return "<class '{$this->__name__}'>"; | |
} | |
static function __calculate_mro($class) { | |
// FIXME: implement accurate MRO resolution | |
$mro = array(); | |
$pending = array($class); | |
while ($pending) { | |
$class = array_shift($pending); | |
$mro[] = $class; | |
$pending = array_merge($pending, $class->__bases__); | |
} | |
return array_unique($mro); | |
} | |
} | |
class Object { | |
public $__class__; | |
public $__dict__; | |
function __construct($class) { | |
$this->__class__ = $class; | |
$this->__dict__ = new __dict__(); | |
} | |
function __set($name, $value) { | |
return $this->__dict__->$name = $value; | |
} | |
function __get($name) { | |
if (isset($this->__dict__->$name)) { | |
return $this->__dict__->$name; | |
} elseif(isset($this->__class__->$name)) { | |
$attr = $this->__class__->$name; | |
if (is_callable($attr)) { | |
$attr = new InstanceMethod($this, $attr); | |
} | |
return $attr; | |
} | |
throw new AttributeError("'{$this->__class__}' object has no attribute '{$name}'"); | |
} | |
function __isset($name) { | |
return isset($this->__dict__->$name) or isset($this->__class__->$name); | |
} | |
function __unset($name) { | |
unset($this->__dict__->$name); | |
} | |
function __call($name, $arguments) { | |
return call_user_func_array($this->$name, $arguments); | |
} | |
function __invoke() { | |
$arguments = func_get_args(); | |
return call_user_func_array($this->__call__, $arguments); | |
} | |
function super() { | |
$super_obj = clone $this; | |
$super_obj->__class__ = $this->__class__->__base__; | |
return $super_obj; | |
} | |
function is_instance_of($class) { | |
foreach ($this->__class__->__mro__ as $class_) { | |
if ($class == $class_) { | |
return True; | |
} | |
} | |
return False; | |
} | |
} | |
class __dict__ { | |
} | |
class InstanceMethod { | |
public $_object; | |
public $_callable; | |
function __construct($object, $callable) { | |
$this->_object = $object; | |
$this->_callable = $callable; | |
} | |
function __invoke() { | |
$arguments = func_get_args(); | |
array_unshift($arguments, $this->_object); | |
return call_user_func_array($this->_callable, $arguments); | |
} | |
} | |
class AttributeError extends RuntimeException { | |
} | |
if (!debug_backtrace()) { | |
// ---------------- | |
// examples | |
// ---------------- | |
// TODO: provide magical method to avoid duplicate "Hoge" string | |
$Hoge = new Type('Hoge', function($class) { | |
$class->__init__ = function($self, $name="world") { | |
$self->name = $name; | |
}; | |
$class->method = function($self) { | |
print "hello {$self->name}\n"; | |
}; | |
}); | |
$Fuga = new Type('Fuga', $Hoge, function($class) { | |
$class->method = function($self) { | |
print "hi, {$self->name}\n"; | |
$self->super()->method(); | |
}; | |
}); | |
$Piyo = new Type('Piyo', $Fuga, function($class) { | |
$class->__init__ = function($self, $name, $message) { | |
$self->message = $message; | |
$self->super()->__init__($name); | |
}; | |
$class->method = function($self) { | |
print "{$self->message} {$self->name}\n"; | |
$self->super()->method(); | |
}; | |
}); | |
print "---------- Hoge ---------\n"; | |
$h = $Hoge('hoge'); | |
$h->method(); | |
$h->method = function() { print "overridden\n"; }; | |
$h->method(); | |
$h2 = $Hoge('hoge2'); | |
$h2->method(); | |
print "---------- Fuga ---------\n"; | |
$f = $Fuga(); | |
$f->method(); | |
print "---------- Piyo ---------\n"; | |
$p = $Piyo('piyo', 'LOL'); | |
$p->method(); | |
$Piyo->method = function($self) { | |
print "{$self->message} {$self->name}!?\n"; | |
$self->super()->method(); | |
}; | |
$p->method(); | |
print "---------- C1 ---------\n"; | |
$C1 = new Type('C1', function($class) { | |
$class->__init__ = function($self) { | |
$self->i = 0; | |
}; | |
$class->incr = function($self) { | |
$self->i++; | |
}; | |
}); | |
$c1 = $C1(); | |
var_dump($c1->i); | |
$c1->incr(); | |
var_dump($c1->i); | |
$c1_2 = $C1(); | |
var_dump($c1_2->i); | |
print "---------- C2 ---------\n"; | |
$C2 = new Type('C2', $C1, function($class) { | |
$class->incr = function($self) { | |
$self->super()->incr(); | |
$self->i++; | |
}; | |
}); | |
$c2 = $C2(); | |
var_dump($c2->i); | |
$c2->incr(); | |
var_dump($c2->i); | |
$C2->__dict__->incr = function($self) { | |
$self->i += 100; | |
}; | |
$c2->incr(); | |
var_dump($c2->i); | |
print "---------- C3 ---------\n"; | |
$C3 = new Type('C3', $C1, function($class) { | |
$class->__init__ = function($self) { | |
$self->i = 1000; | |
}; | |
}); | |
$c3 = $C3(); | |
var_dump($c3->i); | |
$c3->incr(); | |
var_dump($c3->i); | |
var_dump($c3->is_instance_of($C3)); | |
var_dump($c3->is_instance_of($C2)); | |
var_dump($c3->is_instance_of($C1)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment