File indexing completed on 2024-05-12 06:02:26

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Db
0017  * @subpackage Statement
0018  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0019  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0020  * @version    $Id$
0021  */
0022 
0023 /**
0024  * @see Zend_Db
0025  */
0026 // require_once 'Zend/Db.php';
0027 
0028 /**
0029  * @see Zend_Db_Statement_Interface
0030  */
0031 // require_once 'Zend/Db/Statement/Interface.php';
0032 
0033 /**
0034  * Abstract class to emulate a PDOStatement for native database adapters.
0035  *
0036  * @category   Zend
0037  * @package    Zend_Db
0038  * @subpackage Statement
0039  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0040  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0041  */
0042 abstract class Zend_Db_Statement implements Zend_Db_Statement_Interface
0043 {
0044 
0045     /**
0046      * @var resource|object The driver level statement object/resource
0047      */
0048     protected $_stmt = null;
0049 
0050     /**
0051      * @var Zend_Db_Adapter_Abstract
0052      */
0053     protected $_adapter = null;
0054 
0055     /**
0056      * The current fetch mode.
0057      *
0058      * @var integer
0059      */
0060     protected $_fetchMode = Zend_Db::FETCH_ASSOC;
0061 
0062     /**
0063      * Attributes.
0064      *
0065      * @var array
0066      */
0067     protected $_attribute = array();
0068 
0069     /**
0070      * Column result bindings.
0071      *
0072      * @var array
0073      */
0074     protected $_bindColumn = array();
0075 
0076     /**
0077      * Query parameter bindings; covers bindParam() and bindValue().
0078      *
0079      * @var array
0080      */
0081     protected $_bindParam = array();
0082 
0083     /**
0084      * SQL string split into an array at placeholders.
0085      *
0086      * @var array
0087      */
0088     protected $_sqlSplit = array();
0089 
0090     /**
0091      * Parameter placeholders in the SQL string by position in the split array.
0092      *
0093      * @var array
0094      */
0095     protected $_sqlParam = array();
0096 
0097     /**
0098      * @var Zend_Db_Profiler_Query
0099      */
0100     protected $_queryId = null;
0101 
0102     /**
0103      * Constructor for a statement.
0104      *
0105      * @param Zend_Db_Adapter_Abstract $adapter
0106      * @param mixed $sql Either a string or Zend_Db_Select.
0107      */
0108     public function __construct($adapter, $sql)
0109     {
0110         $this->_adapter = $adapter;
0111         if ($sql instanceof Zend_Db_Select) {
0112             $sql = $sql->assemble();
0113         }
0114         $this->_parseParameters($sql);
0115         $this->_prepare($sql);
0116 
0117         $this->_queryId = $this->_adapter->getProfiler()->queryStart($sql);
0118     }
0119 
0120     /**
0121      * Internal method called by abstract statment constructor to setup
0122      * the driver level statement
0123      *
0124      * @return void
0125      */
0126     protected function _prepare($sql)
0127     {
0128         return;
0129     }
0130 
0131     /**
0132      * @param string $sql
0133      * @return void
0134      */
0135     protected function _parseParameters($sql)
0136     {
0137         $sql = $this->_stripQuoted($sql);
0138 
0139         // split into text and params
0140         $this->_sqlSplit = preg_split('/(\?|\:[a-zA-Z0-9_]+)/',
0141             $sql, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
0142 
0143         // map params
0144         $this->_sqlParam = array();
0145         foreach ($this->_sqlSplit as $key => $val) {
0146             if ($val == '?') {
0147                 if ($this->_adapter->supportsParameters('positional') === false) {
0148                     /**
0149                      * @see Zend_Db_Statement_Exception
0150                      */
0151                     // require_once 'Zend/Db/Statement/Exception.php';
0152                     throw new Zend_Db_Statement_Exception("Invalid bind-variable position '$val'");
0153                 }
0154             } else if ($val[0] == ':') {
0155                 if ($this->_adapter->supportsParameters('named') === false) {
0156                     /**
0157                      * @see Zend_Db_Statement_Exception
0158                      */
0159                     // require_once 'Zend/Db/Statement/Exception.php';
0160                     throw new Zend_Db_Statement_Exception("Invalid bind-variable name '$val'");
0161                 }
0162             }
0163             $this->_sqlParam[] = $val;
0164         }
0165 
0166         // set up for binding
0167         $this->_bindParam = array();
0168     }
0169 
0170     /**
0171      * Remove parts of a SQL string that contain quoted strings
0172      * of values or identifiers.
0173      *
0174      * @param string $sql
0175      * @return string
0176      */
0177     protected function _stripQuoted($sql)
0178     {
0179 
0180         // get the character for value quoting
0181         // this should be '
0182         $q = $this->_adapter->quote('a');
0183         $q = $q[0];        
0184         // get the value used as an escaped quote,
0185         // e.g. \' or ''
0186         $qe = $this->_adapter->quote($q);
0187         $qe = substr($qe, 1, 2);
0188         $qe = preg_quote($qe);
0189         $escapeChar = substr($qe,0,1);
0190         // remove 'foo\'bar'
0191         if (!empty($q)) {
0192             $escapeChar = preg_quote($escapeChar);
0193             // this segfaults only after 65,000 characters instead of 9,000
0194             $sql = preg_replace("/$q([^$q{$escapeChar}]*|($qe)*)*$q/s", '', $sql);
0195         }
0196         
0197         // get a version of the SQL statement with all quoted
0198         // values and delimited identifiers stripped out
0199         // remove "foo\"bar"
0200         $sql = preg_replace("/\"(\\\\\"|[^\"])*\"/Us", '', $sql);
0201 
0202         // get the character for delimited id quotes,
0203         // this is usually " but in MySQL is `
0204         $d = $this->_adapter->quoteIdentifier('a');
0205         $d = $d[0];
0206         // get the value used as an escaped delimited id quote,
0207         // e.g. \" or "" or \`
0208         $de = $this->_adapter->quoteIdentifier($d);
0209         $de = substr($de, 1, 2);
0210         $de = preg_quote($de);
0211         // Note: $de and $d where never used..., now they are:
0212         $sql = preg_replace("/$d($de|\\\\{2}|[^$d])*$d/Us", '', $sql);
0213         return $sql;
0214     }
0215 
0216     /**
0217      * Bind a column of the statement result set to a PHP variable.
0218      *
0219      * @param string $column Name the column in the result set, either by
0220      *                       position or by name.
0221      * @param mixed  $param  Reference to the PHP variable containing the value.
0222      * @param mixed  $type   OPTIONAL
0223      * @return bool
0224      */
0225     public function bindColumn($column, &$param, $type = null)
0226     {
0227         $this->_bindColumn[$column] =& $param;
0228         return true;
0229     }
0230 
0231     /**
0232      * Binds a parameter to the specified variable name.
0233      *
0234      * @param mixed $parameter Name the parameter, either integer or string.
0235      * @param mixed $variable  Reference to PHP variable containing the value.
0236      * @param mixed $type      OPTIONAL Datatype of SQL parameter.
0237      * @param mixed $length    OPTIONAL Length of SQL parameter.
0238      * @param mixed $options   OPTIONAL Other options.
0239      * @return bool
0240      */
0241     public function bindParam($parameter, &$variable, $type = null, $length = null, $options = null)
0242     {
0243         if (!is_int($parameter) && !is_string($parameter)) {
0244             /**
0245              * @see Zend_Db_Statement_Exception
0246              */
0247             // require_once 'Zend/Db/Statement/Exception.php';
0248             throw new Zend_Db_Statement_Exception('Invalid bind-variable position');
0249         }
0250 
0251         $position = null;
0252         if (($intval = (int) $parameter) > 0 && $this->_adapter->supportsParameters('positional')) {
0253             if ($intval >= 1 || $intval <= count($this->_sqlParam)) {
0254                 $position = $intval;
0255             }
0256         } else if ($this->_adapter->supportsParameters('named')) {
0257             if ($parameter[0] != ':') {
0258                 $parameter = ':' . $parameter;
0259             }
0260             if (in_array($parameter, $this->_sqlParam) !== false) {
0261                 $position = $parameter;
0262             }
0263         }
0264 
0265         if ($position === null) {
0266             /**
0267              * @see Zend_Db_Statement_Exception
0268              */
0269             // require_once 'Zend/Db/Statement/Exception.php';
0270             throw new Zend_Db_Statement_Exception("Invalid bind-variable position '$parameter'");
0271         }
0272 
0273         // Finally we are assured that $position is valid
0274         $this->_bindParam[$position] =& $variable;
0275         return $this->_bindParam($position, $variable, $type, $length, $options);
0276     }
0277 
0278     /**
0279      * Binds a value to a parameter.
0280      *
0281      * @param mixed $parameter Name the parameter, either integer or string.
0282      * @param mixed $value     Scalar value to bind to the parameter.
0283      * @param mixed $type      OPTIONAL Datatype of the parameter.
0284      * @return bool
0285      */
0286     public function bindValue($parameter, $value, $type = null)
0287     {
0288         return $this->bindParam($parameter, $value, $type);
0289     }
0290 
0291     /**
0292      * Executes a prepared statement.
0293      *
0294      * @param array $params OPTIONAL Values to bind to parameter placeholders.
0295      * @return bool
0296      */
0297     public function execute(array $params = null)
0298     {
0299         /*
0300          * Simple case - no query profiler to manage.
0301          */
0302         if ($this->_queryId === null) {
0303             return $this->_execute($params);
0304         }
0305 
0306         /*
0307          * Do the same thing, but with query profiler
0308          * management before and after the execute.
0309          */
0310         $prof = $this->_adapter->getProfiler();
0311         $qp = $prof->getQueryProfile($this->_queryId);
0312         if ($qp->hasEnded()) {
0313             $this->_queryId = $prof->queryClone($qp);
0314             $qp = $prof->getQueryProfile($this->_queryId);
0315         }
0316         if ($params !== null) {
0317             $qp->bindParams($params);
0318         } else {
0319             $qp->bindParams($this->_bindParam);
0320         }
0321         $qp->start($this->_queryId);
0322 
0323         $retval = $this->_execute($params);
0324 
0325         $prof->queryEnd($this->_queryId);
0326 
0327         return $retval;
0328     }
0329 
0330     /**
0331      * Returns an array containing all of the result set rows.
0332      *
0333      * @param int $style OPTIONAL Fetch mode.
0334      * @param int $col   OPTIONAL Column number, if fetch mode is by column.
0335      * @return array Collection of rows, each in a format by the fetch mode.
0336      */
0337     public function fetchAll($style = null, $col = null)
0338     {
0339         $data = array();
0340         if ($style === Zend_Db::FETCH_COLUMN && $col === null) {
0341             $col = 0;
0342         }
0343         if ($col === null) {
0344             while ($row = $this->fetch($style)) {
0345                 $data[] = $row;
0346             }
0347         } else {
0348             while (false !== ($val = $this->fetchColumn($col))) {
0349                 $data[] = $val;
0350             }
0351         }
0352         return $data;
0353     }
0354 
0355     /**
0356      * Returns a single column from the next row of a result set.
0357      *
0358      * @param int $col OPTIONAL Position of the column to fetch.
0359      * @return string One value from the next row of result set, or false.
0360      */
0361     public function fetchColumn($col = 0)
0362     {
0363         $data = array();
0364         $col = (int) $col;
0365         $row = $this->fetch(Zend_Db::FETCH_NUM);
0366         if (!is_array($row)) {
0367             return false;
0368         }
0369         return $row[$col];
0370     }
0371 
0372     /**
0373      * Fetches the next row and returns it as an object.
0374      *
0375      * @param string $class  OPTIONAL Name of the class to create.
0376      * @param array  $config OPTIONAL Constructor arguments for the class.
0377      * @return mixed One object instance of the specified class, or false.
0378      */
0379     public function fetchObject($class = 'stdClass', array $config = array())
0380     {
0381         $obj = new $class($config);
0382         $row = $this->fetch(Zend_Db::FETCH_ASSOC);
0383         if (!is_array($row)) {
0384             return false;
0385         }
0386         foreach ($row as $key => $val) {
0387             $obj->$key = $val;
0388         }
0389         return $obj;
0390     }
0391 
0392     /**
0393      * Retrieve a statement attribute.
0394      *
0395      * @param string $key Attribute name.
0396      * @return mixed      Attribute value.
0397      */
0398     public function getAttribute($key)
0399     {
0400         if (array_key_exists($key, $this->_attribute)) {
0401             return $this->_attribute[$key];
0402         }
0403     }
0404 
0405     /**
0406      * Set a statement attribute.
0407      *
0408      * @param string $key Attribute name.
0409      * @param mixed  $val Attribute value.
0410      * @return bool
0411      */
0412     public function setAttribute($key, $val)
0413     {
0414         $this->_attribute[$key] = $val;
0415     }
0416 
0417     /**
0418      * Set the default fetch mode for this statement.
0419      *
0420      * @param int   $mode The fetch mode.
0421      * @return bool
0422      * @throws Zend_Db_Statement_Exception
0423      */
0424     public function setFetchMode($mode)
0425     {
0426         switch ($mode) {
0427             case Zend_Db::FETCH_NUM:
0428             case Zend_Db::FETCH_ASSOC:
0429             case Zend_Db::FETCH_BOTH:
0430             case Zend_Db::FETCH_OBJ:
0431                 $this->_fetchMode = $mode;
0432                 break;
0433             case Zend_Db::FETCH_BOUND:
0434             default:
0435                 $this->closeCursor();
0436                 /**
0437                  * @see Zend_Db_Statement_Exception
0438                  */
0439                 // require_once 'Zend/Db/Statement/Exception.php';
0440                 throw new Zend_Db_Statement_Exception('invalid fetch mode');
0441                 break;
0442         }
0443     }
0444 
0445     /**
0446      * Helper function to map retrieved row
0447      * to bound column variables
0448      *
0449      * @param array $row
0450      * @return bool True
0451      */
0452     public function _fetchBound($row)
0453     {
0454         foreach ($row as $key => $value) {
0455             // bindColumn() takes 1-based integer positions
0456             // but fetch() returns 0-based integer indexes
0457             if (is_int($key)) {
0458                 $key++;
0459             }
0460             // set results only to variables that were bound previously
0461             if (isset($this->_bindColumn[$key])) {
0462                 $this->_bindColumn[$key] = $value;
0463             }
0464         }
0465         return true;
0466     }
0467 
0468     /**
0469      * Gets the Zend_Db_Adapter_Abstract for this
0470      * particular Zend_Db_Statement object.
0471      *
0472      * @return Zend_Db_Adapter_Abstract
0473      */
0474     public function getAdapter()
0475     {
0476         return $this->_adapter;
0477     }
0478 
0479     /**
0480      * Gets the resource or object setup by the
0481      * _parse
0482      * @return unknown_type
0483      */
0484     public function getDriverStatement()
0485     {
0486         return $this->_stmt;
0487     }
0488 }