PHP přistupuje ke třídám způsobem známým ze staticky typovaných jazyků a neumožňuje monkey patching, tedy měnit za běhu metody tříd, kopírovat je mezi instancemi a podobně.

Abyste porozuměli, co mám na mysli, vytvořme třídu Greeting s metodou say():

class Greeting
{
	function __construct($name)
	{
		$this->name = $name;
	}

	function say($message)
	{
		echo "$message $this->name.";
	}
}

$g = new Greeting('John');
$g->say('Hello'); // Hello John.

V PHP neočekáváme, že by bylo možné metodu třeba uložit do proměnné či jiného atributu a poté zase zavolat:

$method = $g->say;
$g->greet = $method;
$g->greet('Hello');

Nebo dokonce za chodu přidávat metody nové:

$g->shout = function($message) {
	echo "$message $this->name!!!";
};

$g->shout('Hello'); // Hello John!!!

Na jedné straně je mi líto, že tohle PHP neumí, na straně druhé vidím ve statickém pojetí tříd podstatné výhody. A na straně třetí: v PHP lze tohle chování snadno emulovat.

Emulace dynamiky

Vytvořit funkci jako je výše uvedená shout() a vložit ji do proměnné objektu PHP už umí od verze 5.3. Ale abychom ji mohli zavolat běžným zápisem, musíme si vypomoci magickou metodou __call():

class Greeting
{
	function __call($name, $args)
	{
		if (!isset($this->$name) || !$this->$name instanceof Closure) {
			throw new Exception("Method $name not found.");
		}
		return call_user_func_array($this->$name->bindTo($this, $this), $args);
	}

	...
}

A nyní už bude příklad s metodou shout() fungovat.

Abychom mohli stejně nakládat i se statickými metodami, jako byla třeba výše uvedená say(), doplníme ještě __get():

class Greeting
{
	function __get($name)
	{
		if (!method_exists($this, $name)) {
			throw new Exception("Property $name not found.");
		}
		return function() use ($name) {
			return call_user_func_array(array($this, $name), func_get_args());
		};
	}

	...
}

A nyní bude fungovat i první příklad s přiřazením $method = $g->say a následným voláním.

Pro verzi 5.3

Uvedené příklady vyžadují PHP 5.4. Ve verzi 5.3 jsou closures ořezané a nesmí se v nich používat $this. Řešení by vypadalo trošičku jinak:

// for PHP 5.3

class Greeting
{
	function __call($name, $args)
	{
		if (!isset($this->$name) || !$this->$name instanceof Closure) {
			throw new Exception("Method $name not found.");
		}
		array_unshift($args, $this);
		return call_user_func_array($this->$name, $args);
	}

	function __get($name)
	{
		if (!method_exists($this, $name)) {
			throw new Exception("Property $name not found.");
		}
		return function() use ($name) {
			$args = func_get_args();
			return call_user_func_array(array(array_shift($args), $name), $args);
		};
	}

	...
}

A namísto $this bychom uvnitř closure použili první argument, pojmenovaný třeba $self.

$g->shout = function($self, $message) {
	echo "$message $self->name!!!";
};

Nicméně narozdíl od 5.4 varianty má nyní funkce přístup jen k veřejným proměnným třídy.