Rodičovské problémy Táty Singletonovského objektu

Patrik Votoček

Ač sám singleton moc v lásce nemám (důvody hezky vystihnul David Grudl zde), tak je používám pro obalové třídy modelů((toho M z MVP)) v mé aplikaci. A dnes bych chtěl poukázat na jeden problém, na který v PHP při používání Singletonů můžete narazit. Lepší, než spousta omáčky okolo, bude přejít rovnou k příkladu.

abstract class Singleton
{
	private static $instance;
	private function __construct() {}

	final public static function getInstance()
	{
		if (empty(self::$instance))
			self::$instance = new self;
		return self::$instance;
	}
}
class FooClass extends Singleton {}
class BarClass extends Singleton {}

var_dump(FooClass::getInstance());
var_dump(BarClass::getInstance());

Taťku všech singletoních tříd nám v tomto případě dělá třída Singleton, která na první pohled implementuje základní funkčnost singletoních objetů v PHP. Zkušenější vývojář v PHP už ví, že je tu probém, protože jak volání FooClass::getInstance() tak BarClass::getInstance() způsobí fatal error. Řádek self::$instance = new self; totiž neuloží do statické vlastnosti novou instanci třídyFooClass ani BarClass, nýbrž se pokusí vytvořit instanci třídy Singleton, která je však abstraktní a tak zkolabuje. O tomto problému se ví a tak PHP 5.3 přichází s Late Static Bindings , tak to aplikujeme na náš příklad.

abstract class Singleton
{
	protected static $instance;
	private function __construct() {}

	final public static function getInstance()
	{
		if (empty(static::$instance))
			static::$instance = new static;
		return static::$instance;
	}
}

To by nebylo PHP, aby i v tomto stavu neměly děti s taťkou problém. Příklad nám totiž konečně vrátí zdravého potomka, ale když požádáme po prvním potomkovi i o jeho bratra, tak nám opět bude vrácen potomek první. Statická vlastnost $instance totiž není udržována vrámci jednotlivých potomků, ale železnou ruku má v toto případě stále nezbedný tatínek. Byly tu dokonce i pokusy nahlásit toto chování jako bug viz 44315 a 44457 , ale o bugy se údajně nejedná i když podle mě to bug je (Pokud někdo víte o vláknu, ve kterém se zavedení “dynamic creation of static properties” mluvilo do komentářů, prosím - rád si přečtu, proč že to tam není).

Zbývá nám tedy pokusit se oklikou vyřešit problém s naším nezbedným tatínkem.

abstract class Singleton
{
	private static $instances = array();
	private function __construct() {}

	final public static function getInstance()
	{
		$class = get_called_class();
		if (!isset(self::$instances[$class]))
			self::$instances[$class] = new $class;
		return self::$instances[$class];
	}
}

Pikantní na tomto řešení je, že bude fungovat i na staších verzích PHP5 než 5.3.

« »