Bonjour à tous.
Ca fait un moment que je joues avec les singletons en PHP 5.3. Des singletons simples qui portent leur propre instance aux singletons factories en passant par des singletons génériques à hériter (merci le late static binding), j'ai à peu près fait le tour.
Il y a 1 semaine, je suis tombé sur un singleton factory remarquablement ingénieux fait par Stealth35. Ce singleton permet l'usage de cette syntaxe:
Pratique non ?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 Singleton::PDO($dsn, $user, $pwd); // ... Singleton::PDO()->query($query);
Voici l'implémentation originale du Singleton:
Cette classe très simple fonctionne très bien mais à deux défauts bien gênants:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13 final class Singleton { private static $_instances; public static function __callStatic ($name, $args) { if (!isset(static::$_instances[$name]) || !empty(args)) { $class = new ReflectionClass($name); static::$_instances[$name] = $class->newInstanceArgs($args); } return static::$_instnaces[$name]; } }
- en PHP inférieur à 5.3.4, si l'objet qu'on essaie d'instancier n'a pas de constructeur ou que son constructeur ne reçoit pas de paramètres (SplObjectStorage par exemple) ReflectionClass lèves une ReflexionException lors de l'appel de ReflectionClass::newInstanceArgs()
- il n'est pas possible (ou difficilement / salement) d'instancier des classes d'un autre namespace que global
De plus, ReflectionException semble incappable d'utiliser l'autoloading (et vu que cette classe n'a pratiquement pas de documentation...)
C'est pourquoi je me suis permi de l'améliorer légèrement:
Ainsi, on peut désormais changer le namespace à utiliser pour créer une nouvelle instance de singleton et laisser l'autoloader charger pour nous les classes dynamiquement.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72 final class Singleton extends Object { /** * Singleton's instances * @var object */ private static $_instances; /** * Let the singleton use any namespaced * class. * @var string */ private static $_current_namespace = ''; /** * Use auotloader flag * @var boolean */ private static $_autoload = false; /** * __callStatic overloading. * * Allow the use of the factory. * E.G. * > Singleton::PDO($dsn, $user, $pwd); * * @param string $name * @param array $arguments * @return object */ public static function __callStatic($name, array $args) { $name = static::$_current_namespace . '\\' . $name; if(empty(static::$_instances[$name]) || !empty($args)) { if (class_exists($name, static::$_autoload)) { $class = new ReflectionClass($name); static::$_instances[$name] = !empty($args) ? $class->newInstanceArgs($args) : $class->newInstance(); } else throw new RuntimeException("Class $name not found"); } return static::$_instances[$name]; } /** * Sets the namespace to be used * @param string $ns = '' * @return void */ public static function setNamespace ($ns = '') { if (strpos($ns, '\\') === 0) $ns = substr($ns, 1); static::$_current_namespace = $ns; } /** * Enable the Singleton to use autoloader * @return void */ public static function enableAutoload () { static::$_autoload = true; } /** * Do not let the Singleton to use autoloader * @return void */ public static function disableAutoload () { static::$_autoload = false; } }
E.G.
J'attends vos retours, et plus particulièrement les tiens Stealth.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 Singleton::setNamespace('App\core'); Singleton::enableAutoload(); Singleton::MockObject('test'); // charge \App\Core\MockObject Singleton::MockObject()->test = "hello";
Partager