Skip to content

Instantly share code, notes, and snippets.

@nakamuray
Created August 29, 2011 10:59
Show Gist options
  • Save nakamuray/1178178 to your computer and use it in GitHub Desktop.
Save nakamuray/1178178 to your computer and use it in GitHub Desktop.
python like object system on php
<?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