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!