|
TYPO3 API
SVNRelease
|
00001 <?php 00002 /*************************************************************** 00003 * Copyright notice 00004 * 00005 * (c) 2010-2011 Xavier Perseguers <typo3@perseguers.ch> 00006 * All rights reserved 00007 * 00008 * This script is part of the TYPO3 project. The TYPO3 project is 00009 * free software; you can redistribute it and/or modify 00010 * it under the terms of the GNU General Public License as published by 00011 * the Free Software Foundation; either version 2 of the License, or 00012 * (at your option) any later version. 00013 * 00014 * The GNU General Public License can be found at 00015 * http://www.gnu.org/copyleft/gpl.html. 00016 * A copy is found in the textfile GPL.txt and important notices to the license 00017 * from the author is found in LICENSE.txt distributed with these scripts. 00018 * 00019 * 00020 * This script is distributed in the hope that it will be useful, 00021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00023 * GNU General Public License for more details. 00024 * 00025 * This copyright notice MUST APPEAR in all copies of the script! 00026 ***************************************************************/ 00027 00028 /** 00029 * TYPO3 prepared statement for t3lib_db class. 00030 * 00031 * USE: 00032 * In all TYPO3 scripts when you need to create a prepared query: 00033 * <code> 00034 * $statement = $GLOBALS['TYPO3_DB']->prepare_SELECTquery('*', 'pages', 'uid = :uid'); 00035 * $statement->execute(array(':uid' => 2)); 00036 * while (($row = $statement->fetch()) !== FALSE) { 00037 * // ... 00038 * } 00039 * $statement->free(); 00040 * </code> 00041 * 00042 * @author Xavier Perseguers <typo3@perseguers.ch> 00043 * @package TYPO3 00044 * @subpackage t3lib 00045 */ 00046 class t3lib_db_PreparedStatement { 00047 00048 /** 00049 * Represents the SQL NULL data type. 00050 * @var integer 00051 */ 00052 const PARAM_NULL = 0; 00053 00054 /** 00055 * Represents the SQL INTEGER data type. 00056 * @var integer 00057 */ 00058 const PARAM_INT = 1; 00059 00060 /** 00061 * Represents the SQL CHAR, VARCHAR, or other string data type. 00062 * @var integer 00063 */ 00064 const PARAM_STR = 2; 00065 00066 /** 00067 * Represents a boolean data type. 00068 * @var integer 00069 */ 00070 const PARAM_BOOL = 3; 00071 00072 /** 00073 * Automatically detects underlying type 00074 * @var integer 00075 */ 00076 const PARAM_AUTOTYPE = 4; 00077 00078 /** 00079 * Specifies that the fetch method shall return each row as an array indexed by 00080 * column name as returned in the corresponding result set. If the result set 00081 * contains multiple columns with the same name, t3lib_db_PreparedStatement::FETCH_ASSOC 00082 * returns only a single value per column name. 00083 * @var integer 00084 */ 00085 const FETCH_ASSOC = 2; 00086 00087 /** 00088 * Specifies that the fetch method shall return each row as an array indexed by 00089 * column number as returned in the corresponding result set, starting at column 0. 00090 * @var integer 00091 */ 00092 const FETCH_NUM = 3; 00093 00094 /** 00095 * Query to be executed. 00096 * @var string 00097 */ 00098 protected $query; 00099 00100 /** 00101 * Components of the query to be executed. 00102 * @var array 00103 */ 00104 protected $precompiledQueryParts; 00105 00106 /** 00107 * Table (used to call $GLOBALS['TYPO3_DB']->fullQuoteStr(). 00108 * @var string 00109 */ 00110 protected $table; 00111 00112 /** 00113 * Binding parameters. 00114 * @var array 00115 */ 00116 protected $parameters; 00117 00118 /** 00119 * Default fetch mode. 00120 * @var integer 00121 */ 00122 protected $defaultFetchMode = self::FETCH_ASSOC; 00123 00124 /** 00125 * MySQL result pointer (of SELECT query) / DBAL object. 00126 * @var pointer 00127 */ 00128 protected $resource; 00129 00130 /** 00131 * Creates a new PreparedStatement. Either $query or $queryComponents 00132 * should be used. Typically $query will be used by native MySQL TYPO3_DB 00133 * on a ready-to-be-executed query. On the other hand, DBAL will have 00134 * parse the query and will be able to safely know where parameters are used 00135 * and will use $queryComponents instead. 00136 * This constructor may only be used by t3lib_DB. 00137 * 00138 * @param string $query SQL query to be executed 00139 * @param string FROM table, used to call $GLOBALS['TYPO3_DB']->fullQuoteStr(). 00140 * @param array $precompiledQueryParts Components of the query to be executed 00141 * @access private 00142 */ 00143 public function __construct($query, $table, array $precompiledQueryParts = array()) { 00144 $this->query = $query; 00145 $this->precompiledQueryParts = $precompiledQueryParts; 00146 $this->table = $table; 00147 $this->parameters = array(); 00148 $this->resource = NULL; 00149 } 00150 00151 /** 00152 * Binds an array of values to corresponding named or question mark placeholders in the SQL 00153 * statement that was use to prepare the statement. 00154 * 00155 * Example 1: 00156 * <code> 00157 * $statement = $GLOBALS['TYPO3_DB']->prepare_SELECTquery('*', 'bugs', 'reported_by = ? AND bug_status = ?'); 00158 * $statement->bindValues(array('goofy', 'FIXED')); 00159 * </code> 00160 * 00161 * Example 2: 00162 * <code> 00163 * $statement = $GLOBALS['TYPO3_DB']->prepare_SELECTquery('*', 'bugs', 'reported_by = :nickname AND bug_status = :status'); 00164 * $statement->bindValues(array(':nickname' => 'goofy', ':status' => 'FIXED')); 00165 * </code> 00166 * 00167 * @param array $values The values to bind to the parameter. The PHP type of each array value will be used to decide which PARAM_* type to use (int, string, boolean, null), so make sure your variables are properly casted, if needed. 00168 * @return t3lib_db_PreparedStatement The current prepared statement to allow method chaining 00169 * @api 00170 */ 00171 public function bindValues(array $values) { 00172 foreach ($values as $parameter => $value) { 00173 $key = is_int($parameter) ? $parameter + 1 : $parameter; 00174 $this->bindValue($key, $value, self::PARAM_AUTOTYPE); 00175 } 00176 00177 return $this; 00178 } 00179 00180 /** 00181 * Binds a value to a corresponding named or question mark placeholder in the SQL 00182 * statement that was use to prepare the statement. 00183 * 00184 * Example 1: 00185 * <code> 00186 * $statement = $GLOBALS['TYPO3_DB']->prepare_SELECTquery('*', 'bugs', 'reported_by = ? AND bug_status = ?'); 00187 * $statement->bindValue(1, 'goofy'); 00188 * $statement->bindValue(2, 'FIXED'); 00189 * </code> 00190 * 00191 * Example 2: 00192 * <code> 00193 * $statement = $GLOBALS['TYPO3_DB']->prepare_SELECTquery('*', 'bugs', 'reported_by = :nickname AND bug_status = :status'); 00194 * $statement->bindValue(':nickname', 'goofy'); 00195 * $statement->bindValue(':status', 'FIXED'); 00196 * </code> 00197 * 00198 * @param mixed $parameter Parameter identifier. For a prepared statement using named placeholders, this will be a parameter name of the form :name. For a prepared statement using question mark placeholders, this will be the 1-indexed position of the parameter. 00199 * @param mixed $value The value to bind to the parameter. 00200 * @param integer $data_type Explicit data type for the parameter using the t3lib_db_PreparedStatement::PARAM_* constants. If not given, the PHP type of the value will be used instead (int, string, boolean). 00201 * @return t3lib_db_PreparedStatement The current prepared statement to allow method chaining 00202 * @api 00203 */ 00204 public function bindValue($parameter, $value, $data_type = self::PARAM_AUTOTYPE) { 00205 switch ($data_type) { 00206 case self::PARAM_INT: 00207 if (!is_int($value)) { 00208 throw new InvalidArgumentException('$value is not an integer as expected: ' . $value, 1281868686); 00209 } 00210 break; 00211 case self::PARAM_BOOL: 00212 if (!is_bool($value)) { 00213 throw new InvalidArgumentException('$value is not a boolean as expected: ' . $value, 1281868687); 00214 } 00215 break; 00216 case self::PARAM_NULL: 00217 if (!is_null($value)) { 00218 throw new InvalidArgumentException('$value is not NULL as expected: ' . $value, 1282489834); 00219 } 00220 break; 00221 } 00222 00223 $key = is_int($parameter) ? $parameter - 1 : $parameter; 00224 $this->parameters[$key] = array( 00225 'value' => $value, 00226 'type' => ($data_type == self::PARAM_AUTOTYPE ? $this->guessValueType($value) : $data_type), 00227 ); 00228 00229 return $this; 00230 } 00231 00232 /** 00233 * Executes the prepared statement. If the prepared statement included parameter 00234 * markers, you must either: 00235 * <ul> 00236 * <li>call {@link t3lib_db_PreparedStatement::bindParam()} to bind PHP variables 00237 * to the parameter markers: bound variables pass their value as input</li> 00238 * <li>or pass an array of input-only parameter values</li> 00239 * </ul> 00240 * 00241 * $input_parameters behave as in {@link t3lib_db_PreparedStatement::bindParams()} 00242 * and work for both named parameters and question mark parameters. 00243 * 00244 * Example 1: 00245 * <code> 00246 * $statement = $GLOBALS['TYPO3_DB']->prepare_SELECTquery('*', 'bugs', 'reported_by = ? AND bug_status = ?'); 00247 * $statement->execute(array('goofy', 'FIXED')); 00248 * </code> 00249 * 00250 * Example 2: 00251 * <code> 00252 * $statement = $GLOBALS['TYPO3_DB']->prepare_SELECTquery('*', 'bugs', 'reported_by = :nickname AND bug_status = :status'); 00253 * $statement->execute(array(':nickname' => 'goofy', ':status' => 'FIXED')); 00254 * </code> 00255 * 00256 * @param array $input_parameters An array of values with as many elements as there are bound parameters in the SQL statement being executed. The PHP type of each array value will be used to decide which PARAM_* type to use (int, string, boolean, null), so make sure your variables are properly casted, if needed. 00257 * @return boolean Returns TRUE on success or FALSE on failure. 00258 * @api 00259 */ 00260 public function execute(array $input_parameters = array()) { 00261 $query = $this->query; 00262 $precompiledQueryParts = $this->precompiledQueryParts; 00263 $parameterValues = $this->parameters; 00264 00265 if (count($input_parameters) > 0) { 00266 $parameterValues = array(); 00267 foreach ($input_parameters as $key => $value) { 00268 $parameterValues[$key] = array( 00269 'value' => $value, 00270 'type' => $this->guessValueType($value), 00271 ); 00272 } 00273 } 00274 00275 $this->replaceValuesInQuery($query, $precompiledQueryParts, $parameterValues); 00276 if (count($precompiledQueryParts) > 0) { 00277 $query = implode('', $precompiledQueryParts['queryParts']); 00278 } 00279 $this->resource = $GLOBALS['TYPO3_DB']->exec_PREPAREDquery($query, $precompiledQueryParts); 00280 00281 // Empty binding parameters 00282 $this->parameters = array(); 00283 00284 // Return the success flag 00285 return ($this->resource ? TRUE : FALSE); 00286 } 00287 00288 /** 00289 * Fetches a row from a result set associated with a t3lib_db_PreparedStatement object. 00290 * 00291 * @param integer $fetch_style Controls how the next row will be returned to the caller. This value must be one of the t3lib_db_PreparedStatement::FETCH_* constants. If omitted, default fetch mode for this prepared query will be used. 00292 * @return array Array of rows or FALSE if there are no more rows. 00293 * @api 00294 */ 00295 public function fetch($fetch_style = 0) { 00296 if ($fetch_style == 0) { 00297 $fetch_style = $this->defaultFetchMode; 00298 } 00299 switch ($fetch_style) { 00300 case self::FETCH_ASSOC: 00301 $row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($this->resource); 00302 break; 00303 case self::FETCH_NUM: 00304 $row = $GLOBALS['TYPO3_DB']->sql_fetch_row($this->resource); 00305 break; 00306 default: 00307 throw new InvalidArgumentException('$fetch_style must be either t3lib_db_PreparedStatement::FETCH_ASSOC or t3lib_db_PreparedStatement::FETCH_NUM', 1281646455); 00308 } 00309 return $row; 00310 } 00311 00312 /** 00313 * Moves internal result pointer. 00314 * 00315 * @param integer $rowNumber Where to place the result pointer (0 = start) 00316 * @return boolean Returns TRUE on success or FALSE on failure. 00317 * @api 00318 */ 00319 public function seek($rowNumber) { 00320 return $GLOBALS['TYPO3_DB']->sql_data_seek($this->resource, intval($rowNumber)); 00321 } 00322 00323 /** 00324 * Returns an array containing all of the result set rows. 00325 * 00326 * @param integer $fetch_style Controls the contents of the returned array as documented in {@link t3lib_db_PreparedStatement::fetch()}. 00327 * @return array Array of rows. 00328 * @api 00329 */ 00330 public function fetchAll($fetch_style = 0) { 00331 $rows = array(); 00332 while (($row = $this->fetch($fetch_style)) !== FALSE) { 00333 $rows[] = $row; 00334 } 00335 return $rows; 00336 } 00337 00338 /** 00339 * Releases the cursor. Should always be call after having fetched rows from 00340 * a query execution. 00341 * 00342 * @return void 00343 * @api 00344 */ 00345 public function free() { 00346 $GLOBALS['TYPO3_DB']->sql_free_result($this->resource); 00347 } 00348 00349 /** 00350 * Returns the number of rows affected by the last SQL statement. 00351 * 00352 * @return integer The number of rows. 00353 * @api 00354 */ 00355 public function rowCount() { 00356 return $GLOBALS['TYPO3_DB']->sql_num_rows($this->resource); 00357 } 00358 00359 /** 00360 * Returns the error number on the last execute() call. 00361 * 00362 * @return integer Driver specific error code. 00363 * @api 00364 */ 00365 public function errorCode() { 00366 return $GLOBALS['TYPO3_DB']->sql_errno(); 00367 } 00368 00369 /** 00370 * Returns an array of error information about the last operation performed by this statement handle. 00371 * The array consists of the following fields: 00372 * <ol start="0"> 00373 * <li>Driver specific error code.</li> 00374 * <li>Driver specific error message</li> 00375 * </ol> 00376 * 00377 * @return array Array of error information. 00378 */ 00379 public function errorInfo() { 00380 return array( 00381 $GLOBALS['TYPO3_DB']->sql_errno(), 00382 $GLOBALS['TYPO3_DB']->sql_error(), 00383 ); 00384 } 00385 00386 /** 00387 * Sets the default fetch mode for this prepared query. 00388 * 00389 * @param integer $mode One of the t3lib_db_PreparedStatement::FETCH_* constants 00390 * @return void 00391 * @api 00392 */ 00393 public function setFetchMode($mode) { 00394 switch ($mode) { 00395 case self::FETCH_ASSOC: 00396 case self::FETCH_NUM: 00397 $this->defaultFetchMode = $mode; 00398 break; 00399 default: 00400 throw new InvalidArgumentException('$mode must be either t3lib_db_PreparedStatement::FETCH_ASSOC or t3lib_db_PreparedStatement::FETCH_NUM', 1281875340); 00401 } 00402 } 00403 00404 /** 00405 * Guesses the type of a given value. 00406 * 00407 * @param mixed $value 00408 * @return integer One of the t3lib_db_PreparedStatement::PARAM_* constants 00409 */ 00410 protected function guessValueType($value) { 00411 if (is_bool($value)) { 00412 $type = self::PARAM_BOOL; 00413 } elseif (is_int($value)) { 00414 $type = self::PARAM_INT; 00415 } elseif (is_null($value)) { 00416 $type = self::PARAM_NULL; 00417 } else { 00418 $type = self::PARAM_STR; 00419 } 00420 00421 return $type; 00422 } 00423 00424 /** 00425 * Replaces values for each parameter in a query. 00426 * 00427 * @param string $query 00428 * @param array $precompiledQueryParts 00429 * @param array $parameterValues 00430 * @return void 00431 */ 00432 protected function replaceValuesInQuery(&$query, array &$precompiledQueryParts, array $parameterValues) { 00433 foreach ($parameterValues as $key => $typeValue) { 00434 switch ($typeValue['type']) { 00435 case self::PARAM_NULL: 00436 $value = 'NULL'; 00437 break; 00438 case self::PARAM_INT: 00439 $value = intval($typeValue['value']); 00440 break; 00441 case self::PARAM_STR: 00442 $value = $GLOBALS['TYPO3_DB']->fullQuoteStr($typeValue['value'], $this->table); 00443 break; 00444 case self::PARAM_BOOL: 00445 $value = $typeValue['value'] ? 1 : 0; 00446 break; 00447 default: 00448 throw new InvalidArgumentException( 00449 sprintf('Unknown type %s used for parameter %s.', $typeValue['type'], $key), 00450 1281859196 00451 ); 00452 } 00453 00454 if (is_int($key)) { 00455 if (count($precompiledQueryParts['queryParts']) > 0) { 00456 $precompiledQueryParts['queryParts'][2 * $key + 1] = $value; 00457 } else { 00458 $parts = explode('?', $query, 2); 00459 $parts[0] .= $value; 00460 $query = implode('', $parts); 00461 } 00462 } else { 00463 if (!preg_match('/^:[\w]+$/', $key)) { 00464 throw new InvalidArgumentException('Parameter names must start with ":" followed by an arbitrary number of alphanumerical characters.', 1282348825); 00465 } 00466 00467 for ($i = 1; $i < count($precompiledQueryParts['queryParts']); $i++) { 00468 if ($precompiledQueryParts['queryParts'][$i] === $key) { 00469 $precompiledQueryParts['queryParts'][$i] = $value; 00470 } 00471 } 00472 // Replace the marker (not preceeded by a word character or a ':' but 00473 // followed by a word boundary) 00474 $query = preg_replace('/(?<![\w:])' . $key . '\b/', $value, $query); 00475 } 00476 } 00477 } 00478 } 00479 00480 00481 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/db/class.t3lib_db_PreparedStatement.php'])) { 00482 include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/db/class.t3lib_db_PreparedStatement.php']); 00483 } 00484 00485 ?>
1.8.0