Complex classes like Container often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Container, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
59 | class Container extends Dispatcher implements ArrayAccess, ContainerInterface |
||
60 | { |
||
61 | /** |
||
62 | * The objects store |
||
63 | * @var SplObjectStorage |
||
64 | */ |
||
65 | protected $store = array(); |
||
66 | |||
67 | /** |
||
68 | * Shared instances store |
||
69 | * @var SplObjectStorage |
||
70 | */ |
||
71 | private $_sharedInstances; |
||
|
|||
72 | |||
73 | /** |
||
74 | * Container Properties |
||
75 | * @var array |
||
76 | */ |
||
77 | protected $properties = array(); |
||
78 | |||
79 | /** |
||
80 | * Properties Keys (cached) |
||
81 | * @var array |
||
82 | */ |
||
83 | protected $propertiesMap = array(); |
||
84 | |||
85 | /** |
||
86 | * Delegates Containers |
||
87 | * @var SplObjectStorage |
||
88 | */ |
||
89 | protected $delegates; |
||
90 | |||
91 | /** |
||
92 | * Constructor |
||
93 | * |
||
94 | * @return void |
||
95 | */ |
||
96 | 39 | public function __construct() |
|
97 | { |
||
98 | 39 | $this->store = new SplObjectStorage(); |
|
99 | 39 | $this->delegates = new SplObjectStorage(); |
|
100 | 39 | $this->_sharedInstances = new SplObjectStorage(); |
|
101 | 39 | $this->set('self', $this); |
|
102 | 39 | } |
|
103 | |||
104 | /** |
||
105 | * Registers a definition |
||
106 | * |
||
107 | * @param string $name Identifier |
||
108 | * @param DefinitionInterface|mixed $definition Definition, callable or value |
||
109 | * |
||
110 | * @return Container |
||
111 | */ |
||
112 | 39 | public function set($name, $definition) |
|
113 | { |
||
114 | 39 | if (!$definition instanceof DefinitionInterface) { |
|
115 | 39 | if (is_callable($definition)) { |
|
116 | 23 | $definition = CallableDefinition::factory($definition); |
|
117 | 39 | } elseif (is_array($definition)) { |
|
118 | $definition = ArrayDefinition::factory($definition); |
||
119 | } else { |
||
120 | 39 | $wasObj = is_object($definition); |
|
121 | 39 | $definition = ScalarDefinition::factory($definition); |
|
122 | 39 | if ($wasObj) { |
|
123 | 39 | $definition->setShared(true); |
|
124 | 39 | $this->_sharedInstances->attach($definition, $name); |
|
125 | } |
||
126 | } |
||
127 | } |
||
128 | |||
129 | 39 | $event = new BeforeServiceRegisteredEvent($this, $name, $definition); |
|
130 | 39 | $this->notify($event); |
|
131 | |||
132 | 39 | if ($event->isStopped()) { |
|
133 | return $this; |
||
134 | } |
||
135 | |||
136 | 39 | $this->store->attach($definition, $name); |
|
137 | 39 | $this->notify(new AfterServiceRegisteredEvent($this, $name, $definition)); |
|
138 | |||
139 | 39 | return $this; |
|
140 | } |
||
141 | |||
142 | /** |
||
143 | * Load and returns a definition |
||
144 | * |
||
145 | * @param string $name Identifier |
||
146 | * |
||
147 | * @throws Exceptions\DefinitionNotFoundException if $name isn't a valid identifier |
||
148 | * @return mixed |
||
149 | */ |
||
150 | 26 | public function get($name) |
|
151 | { |
||
152 | 26 | if ($name instanceof Reference) { |
|
153 | $name = $name->getName(); |
||
154 | } |
||
155 | |||
156 | 26 | if (!$this->has($name)) { |
|
157 | 6 | return $this->getFromDelegate($name); |
|
158 | } |
||
159 | |||
160 | 20 | foreach ($this->store as $def) { |
|
161 | 20 | if ($this->store->getInfo() === $name) { |
|
162 | 20 | $definition = $def; |
|
163 | 20 | break; |
|
164 | } |
||
165 | } |
||
166 | |||
167 | /** @var DefinitionInterface $definition */ |
||
168 | 20 | if ($definition->isShared()) { |
|
169 | 4 | foreach ($this->_sharedInstances as $inst) { |
|
170 | 4 | if ($this->_sharedInstances->getInfo() === $name) { |
|
171 | 4 | return $inst; |
|
172 | } |
||
173 | } |
||
174 | } |
||
175 | |||
176 | 20 | $event = new BeforeServiceLoadedEvent($this, $name, $definition); |
|
177 | 20 | $this->notify($event); |
|
178 | |||
179 | // the event has been stopped |
||
180 | 20 | if ($event->isStopped()) { |
|
181 | 1 | $return = $event->getReturnValue(); |
|
182 | |||
183 | 1 | if ($definition->isShared()) { |
|
184 | $this->_sharedInstances->attach($return, $name); |
||
185 | } |
||
186 | |||
187 | 1 | return $return; |
|
188 | } |
||
189 | |||
190 | 19 | $return = $definition->invoke($this, $name); |
|
191 | 19 | if ($definition->isShared()) { |
|
192 | 4 | $this->_sharedInstances->attach($return, $name); |
|
193 | } |
||
194 | |||
195 | 19 | $afterEvent = new AfterServiceLoadedEvent($this, $name, $definition, $return); |
|
196 | 19 | $this->notify($afterEvent); |
|
197 | |||
198 | 19 | return $return; |
|
199 | } |
||
200 | |||
201 | /** |
||
202 | * Loads properties from an INI file as definitions. |
||
203 | * Theses properties can then be referenced like @propName in other |
||
204 | * definitions. |
||
205 | * |
||
206 | * @param string $iniFile Path/to/file.ini |
||
207 | * @param null|string $category The INI category to be parsed |
||
208 | * |
||
209 | * @return Container |
||
210 | * @throws Exception |
||
211 | */ |
||
212 | 5 | public function iniProperties($iniFile, $category = null) |
|
213 | { |
||
214 | 5 | if (!is_file($iniFile) || !is_readable($iniFile)) { |
|
215 | throw new Exception('INI file not found/readable: '. $iniFile); |
||
216 | } |
||
217 | |||
218 | 5 | $props = parse_ini_file($iniFile, ($category !== null)); |
|
219 | 5 | if ($category !== null) { |
|
220 | $props = (isset($props[$category]) ? $props[$category] : false); |
||
221 | } |
||
222 | |||
223 | 5 | if (!is_array($props)) { |
|
224 | throw new Exception("No properties found in: $iniFile [$category]"); |
||
225 | } |
||
226 | |||
227 | 5 | foreach ($props as $key => $prop) { |
|
228 | 5 | $this->properties[$key] = str_replace(':packageDir', dirname($iniFile), $prop); |
|
229 | 5 | $this->propertiesMap[$key] = ":". $key; |
|
230 | } |
||
231 | |||
232 | 5 | return $this; |
|
233 | } |
||
234 | |||
235 | /** |
||
236 | * Returns a property (or $default if not defined) |
||
237 | * |
||
238 | * @param string $propName The property name |
||
239 | * @param mixed $default Default value if the property is not defined |
||
240 | * |
||
241 | * @return mixed |
||
242 | */ |
||
243 | 3 | public function getProperty($propName, $default = null) |
|
244 | { |
||
245 | 3 | return (array_key_exists($propName, $this->properties) ? |
|
246 | 3 | $this->propertizeString($this->properties[$propName]) : |
|
247 | 1 | (is_string($default) ? |
|
248 | $this->propertizeString($default) : |
||
249 | 3 | $default |
|
250 | ) |
||
251 | ); |
||
252 | } |
||
253 | |||
254 | /** |
||
255 | * Defines a property. |
||
256 | * |
||
257 | * If the $value is null, the property will be unset. |
||
258 | * |
||
259 | * It recommended to store only strings as property values. Register a |
||
260 | * new Di definition for any other type. |
||
261 | * |
||
262 | * @param string $propName Property name |
||
263 | * @param null|string $value The prop value |
||
264 | * |
||
265 | * @return Container |
||
266 | */ |
||
267 | 7 | public function setProperty($propName, $value = null) |
|
268 | { |
||
269 | 7 | if (array_key_exists($propName, $this->properties) && $value === null) { |
|
270 | unset($this->properties[$propName]); |
||
271 | unset($this->propertiesMap[$propName]); |
||
272 | return $this; |
||
273 | } |
||
274 | |||
275 | 7 | $this->properties[(string)$propName] = (string)$value; |
|
276 | 7 | $this->propertiesMap[(string)$propName] = ":". (string)$propName; |
|
277 | |||
278 | 7 | return $this; |
|
279 | } |
||
280 | |||
281 | |||
282 | /** |
||
283 | * Transform properties references to their respective value |
||
284 | * |
||
285 | * @param string $str String to be transformed |
||
286 | * |
||
287 | * @return string |
||
288 | */ |
||
289 | 22 | public function propertizeString($str) |
|
290 | { |
||
291 | 22 | return str_replace( |
|
292 | 22 | array_values($this->propertiesMap), |
|
293 | 22 | array_values($this->properties), |
|
294 | $str |
||
295 | ); |
||
296 | } |
||
297 | |||
298 | /** |
||
299 | * Unregisters a definition |
||
300 | * |
||
301 | * @param string $name Identifier |
||
302 | * |
||
303 | * @throws Exceptions\DefinitionNotFoundException if $name isn't a valid identifier |
||
304 | * @return boolean true on success |
||
305 | */ |
||
306 | 3 | public function unregister($name) |
|
307 | { |
||
308 | 3 | if (!$this->has($name)) { |
|
309 | 1 | throw new Exceptions\DefinitionNotFoundException($name); |
|
310 | } |
||
311 | |||
312 | 2 | $this->store->detach($this->getDefinition($name)); |
|
313 | 2 | foreach ($this->_sharedInstances as $inst => $defName) { |
|
314 | 2 | if ($defName === $name) { |
|
315 | $this->_sharedInstances->detach((object)$inst); |
||
316 | 2 | break; |
|
317 | } |
||
318 | } |
||
319 | |||
320 | 2 | return true; |
|
321 | } |
||
322 | |||
323 | /** |
||
324 | * Tells if a definition exists at $offset |
||
325 | * |
||
326 | * @param string $name Identifier |
||
327 | * |
||
328 | * @return boolean |
||
329 | */ |
||
330 | 27 | public function has($name) |
|
331 | { |
||
332 | 27 | foreach ($this->store as $def) { |
|
333 | 27 | if ($this->store->getInfo() === $name) { |
|
334 | 27 | return true; |
|
335 | } |
||
336 | } |
||
337 | |||
338 | 15 | return false; |
|
339 | } |
||
340 | |||
341 | /** |
||
342 | * Returns a Definition |
||
343 | * |
||
344 | * @param string $name Identifier |
||
345 | * |
||
346 | * @return DefinitionInterface |
||
347 | * @throws Exceptions\DefinitionNotFoundException if $name isn't a valid identifier |
||
348 | */ |
||
349 | 4 | public function getDefinition($name) |
|
350 | { |
||
351 | 4 | foreach ($this->store as $def) { |
|
352 | 4 | if ($this->store->getInfo() === $name) { |
|
353 | 4 | return $def; |
|
354 | } |
||
355 | } |
||
356 | |||
357 | throw new Exceptions\DefinitionNotFoundException($name); |
||
358 | } |
||
359 | |||
360 | /** |
||
361 | * Tells if a definition is registered at $offset |
||
362 | * |
||
363 | * @param string $offset Identifier |
||
364 | * |
||
365 | * @return boolean |
||
366 | */ |
||
367 | public function offsetExists($offset) |
||
371 | |||
372 | /** |
||
373 | * Loads and returns a definition |
||
374 | * |
||
375 | * @param string $offset Identifier |
||
376 | * |
||
377 | * @return mixed |
||
378 | */ |
||
379 | public function offsetGet($offset) |
||
383 | |||
384 | /** |
||
385 | * Registers a definition |
||
386 | * |
||
387 | * @param string $offset Identifier |
||
388 | * @param mixed $value Definition |
||
389 | * |
||
390 | * @return Container |
||
391 | */ |
||
392 | 16 | public function offsetSet($offset, $value) |
|
396 | |||
397 | /** |
||
398 | * Unregisters a Definition |
||
399 | * |
||
400 | * @param string $offset Identifier |
||
401 | * |
||
402 | * @return boolean |
||
403 | */ |
||
404 | public function offsetUnset($offset) |
||
408 | |||
409 | /** |
||
410 | * Adds a delegate/backup Container. |
||
411 | * |
||
412 | * @param ContainerInterface $container |
||
413 | * |
||
414 | * @return ContainerInterface |
||
415 | */ |
||
416 | public function delegate(ContainerInterface $container) |
||
426 | |||
427 | /** |
||
428 | * Tells if a service is in a delegated Container |
||
429 | * |
||
430 | * @param string $name |
||
431 | * |
||
432 | * @return boolean |
||
433 | */ |
||
434 | public function hasInDelegate($name) |
||
435 | { |
||
436 | foreach ($this->delegates as $container) { |
||
445 | |||
446 | /** |
||
447 | * Loads a definition from the first delegated Container having in (FIFO) |
||
448 | * |
||
449 | * @param string $name Service identifier |
||
450 | * |
||
451 | * @throws Exceptions\DefinitionNotFoundException when the service is not found |
||
452 | * @return mixed |
||
453 | */ |
||
454 | 6 | public function getFromDelegate($name) |
|
465 | |||
466 | /** |
||
467 | * Search definitions |
||
468 | * |
||
469 | * @param array $query Search query |
||
470 | * |
||
471 | * @return array<DefinitionInterface> |
||
472 | */ |
||
473 | 1 | public function search(array $query) |
|
486 | } |
This check examines a number of code elements and verifies that they conform to the given naming conventions.
You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.