1<?php 2/* 3 * $Id: Statement.php 1532 2007-05-31 17:45:07Z 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.doctrine-project.org>. 20 */ 21 22/** 23 * Doctrine_Connection_Statement 24 * 25 * @package Doctrine 26 * @subpackage Connection 27 * @author Konsta Vesterinen <kvesteri@cc.hut.fi> 28 * @license http://www.opensource.org/licenses/lgpl-license.php LGPL 29 * @link www.doctrine-project.org 30 * @since 1.0 31 * @version $Revision: 1532 $ 32 */ 33class Doctrine_Connection_Statement implements Doctrine_Adapter_Statement_Interface 34{ 35 /** 36 * @var Doctrine_Connection $conn Doctrine_Connection object, every connection 37 * statement holds an instance of Doctrine_Connection 38 */ 39 protected $_conn; 40 41 /** 42 * @var mixed $_stmt PDOStatement object, boolean false or Doctrine_Adapter_Statement object 43 */ 44 protected $_stmt; 45 46 /** 47 * constructor 48 * 49 * @param Doctrine_Connection $conn Doctrine_Connection object, every connection 50 * statement holds an instance of Doctrine_Connection 51 * @param mixed $stmt 52 */ 53 public function __construct(Doctrine_Connection $conn, $stmt) 54 { 55 $this->_conn = $conn; 56 $this->_stmt = $stmt; 57 58 if ($stmt === false) { 59 throw new Doctrine_Exception('Unknown statement object given.'); 60 } 61 } 62 /** 63 * destructor 64 * 65 * make sure that the cursor is closed 66 * 67 */ 68 public function __destruct() 69 { 70 $this->closeCursor(); 71 } 72 /** 73 * getConnection 74 * returns the connection object this statement uses 75 * 76 * @return Doctrine_Connection 77 */ 78 public function getConnection() 79 { 80 return $this->_conn; 81 } 82 83 public function getStatement() 84 { 85 return $this->_stmt; 86 } 87 88 public function getQuery() 89 { 90 return $this->_stmt->queryString; 91 } 92 93 /** 94 * bindColumn 95 * Bind a column to a PHP variable 96 * 97 * @param mixed $column Number of the column (1-indexed) or name of the column in the result set. 98 * If using the column name, be aware that the name should match 99 * the case of the column, as returned by the driver. 100 * 101 * @param string $param Name of the PHP variable to which the column will be bound. 102 * @param integer $type Data type of the parameter, specified by the Doctrine_Core::PARAM_* constants. 103 * @return boolean Returns TRUE on success or FALSE on failure 104 */ 105 public function bindColumn($column, $param, $type = null) 106 { 107 if ($type === null) { 108 return $this->_stmt->bindColumn($column, $param); 109 } else { 110 return $this->_stmt->bindColumn($column, $param, $type); 111 } 112 } 113 114 /** 115 * bindValue 116 * Binds a value to a corresponding named or question mark 117 * placeholder in the SQL statement that was use to prepare the statement. 118 * 119 * @param mixed $param Parameter identifier. For a prepared statement using named placeholders, 120 * this will be a parameter name of the form :name. For a prepared statement 121 * using question mark placeholders, this will be the 1-indexed position of the parameter 122 * 123 * @param mixed $value The value to bind to the parameter. 124 * @param integer $type Explicit data type for the parameter using the Doctrine_Core::PARAM_* constants. 125 * 126 * @return boolean Returns TRUE on success or FALSE on failure. 127 */ 128 public function bindValue($param, $value, $type = null) 129 { 130 if ($type === null) { 131 return $this->_stmt->bindValue($param, $value); 132 } else { 133 return $this->_stmt->bindValue($param, $value, $type); 134 } 135 } 136 137 /** 138 * bindParam 139 * Binds a PHP variable to a corresponding named or question mark placeholder in the 140 * SQL statement that was use to prepare the statement. Unlike Doctrine_Adapter_Statement_Interface->bindValue(), 141 * the variable is bound as a reference and will only be evaluated at the time 142 * that Doctrine_Adapter_Statement_Interface->execute() is called. 143 * 144 * Most parameters are input parameters, that is, parameters that are 145 * used in a read-only fashion to build up the query. Some drivers support the invocation 146 * of stored procedures that return data as output parameters, and some also as input/output 147 * parameters that both send in data and are updated to receive it. 148 * 149 * @param mixed $param Parameter identifier. For a prepared statement using named placeholders, 150 * this will be a parameter name of the form :name. For a prepared statement 151 * using question mark placeholders, this will be the 1-indexed position of the parameter 152 * 153 * @param mixed $variable Name of the PHP variable to bind to the SQL statement parameter. 154 * 155 * @param integer $type Explicit data type for the parameter using the Doctrine_Core::PARAM_* constants. To return 156 * an INOUT parameter from a stored procedure, use the bitwise OR operator to set the 157 * Doctrine_Core::PARAM_INPUT_OUTPUT bits for the data_type parameter. 158 * 159 * @param integer $length Length of the data type. To indicate that a parameter is an OUT parameter 160 * from a stored procedure, you must explicitly set the length. 161 * @param mixed $driverOptions 162 * @return boolean Returns TRUE on success or FALSE on failure. 163 */ 164 public function bindParam($column, &$variable, $type = null, $length = null, $driverOptions = array()) 165 { 166 if ($type === null) { 167 return $this->_stmt->bindParam($column, $variable); 168 } else { 169 return $this->_stmt->bindParam($column, $variable, $type, $length, $driverOptions); 170 } 171 } 172 173 /** 174 * closeCursor 175 * Closes the cursor, enabling the statement to be executed again. 176 * 177 * @return boolean Returns TRUE on success or FALSE on failure. 178 */ 179 public function closeCursor() 180 { 181 return $this->_stmt->closeCursor(); 182 } 183 184 /** 185 * columnCount 186 * Returns the number of columns in the result set 187 * 188 * @return integer Returns the number of columns in the result set represented 189 * by the Doctrine_Adapter_Statement_Interface object. If there is no result set, 190 * this method should return 0. 191 */ 192 public function columnCount() 193 { 194 return $this->_stmt->columnCount(); 195 } 196 197 /** 198 * errorCode 199 * Fetch the SQLSTATE associated with the last operation on the statement handle 200 * 201 * @see Doctrine_Adapter_Interface::errorCode() 202 * @return string error code string 203 */ 204 public function errorCode() 205 { 206 return $this->_stmt->errorCode(); 207 } 208 209 /** 210 * errorInfo 211 * Fetch extended error information associated with the last operation on the statement handle 212 * 213 * @see Doctrine_Adapter_Interface::errorInfo() 214 * @return array error info array 215 */ 216 public function errorInfo() 217 { 218 return $this->_stmt->errorInfo(); 219 } 220 221 /** 222 * execute 223 * Executes a prepared statement 224 * 225 * If the prepared statement included parameter markers, you must either: 226 * call PDOStatement->bindParam() to bind PHP variables to the parameter markers: 227 * bound variables pass their value as input and receive the output value, 228 * if any, of their associated parameter markers or pass an array of input-only 229 * parameter values 230 * 231 * 232 * @param array $params An array of values with as many elements as there are 233 * bound parameters in the SQL statement being executed. 234 * @return boolean Returns TRUE on success or FALSE on failure. 235 */ 236 public function execute($params = array()) 237 { 238 try { 239 $event = new Doctrine_Event($this, Doctrine_Event::STMT_EXECUTE, $this->getQuery(), $params); 240 $this->_conn->getListener()->preStmtExecute($event); 241 242 $result = true; 243 if ( ! $event->skipOperation) { 244 245 if ($this->_conn->getAttribute(Doctrine_Core::ATTR_PORTABILITY) & Doctrine_Core::PORTABILITY_EMPTY_TO_NULL) { 246 foreach ($params as $key => $value) { 247 if ($value === '') { 248 $params[$key] = null; 249 } 250 } 251 } 252 253 if ($params) { 254 $pos = 0; 255 foreach ($params as $key => $value) { 256 $pos++; 257 $param = is_numeric($key) ? $pos : $key; 258 if (is_resource($value)) { 259 $this->_stmt->bindParam($param, $params[$key], Doctrine_Core::PARAM_LOB); 260 } else { 261 $this->_stmt->bindParam($param, $params[$key]); 262 } 263 } 264 } 265 266 $result = $this->_stmt->execute(); 267 268 $this->_conn->incrementQueryCount(); 269 } 270 271 $this->_conn->getListener()->postStmtExecute($event); 272 273 //fix a possible "ORA-01000: maximum open cursors exceeded" when many non-SELECTs are executed and the profiling is enabled 274 if ('Oracle' == $this->getConnection()->getDriverName()) { 275 $queryBeginningSubstring = strtoupper(substr(ltrim($this->_stmt->queryString), 0, 6)); 276 if ($queryBeginningSubstring != 'SELECT' && substr($queryBeginningSubstring, 0, 4) != 'WITH' ){ 277 $this->closeCursor(); 278 } 279 } 280 281 return $result; 282 } catch (PDOException $e) { 283 } catch (Doctrine_Adapter_Exception $e) { 284 } 285 286 $this->_conn->rethrowException($e, $this); 287 288 return false; 289 } 290 291 /** 292 * fetch 293 * 294 * @see Doctrine_Core::FETCH_* constants 295 * @param integer $fetchStyle Controls how the next row will be returned to the caller. 296 * This value must be one of the Doctrine_Core::FETCH_* constants, 297 * defaulting to Doctrine_Core::FETCH_BOTH 298 * 299 * @param integer $cursorOrientation For a PDOStatement object representing a scrollable cursor, 300 * this value determines which row will be returned to the caller. 301 * This value must be one of the Doctrine_Core::FETCH_ORI_* constants, defaulting to 302 * Doctrine_Core::FETCH_ORI_NEXT. To request a scrollable cursor for your 303 * Doctrine_Adapter_Statement_Interface object, 304 * you must set the Doctrine_Core::ATTR_CURSOR attribute to Doctrine_Core::CURSOR_SCROLL when you 305 * prepare the SQL statement with Doctrine_Adapter_Interface->prepare(). 306 * 307 * @param integer $cursorOffset For a Doctrine_Adapter_Statement_Interface object representing a scrollable cursor for which the 308 * $cursorOrientation parameter is set to Doctrine_Core::FETCH_ORI_ABS, this value specifies 309 * the absolute number of the row in the result set that shall be fetched. 310 * 311 * For a Doctrine_Adapter_Statement_Interface object representing a scrollable cursor for 312 * which the $cursorOrientation parameter is set to Doctrine_Core::FETCH_ORI_REL, this value 313 * specifies the row to fetch relative to the cursor position before 314 * Doctrine_Adapter_Statement_Interface->fetch() was called. 315 * 316 * @return mixed 317 */ 318 public function fetch($fetchMode = Doctrine_Core::FETCH_BOTH, 319 $cursorOrientation = Doctrine_Core::FETCH_ORI_NEXT, 320 $cursorOffset = null) 321 { 322 $event = new Doctrine_Event($this, Doctrine_Event::STMT_FETCH, $this->getQuery()); 323 324 $event->fetchMode = $fetchMode; 325 $event->cursorOrientation = $cursorOrientation; 326 $event->cursorOffset = $cursorOffset; 327 328 $data = $this->_conn->getListener()->preFetch($event); 329 330 if ( ! $event->skipOperation) { 331 $data = $this->_stmt->fetch($fetchMode, $cursorOrientation, $cursorOffset); 332 } 333 334 $this->_conn->getListener()->postFetch($event); 335 336 return $data; 337 } 338 339 /** 340 * fetchAll 341 * Returns an array containing all of the result set rows 342 * 343 * @param integer $fetchMode Controls how the next row will be returned to the caller. 344 * This value must be one of the Doctrine_Core::FETCH_* constants, 345 * defaulting to Doctrine_Core::FETCH_BOTH 346 * 347 * @param integer $columnIndex Returns the indicated 0-indexed column when the value of $fetchStyle is 348 * Doctrine_Core::FETCH_COLUMN. Defaults to 0. 349 * 350 * @return array 351 */ 352 public function fetchAll($fetchMode = Doctrine_Core::FETCH_BOTH, 353 $columnIndex = null) 354 { 355 $event = new Doctrine_Event($this, Doctrine_Event::STMT_FETCHALL, $this->getQuery()); 356 $event->fetchMode = $fetchMode; 357 $event->columnIndex = $columnIndex; 358 359 $this->_conn->getListener()->preFetchAll($event); 360 361 if ( ! $event->skipOperation) { 362 if ($columnIndex !== null) { 363 $data = $this->_stmt->fetchAll($fetchMode, $columnIndex); 364 } else { 365 $data = $this->_stmt->fetchAll($fetchMode); 366 } 367 368 $event->data = $data; 369 } 370 371 $this->_conn->getListener()->postFetchAll($event); 372 373 return $data; 374 } 375 376 /** 377 * fetchColumn 378 * Returns a single column from the next row of a 379 * result set or FALSE if there are no more rows. 380 * 381 * @param integer $columnIndex 0-indexed number of the column you wish to retrieve from the row. If no 382 * value is supplied, Doctrine_Adapter_Statement_Interface->fetchColumn() 383 * fetches the first column. 384 * 385 * @return string returns a single column in the next row of a result set. 386 */ 387 public function fetchColumn($columnIndex = 0) 388 { 389 return $this->_stmt->fetchColumn($columnIndex); 390 } 391 392 /** 393 * fetchObject 394 * Fetches the next row and returns it as an object. 395 * 396 * Fetches the next row and returns it as an object. This function is an alternative to 397 * Doctrine_Adapter_Statement_Interface->fetch() with Doctrine_Core::FETCH_CLASS or Doctrine_Core::FETCH_OBJ style. 398 * 399 * @param string $className Name of the created class, defaults to stdClass. 400 * @param array $args Elements of this array are passed to the constructor. 401 * 402 * @return mixed an instance of the required class with property names that correspond 403 * to the column names or FALSE in case of an error. 404 */ 405 public function fetchObject($className = 'stdClass', $args = array()) 406 { 407 return $this->_stmt->fetchObject($className, $args); 408 } 409 410 /** 411 * getAttribute 412 * Retrieve a statement attribute 413 * 414 * @param integer $attribute 415 * @see Doctrine_Core::ATTR_* constants 416 * @return mixed the attribute value 417 */ 418 public function getAttribute($attribute) 419 { 420 return $this->_stmt->getAttribute($attribute); 421 } 422 423 /** 424 * getColumnMeta 425 * Returns metadata for a column in a result set 426 * 427 * @param integer $column The 0-indexed column in the result set. 428 * 429 * @return array Associative meta data array with the following structure: 430 * 431 * native_type The PHP native type used to represent the column value. 432 * driver:decl_ type The SQL type used to represent the column value in the database. If the column in the result set is the result of a function, this value is not returned by PDOStatement->getColumnMeta(). 433 * flags Any flags set for this column. 434 * name The name of this column as returned by the database. 435 * len The length of this column. Normally -1 for types other than floating point decimals. 436 * precision The numeric precision of this column. Normally 0 for types other than floating point decimals. 437 * pdo_type The type of this column as represented by the PDO::PARAM_* constants. 438 */ 439 public function getColumnMeta($column) 440 { 441 return $this->_stmt->getColumnMeta($column); 442 } 443 444 /** 445 * nextRowset 446 * Advances to the next rowset in a multi-rowset statement handle 447 * 448 * Some database servers support stored procedures that return more than one rowset 449 * (also known as a result set). The nextRowset() method enables you to access the second 450 * and subsequent rowsets associated with a PDOStatement object. Each rowset can have a 451 * different set of columns from the preceding rowset. 452 * 453 * @return boolean Returns TRUE on success or FALSE on failure. 454 */ 455 public function nextRowset() 456 { 457 return $this->_stmt->nextRowset(); 458 } 459 460 /** 461 * rowCount 462 * rowCount() returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement 463 * executed by the corresponding object. 464 * 465 * If the last SQL statement executed by the associated Statement object was a SELECT statement, 466 * some databases may return the number of rows returned by that statement. However, 467 * this behaviour is not guaranteed for all databases and should not be 468 * relied on for portable applications. 469 * 470 * @return integer Returns the number of rows. 471 */ 472 public function rowCount() 473 { 474 return $this->_stmt->rowCount(); 475 } 476 477 /** 478 * setAttribute 479 * Set a statement attribute 480 * 481 * @param integer $attribute 482 * @param mixed $value the value of given attribute 483 * @return boolean Returns TRUE on success or FALSE on failure. 484 */ 485 public function setAttribute($attribute, $value) 486 { 487 return $this->_stmt->setAttribute($attribute, $value); 488 } 489 490 /** 491 * setFetchMode 492 * Set the default fetch mode for this statement 493 * 494 * @param integer $mode The fetch mode must be one of the Doctrine_Core::FETCH_* constants. 495 * @return boolean Returns 1 on success or FALSE on failure. 496 */ 497 public function setFetchMode($mode, $arg1 = null, $arg2 = null) 498 { 499 return $this->_stmt->setFetchMode($mode, $arg1, $arg2); 500 } 501} 502