ClassRegistry   C
last analyzed

Complexity

Total Complexity 61

Size/Duplication

Total Lines 338
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 338
rs 6.018
wmc 61
lcom 1
cbo 4

13 Methods

Rating   Name   Duplication   Size   Complexity  
A getInstance() 0 7 2
F init() 0 106 30
A addObject() 0 9 2
A removeObject() 0 7 2
A isKeySet() 0 6 2
A keys() 0 3 1
A getObject() 0 14 3
B config() 0 16 8
B _duplicate() 0 11 5
A map() 0 8 2
A mapKeys() 0 3 1
A _getMap() 0 5 2
A flush() 0 5 1

How to fix   Complexity   

Complex Class

Complex classes like ClassRegistry 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 ClassRegistry, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
4
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
5
 *
6
 * Licensed under The MIT License
7
 * For full copyright and license information, please see the LICENSE.txt
8
 * Redistributions of files must retain the above copyright notice.
9
 *
10
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
11
 * @link          http://cakephp.org CakePHP(tm) Project
12
 * @package       Cake.Utility
13
 * @since         CakePHP(tm) v 0.9.2
14
 * @license       http://www.opensource.org/licenses/mit-license.php MIT License
15
 */
16
17
/**
18
 * Included libraries.
19
 */
20
App::uses('Model', 'Model');
21
App::uses('AppModel', 'Model');
22
App::uses('ConnectionManager', 'Model');
23
24
/**
25
 * Class Collections.
26
 *
27
 * A repository for class objects, each registered with a key.
28
 * If you try to add an object with the same key twice, nothing will come of it.
29
 * If you need a second instance of an object, give it another key.
30
 *
31
 * @package       Cake.Utility
32
 */
33
class ClassRegistry {
34
35
/**
36
 * Names of classes with their objects.
37
 *
38
 * @var array
39
 */
40
	protected $_objects = array();
41
42
/**
43
 * Names of class names mapped to the object in the registry.
44
 *
45
 * @var array
46
 */
47
	protected $_map = array();
48
49
/**
50
 * Default constructor parameter settings, indexed by type
51
 *
52
 * @var array
53
 */
54
	protected $_config = array();
55
56
/**
57
 * Return a singleton instance of the ClassRegistry.
58
 *
59
 * @return ClassRegistry instance
60
 */
61
	public static function getInstance() {
62
		static $instance = array();
63
		if (!$instance) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $instance of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
64
			$instance[0] = new ClassRegistry();
65
		}
66
		return $instance[0];
67
	}
68
69
/**
70
 * Loads a class, registers the object in the registry and returns instance of the object. ClassRegistry::init()
71
 * is used as a factory for models, and handle correct injecting of settings, that assist in testing.
72
 *
73
 * Examples
74
 * Simple Use: Get a Post model instance ```ClassRegistry::init('Post');```
75
 *
76
 * Expanded: ```array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry');```
77
 *
78
 * Model Classes can accept optional ```array('id' => $id, 'table' => $table, 'ds' => $ds, 'alias' => $alias);```
79
 *
80
 * When $class is a numeric keyed array, multiple class instances will be stored in the registry,
81
 *  no instance of the object will be returned
82
 * {{{
83
 * array(
84
 *		array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry'),
85
 *		array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry'),
86
 *		array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry')
87
 * );
88
 * }}}
89
 * @param string|array $class as a string or a single key => value array instance will be created,
90
 *  stored in the registry and returned.
91
 * @param boolean $strict if set to true it will return false if the class was not found instead
92
 *	of trying to create an AppModel
93
 * @return object instance of ClassName.
94
 * @throws CakeException when you try to construct an interface or abstract class.
95
 */
