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