# Source for file Connection.php

Documentation is available at Connection.php

1. <?php
2. /*
3. * $Id: Connection.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. Doctrine::autoload('Doctrine_Configurable');
22. /**
23. * Doctrine_Connection
24. *
25. * A wrapper layer on top of PDO / Doctrine_Adapter
26. *
27. * Doctrine_Connection is the heart of any Doctrine based application.
28. *
29. * 1. Event listeners
30. * An easy to use, pluggable eventlistener architecture. Aspects such as
31. * logging, query profiling and caching can be easily implemented through
32. * the use of these listeners
33. *
34. * 2. Lazy-connecting
35. * Creating an instance of Doctrine_Connection does not connect
36. * to database. Connecting to database is only invoked when actually needed
37. * (for example when query() is being called)
38. *
39. * 3. Convenience methods
40. * Doctrine_Connection provides many convenience methods such as fetchAll(), fetchOne() etc.
41. *
42. * 4. Modular structure
43. * Higher level functionality such as schema importing, exporting, sequence handling etc.
44. * is divided into modules. For a full list of connection modules see
45. * Doctrine_Connection::$_modules
46. *
47. * @package Doctrine
48. * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
49. * @category Object Relational Mapping
50. * @link www.phpdoctrine.com
51. * @since 1.0
52. * @version $Revision: 2290 $
53. * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
54. * @author Lukas Smith <smith@pooteeweet.org> (MDB2 library)
55. */
56. abstract class Doctrine_Connection extends Doctrine_Configurable implements Countable, IteratorAggregate
57. {
58. /**
59. * @var $dbh the database handler
60. */
61. protected $dbh;
62. /**
63. * @var array $tables an array containing all the initialized Doctrine_Table objects
64. * keys representing Doctrine_Table component names and values as Doctrine_Table objects
65. */
66. protected $tables = array();
67. /**
68. * @var string $driverName the name of this connection driver
69. */
70. protected $driverName;
71. /**
72. * @var boolean $isConnected whether or not a connection has been established
73. */
74. protected $isConnected = false;
75. /**
76. * @var array $supported an array containing all features this driver supports,
77. * keys representing feature names and values as
78. * one of the following (true, false, 'emulated')
79. */
80. protected $supported = array();
81. /**
82. * @var array $pendingAttributes An array of pending attributes. When setting attributes
83. * no connection is needed. When connected all the pending
84. * attributes are passed to the underlying adapter (usually PDO) instance.
85. */
86. protected $pendingAttributes = array();
87. /**
88. * @var array $modules an array containing all modules
89. * transaction Doctrine_Transaction driver, handles savepoint and transaction isolation abstraction
90. *
91. * expression Doctrine_Expression driver, handles expression abstraction
92. *
93. * dataDict Doctrine_DataDict driver, handles datatype abstraction
94. *
95. * export Doctrine_Export driver, handles db structure modification abstraction (contains
96. * methods such as alterTable, createConstraint etc.)
97. * import Doctrine_Import driver, handles db schema reading
98. *
99. * sequence Doctrine_Sequence driver, handles sequential id generation and retrieval
100. *
101. * unitOfWork Doctrine_Connection_UnitOfWork handles many orm functionalities such as object
102. * deletion and saving
103. *
104. * formatter Doctrine_Formatter handles data formatting, quoting and escaping
105. *
106. * @see Doctrine_Connection::__get()
107. * @see Doctrine_DataDict
108. * @see Doctrine_Expression
109. * @see Doctrine_Export
110. * @see Doctrine_Transaction
111. * @see Doctrine_Sequence
112. * @see Doctrine_Connection_UnitOfWork
113. * @see Doctrine_Formatter
114. */
115. private $modules = array('transaction' => false,
116. 'expression' => false,
117. 'dataDict' => false,
118. 'export' => false,
119. 'import' => false,
120. 'sequence' => false,
121. 'unitOfWork' => false,
122. 'formatter' => false,
123. 'util' => false,
124. );
125. /**
126. * @var array $properties an array of connection properties
127. */
128. protected $properties = array('sql_comments' => array(array('start' => '--', 'end' => "\n", 'escape' => false),
129. array('start' => '/*', 'end' => '*/', 'escape' => false)),
130. 'identifier_quoting' => array('start' => '"', 'end' => '"','escape' => '"'),
131. 'string_quoting' => array('start' => "'",
132. 'end' => "'",
133. 'escape' => false,
134. 'escape_pattern' => false),
135. 'wildcards' => array('%', '_'),
136. 'varchar_max_length' => 255,
137. );
138. /**
139. * @var array $serverInfo
140. */
141. protected $serverInfo = array();
143. protected $options = array();
144. /**
145. * @var array $availableDrivers an array containing all availible drivers
146. */
147. private static $availableDrivers = array(
148. 'Mysql',
149. 'Pgsql',
150. 'Oracle',
151. 'Informix',
152. 'Mssql',
153. 'Sqlite',
154. 'Firebird'
155. );
156. protected $_count;
158. /**
159. * the constructor
160. *
161. * @param Doctrine_Manager $manager the manager object
162. * @param PDO|Doctrine_Adapter_Interface$adapter database driver
163. */
164. public function __construct(Doctrine_Manager $manager, $adapter, $user = null, $pass = null)
165. {
166. if (is_object($adapter)) {
167. if ( ! ($adapter instanceof PDO) && ! in_array('Doctrine_Adapter_Interface', class_implements($adapter))) {
168. throw new Doctrine_Connection_Exception('First argument should be an instance of PDO or implement Doctrine_Adapter_Interface');
169. }
170. $this->dbh = $adapter;
172. $this->isConnected = true;
174. } elseif(is_array($adapter)) {
175. $this->pendingAttributes[Doctrine::ATTR_DRIVER_NAME] = $adapter['scheme'];
177. $this->options['dsn'] = $adapter['dsn'];
178. $this->options['username'] = $adapter['user'];
179. $this->options['password'] = $adapter['pass'];
180. }
182. $this->setParent($manager);
184. $this->setAttribute(Doctrine::ATTR_CASE, Doctrine::CASE_NATURAL);
185. $this->setAttribute(Doctrine::ATTR_ERRMODE, Doctrine::ERRMODE_EXCEPTION);
187. $this->getAttribute(Doctrine::ATTR_LISTENER)->onOpen($this);
188. }
189. /**
190. * getAttribute
191. * retrieves a database connection attribute
192. *
193. * @param integer $attribute
194. * @return mixed
195. */
196. public function getAttribute($attribute)
197. {
199. if ($attribute >= 100) {
200. if ( ! isset($this->attributes[$attribute])) {
201. return $this->parent->getAttribute($attribute);
202. }
203. return $this->attributes[$attribute];
204. }
206. if ($this->isConnected) {
207. try {
208. return $this->dbh->getAttribute($attribute);
209. } catch(Exception $e) {
210. throw new Doctrine_Connection_Exception('Attribute ' . $attribute . ' not found.');
211. }
212. } else {
213. if ( ! isset($this->pendingAttributes[$attribute])) {
214. $this->connect();
215. $this->getAttribute($attribute);
216. }
218. return $this->pendingAttributes[$attribute];
219. }
220. }
221. /**
222. * returns an array of available PDO drivers
223. */
224. public static function getAvailableDrivers()
225. {
226. return PDO::getAvailableDrivers();
227. }
228. /**
229. * setAttribute
230. * sets an attribute
231. *
232. * @param integer $attribute
233. * @param mixed $value
234. * @return boolean
235. */
236. public function setAttribute($attribute, $value)
237. {
238. if ($attribute >= 100) {
239. parent::setAttribute($attribute, $value);
240. } else {
241. if ($this->isConnected) {
242. $this->dbh->setAttribute($attribute, $value);
243. } else {
244. $this->pendingAttributes[$attribute] = $value;
245. }
246. }
247. return $this;
248. }
249. /**
250. * getName
251. * returns the name of this driver
252. *
253. * @return string the name of this driver
254. */
255. public function getName()
256. {
257. return $this->driverName;
258. }
259. /**
260. * __get
261. * lazy loads given module and returns it
262. *
263. * @see Doctrine_DataDict
264. * @see Doctrine_Expression
265. * @see Doctrine_Export
266. * @see Doctrine_Transaction
267. * @see Doctrine_Connection::$modules all availible modules
268. * @param string $name the name of the module to get
269. * @throws Doctrine_Connection_Exception if trying to get an unknown module
270. * @return Doctrine_Connection_Module connection module
271. */
272. public function __get($name)
273. {
274. if (isset($this->properties[$name])) {
275. return $this->properties[$name];
276. }
278. if ( ! isset($this->modules[$name])) {
279. throw new Doctrine_Connection_Exception('Unknown module / property ' . $name);
280. }
281. if ($this->modules[$name] === false) {
282. switch ($name) {
283. case 'unitOfWork':
284. $this->modules[$name] = new Doctrine_Connection_UnitOfWork($this);
285. break;
286. case 'formatter':
287. $this->modules[$name] = new Doctrine_Formatter($this);
288. break;
289. default:
290. $class = 'Doctrine_' . ucwords($name) . '_' . $this->getName();
291. $this->modules[$name] = new $class($this);
292. }
293. }
295. return $this->modules[$name];
296. }
297. /**
298. * returns the manager that created this connection
299. *
300. * @return Doctrine_Manager
301. */
302. public function getManager()
303. {
304. return $this->getParent();
305. }
306. /**
307. * returns the database handler of which this connection uses
308. *
309. * @return PDO the database handler
310. */
311. public function getDbh()
312. {
313. $this->connect();
315. return $this->dbh;
316. }
317. /**
318. * connect
319. * connects into database
320. *
321. * @return boolean
322. */
323. public function connect()
324. {
326. if ($this->isConnected) {
327. return false;
328. }
330. $event = new Doctrine_Event($this, Doctrine_Event::CONN_CONNECT);
332. $this->getListener()->preConnect($event);
334. $e = explode(':', $this->options['dsn']);
335. $found = false;
337. if (extension_loaded('pdo')) {
338. if (in_array($e[0], PDO::getAvailableDrivers())) {
339. $this->dbh = new PDO($this->options['dsn'], $this->options['username'], $this->options['password']);
340. $this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
341. $found = true;
342. }
343. }
345. if ( ! $found) {
346. $class = 'Doctrine_Adapter_' . ucwords($e[0]);
348. if (class_exists($class)) {
349. $this->dbh = new $class($this->options['dsn'], $this->options['username'], $this->options['password']);
350. } else {
351. throw new Doctrine_Connection_Exception("Couldn't locate driver named " . $e[0]);
352. }
353. }
355. // attach the pending attributes to adapter
356. foreach($this->pendingAttributes as $attr => $value) {
357. // some drivers don't support setting this so we just skip it
358. if($attr == Doctrine::ATTR_DRIVER_NAME) {
359. continue;
360. }
361. $this->dbh->setAttribute($attr, $value);
362. }
364. $this->isConnected = true;
366. $this->getListener()->postConnect($event);
367. return true;
368. }
370. public function incrementQueryCount()
371. {
372. $this->_count++;
373. }
374. /**
375. * converts given driver name
376. *
377. * @param
378. */
379. public function driverName($name)
380. {
381. }
382. /**
383. * supports
384. *
385. * @param string $feature the name of the feature
386. * @return boolean whether or not this drivers supports given feature
387. */
388. public function supports($feature)
389. {
390. return (isset($this->supported[$feature])
391. && ($this->supported[$feature] === 'emulated'
392. || $this->supported[$feature]));
393. }
394. /**
395. * Execute a SQL REPLACE query. A REPLACE query is identical to a INSERT
396. * query, except that if there is already a row in the table with the same
397. * key field values, the REPLACE query just updates its values instead of
398. * inserting a new row.
399. *
400. * The REPLACE type of query does not make part of the SQL standards. Since
401. * practically only MySQL and SQLIte implement it natively, this type of
402. * query isemulated through this method for other DBMS using standard types
403. * of queries inside a transaction to assure the atomicity of the operation.
404. *
405. * @param string name of the table on which the REPLACE query will
406. * be executed.
407. *
408. * @param array an associative array that describes the fields and the
409. * values that will be inserted or updated in the specified table. The
410. * indexes of the array are the names of all the fields of the table.
411. *
412. * The values of the array are values to be assigned to the specified field.
413. *
414. * @param array $keys an array containing all key fields (primary key fields
415. * or unique index fields) for this table
416. *
417. * the uniqueness of a row will be determined according to
418. * the provided key fields
419. *
420. * this method will fail if no key fields are specified
421. *
422. * @throws Doctrine_Connection_Exception if this driver doesn't support replace
423. * @throws Doctrine_Connection_Exception if some of the key values was null
424. * @throws Doctrine_Connection_Exception if there were no key fields
425. * @throws PDOException if something fails at PDO level
426. * @return integer number of rows affected
427. */
428. public function replace($table, array $fields, array $keys)
429. {
430. //if ( ! $this->supports('replace'))
431. // throw new Doctrine_Connection_Exception('replace query is not supported');
433. if (empty($keys)) {
434. throw new Doctrine_Connection_Exception('Not specified which fields are keys');
435. }
436. $condition = $values = array();
438. foreach ($fields as $name => $value) {
439. $values[$name] = $value;
441. if (in_array($name, $keys)) {
442. if ($value === null)
443. throw new Doctrine_Connection_Exception('key value '.$name.' may not be null');
445. $condition[] = $name . ' = ?';
446. $conditionValues[] = $value;
447. }
448. }
450. $query = 'DELETE FROM ' . $this->quoteIdentifier($table) . ' WHERE ' . implode(' AND ', $condition);
451. $affectedRows = $this->exec($query);
453. $this->insert($table, $values);
455. $affectedRows++;
458. return $affectedRows;
459. }
460. /**
461. * Inserts a table row with specified data.
462. *
463. * @param string $table The table to insert data into.
464. * @param array $values An associateve array containing column-value pairs.
465. * @return boolean
466. */
467. public function insert($table, array $values = array()) {
468. if (empty($values)) {
469. return false;
470. }
472. // column names are specified as array keys
473. $cols = array();
474. // the query VALUES will contain either expresions (eg 'NOW()') or ?
475. $a = array();
476. foreach ($values as $k => $value) {
477. $cols[] = $this->quoteIdentifier($k);
478. if ($value instanceof Doctrine_Expression) {
479. $a[] = $value->getSql();
480. unset($values[$k]);
481. } else {
482. $a[] = '?';
483. }
484. }
486. // build the statement
487. $query = 'INSERT INTO ' . $this->quoteIdentifier($table)
488. . ' (' . implode(', ', $cols) . ') '
489. . 'VALUES (';
491. $query .= implode(', ', $a) . ')';
492. // prepare and execute the statement
494. $this->exec($query, array_values($values));
496. return true;
497. }
498. /**
499. * Set the charset on the current connection
500. *
501. * @param string charset
502. *
503. * @return void
504. */
505. public function setCharset($charset)
506. {
508. }
509. /**
510. * Quote a string so it can be safely used as a table or column name
511. *
512. * Delimiting style depends on which database driver is being used.
513. *
514. * NOTE: just because you CAN use delimited identifiers doesn't mean
515. * you SHOULD use them. In general, they end up causing way more
516. * problems than they solve.
517. *
518. * Portability is broken by using the following characters inside
519. * delimited identifiers:
520. * + backtick (<kbd>`</kbd>) -- due to MySQL
521. * + double quote (<kbd>"</kbd>) -- due to Oracle
522. * + brackets (<kbd>[</kbd> or <kbd>]</kbd>) -- due to Access
523. *
524. * Delimited identifiers are known to generally work correctly under
525. * the following drivers:
526. * + mssql
527. * + mysql
528. * + mysqli
529. * + oci8
530. * + pgsql
531. * + sqlite
532. *
533. * InterBase doesn't seem to be able to use delimited identifiers
534. * via PHP 4. They work fine under PHP 5.
535. *
536. * @param string $str identifier name to be quoted
537. * @param bool $checkOption check the 'quote_identifier' option
538. *
539. * @return string quoted identifier string
540. */
541. public function quoteIdentifier($str, $checkOption = true)
542. {
543. // quick fix for the identifiers that contain a dot
544. if (strpos($str, '.')) {
545. $e = explode('.', $str);
547. return $this->formatter->quoteIdentifier($e[0], $checkOption) . '.'
548. . $this->formatter->quoteIdentifier($e[1], $checkOption);
549. }
550. return $this->formatter->quoteIdentifier($str, $checkOption);
551. }
552. /**
553. * convertBooleans
554. * some drivers need the boolean values to be converted into integers
555. * when using DQL API
556. *
557. * This method takes care of that conversion
558. *
559. * @param array $item
560. * @return void
561. */
562. public function convertBooleans($item)
563. {
564. return $this->formatter->convertBooleans($item);
565. }
566. /**
567. * quote
568. * quotes given input parameter
569. *
570. * @param mixed $input parameter to be quoted
571. * @param string $type
572. * @return mixed
573. */
574. public function quote($input, $type = null)
575. {
576. return $this->formatter->quote($input, $type);
577. }
578. /**
579. * Set the date/time format for the current connection
580. *
581. * @param string time format
582. *
583. * @return void
584. */
585. public function setDateFormat($format = null)
586. {
587. }
588. /**
589. * fetchAll
590. *
591. * @param string $statement sql query to be executed
592. * @param array $params prepared statement params
593. * @return array
594. */
595. public function fetchAll($statement, array $params = array())
596. {
597. return $this->execute($statement, $params)->fetchAll(Doctrine::FETCH_ASSOC);
598. }
599. /**
600. * fetchOne
601. *
602. * @param string $statement sql query to be executed
603. * @param array $params prepared statement params
604. * @param int $colnum 0-indexed column number to retrieve
605. * @return mixed
606. */
607. public function fetchOne($statement, array $params = array(), $colnum = 0)
608. {
609. return $this->execute($statement, $params)->fetchColumn($colnum);
610. }
611. /**
612. * fetchRow
613. *
614. * @param string $statement sql query to be executed
615. * @param array $params prepared statement params
616. * @return array
617. */
618. public function fetchRow($statement, array $params = array())
619. {
620. return $this->execute($statement, $params)->fetch(Doctrine::FETCH_ASSOC);
621. }
622. /**
623. * fetchArray
624. *
625. * @param string $statement sql query to be executed
626. * @param array $params prepared statement params
627. * @return array
628. */
629. public function fetchArray($statement, array $params = array())
630. {
631. return $this->execute($statement, $params)->fetch(Doctrine::FETCH_NUM);
632. }
633. /**
634. * fetchColumn
635. *
636. * @param string $statement sql query to be executed
637. * @param array $params prepared statement params
638. * @param int $colnum 0-indexed column number to retrieve
639. * @return array
640. */
641. public function fetchColumn($statement, array $params = array(), $colnum = 0)
642. {
643. return $this->execute($statement, $params)->fetchAll(Doctrine::FETCH_COLUMN, $colnum);
644. }
645. /**
646. * fetchAssoc
647. *
648. * @param string $statement sql query to be executed
649. * @param array $params prepared statement params
650. * @return array
651. */
652. public function fetchAssoc($statement, array $params = array())
653. {
654. return $this->execute($statement, $params)->fetchAll(Doctrine::FETCH_ASSOC);
655. }
656. /**
657. * fetchBoth
658. *
659. * @param string $statement sql query to be executed
660. * @param array $params prepared statement params
661. * @return array
662. */
663. public function fetchBoth($statement, array $params = array())
664. {
665. return $this->execute($statement, $params)->fetchAll(Doctrine::FETCH_BOTH);
666. }
667. /**
668. * query
669. * queries the database using Doctrine Query Language
670. * returns a collection of Doctrine_Record objects
671. *
672. * <code>
673. * $users = $conn->query('SELECT u.* FROM User u');
674. *
675. * $users = $conn->query('SELECT u.* FROM User u WHERE u.name LIKE ?', array('someone'));
676. * </code>
677. *
678. * @param string $query DQL query
679. * @param array $params query parameters
680. * @see Doctrine_Query
681. * @return Doctrine_Collection Collection of Doctrine_Record objects
682. */
683. public function query($query, array $params = array())
684. {
685. $parser = new Doctrine_Query($this);
687. return $parser->query($query, $params);
688. }
689. /**
690. * prepare
691. *
692. * @param string $statement
693. */
694. public function prepare($statement)
695. {
696. $this->connect();
698. try {
699. $event = new Doctrine_Event($this, Doctrine_Event::CONN_PREPARE, $statement);
701. $this->getAttribute(Doctrine::ATTR_LISTENER)->prePrepare($event);
703. $stmt = false;
705. if ( ! $event->skipOperation) {
706. $stmt = $this->dbh->prepare($statement);
707. }
709. $this->getAttribute(Doctrine::ATTR_LISTENER)->postPrepare($event);
711. return new Doctrine_Connection_Statement($this, $stmt);
712. } catch(Doctrine_Adapter_Exception $e) {
713. } catch(PDOException $e) { }
715. $this->rethrowException($e, $this);
716. }
717. /**
718. * query
719. * queries the database using Doctrine Query Language and returns
720. * the first record found
721. *
722. * <code>
723. * $user = $conn->queryOne('SELECT u.* FROM User u WHERE u.id = ?', array(1));
724. *
725. * $user = $conn->queryOne('SELECT u.* FROM User u WHERE u.name LIKE ? AND u.password = ?',
726. * array('someone', 'password')
727. * );
728. * </code>
729. *
730. * @param string $query DQL query
731. * @param array $params query parameters
732. * @see Doctrine_Query
733. * @return Doctrine_Record|false Doctrine_Record object on success,
734. * boolean false on failure
735. */
736. public function queryOne($query, array $params = array())
737. {
738. $parser = new Doctrine_Query($this);
740. $coll = $parser->query($query, $params);
741. if ( ! $coll->contains(0)) {
742. return false;
743. }
744. return $coll[0];
745. }
746. /**
747. * queries the database with limit and offset
748. * added to the query and returns a PDOStatement object
749. *
750. * @param string $query
751. * @param integer $limit
752. * @param integer $offset
753. * @return PDOStatement
754. */
755. public function select($query, $limit = 0, $offset = 0)
756. {
757. if ($limit > 0 || $offset > 0) {
758. $query = $this->modifyLimitQuery($query, $limit, $offset);
759. }
760. return $this->dbh->query($query);
761. }
762. /**
763. * standaloneQuery
764. *
765. * @param string $query sql query
766. * @param array $params query parameters
767. *
768. * @return PDOStatement|Doctrine_Adapter_Statement
769. */
770. public function standaloneQuery($query, $params = array())
771. {
772. return $this->execute($query, $params);
773. }
774. /**
775. * execute
776. * @param string $query sql query
777. * @param array $params query parameters
778. *
779. * @return PDOStatement|Doctrine_Adapter_Statement
780. */
781. public function execute($query, array $params = array())
782. {
783. $this->connect();
785. try {
786. if ( ! empty($params)) {
787. $stmt = $this->prepare($query);
788. $stmt->execute($params);
789. return $stmt;
790. } else {
791. $event = new Doctrine_Event($this, Doctrine_Event::CONN_QUERY, $query, $params);
793. $this->getAttribute(Doctrine::ATTR_LISTENER)->preQuery($event);
795. if ( ! $event->skipOperation) {
796. $stmt = $this->dbh->query($query);
798. $this->_count++;
799. }
800. $this->getAttribute(Doctrine::ATTR_LISTENER)->postQuery($event);
802. return $stmt;
803. }
804. } catch(Doctrine_Adapter_Exception $e) {
805. } catch(PDOException $e) { }
807. $this->rethrowException($e, $this);
808. }
809. /**
810. * exec
811. * @param string $query sql query
812. * @param array $params query parameters
813. *
814. * @return PDOStatement|Doctrine_Adapter_Statement
815. */
816. public function exec($query, array $params = array()) {
817. $this->connect();
819. try {
820. if ( ! empty($params)) {
821. $stmt = $this->prepare($query);
822. $stmt->execute($params);
824. return $stmt->rowCount();
825. } else {
826. $event = new Doctrine_Event($this, Doctrine_Event::CONN_EXEC, $query, $params);
828. $this->getAttribute(Doctrine::ATTR_LISTENER)->preExec($event);
830. if ( ! $event->skipOperation) {
831. $count = $this->dbh->exec($query);
833. $this->_count++;
834. }
835. $this->getAttribute(Doctrine::ATTR_LISTENER)->postExec($event);
837. return $count;
838. }
839. } catch(Doctrine_Adapter_Exception $e) {
840. } catch(PDOException $e) { }
842. $this->rethrowException($e, $this);
843. }
844. /**
845. * rethrowException
846. *
847. * @throws Doctrine_Connection_Exception
848. */
849. public function rethrowException(Exception $e, $invoker)
850. {
851. $event = new Doctrine_Event($this, Doctrine_Event::CONN_ERROR);
853. $this->getListener()->preError($event);
855. $name = 'Doctrine_Connection_' . $this->driverName . '_Exception';
857. $exc = new $name($e->getMessage(), (int) $e->getCode());
858. if ( ! is_array($e->errorInfo)) {
859. $e->errorInfo = array(null, null, null, null);
860. }
861. $exc->processErrorInfo($e->errorInfo);
863. if ($this->getAttribute(Doctrine::ATTR_THROW_EXCEPTIONS)) {
864. throw $exc;
865. }
867. $this->getListener()->postError($event);
868. }
869. /**
870. * hasTable
871. * whether or not this connection has table $name initialized
872. *
873. * @param mixed $name
874. * @return boolean
875. */
876. public function hasTable($name)
877. {
878. return isset($this->tables[$name]);
879. }
880. /**
881. * returns a table object for given component name
882. *
883. * @param string $name component name
884. * @return object Doctrine_Table
885. */
886. public function getTable($name, $allowExport = true)
887. {
888. if (isset($this->tables[$name])) {
889. return $this->tables[$name];
890. }
891. $class = $name . 'Table';
893. if (class_exists($class) && in_array('Doctrine_Table', class_parents($class))) {
894. $table = new $class($name, $this);
895. } else {
896. $table = new Doctrine_Table($name, $this);
897. }
899. $this->tables[$name] = $table;
902. return $table;
903. }
904. /**
905. * returns an array of all initialized tables
906. *
907. * @return array
908. */
909. public function getTables()
910. {
911. return $this->tables;
912. }
913. /**
914. * returns an iterator that iterators through all
915. * initialized table objects
916. *
917. * <code>
918. * foreach ($conn as $index => $table) {
919. * print $table; // get a string representation of each table object
920. * }
921. * </code>
922. *
923. * @return ArrayIterator SPL ArrayIterator object
924. */
925. public function getIterator()
926. {
927. return new ArrayIterator($this->tables);
928. }
929. /**
930. * returns the count of initialized table objects
931. *
932. * @return integer
933. */
934. public function count()
935. {
936. return $this->_count;
937. }
938. /**
939. * addTable
940. * adds a Doctrine_Table object into connection registry
941. *
942. * @param $table a Doctrine_Table object to be added into registry
943. * @return boolean
944. */
945. public function addTable(Doctrine_Table $table)
946. {
947. $name = $table->getComponentName();
949. if (isset($this->tables[$name])) {
950. return false;
951. }
952. $this->tables[$name] = $table;
953. return true;
954. }
955. /**
956. * create
957. * creates a record
958. *
959. * create creates a record
960. * @param string $name component name
961. * @return Doctrine_Record Doctrine_Record object
962. */
963. public function create($name)
964. {
965. return $this->getTable($name)->create();
966. }
967. /**
968. * flush
969. * saves all the records from all tables
970. * this operation is isolated using a transaction
971. *
972. * @throws PDOException if something went wrong at database level
973. * @return void
974. */
975. public function flush()
976. {
977. $this->beginTransaction();
978. $this->unitOfWork->saveAll();
979. $this->commit();
980. }
981. /**
982. * clear
983. * clears all repositories
984. *
985. * @return void
986. */
987. public function clear()
988. {
989. foreach ($this->tables as $k => $table) {
990. $table->getRepository()->evictAll();
991. $table->clear();
992. }
993. }
994. /**
995. * evictTables
996. * evicts all tables
997. *
998. * @return void
999. */
1000. public function evictTables()
1001. {
1002. $this->tables = array();
1003. $this->exported = array();
1004. }
1005. /**
1006. * close
1007. * closes the connection
1008. *
1009. * @return void
1010. */
1011. public function close()
1012. {
1013. $event = new Doctrine_Event($this, Doctrine_Event::CONN_CLOSE);
1015. $this->getAttribute(Doctrine::ATTR_LISTENER)->preClose($event);
1017. $this->clear();
1019. unset($this->dbh);
1020. $this->isConnected = false;
1022. $this->getAttribute(Doctrine::ATTR_LISTENER)->postClose($event);
1023. }
1024. /**
1025. * get the current transaction nesting level
1026. *
1027. * @return integer
1028. */
1029. public function getTransactionLevel()
1030. {
1031. return $this->transaction->getTransactionLevel();
1032. }
1033. /**
1034. * errorCode
1035. * Fetch the SQLSTATE associated with the last operation on the database handle
1036. *
1037. * @return integer
1038. */
1039. public function errorCode()
1040. {
1041. $this->connect();
1043. return $this->dbh->errorCode();
1044. }
1045. /**
1046. * errorInfo
1047. * Fetch extended error information associated with the last operation on the database handle
1048. *
1049. * @return array
1050. */
1051. public function errorInfo()
1052. {
1053. $this->connect();
1055. return $this->dbh->errorInfo();
1056. }
1057. /**
1058. * lastInsertId
1059. *
1060. * Returns the ID of the last inserted row, or the last value from a sequence object,
1061. * depending on the underlying driver.
1062. *
1063. * Note: This method may not return a meaningful or consistent result across different drivers,
1064. * because the underlying database may not even support the notion of auto-increment fields or sequences.
1065. *
1066. * @param string $table name of the table into which a new row was inserted
1067. * @param string $field name of the field into which a new row was inserted
1068. */
1069. public function lastInsertId($table = null, $field = null)
1070. {
1071. return $this->sequence->lastInsertId($table, $field);
1072. }
1073. /**
1074. * beginTransaction
1075. * Start a transaction or set a savepoint.
1076. *
1077. * if trying to set a savepoint and there is no active transaction
1078. * a new transaction is being started
1079. *
1080. * Listeners: onPreTransactionBegin, onTransactionBegin
1081. *
1082. * @param string $savepoint name of a savepoint to set
1083. * @throws Doctrine_Transaction_Exception if the transaction fails at database level
1084. * @return integer current transaction nesting level
1085. */
1086. public function beginTransaction($savepoint = null)
1087. {
1088. $this->transaction->beginTransaction($savepoint);
1089. }
1090. /**
1091. * commit
1092. * Commit the database changes done during a transaction that is in
1093. * progress or release a savepoint. This function may only be called when
1094. * auto-committing is disabled, otherwise it will fail.
1095. *
1096. * Listeners: onPreTransactionCommit, onTransactionCommit
1097. *
1098. * @param string $savepoint name of a savepoint to release
1099. * @throws Doctrine_Transaction_Exception if the transaction fails at PDO level
1100. * @throws Doctrine_Validator_Exception if the transaction fails due to record validations
1101. * @return boolean false if commit couldn't be performed, true otherwise
1102. */
1103. public function commit($savepoint = null)
1104. {
1105. $this->transaction->commit($savepoint);
1106. }
1107. /**
1108. * rollback
1109. * Cancel any database changes done during a transaction or since a specific
1110. * savepoint that is in progress. This function may only be called when
1111. * auto-committing is disabled, otherwise it will fail. Therefore, a new
1112. * transaction is implicitly started after canceling the pending changes.
1113. *
1114. * this method can be listened with onPreTransactionRollback and onTransactionRollback
1115. * eventlistener methods
1116. *
1117. * @param string $savepoint name of a savepoint to rollback to
1118. * @throws Doctrine_Transaction_Exception if the rollback operation fails at database level
1119. * @return boolean false if rollback couldn't be performed, true otherwise
1120. */
1121. public function rollback($savepoint = null)
1122. {
1123. $this->transaction->rollback($savepoint);
1124. }
1126. /**
1127. * returns a string representation of this object
1128. * @return string
1129. */
1130. public function __toString()
1131. {
1132. return Doctrine_Lib::getConnectionAsString($this);
1133. }
1134. }