# Source for file Manager.php

Documentation is available at Manager.php

1. <?php
2. /*
3. * $Id: Manager.php 2290 2007-08-29 21:57:46Z zYne $
4. *
5. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
6. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
7. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
8. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
9. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
10. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
11. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
12. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
13. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
15. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16. *
17. * This software consists of voluntary contributions made by many individuals
18. * and is licensed under the LGPL. For more information, see
19. * <http://www.phpdoctrine.com>.
20. */
21. /**
22. *
23. * Doctrine_Manager is the base component of all doctrine based projects.
24. * It opens and keeps track of all connections (database connections).
25. *
26. * @package Doctrine
27. * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
28. * @category Object Relational Mapping
29. * @link www.phpdoctrine.com
30. * @since 1.0
31. * @version $Revision: 2290 $
32. * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
33. */
34. class Doctrine_Manager extends Doctrine_Configurable implements Countable, IteratorAggregate
35. {
36. /**
37. * @var array $connections an array containing all the opened connections
38. */
39. protected $_connections = array();
40. /**
41. * @var array $bound an array containing all components that have a bound connection
42. */
43. protected $_bound = array();
44. /**
45. * @var integer $index the incremented index
46. */
47. protected $_index = 0;
48. /**
49. * @var integer $currIndex the current connection index
50. */
51. protected $_currIndex = 0;
52. /**
53. * @var string $root root directory
54. */
55. protected $_root;
56. /**
57. * @var array $_integrityActions an array containing all registered integrity actions
58. * used when emulating these actions
59. */
60. protected $_integrityActions = array();
62. protected static $driverMap = array('oci' => 'oracle');
63. /**
64. * constructor
65. *
66. * this is private constructor (use getInstance to get an instance of this class)
67. */
68. private function __construct()
69. {
70. $this->_root = dirname(__FILE__);
72. Doctrine_Object::initNullObject(new Doctrine_Null);
73. }
74. public function addDeleteAction($componentName, $foreignComponent, $action)
75. {
76. $this->_integrityActions[$componentName]['onDelete'][$foreignComponent] = $action;
77. }
78. public function addUpdateAction($componentName, $foreignComponent, $action)
79. {
80. $this->_integrityActions[$componentName]['onUpdate'][$foreignComponent] = $action;
81. }
82. public function getDeleteActions($componentName)
83. {
84. if ( ! isset($this->_integrityActions[$componentName]['onDelete'])) {
85. return null;
86. }
88. return $this->_integrityActions[$componentName]['onDelete'];
89. }
90. public function getUpdateActions($componentName)
91. {
92. if ( ! isset($this->_integrityActions[$componentName]['onUpdate'])) {
93. return null;
94. }
96. return $this->_integrityActions[$componentName]['onUpdate'];
97. }
98. /**
99. * setDefaultAttributes
100. * sets default attributes
101. *
102. * @return boolean
103. */
104. public function setDefaultAttributes()
105. {
106. static $init = false;
107. if ( ! $init) {
108. $init = true;
109. $attributes = array(
110. Doctrine::ATTR_CACHE => null,
111. Doctrine::ATTR_LOAD_REFERENCES => true,
112. Doctrine::ATTR_LISTENER => new Doctrine_EventListener(),
113. Doctrine::ATTR_RECORD_LISTENER => new Doctrine_Record_Listener(),
114. Doctrine::ATTR_THROW_EXCEPTIONS => true,
115. Doctrine::ATTR_LOCKMODE => 1,
116. Doctrine::ATTR_VLD => false,
117. Doctrine::ATTR_AUTO_LENGTH_VLD => true,
118. Doctrine::ATTR_AUTO_TYPE_VLD => true,
119. Doctrine::ATTR_QUERY_LIMIT => Doctrine::LIMIT_RECORDS,
120. Doctrine::ATTR_IDXNAME_FORMAT => "%s_idx",
121. Doctrine::ATTR_SEQNAME_FORMAT => "%s_seq",
122. Doctrine::ATTR_QUOTE_IDENTIFIER => false,
123. Doctrine::ATTR_SEQCOL_NAME => 'id',
124. Doctrine::ATTR_PORTABILITY => Doctrine::PORTABILITY_ALL,
125. Doctrine::ATTR_EXPORT => Doctrine::EXPORT_ALL,
126. Doctrine::ATTR_DECIMAL_PLACES => 2,
127. );
128. foreach ($attributes as $attribute => $value) {
129. $old = $this->getAttribute($attribute);
130. if ($old === null) {
131. $this->setAttribute($attribute,$value);
132. }
133. }
134. return true;
135. }
136. return false;
137. }
138. /**
139. * returns the root directory of Doctrine
140. *
141. * @return string
142. */
143. final public function getRoot()
144. {
145. return $this->_root;
146. }
147. /**
148. * getInstance
149. * returns an instance of this class
150. * (this class uses the singleton pattern)
151. *
152. * @return Doctrine_Manager
153. */
154. public static function getInstance()
155. {
156. static $instance;
157. if ( ! isset($instance)) {
158. $instance = new self();
159. }
160. return $instance;
161. }
162. /**
163. * connection
164. *
165. * if the adapter parameter is set this method acts as
166. * a short cut for Doctrine_Manager::getInstance()->openConnection($adapter, $name);
167. *
168. * if the adapter paramater is not set this method acts as
169. * a short cut for Doctrine_Manager::getInstance()->getCurrentConnection()
170. *
171. * @param PDO|Doctrine_Adapter_Interface$adapter database driver
172. * @param string $name name of the connection, if empty numeric key is used
173. * @throws Doctrine_Manager_Exception if trying to bind a connection with an existing name
174. * @return Doctrine_Connection
175. */
176. public static function connection($adapter = null, $name = null)
177. {
178. if ($adapter == null) {
179. return Doctrine_Manager::getInstance()->getCurrentConnection();
180. } else {
181. return Doctrine_Manager::getInstance()->openConnection($adapter, $name);
182. }
183. }
184. /**
185. * openConnection
186. * opens a new connection and saves it to Doctrine_Manager->connections
187. *
188. * @param PDO|Doctrine_Adapter_Interface$adapter database driver
189. * @param string $name name of the connection, if empty numeric key is used
190. * @throws Doctrine_Manager_Exception if trying to bind a connection with an existing name
191. * @throws Doctrine_Manager_Exception if trying to open connection for unknown driver
192. * @return Doctrine_Connection
193. */
194. public function openConnection($adapter, $name = null, $setCurrent = true)
195. {
196. if (is_object($adapter)) {
197. if ( ! ($adapter instanceof PDO) && ! in_array('Doctrine_Adapter_Interface', class_implements($adapter))) {
198. throw new Doctrine_Manager_Exception("First argument should be an instance of PDO or implement Doctrine_Adapter_Interface");
199. }
201. $driverName = $adapter->getAttribute(Doctrine::ATTR_DRIVER_NAME);
202. } elseif (is_array($adapter)) {
203. if ( ! isset($adapter[0])) {
204. throw new Doctrine_Manager_Exception('Empty data source name given.');
205. }
206. $e = explode(':', $adapter[0]);
208. if($e[0] == 'uri') {
209. $e[0] = 'odbc';
210. }
212. $parts['dsn'] = $adapter[0];
213. $parts['scheme'] = $e[0];
214. $parts['user'] = (isset($adapter[1])) ? $adapter[1] : null;
215. $parts['pass'] = (isset($adapter[2])) ? $adapter[2] : null;
217. $driverName = $e[0];
218. $adapter = $parts;
219. } else {
220. $parts = $this->parseDsn($adapter);
222. $driverName = $parts['scheme'];
224. $adapter = $parts;
225. }
227. // initialize the default attributes
228. $this->setDefaultAttributes();
230. if ($name !== null) {
231. $name = (string) $name;
232. if (isset($this->_connections[$name])) {
233. return $this->_connections[$name];
234. }
235. } else {
236. $name = $this->_index;
237. $this->_index++;
238. }
240. $drivers = array('mysql' => 'Doctrine_Connection_Mysql',
241. 'sqlite' => 'Doctrine_Connection_Sqlite',
242. 'pgsql' => 'Doctrine_Connection_Pgsql',
243. 'oci' => 'Doctrine_Connection_Oracle',
244. 'oci8' => 'Doctrine_Connection_Oracle',
245. 'oracle' => 'Doctrine_Connection_Oracle',
246. 'mssql' => 'Doctrine_Connection_Mssql',
247. 'dblib' => 'Doctrine_Connection_Mssql',
248. 'firebird' => 'Doctrine_Connection_Firebird',
249. 'informix' => 'Doctrine_Connection_Informix',
250. 'mock' => 'Doctrine_Connection_Mock');
251. if ( ! isset($drivers[$driverName])) {
252. throw new Doctrine_Manager_Exception('Unknown driver ' . $driverName);
253. }
254. $className = $drivers[$driverName];
255. $conn = new $className($this, $adapter);
257. $this->_connections[$name] = $conn;
259. if ($setCurrent) {
260. $this->_currIndex = $name;
261. }
262. return $this->_connections[$name];
263. }
264. /**
265. * parseDsn
266. *
267. * @param string $dsn
268. * @return array Parsed contents of DSN
269. */
270. public function parseDsn($dsn)
271. {
272. // silence any warnings
273. $parts = @parse_url($dsn);
275. $names = array('dsn', 'scheme', 'host', 'port', 'user', 'pass', 'path', 'query', 'fragment');
277. foreach ($names as $name) {
278. if ( ! isset($parts[$name])) {
279. $parts[$name] = null;
280. }
281. }
283. if (count($parts) == 0 || ! isset($parts['scheme'])) {
284. throw new Doctrine_Manager_Exception('Empty data source name');
285. }
287. switch ($parts['scheme']) {
288. case 'sqlite':
289. case 'sqlite2':
290. case 'sqlite3':
291. if (isset($parts['host']) && $parts['host'] == ':memory') {
292. $parts['database'] = ':memory:';
293. $parts['dsn'] = 'sqlite::memory:';
294. } else {
295. $parts['database'] = $parts['path'];
296. $parts['dsn'] = $parts['scheme'] . ':' . $parts['path'];
297. }
299. break;
300. case 'mysql':
301. case 'informix':
302. case 'oci8':
303. case 'oci':
304. case 'mssql':
305. case 'firebird':
306. case 'dblib':
307. case 'pgsql':
308. case 'odbc':
309. case 'mock':
310. case 'oracle':
311. if ( ! isset($parts['path']) || $parts['path'] == '/') {
312. throw new Doctrine_Manager_Exception('No database available in data source name');
313. }
314. if (isset($parts['path'])) {
315. $parts['database'] = substr($parts['path'], 1);
316. }
317. if ( ! isset($parts['host'])) {
318. throw new Doctrine_Manager_Exception('No hostname set in data source name');
319. }
321. if (isset(self::$driverMap[$parts['scheme']])) {
322. $parts['scheme'] = self::$driverMap[$parts['scheme']];
323. }
325. $parts['dsn'] = $parts['scheme'] . ':host='
326. . $parts['host'] . ';dbname='
327. . $parts['database'];
329. if (isset($parts['port'])) {
330. // append port to dsn if supplied
331. $parts['dsn'] .= ';port=' . $parts['port'];
332. }
333. break;
334. default:
335. throw new Doctrine_Manager_Exception('Unknown driver '.$parts['scheme']);
336. }
339. return $parts;
340. }
341. /**
342. * getConnection
343. * @param integer $index
344. * @return object Doctrine_Connection
345. * @throws Doctrine_Manager_Exception if trying to get a non-existent connection
346. */
347. public function getConnection($name)
348. {
349. if ( ! isset($this->_connections[$name])) {
350. throw new Doctrine_Manager_Exception('Unknown connection: ' . $name);
351. }
353. return $this->_connections[$name];
354. }
355. /**
356. * getComponentAlias
357. * retrieves the alias for given component name
358. * if the alias couldn't be found, this method returns the given
359. * component name
360. *
361. * @param string $componentName
362. * @return string the component alias
363. */
364. public function getComponentAlias($componentName)
365. {
366. if (isset($this->componentAliases[$componentName])) {
367. return $this->componentAliases[$componentName];
368. }
370. return $componentName;
371. }
372. /**
373. * sets an alias for given component name
374. * very useful when building a large framework with a possibility
375. * to override any given class
376. *
377. * @param string $componentName the name of the component
378. * @param string $alias
379. * @return Doctrine_Manager
380. */
381. public function setComponentAlias($componentName, $alias)
382. {
383. $this->componentAliases[$componentName] = $alias;
385. return $this;
386. }
387. /**
388. * getConnectionName
389. *
390. * @param Doctrine_Connection $conn connection object to be searched for
391. * @return string the name of the connection
392. */
393. public function getConnectionName(Doctrine_Connection $conn)
394. {
395. return array_search($conn, $this->_connections, true);
396. }
397. /**
398. * bindComponent
399. * binds given component to given connection
400. * this means that when ever the given component uses a connection
401. * it will be using the bound connection instead of the current connection
402. *
403. * @param string $componentName
404. * @param string $connectionName
405. * @return boolean
406. */
407. public function bindComponent($componentName, $connectionName)
408. {
409. $this->_bound[$componentName] = $connectionName;
410. }
411. /**
412. * getConnectionForComponent
413. *
414. * @param string $componentName
415. * @return Doctrine_Connection
416. */
417. public function getConnectionForComponent($componentName = null)
418. {
419. if (isset($this->_bound[$componentName])) {
420. return $this->getConnection($this->_bound[$componentName]);
421. }
422. return $this->getCurrentConnection();
423. }
424. /**
425. * getTable
426. * this is the same as Doctrine_Connection::getTable() except
427. * that it works seamlessly in multi-server/connection environment
428. *
429. * @see Doctrine_Connection::getTable()
430. * @param string $componentName
431. * @return Doctrine_Table
432. */
433. public function getTable($componentName)
434. {
435. return $this->getConnectionForComponent($componentName)->getTable($componentName);
436. }
437. /**
438. * table
439. * this is the same as Doctrine_Connection::getTable() except
440. * that it works seamlessly in multi-server/connection environment
441. *
442. * @see Doctrine_Connection::getTable()
443. * @param string $componentName
444. * @return Doctrine_Table
445. */
446. public static function table($componentName)
447. {
448. return Doctrine_Manager::getInstance()
449. ->getConnectionForComponent($componentName)
450. ->getTable($componentName);
451. }
452. /**
453. * closes the connection
454. *
455. * @param Doctrine_Connection $connection
456. * @return void
457. */
458. public function closeConnection(Doctrine_Connection $connection)
459. {
460. $connection->close();
462. $key = array_search($connection, $this->_connections, true);
464. if ($key !== false) {
465. unset($this->_connections[$key]);
466. }
467. $this->_currIndex = key($this->_connections);
469. unset($connection);
470. }
471. /**
472. * getConnections
473. * returns all opened connections
474. *
475. * @return array
476. */
477. public function getConnections()
478. {
479. return $this->_connections;
480. }
481. /**
482. * setCurrentConnection
483. * sets the current connection to $key
484. *
485. * @param mixed $key the connection key
486. * @throws InvalidKeyException
487. * @return void
488. */
489. public function setCurrentConnection($key)
490. {
491. $key = (string) $key;
492. if ( ! isset($this->_connections[$key])) {
493. throw new InvalidKeyException();
494. }
495. $this->_currIndex = $key;
496. }
497. /**
498. * contains
499. * whether or not the manager contains specified connection
500. *
501. * @param mixed $key the connection key
502. * @return boolean
503. */
504. public function contains($key)
505. {
506. return isset($this->_connections[$key]);
507. }
508. /**
509. * count
510. * returns the number of opened connections
511. *
512. * @return integer
513. */
514. public function count()
515. {
516. return count($this->_connections);
517. }
518. /**
519. * getIterator
520. * returns an ArrayIterator that iterates through all connections
521. *
522. * @return ArrayIterator
523. */
524. public function getIterator()
525. {
526. return new ArrayIterator($this->_connections);
527. }
528. /**
529. * getCurrentConnection
530. * returns the current connection
531. *
532. * @throws Doctrine_Connection_Exception if there are no open connections
533. * @return Doctrine_Connection
534. */
535. public function getCurrentConnection()
536. {
537. $i = $this->_currIndex;
538. if ( ! isset($this->_connections[$i])) {
539. throw new Doctrine_Connection_Exception();
540. }
541. return $this->_connections[$i];
542. }
543. /**
544. * __toString
545. * returns a string representation of this object
546. *
547. * @return string
548. */
549. public function __toString()
550. {
551. $r[] = "<pre>";
552. $r[] = "Doctrine_Manager";
553. $r[] = "Connections : ".count($this->_connections);
554. $r[] = "</pre>";
555. return implode("\n",$r);
556. }
557. }