File indexing completed on 2025-01-19 05:21:02
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 Adapter 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 /** 0025 * @see Zend_Db_Adapter_Abstract 0026 */ 0027 // require_once 'Zend/Db/Adapter/Abstract.php'; 0028 0029 /** 0030 * @see Zend_Db_Profiler 0031 */ 0032 // require_once 'Zend/Db/Profiler.php'; 0033 0034 /** 0035 * @see Zend_Db_Select 0036 */ 0037 // require_once 'Zend/Db/Select.php'; 0038 0039 /** 0040 * @see Zend_Db_Statement_Mysqli 0041 */ 0042 // require_once 'Zend/Db/Statement/Mysqli.php'; 0043 0044 0045 /** 0046 * @category Zend 0047 * @package Zend_Db 0048 * @subpackage Adapter 0049 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0050 * @license http://framework.zend.com/license/new-bsd New BSD License 0051 */ 0052 class Zend_Db_Adapter_Mysqli extends Zend_Db_Adapter_Abstract 0053 { 0054 0055 /** 0056 * Keys are UPPERCASE SQL datatypes or the constants 0057 * Zend_Db::INT_TYPE, Zend_Db::BIGINT_TYPE, or Zend_Db::FLOAT_TYPE. 0058 * 0059 * Values are: 0060 * 0 = 32-bit integer 0061 * 1 = 64-bit integer 0062 * 2 = float or decimal 0063 * 0064 * @var array Associative array of datatypes to values 0, 1, or 2. 0065 */ 0066 protected $_numericDataTypes = array( 0067 Zend_Db::INT_TYPE => Zend_Db::INT_TYPE, 0068 Zend_Db::BIGINT_TYPE => Zend_Db::BIGINT_TYPE, 0069 Zend_Db::FLOAT_TYPE => Zend_Db::FLOAT_TYPE, 0070 'INT' => Zend_Db::INT_TYPE, 0071 'INTEGER' => Zend_Db::INT_TYPE, 0072 'MEDIUMINT' => Zend_Db::INT_TYPE, 0073 'SMALLINT' => Zend_Db::INT_TYPE, 0074 'TINYINT' => Zend_Db::INT_TYPE, 0075 'BIGINT' => Zend_Db::BIGINT_TYPE, 0076 'SERIAL' => Zend_Db::BIGINT_TYPE, 0077 'DEC' => Zend_Db::FLOAT_TYPE, 0078 'DECIMAL' => Zend_Db::FLOAT_TYPE, 0079 'DOUBLE' => Zend_Db::FLOAT_TYPE, 0080 'DOUBLE PRECISION' => Zend_Db::FLOAT_TYPE, 0081 'FIXED' => Zend_Db::FLOAT_TYPE, 0082 'FLOAT' => Zend_Db::FLOAT_TYPE 0083 ); 0084 0085 /** 0086 * @var Zend_Db_Statement_Mysqli 0087 */ 0088 protected $_stmt = null; 0089 0090 /** 0091 * Default class name for a DB statement. 0092 * 0093 * @var string 0094 */ 0095 protected $_defaultStmtClass = 'Zend_Db_Statement_Mysqli'; 0096 0097 /** 0098 * Quote a raw string. 0099 * 0100 * @param mixed $value Raw string 0101 * 0102 * @return string Quoted string 0103 */ 0104 protected function _quote($value) 0105 { 0106 if (is_int($value) || is_float($value)) { 0107 return $value; 0108 } 0109 $this->_connect(); 0110 return "'" . $this->_connection->real_escape_string($value) . "'"; 0111 } 0112 0113 /** 0114 * Returns the symbol the adapter uses for delimiting identifiers. 0115 * 0116 * @return string 0117 */ 0118 public function getQuoteIdentifierSymbol() 0119 { 0120 return "`"; 0121 } 0122 0123 /** 0124 * Returns a list of the tables in the database. 0125 * 0126 * @return array 0127 */ 0128 public function listTables() 0129 { 0130 $result = array(); 0131 // Use mysqli extension API, because SHOW doesn't work 0132 // well as a prepared statement on MySQL 4.1. 0133 $sql = 'SHOW TABLES'; 0134 if ($queryResult = $this->getConnection()->query($sql)) { 0135 while ($row = $queryResult->fetch_row()) { 0136 $result[] = $row[0]; 0137 } 0138 $queryResult->close(); 0139 } else { 0140 /** 0141 * @see Zend_Db_Adapter_Mysqli_Exception 0142 */ 0143 // require_once 'Zend/Db/Adapter/Mysqli/Exception.php'; 0144 throw new Zend_Db_Adapter_Mysqli_Exception($this->getConnection()->error); 0145 } 0146 return $result; 0147 } 0148 0149 /** 0150 * Returns the column descriptions for a table. 0151 * 0152 * The return value is an associative array keyed by the column name, 0153 * as returned by the RDBMS. 0154 * 0155 * The value of each array element is an associative array 0156 * with the following keys: 0157 * 0158 * SCHEMA_NAME => string; name of database or schema 0159 * TABLE_NAME => string; 0160 * COLUMN_NAME => string; column name 0161 * COLUMN_POSITION => number; ordinal position of column in table 0162 * DATA_TYPE => string; SQL datatype name of column 0163 * DEFAULT => string; default expression of column, null if none 0164 * NULLABLE => boolean; true if column can have nulls 0165 * LENGTH => number; length of CHAR/VARCHAR 0166 * SCALE => number; scale of NUMERIC/DECIMAL 0167 * PRECISION => number; precision of NUMERIC/DECIMAL 0168 * UNSIGNED => boolean; unsigned property of an integer type 0169 * PRIMARY => boolean; true if column is part of the primary key 0170 * PRIMARY_POSITION => integer; position of column in primary key 0171 * IDENTITY => integer; true if column is auto-generated with unique values 0172 * 0173 * @param string $tableName 0174 * @param string $schemaName OPTIONAL 0175 * @return array 0176 */ 0177 public function describeTable($tableName, $schemaName = null) 0178 { 0179 /** 0180 * @todo use INFORMATION_SCHEMA someday when 0181 * MySQL's implementation isn't too slow. 0182 */ 0183 0184 if ($schemaName) { 0185 $sql = 'DESCRIBE ' . $this->quoteIdentifier("$schemaName.$tableName", true); 0186 } else { 0187 $sql = 'DESCRIBE ' . $this->quoteIdentifier($tableName, true); 0188 } 0189 0190 /** 0191 * Use mysqli extension API, because DESCRIBE doesn't work 0192 * well as a prepared statement on MySQL 4.1. 0193 */ 0194 if ($queryResult = $this->getConnection()->query($sql)) { 0195 while ($row = $queryResult->fetch_assoc()) { 0196 $result[] = $row; 0197 } 0198 $queryResult->close(); 0199 } else { 0200 /** 0201 * @see Zend_Db_Adapter_Mysqli_Exception 0202 */ 0203 // require_once 'Zend/Db/Adapter/Mysqli/Exception.php'; 0204 throw new Zend_Db_Adapter_Mysqli_Exception($this->getConnection()->error); 0205 } 0206 0207 $desc = array(); 0208 0209 $row_defaults = array( 0210 'Length' => null, 0211 'Scale' => null, 0212 'Precision' => null, 0213 'Unsigned' => null, 0214 'Primary' => false, 0215 'PrimaryPosition' => null, 0216 'Identity' => false 0217 ); 0218 $i = 1; 0219 $p = 1; 0220 foreach ($result as $key => $row) { 0221 $row = array_merge($row_defaults, $row); 0222 if (preg_match('/unsigned/', $row['Type'])) { 0223 $row['Unsigned'] = true; 0224 } 0225 if (preg_match('/^((?:var)?char)\((\d+)\)/', $row['Type'], $matches)) { 0226 $row['Type'] = $matches[1]; 0227 $row['Length'] = $matches[2]; 0228 } else if (preg_match('/^decimal\((\d+),(\d+)\)/', $row['Type'], $matches)) { 0229 $row['Type'] = 'decimal'; 0230 $row['Precision'] = $matches[1]; 0231 $row['Scale'] = $matches[2]; 0232 } else if (preg_match('/^float\((\d+),(\d+)\)/', $row['Type'], $matches)) { 0233 $row['Type'] = 'float'; 0234 $row['Precision'] = $matches[1]; 0235 $row['Scale'] = $matches[2]; 0236 } else if (preg_match('/^((?:big|medium|small|tiny)?int)\((\d+)\)/', $row['Type'], $matches)) { 0237 $row['Type'] = $matches[1]; 0238 /** 0239 * The optional argument of a MySQL int type is not precision 0240 * or length; it is only a hint for display width. 0241 */ 0242 } 0243 if (strtoupper($row['Key']) == 'PRI') { 0244 $row['Primary'] = true; 0245 $row['PrimaryPosition'] = $p; 0246 if ($row['Extra'] == 'auto_increment') { 0247 $row['Identity'] = true; 0248 } else { 0249 $row['Identity'] = false; 0250 } 0251 ++$p; 0252 } 0253 $desc[$this->foldCase($row['Field'])] = array( 0254 'SCHEMA_NAME' => null, // @todo 0255 'TABLE_NAME' => $this->foldCase($tableName), 0256 'COLUMN_NAME' => $this->foldCase($row['Field']), 0257 'COLUMN_POSITION' => $i, 0258 'DATA_TYPE' => $row['Type'], 0259 'DEFAULT' => $row['Default'], 0260 'NULLABLE' => (bool) ($row['Null'] == 'YES'), 0261 'LENGTH' => $row['Length'], 0262 'SCALE' => $row['Scale'], 0263 'PRECISION' => $row['Precision'], 0264 'UNSIGNED' => $row['Unsigned'], 0265 'PRIMARY' => $row['Primary'], 0266 'PRIMARY_POSITION' => $row['PrimaryPosition'], 0267 'IDENTITY' => $row['Identity'] 0268 ); 0269 ++$i; 0270 } 0271 return $desc; 0272 } 0273 0274 /** 0275 * Creates a connection to the database. 0276 * 0277 * @return void 0278 * @throws Zend_Db_Adapter_Mysqli_Exception 0279 */ 0280 protected function _connect() 0281 { 0282 if ($this->_connection) { 0283 return; 0284 } 0285 0286 if (!extension_loaded('mysqli')) { 0287 /** 0288 * @see Zend_Db_Adapter_Mysqli_Exception 0289 */ 0290 // require_once 'Zend/Db/Adapter/Mysqli/Exception.php'; 0291 throw new Zend_Db_Adapter_Mysqli_Exception('The Mysqli extension is required for this adapter but the extension is not loaded'); 0292 } 0293 0294 if (isset($this->_config['port'])) { 0295 $port = (integer) $this->_config['port']; 0296 } else { 0297 $port = null; 0298 } 0299 0300 if (isset($this->_config['socket'])) { 0301 $socket = $this->_config['socket']; 0302 } else { 0303 $socket = null; 0304 } 0305 0306 $this->_connection = mysqli_init(); 0307 0308 if(!empty($this->_config['driver_options'])) { 0309 foreach($this->_config['driver_options'] as $option=>$value) { 0310 if(is_string($option)) { 0311 // Suppress warnings here 0312 // Ignore it if it's not a valid constant 0313 $option = @constant(strtoupper($option)); 0314 if($option === null) 0315 continue; 0316 } 0317 mysqli_options($this->_connection, $option, $value); 0318 } 0319 } 0320 0321 // Suppress connection warnings here. 0322 // Throw an exception instead. 0323 $_isConnected = @mysqli_real_connect( 0324 $this->_connection, 0325 $this->_config['host'], 0326 $this->_config['username'], 0327 $this->_config['password'], 0328 $this->_config['dbname'], 0329 $port, 0330 $socket 0331 ); 0332 0333 if ($_isConnected === false || mysqli_connect_errno()) { 0334 0335 $this->closeConnection(); 0336 /** 0337 * @see Zend_Db_Adapter_Mysqli_Exception 0338 */ 0339 // require_once 'Zend/Db/Adapter/Mysqli/Exception.php'; 0340 throw new Zend_Db_Adapter_Mysqli_Exception(mysqli_connect_error()); 0341 } 0342 0343 if (!empty($this->_config['charset'])) { 0344 mysqli_set_charset($this->_connection, $this->_config['charset']); 0345 } 0346 } 0347 0348 /** 0349 * Test if a connection is active 0350 * 0351 * @return boolean 0352 */ 0353 public function isConnected() 0354 { 0355 return ((bool) ($this->_connection instanceof mysqli)); 0356 } 0357 0358 /** 0359 * Force the connection to close. 0360 * 0361 * @return void 0362 */ 0363 public function closeConnection() 0364 { 0365 if ($this->isConnected()) { 0366 $this->_connection->close(); 0367 } 0368 $this->_connection = null; 0369 } 0370 0371 /** 0372 * Prepare a statement and return a PDOStatement-like object. 0373 * 0374 * @param string $sql SQL query 0375 * @return Zend_Db_Statement_Mysqli 0376 */ 0377 public function prepare($sql) 0378 { 0379 $this->_connect(); 0380 if ($this->_stmt) { 0381 $this->_stmt->close(); 0382 } 0383 $stmtClass = $this->_defaultStmtClass; 0384 if (!class_exists($stmtClass)) { 0385 // require_once 'Zend/Loader.php'; 0386 Zend_Loader::loadClass($stmtClass); 0387 } 0388 $stmt = new $stmtClass($this, $sql); 0389 if ($stmt === false) { 0390 return false; 0391 } 0392 $stmt->setFetchMode($this->_fetchMode); 0393 $this->_stmt = $stmt; 0394 return $stmt; 0395 } 0396 0397 /** 0398 * Gets the last ID generated automatically by an IDENTITY/AUTOINCREMENT column. 0399 * 0400 * As a convention, on RDBMS brands that support sequences 0401 * (e.g. Oracle, PostgreSQL, DB2), this method forms the name of a sequence 0402 * from the arguments and returns the last id generated by that sequence. 0403 * On RDBMS brands that support IDENTITY/AUTOINCREMENT columns, this method 0404 * returns the last value generated for such a column, and the table name 0405 * argument is disregarded. 0406 * 0407 * MySQL does not support sequences, so $tableName and $primaryKey are ignored. 0408 * 0409 * @param string $tableName OPTIONAL Name of table. 0410 * @param string $primaryKey OPTIONAL Name of primary key column. 0411 * @return string 0412 * @todo Return value should be int? 0413 */ 0414 public function lastInsertId($tableName = null, $primaryKey = null) 0415 { 0416 $mysqli = $this->_connection; 0417 return (string) $mysqli->insert_id; 0418 } 0419 0420 /** 0421 * Begin a transaction. 0422 * 0423 * @return void 0424 */ 0425 protected function _beginTransaction() 0426 { 0427 $this->_connect(); 0428 $this->_connection->autocommit(false); 0429 } 0430 0431 /** 0432 * Commit a transaction. 0433 * 0434 * @return void 0435 */ 0436 protected function _commit() 0437 { 0438 $this->_connect(); 0439 $this->_connection->commit(); 0440 $this->_connection->autocommit(true); 0441 } 0442 0443 /** 0444 * Roll-back a transaction. 0445 * 0446 * @return void 0447 */ 0448 protected function _rollBack() 0449 { 0450 $this->_connect(); 0451 $this->_connection->rollback(); 0452 $this->_connection->autocommit(true); 0453 } 0454 0455 /** 0456 * Set the fetch mode. 0457 * 0458 * @param int $mode 0459 * @return void 0460 * @throws Zend_Db_Adapter_Mysqli_Exception 0461 */ 0462 public function setFetchMode($mode) 0463 { 0464 switch ($mode) { 0465 case Zend_Db::FETCH_LAZY: 0466 case Zend_Db::FETCH_ASSOC: 0467 case Zend_Db::FETCH_NUM: 0468 case Zend_Db::FETCH_BOTH: 0469 case Zend_Db::FETCH_NAMED: 0470 case Zend_Db::FETCH_OBJ: 0471 $this->_fetchMode = $mode; 0472 break; 0473 case Zend_Db::FETCH_BOUND: // bound to PHP variable 0474 /** 0475 * @see Zend_Db_Adapter_Mysqli_Exception 0476 */ 0477 // require_once 'Zend/Db/Adapter/Mysqli/Exception.php'; 0478 throw new Zend_Db_Adapter_Mysqli_Exception('FETCH_BOUND is not supported yet'); 0479 break; 0480 default: 0481 /** 0482 * @see Zend_Db_Adapter_Mysqli_Exception 0483 */ 0484 // require_once 'Zend/Db/Adapter/Mysqli/Exception.php'; 0485 throw new Zend_Db_Adapter_Mysqli_Exception("Invalid fetch mode '$mode' specified"); 0486 } 0487 } 0488 0489 /** 0490 * Adds an adapter-specific LIMIT clause to the SELECT statement. 0491 * 0492 * @param string $sql 0493 * @param int $count 0494 * @param int $offset OPTIONAL 0495 * @return string 0496 */ 0497 public function limit($sql, $count, $offset = 0) 0498 { 0499 $count = intval($count); 0500 if ($count <= 0) { 0501 /** 0502 * @see Zend_Db_Adapter_Mysqli_Exception 0503 */ 0504 // require_once 'Zend/Db/Adapter/Mysqli/Exception.php'; 0505 throw new Zend_Db_Adapter_Mysqli_Exception("LIMIT argument count=$count is not valid"); 0506 } 0507 0508 $offset = intval($offset); 0509 if ($offset < 0) { 0510 /** 0511 * @see Zend_Db_Adapter_Mysqli_Exception 0512 */ 0513 // require_once 'Zend/Db/Adapter/Mysqli/Exception.php'; 0514 throw new Zend_Db_Adapter_Mysqli_Exception("LIMIT argument offset=$offset is not valid"); 0515 } 0516 0517 $sql .= " LIMIT $count"; 0518 if ($offset > 0) { 0519 $sql .= " OFFSET $offset"; 0520 } 0521 0522 return $sql; 0523 } 0524 0525 /** 0526 * Check if the adapter supports real SQL parameters. 0527 * 0528 * @param string $type 'positional' or 'named' 0529 * @return bool 0530 */ 0531 public function supportsParameters($type) 0532 { 0533 switch ($type) { 0534 case 'positional': 0535 return true; 0536 case 'named': 0537 default: 0538 return false; 0539 } 0540 } 0541 0542 /** 0543 * Retrieve server version in PHP style 0544 * 0545 *@return string 0546 */ 0547 public function getServerVersion() 0548 { 0549 $this->_connect(); 0550 $version = $this->_connection->server_version; 0551 $major = (int) ($version / 10000); 0552 $minor = (int) ($version % 10000 / 100); 0553 $revision = (int) ($version % 100); 0554 return $major . '.' . $minor . '.' . $revision; 0555 } 0556 }