1: <?php
2: /**
3: * Class collections.
4: *
5: * A repository for class objects, each registered with a key.
6: *
7: * PHP 5
8: *
9: * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
10: * Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
11: *
12: * Licensed under The MIT License
13: * Redistributions of files must retain the above copyright notice.
14: *
15: * @copyright Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
16: * @link http://cakephp.org CakePHP(tm) Project
17: * @package Cake.Utility
18: * @since CakePHP(tm) v 0.9.2
19: * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
20: */
21:
22: /**
23: * Class Collections.
24: *
25: * A repository for class objects, each registered with a key.
26: * If you try to add an object with the same key twice, nothing will come of it.
27: * If you need a second instance of an object, give it another key.
28: *
29: * @package Cake.Utility
30: */
31: class ClassRegistry {
32:
33: /**
34: * Names of classes with their objects.
35: *
36: * @var array
37: */
38: protected $_objects = array();
39:
40: /**
41: * Names of class names mapped to the object in the registry.
42: *
43: * @var array
44: */
45: protected $_map = array();
46:
47: /**
48: * Default constructor parameter settings, indexed by type
49: *
50: * @var array
51: */
52: protected $_config = array();
53:
54: /**
55: * Return a singleton instance of the ClassRegistry.
56: *
57: * @return ClassRegistry instance
58: */
59: public static function &getInstance() {
60: static $instance = array();
61: if (!$instance) {
62: $instance[0] = new ClassRegistry();
63: }
64: return $instance[0];
65: }
66:
67: /**
68: * Loads a class, registers the object in the registry and returns instance of the object. ClassRegistry::init()
69: * is used as a factory for models, and handle correct injecting of settings, that assist in testing.
70: *
71: * Examples
72: * Simple Use: Get a Post model instance ```ClassRegistry::init('Post');```
73: *
74: * Expanded: ```array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry', 'type' => 'Model');```
75: *
76: * Model Classes can accept optional ```array('id' => $id, 'table' => $table, 'ds' => $ds, 'alias' => $alias);```
77: *
78: * When $class is a numeric keyed array, multiple class instances will be stored in the registry,
79: * no instance of the object will be returned
80: * {{{
81: * array(
82: * array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry'),
83: * array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry'),
84: * array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry')
85: * );
86: * }}}
87: * @param mixed $class as a string or a single key => value array instance will be created,
88: * stored in the registry and returned.
89: * @param boolean $strict if set to true it will return false if the class was not found instead
90: * of trying to create an AppModel
91: * @return object instance of ClassName.
92: * @throws CakeException when you try to construct an interface or abstract class.
93: */
94: public static function init($class, $strict = false) {
95: $_this = ClassRegistry::getInstance();
96: $false = false;
97: $true = true;
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 = isset($_this->_config['Model']) ? $_this->_config['Model'] : array();
108: $count = count($objects);
109:
110: foreach ($objects as $key => $settings) {
111: if (is_array($settings)) {
112: $pluginPath = null;
113: $settings = array_merge($defaults, $settings);
114: $class = $settings['class'];
115:
116: list($plugin, $class) = pluginSplit($class);
117: if ($plugin) {
118: $pluginPath = $plugin . '.';
119: }
120:
121: if (empty($settings['alias'])) {
122: $settings['alias'] = $class;
123: }
124: $alias = $settings['alias'];
125:
126: if ($model = $_this->_duplicate($alias, $class)) {
127: $_this->map($alias, $class);
128: return $model;
129: }
130:
131: App::uses('Model', 'Model');
132: App::uses('AppModel', 'Model');
133: App::uses($plugin . 'AppModel', $pluginPath . 'Model');
134: App::uses($class, $pluginPath . 'Model');
135:
136: if (class_exists($class) || interface_exists($class)) {
137: $reflection = new ReflectionClass($class);
138: if ($reflection->isAbstract() || $reflection->isInterface()) {
139: throw new CakeException(__d('cake_dev', 'Cannot create instance of %s, as it is abstract or is an interface', $class));
140: }
141: if ($reflection->getConstructor()) {
142: $instance = $reflection->newInstance($settings);
143: } else {
144: $instance = $reflection->newInstance();
145: }
146: if ($strict) {
147: $instance = ($instance instanceof Model) ? $instance : null;
148: }
149: }
150: if (!isset($instance)) {
151: if ($strict) {
152: return false;
153: } elseif ($plugin && class_exists($plugin . 'AppModel')) {
154: $appModel = $plugin . 'AppModel';
155: } else {
156: $appModel = 'AppModel';
157: }
158: if (!empty($appModel)) {
159: $settings['name'] = $class;
160: $instance = new $appModel($settings);
161: }
162:
163: if (!isset($instance)) {
164: trigger_error(__d('cake_dev', '(ClassRegistry::init() could not create instance of %1$s class %2$s ', $class, $type), E_USER_WARNING);
165: return $false;
166: }
167: }
168: $_this->map($alias, $class);
169: } elseif (is_numeric($settings)) {
170: trigger_error(__d('cake_dev', '(ClassRegistry::init() Attempted to create instance of a class with a numeric name'), E_USER_WARNING);
171: return $false;
172: }
173: }
174:
175: if ($count > 1) {
176: return $true;
177: }
178: return $instance;
179: }
180:
181: /**
182: * Add $object to the registry, associating it with the name $key.
183: *
184: * @param string $key Key for the object in registry
185: * @param mixed $object Object to store
186: * @return boolean True if the object was written, false if $key already exists
187: */
188: public static function addObject($key, $object) {
189: $_this = ClassRegistry::getInstance();
190: $key = Inflector::underscore($key);
191: if (!isset($_this->_objects[$key])) {
192: $_this->_objects[$key] = $object;
193: return true;
194: }
195: return false;
196: }
197:
198: /**
199: * Remove object which corresponds to given key.
200: *
201: * @param string $key Key of object to remove from registry
202: * @return void
203: */
204: public static function removeObject($key) {
205: $_this = ClassRegistry::getInstance();
206: $key = Inflector::underscore($key);
207: if (isset($_this->_objects[$key])) {
208: unset($_this->_objects[$key]);
209: }
210: }
211:
212: /**
213: * Returns true if given key is present in the ClassRegistry.
214: *
215: * @param string $key Key to look for
216: * @return boolean true if key exists in registry, false otherwise
217: */
218: public static function isKeySet($key) {
219: $_this = ClassRegistry::getInstance();
220: $key = Inflector::underscore($key);
221: if (isset($_this->_objects[$key])) {
222: return true;
223: } elseif (isset($_this->_map[$key])) {
224: return true;
225: }
226: return false;
227: }
228:
229: /**
230: * Get all keys from the registry.
231: *
232: * @return array Set of keys stored in registry
233: */
234: public static function keys() {
235: $_this = ClassRegistry::getInstance();
236: return array_keys($_this->_objects);
237: }
238:
239: /**
240: * Return object which corresponds to given key.
241: *
242: * @param string $key Key of object to look for
243: * @return mixed Object stored in registry or boolean false if the object does not exist.
244: */
245: public static function &getObject($key) {
246: $_this = ClassRegistry::getInstance();
247: $key = Inflector::underscore($key);
248: $return = false;
249: if (isset($_this->_objects[$key])) {
250: $return = $_this->_objects[$key];
251: } else {
252: $key = $_this->_getMap($key);
253: if (isset($_this->_objects[$key])) {
254: $return = $_this->_objects[$key];
255: }
256: }
257: return $return;
258: }
259:
260: /**
261: * Sets the default constructor parameter for an object type
262: *
263: * @param string $type Type of object. If this parameter is omitted, defaults to "Model"
264: * @param array $param The parameter that will be passed to object constructors when objects
265: * of $type are created
266: * @return mixed Void if $param is being set. Otherwise, if only $type is passed, returns
267: * the previously-set value of $param, or null if not set.
268: */
269: public static function config($type, $param = array()) {
270: $_this = ClassRegistry::getInstance();
271:
272: if (empty($param) && is_array($type)) {
273: $param = $type;
274: $type = 'Model';
275: } elseif (is_null($param)) {
276: unset($_this->_config[$type]);
277: } elseif (empty($param) && is_string($type)) {
278: return isset($_this->_config[$type]) ? $_this->_config[$type] : null;
279: }
280: $_this->_config[$type] = $param;
281: }
282:
283: /**
284: * Checks to see if $alias is a duplicate $class Object
285: *
286: * @param string $alias
287: * @param string $class
288: * @return boolean
289: */
290: protected function &_duplicate($alias, $class) {
291: $duplicate = false;
292: if ($this->isKeySet($alias)) {
293: $model = $this->getObject($alias);
294: if (is_object($model) && (is_a($model, $class) || $model->alias === $class)) {
295: $duplicate = $model;
296: }
297: unset($model);
298: }
299: return $duplicate;
300: }
301:
302: /**
303: * Add a key name pair to the registry to map name to class in the registry.
304: *
305: * @param string $key Key to include in map
306: * @param string $name Key that is being mapped
307: * @return void
308: */
309: public static function map($key, $name) {
310: $_this = ClassRegistry::getInstance();
311: $key = Inflector::underscore($key);
312: $name = Inflector::underscore($name);
313: if (!isset($_this->_map[$key])) {
314: $_this->_map[$key] = $name;
315: }
316: }
317:
318: /**
319: * Get all keys from the map in the registry.
320: *
321: * @return array Keys of registry's map
322: */
323: public static function mapKeys() {
324: $_this = ClassRegistry::getInstance();
325: return array_keys($_this->_map);
326: }
327:
328: /**
329: * Return the name of a class in the registry.
330: *
331: * @param string $key Key to find in map
332: * @return string Mapped value
333: */
334: protected function _getMap($key) {
335: if (isset($this->_map[$key])) {
336: return $this->_map[$key];
337: }
338: }
339:
340: /**
341: * Flushes all objects from the ClassRegistry.
342: *
343: * @return void
344: */
345: public static function flush() {
346: $_this = ClassRegistry::getInstance();
347: $_this->_objects = array();
348: $_this->_map = array();
349: }
350: }
351: