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 CountableIteratorAggregate
  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();
  142.     
  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;
  157.  
  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;
  171.  
  172.             $this->isConnected = true;
  173.  
  174.         elseif(is_array($adapter)) {
  175.             $this->pendingAttributes[Doctrine::ATTR_DRIVER_NAME$adapter['scheme'];
  176.  
  177.             $this->options['dsn']      $adapter['dsn'];
  178.             $this->options['username'$adapter['user'];
  179.             $this->options['password'$adapter['pass'];
  180.         }
  181.  
  182.         $this->setParent($manager);
  183.  
  184.         $this->setAttribute(Doctrine::ATTR_CASEDoctrine::CASE_NATURAL);
  185.         $this->setAttribute(Doctrine::ATTR_ERRMODEDoctrine::ERRMODE_EXCEPTION);
  186.  
  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.     {
  198.  
  199.         if ($attribute >= 100{
  200.             if isset($this->attributes[$attribute])) {
  201.                 return $this->parent->getAttribute($attribute);
  202.             }
  203.             return $this->attributes[$attribute];
  204.         }
  205.  
  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.             }
  217.  
  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.         }
  277.  
  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[$namenew Doctrine_Connection_UnitOfWork($this);
  285.                     break;
  286.                 case 'formatter':
  287.                     $this->modules[$namenew Doctrine_Formatter($this);
  288.                     break;
  289.                 default:
  290.                     $class 'Doctrine_' ucwords($name'_' $this->getName();
  291.                     $this->modules[$namenew $class($this);
  292.                 }
  293.         }
  294.  
  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();
  314.         
  315.         return $this->dbh;
  316.     }
  317.     /**
  318.      * connect
  319.      * connects into database
  320.      *
  321.      * @return boolean 
  322.      */
  323.     public function connect()
  324.     {
  325.  
  326.         if ($this->isConnected{
  327.             return false;
  328.         }
  329.  
  330.         $event new Doctrine_Event($thisDoctrine_Event::CONN_CONNECT);
  331.  
  332.         $this->getListener()->preConnect($event);
  333.  
  334.         $e     explode(':'$this->options['dsn']);
  335.         $found false;
  336.         
  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_ERRMODEPDO::ERRMODE_EXCEPTION);
  341.                 $found true;
  342.             }
  343.         }
  344.  
  345.         if $found{
  346.             $class 'Doctrine_Adapter_' ucwords($e[0]);
  347.  
  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.         }
  354.  
  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.         }
  363.  
  364.         $this->isConnected = true;
  365.  
  366.         $this->getListener()->postConnect($event);
  367.         return true;
  368.     }
  369.     
  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($tablearray $fieldsarray $keys)
  429.     {
  430.         //if ( ! $this->supports('replace'))
  431.         //    throw new Doctrine_Connection_Exception('replace query is not supported');
  432.  
  433.         if (empty($keys)) {
  434.             throw new Doctrine_Connection_Exception('Not specified which fields are keys');
  435.         }
  436.         $condition $values array();
  437.  
  438.         foreach ($fields as $name => $value{
  439.             $values[$name$value;
  440.  
  441.             if (in_array($name$keys)) {
  442.                 if ($value === null)
  443.                     throw new Doctrine_Connection_Exception('key value '.$name.' may not be null');
  444.  
  445.                 $condition[]       $name ' = ?';
  446.                 $conditionValues[$value;
  447.             }
  448.         }
  449.  
  450.         $query          'DELETE FROM ' $this->quoteIdentifier($table' WHERE ' implode(' AND '$condition);
  451.         $affectedRows   $this->exec($query);
  452.  
  453.         $this->insert($table$values);
  454.  
  455.         $affectedRows++;
  456.  
  457.  
  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($tablearray $values array()) {
  468.         if (empty($values)) {
  469.             return false;
  470.         }
  471.  
  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.         }
  485.  
  486.         // build the statement
  487.         $query 'INSERT INTO ' $this->quoteIdentifier($table
  488.                . ' (' implode(', '$cols') '
  489.                . 'VALUES (';
  490.  
  491.         $query .= implode(', '$a')';
  492.         // prepare and execute the statement
  493.  
  494.         $this->exec($queryarray_values($values));
  495.  
  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.     {
  507.  
  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);
  546.             
  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($statementarray $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($statementarray $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($statementarray $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($statementarray $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($statementarray $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($statementarray $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($statementarray $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($queryarray $params array()) 
  684.     {
  685.         $parser new Doctrine_Query($this);
  686.  
  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();
  697.  
  698.         try {
  699.             $event new Doctrine_Event($thisDoctrine_Event::CONN_PREPARE$statement);
  700.     
  701.             $this->getAttribute(Doctrine::ATTR_LISTENER)->prePrepare($event);
  702.  
  703.             $stmt false;
  704.     
  705.             if $event->skipOperation{
  706.                 $stmt $this->dbh->prepare($statement);
  707.             }
  708.     
  709.             $this->getAttribute(Doctrine::ATTR_LISTENER)->postPrepare($event);
  710.             
  711.             return new Doctrine_Connection_Statement($this$stmt);
  712.         catch(Doctrine_Adapter_Exception $e{
  713.         catch(PDOException $e}
  714.  
  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($queryarray $params array()) 
  737.     {
  738.         $parser new Doctrine_Query($this);
  739.  
  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 || $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($queryarray $params array())
  782.     {
  783.         $this->connect();
  784.  
  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($thisDoctrine_Event::CONN_QUERY$query$params);
  792.  
  793.                 $this->getAttribute(Doctrine::ATTR_LISTENER)->preQuery($event);
  794.  
  795.                 if $event->skipOperation{
  796.                     $stmt $this->dbh->query($query);
  797.  
  798.                     $this->_count++;
  799.                 }
  800.                 $this->getAttribute(Doctrine::ATTR_LISTENER)->postQuery($event);
  801.  
  802.                 return $stmt;
  803.             }
  804.         catch(Doctrine_Adapter_Exception $e{
  805.         catch(PDOException $e}
  806.  
  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($queryarray $params array()) {
  817.         $this->connect();
  818.  
  819.         try {
  820.             if empty($params)) {
  821.                 $stmt $this->prepare($query);
  822.                 $stmt->execute($params);
  823.  
  824.                 return $stmt->rowCount();
  825.             else {
  826.                 $event new Doctrine_Event($thisDoctrine_Event::CONN_EXEC$query$params);
  827.  
  828.                 $this->getAttribute(Doctrine::ATTR_LISTENER)->preExec($event);
  829.  
  830.                 if $event->skipOperation{
  831.                     $count $this->dbh->exec($query);
  832.  
  833.                     $this->_count++;
  834.                 }
  835.                 $this->getAttribute(Doctrine::ATTR_LISTENER)->postExec($event);
  836.  
  837.                 return $count;
  838.             }
  839.         catch(Doctrine_Adapter_Exception $e{
  840.         catch(PDOException $e}
  841.  
  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($thisDoctrine_Event::CONN_ERROR);
  852.  
  853.         $this->getListener()->preError($event);
  854.  
  855.         $name 'Doctrine_Connection_' $this->driverName . '_Exception';
  856.  
  857.         $exc  new $name($e->getMessage()(int) $e->getCode());
  858.         if is_array($e->errorInfo)) {
  859.             $e->errorInfo = array(nullnullnullnull);
  860.         }
  861.         $exc->processErrorInfo($e->errorInfo);
  862.  
  863.          if ($this->getAttribute(Doctrine::ATTR_THROW_EXCEPTIONS)) {
  864.             throw $exc;
  865.         }
  866.         
  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';
  892.  
  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.         }
  898.  
  899.         $this->tables[$name$table;
  900.  
  901.  
  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();
  948.  
  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($thisDoctrine_Event::CONN_CLOSE);
  1014.  
  1015.         $this->getAttribute(Doctrine::ATTR_LISTENER)->preClose($event);
  1016.  
  1017.         $this->clear();
  1018.         
  1019.         unset($this->dbh);
  1020.         $this->isConnected false;
  1021.  
  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();
  1042.  
  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();
  1054.  
  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.     }
  1125.  
  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. }