96
	public static function init($class, $strict = false) {
97
		$_this = ClassRegistry::getInstance();
98
99
		if (is_array($class)) {
100
			$objects = $class;
101
			if (!isset($class[0])) {
102
				$objects = array($class);
103
			}
104
		} else {
105
			$objects = array(array('class' => $class));
106
		}
107
		$defaults = array();
108
		if (isset($_this->_config['Model'])) {
109
			$defaults = $_this->_config['Model'];
110
		}
111
		$count = count($objects);
112
		$availableDs = null;
113
114
		foreach ($objects as $settings) {
115
			if (is_numeric($settings)) {
116
				trigger_error(__d('cake_dev', '(ClassRegistry::init() Attempted to create instance of a class with a numeric name'), E_USER_WARNING);
117
				return false;
118
			}
119
120
			if (is_array($settings)) {
121
				$pluginPath = null;
122
				$settings = array_merge($defaults, $settings);
123
				$class = $settings['class'];
124
125
				list($plugin, $class) = pluginSplit($class);
126
				if ($plugin) {
127
					$pluginPath = $plugin . '.';
128
					$settings['plugin'] = $plugin;
129
				}
130
131
				if (empty($settings['alias'])) {
132
					$settings['alias'] = $class;
133
				}
134
				$alias = $settings['alias'];
135
136
				$model = $_this->_duplicate($alias, $class);
137
				if ($model) {
138
					$_this->map($alias, $class);
139
					return $model;
140
				}
141
142
				App::uses($plugin . 'AppModel', $pluginPath . 'Model');
143
				App::uses($class, $pluginPath . 'Model');
144
145
				if (class_exists($class) || interface_exists($class)) {
146
					$reflection = new ReflectionClass($class);
147
					if ($reflection->isAbstract() || $reflection->isInterface()) {
148
						throw new CakeException(__d('cake_dev', 'Cannot create instance of %s, as it is abstract or is an interface', $class));
149
					}
150
					$testing = isset($settings['testing']) ? $settings['testing'] : false;
151
					if ($testing) {
152
						$settings['ds'] = 'test';
153
						$defaultProperties = $reflection->getDefaultProperties();
154
						if (isset($defaultProperties['useDbConfig'])) {
155
							$useDbConfig = $defaultProperties['useDbConfig'];
156
							if ($availableDs === null) {
157
								$availableDs = array_keys(ConnectionManager::enumConnectionObjects());
158
							}
159
							if (in_array('test_' . $useDbConfig, $availableDs)) {
160
								$useDbConfig = 'test_' . $useDbConfig;
161
							}
162
							if (strpos($useDbConfig, 'test') === 0) {
163
								$settings['ds'] = $useDbConfig;
164
							}
165
						}
166
					}
167
					if ($reflection->getConstructor()) {
168
						$instance = $reflection->newInstance($settings);
169
					} else {
170
						$instance = $reflection->newInstance();
171
					}
172
					if ($strict && !$instance instanceof Model) {
173
						$instance = null;
174
					}
175
				}
176
				if (!isset($instance)) {
177
					$appModel = 'AppModel';
178
					if ($strict) {
179
						return false;
180
					} elseif ($plugin && class_exists($plugin . 'AppModel')) {
181
						$appModel = $plugin . 'AppModel';
182
					}
183
					if (!empty($appModel)) {
184
						$settings['name'] = $class;
185
						$instance = new $appModel($settings);
186
					}
187
188
					if (!isset($instance)) {
189
						trigger_error(__d('cake_dev', '(ClassRegistry::init() could not create instance of %s', $class), E_USER_WARNING);
190
						return false;
191
					}
192
				}
193
				$_this->map($alias, $class);
194
			}
195
		}
196
197
		if ($count > 1) {
198
			return true;
199
		}
200
		return $instance;
0 ignored issues
show
Bug introduced by
The variable $instance does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
201
	}
202
203
/**
204
 * Add $object to the registry, associating it with the name $key.
205
 *
206
 * @param string $key Key for the object in registry
207
 * @param object $object Object to store
208
 * @return boolean True if the object was written, false if $key already exists
209
 */
210
	public static function addObject($key, $object) {
211
		$_this = ClassRegistry::getInstance();
212
		$key = Inflector::underscore($key);
213
		if (!isset($_this->_objects[$key])) {
214
			$_this->_objects[$key] = $object;
215
			return true;
216
		}
217
		return false;
218
	}
219
220
/**
221
 * Remove object which corresponds to given key.
222
 *
223
 * @param string $key Key of object to remove from registry
224
 * @return void
225
 */
226
	public static function removeObject($key) {
227
		$_this = ClassRegistry::getInstance();
228
		$key = Inflector::underscore($key);
229
		if (isset($_this->_objects[$key])) {
230
			unset($_this->_objects[$key]);
231
		}
232
	}
