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