File indexing completed on 2025-01-19 05:21:03
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 }