233
234
/**
235
 * Returns true if given key is present in the ClassRegistry.
236
 *
237
 * @param string $key Key to look for
238
 * @return boolean true if key exists in registry, false otherwise
239
 */
240
	public static function isKeySet($key) {
241
		$_this = ClassRegistry::getInstance();
242
		$key = Inflector::underscore($key);
243
244
		return isset($_this->_objects[$key]) || isset($_this->_map[$key]);
245
	}
246
247
/**
248
 * Get all keys from the registry.
249
 *
250
 * @return array Set of keys stored in registry
251
 */
252
	public static function keys() {
253
		return array_keys(ClassRegistry::getInstance()->_objects);
254
	}
255
256
/**
257
 * Return object which corresponds to given key.
258
 *
259
 * @param string $key Key of object to look for
260
 * @return mixed Object stored in registry or boolean false if the object does not exist.
261
 */
262
	public static function getObject($key) {
263
		$_this = ClassRegistry::getInstance();
264
		$key = Inflector::underscore($key);
265
		$return = false;
266
		if (isset($_this->_objects[$key])) {
267
			$return = $_this->_objects[$key];
268
		} else {
269
			$key = $_this->_getMap($key);
270
			if (isset($_this->_objects[$key])) {
271
				$return = $_this->_objects[$key];
272
			}
273
		}
274
		return $return;
275
	}
276
277
/**
278
 * Sets the default constructor parameter for an object type
279
 *
280
 * @param string $type Type of object. If this parameter is omitted, defaults to "Model"
281
 * @param array $param The parameter that will be passed to object constructors when objects
282
 *                      of $type are created
283
 * @return mixed Void if $param is being set. Otherwise, if only $type is passed, returns
284
 *               the previously-set value of $param, or null if not set.
285
 */
286
	public static function config($type, $param = array()) {
287
		$_this = ClassRegistry::getInstance();
288
289
		if (empty($param) && is_array($type)) {
290
			$param = $type;
291
			$type = 'Model';
292
		} elseif ($param === null) {
293
			unset($_this->_config[$type]);
294
		} elseif (empty($param) && is_string($type)) {
295
			return isset($_this->_config[$type]) ? $_this->_config[$type] : null;
296
		}
297
		if (isset($_this->_config[$type]['testing'])) {
298
			$param['testing'] = true;
299
		}
300
		$_this->_config[$type] = $param;
301
	}
302
303
/**
304
 * Checks to see if $alias is a duplicate $class Object
305
 *
306
 * @param string $alias
307
 * @param string $class
308
 * @return boolean
309
 */
310
	protected function &_duplicate($alias, $class) {
311
		$duplicate = false;
312
		if ($this->isKeySet($alias)) {
313
			$model = $this->getObject($alias);
314
			if (is_object($model) && ($model instanceof $class || $model->alias === $class)) {
315
				$duplicate = $model;
316
			}
317
			unset($model);
318
		}
319
		return $duplicate;
320
	}
321
322
/**
323
 * Add a key name pair to the registry to map name to class in the registry.
324
 *
325
 * @param string $key Key to include in map
326
 * @param string $name Key that is being mapped
327
 * @return void
328
 */
329
	public static function map($key, $name) {
330
		$_this = ClassRegistry::getInstance();
331
		$key = Inflector::underscore($key);
332
		$name = Inflector::underscore($name);
333
		if (!isset($_this->_map[$key])) {
334
			$_this->_map[$key] = $name;
335
		}
336
	}
337
338
/**
339
 * Get all keys from the map in the registry.
340
 *
341
 * @return array Keys of registry's map
342
 */
343
	public static function mapKeys() {
344
		return array_keys(ClassRegistry::getInstance()->_map);
345
	}
346
347
/**
348
 * Return the name of a class in the registry.
349
 *
350
 * @param string $key Key to find in map
351
 * @return string Mapped value
352
 */
353
	protected function _getMap($key) {
354
		if (isset($this->_map[$key])) {
355
			return $this->_map[$key];
356
		}
357
	}
358
359
/**
360
 * Flushes all objects from the ClassRegistry.
361
 *
362
 * @return void
363
 */
364
	public static function flush() {
365
		$_this = ClassRegistry::getInstance();
366
		$_this->_objects = array();
367
		$_this->_map = array();
368
	}
369
370
}
371