Rodičovské problémy Táty Singletonovského objektu
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.