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