Prototype-based programming
class UndefinedException extends Exception { public function __construct($msg, $file, $line) { $this->message = $msg; $this->file = $file; $this->line = $line; } } class Backtrace { /** * Return caller information from the backtrace */ static public function caller() { $bt = debug_backtrace(0); $i = count($bt) - 1; return $bt[$i]; } } class Prototype { public $prototype; public function __construct($parent = null) { $this->prototype = $parent; } public function __call($name, $args) { // Check closure $func = isset($this->$name) ? $this->$name : null; if (is_callable($func)) { return call_user_func_array($func, $args); } // Check prototype if ($this->prototype) { return call_user_func_array(array($this->prototype, $name), $args); } $caller = Backtrace::caller(); throw new UndefinedException('Undefined method: ' . $name, $caller['file'], $caller['line']); } public function __get($name) { if ($this->prototype) { return $this->prototype->$name; } $caller = Backtrace::caller(); throw new UndefinedException('Undefined property: ' . $name, $caller['file'], $caller['line']); } final public function begetObject() { return new Prototype($this); } }Here is an example of using it:
$a = new Prototype(); $a->greet = function() { echo "Hello World!\n"; }; $b = $a->begetObject(); $b->greet(); // Prints: Hello World!