1: <?php
2: /**
3: * Datasource connection manager
4: *
5: * Provides an interface for loading and enumerating connections defined in app/Config/database.php
6: *
7: * PHP 5
8: *
9: * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
10: * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
11: *
12: * Licensed under The MIT License
13: * For full copyright and license information, please see the LICENSE.txt
14: * Redistributions of files must retain the above copyright notice.
15: *
16: * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
17: * @link http://cakephp.org CakePHP(tm) Project
18: * @package Cake.Model
19: * @since CakePHP(tm) v 0.10.x.1402
20: * @license http://www.opensource.org/licenses/mit-license.php MIT License
21: */
22:
23: App::uses('DataSource', 'Model/Datasource');
24:
25: /**
26: * Manages loaded instances of DataSource objects
27: *
28: * Provides an interface for loading and enumerating connections defined in
29: * app/Config/database.php
30: *
31: * @package Cake.Model
32: */
33: class ConnectionManager {
34:
35: /**
36: * Holds a loaded instance of the Connections object
37: *
38: * @var DATABASE_CONFIG
39: */
40: public static $config = null;
41:
42: /**
43: * Holds instances DataSource objects
44: *
45: * @var array
46: */
47: protected static $_dataSources = array();
48:
49: /**
50: * Contains a list of all file and class names used in Connection settings
51: *
52: * @var array
53: */
54: protected static $_connectionsEnum = array();
55:
56: /**
57: * Indicates if the init code for this class has already been executed
58: *
59: * @var boolean
60: */
61: protected static $_init = false;
62:
63: /**
64: * Loads connections configuration.
65: *
66: * @return void
67: */
68: protected static function _init() {
69: include_once APP . 'Config' . DS . 'database.php';
70: if (class_exists('DATABASE_CONFIG')) {
71: self::$config = new DATABASE_CONFIG();
72: }
73: self::$_init = true;
74: }
75:
76: /**
77: * Gets a reference to a DataSource object
78: *
79: * @param string $name The name of the DataSource, as defined in app/Config/database.php
80: * @return DataSource Instance
81: * @throws MissingDatasourceException
82: */
83: public static function getDataSource($name) {
84: if (empty(self::$_init)) {
85: self::_init();
86: }
87:
88: if (!empty(self::$_dataSources[$name])) {
89: return self::$_dataSources[$name];
90: }
91:
92: if (empty(self::$_connectionsEnum[$name])) {
93: self::_getConnectionObject($name);
94: }
95:
96: self::loadDataSource($name);
97: $conn = self::$_connectionsEnum[$name];
98: $class = $conn['classname'];
99:
100: if (strpos(App::location($class), 'Datasource') === false) {
101: throw new MissingDatasourceException(array(
102: 'class' => $class,
103: 'plugin' => null,
104: 'message' => 'Datasource is not found in Model/Datasource package.'
105: ));
106: }
107: self::$_dataSources[$name] = new $class(self::$config->{$name});
108: self::$_dataSources[$name]->configKeyName = $name;
109:
110: return self::$_dataSources[$name];
111: }
112:
113: /**
114: * Gets the list of available DataSource connections
115: * This will only return the datasources instantiated by this manager
116: * It differs from enumConnectionObjects, since the latter will return all configured connections
117: *
118: * @return array List of available connections
119: */
120: public static function sourceList() {
121: if (empty(self::$_init)) {
122: self::_init();
123: }
124: return array_keys(self::$_dataSources);
125: }
126:
127: /**
128: * Gets a DataSource name from an object reference.
129: *
130: * @param DataSource $source DataSource object
131: * @return string Datasource name, or null if source is not present
132: * in the ConnectionManager.
133: */
134: public static function getSourceName($source) {
135: if (empty(self::$_init)) {
136: self::_init();
137: }
138: foreach (self::$_dataSources as $name => $ds) {
139: if ($ds === $source) {
140: return $name;
141: }
142: }
143: return null;
144: }
145:
146: /**
147: * Loads the DataSource class for the given connection name
148: *
149: * @param string|array $connName A string name of the connection, as defined in app/Config/database.php,
150: * or an array containing the filename (without extension) and class name of the object,
151: * to be found in app/Model/Datasource/ or lib/Cake/Model/Datasource/.
152: * @return boolean True on success, null on failure or false if the class is already loaded
153: * @throws MissingDatasourceException
154: */
155: public static function loadDataSource($connName) {
156: if (empty(self::$_init)) {
157: self::_init();
158: }
159:
160: if (is_array($connName)) {
161: $conn = $connName;
162: } else {
163: $conn = self::$_connectionsEnum[$connName];
164: }
165:
166: if (class_exists($conn['classname'], false)) {
167: return false;
168: }
169:
170: $plugin = $package = null;
171: if (!empty($conn['plugin'])) {
172: $plugin = $conn['plugin'] . '.';
173: }
174: if (!empty($conn['package'])) {
175: $package = '/' . $conn['package'];
176: }
177:
178: App::uses($conn['classname'], $plugin . 'Model/Datasource' . $package);
179: if (!class_exists($conn['classname'])) {
180: throw new MissingDatasourceException(array(
181: 'class' => $conn['classname'],
182: 'plugin' => substr($plugin, 0, -1)
183: ));
184: }
185: return true;
186: }
187:
188: /**
189: * Return a list of connections
190: *
191: * @return array An associative array of elements where the key is the connection name
192: * (as defined in Connections), and the value is an array with keys 'filename' and 'classname'.
193: */
194: public static function enumConnectionObjects() {
195: if (empty(self::$_init)) {
196: self::_init();
197: }
198: return (array)self::$config;
199: }
200:
201: /**
202: * Dynamically creates a DataSource object at runtime, with the given name and settings
203: *
204: * @param string $name The DataSource name
205: * @param array $config The DataSource configuration settings
206: * @return DataSource A reference to the DataSource object, or null if creation failed
207: */
208: public static function create($name = '', $config = array()) {
209: if (empty(self::$_init)) {
210: self::_init();
211: }
212:
213: if (empty($name) || empty($config) || array_key_exists($name, self::$_connectionsEnum)) {
214: return null;
215: }
216: self::$config->{$name} = $config;
217: self::$_connectionsEnum[$name] = self::_connectionData($config);
218: $return = self::getDataSource($name);
219: return $return;
220: }
221:
222: /**
223: * Removes a connection configuration at runtime given its name
224: *
225: * @param string $name the connection name as it was created
226: * @return boolean success if connection was removed, false if it does not exist
227: */
228: public static function drop($name) {
229: if (empty(self::$_init)) {
230: self::_init();
231: }
232:
233: if (!isset(self::$config->{$name})) {
234: return false;
235: }
236: unset(self::$_connectionsEnum[$name], self::$_dataSources[$name], self::$config->{$name});
237: return true;
238: }
239:
240: /**
241: * Gets a list of class and file names associated with the user-defined DataSource connections
242: *
243: * @param string $name Connection name
244: * @return void
245: * @throws MissingDatasourceConfigException
246: */
247: protected static function _getConnectionObject($name) {
248: if (!empty(self::$config->{$name})) {
249: self::$_connectionsEnum[$name] = self::_connectionData(self::$config->{$name});
250: } else {
251: throw new MissingDatasourceConfigException(array('config' => $name));
252: }
253: }
254:
255: /**
256: * Returns the file, class name, and parent for the given driver.
257: *
258: * @param array $config Array with connection configuration. Key 'datasource' is required
259: * @return array An indexed array with: filename, classname, plugin and parent
260: */
261: protected static function _connectionData($config) {
262: $package = $classname = $plugin = null;
263:
264: list($plugin, $classname) = pluginSplit($config['datasource']);
265: if (strpos($classname, '/') !== false) {
266: $package = dirname($classname);
267: $classname = basename($classname);
268: }
269: return compact('package', 'classname', 'plugin');
270: }
271:
272: }
273: