14
14
use CodeIgniter \Database \ConnectionInterface ;
15
15
use CodeIgniter \Model ;
16
16
use Config \Services ;
17
+ use InvalidArgumentException ;
17
18
18
19
/**
19
20
* Factories for creating instances.
@@ -51,9 +52,11 @@ class Factories
51
52
];
52
53
53
54
/**
54
- * Mapping of class basenames (no namespace) to
55
+ * Mapping of classnames (with or without namespace) to
55
56
* their instances.
56
57
*
58
+ * [component => [name => FQCN]]
59
+ *
57
60
* @var array<string, array<string, string>>
58
61
* @phpstan-var array<string, array<string, class-string>>
59
62
*/
@@ -72,6 +75,37 @@ class Factories
72
75
*/
73
76
protected static $ instances = [];
74
77
78
+ /**
79
+ * Define the class to load. You can *override* the concrete class.
80
+ *
81
+ * @param string $component Lowercase, plural component name
82
+ * @param string $name Classname. The first parameter of Factories magic method
83
+ * @param string $classname FQCN to load
84
+ * @phpstan-param class-string $classname FQCN to load
85
+ */
86
+ public static function define (string $ component , string $ name , string $ classname ): void
87
+ {
88
+ if (isset (self ::$ basenames [$ component ][$ name ])) {
89
+ if (self ::$ basenames [$ component ][$ name ] === $ classname ) {
90
+ return ;
91
+ }
92
+
93
+ throw new InvalidArgumentException (
94
+ 'Already defined in Factories: ' . $ component . ' ' . $ name . ' -> ' . self ::$ basenames [$ component ][$ name ]
95
+ );
96
+ }
97
+
98
+ if (! class_exists ($ classname )) {
99
+ throw new InvalidArgumentException ('No such class: ' . $ classname );
100
+ }
101
+
102
+ // Force a configuration to exist for this component.
103
+ // Otherwise, getOptions() will reset the component.
104
+ self ::getOptions ($ component );
105
+
106
+ self ::$ basenames [$ component ][$ name ] = $ classname ;
107
+ }
108
+
75
109
/**
76
110
* Loads instances based on the method component name. Either
77
111
* creates a new instance or returns an existing shared instance.
@@ -88,22 +122,52 @@ public static function __callStatic(string $component, array $arguments)
88
122
$ options = array_merge (self ::getOptions (strtolower ($ component )), $ options );
89
123
90
124
if (! $ options ['getShared ' ]) {
125
+ if (isset (self ::$ basenames [$ component ][$ name ])) {
126
+ $ class = self ::$ basenames [$ component ][$ name ];
127
+
128
+ return new $ class (...$ arguments );
129
+ }
130
+
91
131
if ($ class = self ::locateClass ($ options , $ name )) {
92
132
return new $ class (...$ arguments );
93
133
}
94
134
95
135
return null ;
96
136
}
97
137
98
- $ basename = self ::getBasename ($ name );
99
-
100
138
// Check for an existing instance
101
- if (isset (self ::$ basenames [$ options ['component ' ]][$ basename ])) {
102
- $ class = self ::$ basenames [$ options ['component ' ]][$ basename ];
139
+ if (isset (self ::$ basenames [$ options ['component ' ]][$ name ])) {
140
+ $ class = self ::$ basenames [$ options ['component ' ]][$ name ];
103
141
104
142
// Need to verify if the shared instance matches the request
105
143
if (self ::verifyInstanceOf ($ options , $ class )) {
144
+ if (isset (self ::$ instances [$ options ['component ' ]][$ class ])) {
145
+ return self ::$ instances [$ options ['component ' ]][$ class ];
146
+ }
147
+ self ::$ instances [$ options ['component ' ]][$ class ] = new $ class (...$ arguments );
148
+
106
149
return self ::$ instances [$ options ['component ' ]][$ class ];
150
+
151
+ }
152
+ }
153
+
154
+ // Check for an existing Config instance with basename.
155
+ if (self ::isConfig ($ options ['component ' ])) {
156
+ $ basename = self ::getBasename ($ name );
157
+
158
+ if (isset (self ::$ basenames [$ options ['component ' ]][$ basename ])) {
159
+ $ class = self ::$ basenames [$ options ['component ' ]][$ basename ];
160
+
161
+ // Need to verify if the shared instance matches the request
162
+ if (self ::verifyInstanceOf ($ options , $ class )) {
163
+ if (isset (self ::$ instances [$ options ['component ' ]][$ class ])) {
164
+ return self ::$ instances [$ options ['component ' ]][$ class ];
165
+ }
166
+ self ::$ instances [$ options ['component ' ]][$ class ] = new $ class (...$ arguments );
167
+
168
+ return self ::$ instances [$ options ['component ' ]][$ class ];
169
+
170
+ }
107
171
}
108
172
}
109
173
@@ -112,8 +176,13 @@ public static function __callStatic(string $component, array $arguments)
112
176
return null ;
113
177
}
114
178
115
- self ::$ instances [$ options ['component ' ]][$ class ] = new $ class (...$ arguments );
116
- self ::$ basenames [$ options ['component ' ]][$ basename ] = $ class ;
179
+ self ::$ instances [$ options ['component ' ]][$ class ] = new $ class (...$ arguments );
180
+ self ::$ basenames [$ options ['component ' ]][$ name ] = $ class ;
181
+
182
+ // If a short classname is specified, also register FQCN to share the instance.
183
+ if (! isset (self ::$ basenames [$ options ['component ' ]][$ class ])) {
184
+ self ::$ basenames [$ options ['component ' ]][$ class ] = $ class ;
185
+ }
117
186
118
187
return self ::$ instances [$ options ['component ' ]][$ class ];
119
188
}
@@ -153,7 +222,9 @@ class_exists($name, false)
153
222
154
223
// If an App version was requested then see if it verifies
155
224
if (
156
- $ options ['preferApp ' ] && class_exists ($ appname )
225
+ // preferApp is used only for no namespace class or Config class.
226
+ (strpos ($ name , '\\' ) === false || self ::isConfig ($ options ['component ' ]))
227
+ && $ options ['preferApp ' ] && class_exists ($ appname )
157
228
&& self ::verifyInstanceOf ($ options , $ name )
158
229
) {
159
230
return $ appname ;
@@ -326,8 +397,12 @@ public static function injectMock(string $component, string $name, object $insta
326
397
$ class = get_class ($ instance );
327
398
$ basename = self ::getBasename ($ name );
328
399
329
- self ::$ instances [$ component ][$ class ] = $ instance ;
330
- self ::$ basenames [$ component ][$ basename ] = $ class ;
400
+ self ::$ instances [$ component ][$ class ] = $ instance ;
401
+ self ::$ basenames [$ component ][$ name ] = $ class ;
402
+
403
+ if (self ::isConfig ($ component )) {
404
+ self ::$ basenames [$ component ][$ basename ] = $ class ;
405
+ }
331
406
}
332
407
333
408
/**
0 commit comments