1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21:
22:
23:
24: 25: 26:
27: require_once 'Zend/Db/Adapter/Abstract.php';
28:
29: 30: 31:
32: require_once 'Zend/Db/Profiler.php';
33:
34: 35: 36:
37: require_once 'Zend/Db/Select.php';
38:
39: 40: 41:
42: require_once 'Zend/Db/Statement/Mysqli.php';
43:
44:
45: 46: 47: 48: 49: 50: 51:
52: class Zend_Db_Adapter_Mysqli extends Zend_Db_Adapter_Abstract
53: {
54:
55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65:
66: protected $_numericDataTypes = array(
67: Zend_Db::INT_TYPE => Zend_Db::INT_TYPE,
68: Zend_Db::BIGINT_TYPE => Zend_Db::BIGINT_TYPE,
69: Zend_Db::FLOAT_TYPE => Zend_Db::FLOAT_TYPE,
70: 'INT' => Zend_Db::INT_TYPE,
71: 'INTEGER' => Zend_Db::INT_TYPE,
72: 'MEDIUMINT' => Zend_Db::INT_TYPE,
73: 'SMALLINT' => Zend_Db::INT_TYPE,
74: 'TINYINT' => Zend_Db::INT_TYPE,
75: 'BIGINT' => Zend_Db::BIGINT_TYPE,
76: 'SERIAL' => Zend_Db::BIGINT_TYPE,
77: 'DEC' => Zend_Db::FLOAT_TYPE,
78: 'DECIMAL' => Zend_Db::FLOAT_TYPE,
79: 'DOUBLE' => Zend_Db::FLOAT_TYPE,
80: 'DOUBLE PRECISION' => Zend_Db::FLOAT_TYPE,
81: 'FIXED' => Zend_Db::FLOAT_TYPE,
82: 'FLOAT' => Zend_Db::FLOAT_TYPE
83: );
84:
85: 86: 87:
88: protected $_stmt = null;
89:
90: 91: 92: 93: 94:
95: protected $_defaultStmtClass = 'Zend_Db_Statement_Mysqli';
96:
97: 98: 99: 100: 101: 102: 103:
104: protected function _quote($value)
105: {
106: if (is_int($value) || is_float($value)) {
107: return $value;
108: }
109: $this->_connect();
110: return "'" . $this->_connection->real_escape_string($value) . "'";
111: }
112:
113: 114: 115: 116: 117:
118: public function getQuoteIdentifierSymbol()
119: {
120: return "`";
121: }
122:
123: 124: 125: 126: 127:
128: public function listTables()
129: {
130: $result = array();
131:
132:
133: $sql = 'SHOW TABLES';
134: if ($queryResult = $this->getConnection()->query($sql)) {
135: while ($row = $queryResult->fetch_row()) {
136: $result[] = $row[0];
137: }
138: $queryResult->close();
139: } else {
140: 141: 142:
143: require_once 'Zend/Db/Adapter/Mysqli/Exception.php';
144: throw new Zend_Db_Adapter_Mysqli_Exception($this->getConnection()->error);
145: }
146: return $result;
147: }
148:
149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176:
177: public function describeTable($tableName, $schemaName = null)
178: {
179: 180: 181: 182:
183:
184: if ($schemaName) {
185: $sql = 'DESCRIBE ' . $this->quoteIdentifier("$schemaName.$tableName", true);
186: } else {
187: $sql = 'DESCRIBE ' . $this->quoteIdentifier($tableName, true);
188: }
189:
190: 191: 192: 193:
194: if ($queryResult = $this->getConnection()->query($sql)) {
195: while ($row = $queryResult->fetch_assoc()) {
196: $result[] = $row;
197: }
198: $queryResult->close();
199: } else {
200: 201: 202:
203: require_once 'Zend/Db/Adapter/Mysqli/Exception.php';
204: throw new Zend_Db_Adapter_Mysqli_Exception($this->getConnection()->error);
205: }
206:
207: $desc = array();
208:
209: $row_defaults = array(
210: 'Length' => null,
211: 'Scale' => null,
212: 'Precision' => null,
213: 'Unsigned' => null,
214: 'Primary' => false,
215: 'PrimaryPosition' => null,
216: 'Identity' => false
217: );
218: $i = 1;
219: $p = 1;
220: foreach ($result as $key => $row) {
221: $row = array_merge($row_defaults, $row);
222: if (preg_match('/unsigned/', $row['Type'])) {
223: $row['Unsigned'] = true;
224: }
225: if (preg_match('/^((?:var)?char)\((\d+)\)/', $row['Type'], $matches)) {
226: $row['Type'] = $matches[1];
227: $row['Length'] = $matches[2];
228: } else if (preg_match('/^decimal\((\d+),(\d+)\)/', $row['Type'], $matches)) {
229: $row['Type'] = 'decimal';
230: $row['Precision'] = $matches[1];
231: $row['Scale'] = $matches[2];
232: } else if (preg_match('/^float\((\d+),(\d+)\)/', $row['Type'], $matches)) {
233: $row['Type'] = 'float';
234: $row['Precision'] = $matches[1];
235: $row['Scale'] = $matches[2];
236: } else if (preg_match('/^((?:big|medium|small|tiny)?int)\((\d+)\)/', $row['Type'], $matches)) {
237: $row['Type'] = $matches[1];
238: 239: 240: 241:
242: }
243: if (strtoupper($row['Key']) == 'PRI') {
244: $row['Primary'] = true;
245: $row['PrimaryPosition'] = $p;
246: if ($row['Extra'] == 'auto_increment') {
247: $row['Identity'] = true;
248: } else {
249: $row['Identity'] = false;
250: }
251: ++$p;
252: }
253: $desc[$this->foldCase($row['Field'])] = array(
254: 'SCHEMA_NAME' => null,
255: 'TABLE_NAME' => $this->foldCase($tableName),
256: 'COLUMN_NAME' => $this->foldCase($row['Field']),
257: 'COLUMN_POSITION' => $i,
258: 'DATA_TYPE' => $row['Type'],
259: 'DEFAULT' => $row['Default'],
260: 'NULLABLE' => (bool) ($row['Null'] == 'YES'),
261: 'LENGTH' => $row['Length'],
262: 'SCALE' => $row['Scale'],
263: 'PRECISION' => $row['Precision'],
264: 'UNSIGNED' => $row['Unsigned'],
265: 'PRIMARY' => $row['Primary'],
266: 'PRIMARY_POSITION' => $row['PrimaryPosition'],
267: 'IDENTITY' => $row['Identity']
268: );
269: ++$i;
270: }
271: return $desc;
272: }
273:
274: 275: 276: 277: 278: 279:
280: protected function _connect()
281: {
282: if ($this->_connection) {
283: return;
284: }
285:
286: if (!extension_loaded('mysqli')) {
287: 288: 289:
290: require_once 'Zend/Db/Adapter/Mysqli/Exception.php';
291: throw new Zend_Db_Adapter_Mysqli_Exception('The Mysqli extension is required for this adapter but the extension is not loaded');
292: }
293:
294: if (isset($this->_config['port'])) {
295: $port = (integer) $this->_config['port'];
296: } else {
297: $port = null;
298: }
299:
300: $this->_connection = mysqli_init();
301:
302: if(!empty($this->_config['driver_options'])) {
303: foreach($this->_config['driver_options'] as $option=>$value) {
304: if(is_string($option)) {
305:
306:
307: $option = @constant(strtoupper($option));
308: if($option === null)
309: continue;
310: }
311: mysqli_options($this->_connection, $option, $value);
312: }
313: }
314:
315:
316:
317: $_isConnected = @mysqli_real_connect(
318: $this->_connection,
319: $this->_config['host'],
320: $this->_config['username'],
321: $this->_config['password'],
322: $this->_config['dbname'],
323: $port
324: );
325:
326: if ($_isConnected === false || mysqli_connect_errno()) {
327:
328: $this->closeConnection();
329: 330: 331:
332: require_once 'Zend/Db/Adapter/Mysqli/Exception.php';
333: throw new Zend_Db_Adapter_Mysqli_Exception(mysqli_connect_error());
334: }
335:
336: if (!empty($this->_config['charset'])) {
337: mysqli_set_charset($this->_connection, $this->_config['charset']);
338: }
339: }
340:
341: 342: 343: 344: 345:
346: public function isConnected()
347: {
348: return ((bool) ($this->_connection instanceof mysqli));
349: }
350:
351: 352: 353: 354: 355:
356: public function closeConnection()
357: {
358: if ($this->isConnected()) {
359: $this->_connection->close();
360: }
361: $this->_connection = null;
362: }
363:
364: 365: 366: 367: 368: 369:
370: public function prepare($sql)
371: {
372: $this->_connect();
373: if ($this->_stmt) {
374: $this->_stmt->close();
375: }
376: $stmtClass = $this->_defaultStmtClass;
377: if (!class_exists($stmtClass)) {
378: require_once 'Zend/Loader.php';
379: Zend_Loader::loadClass($stmtClass);
380: }
381: $stmt = new $stmtClass($this, $sql);
382: if ($stmt === false) {
383: return false;
384: }
385: $stmt->setFetchMode($this->_fetchMode);
386: $this->_stmt = $stmt;
387: return $stmt;
388: }
389:
390: 391: 392: 393: 394: 395: 396: 397: 398: 399: 400: 401: 402: 403: 404: 405: 406:
407: public function lastInsertId($tableName = null, $primaryKey = null)
408: {
409: $mysqli = $this->_connection;
410: return (string) $mysqli->insert_id;
411: }
412:
413: 414: 415: 416: 417:
418: protected function _beginTransaction()
419: {
420: $this->_connect();
421: $this->_connection->autocommit(false);
422: }
423:
424: 425: 426: 427: 428:
429: protected function _commit()
430: {
431: $this->_connect();
432: $this->_connection->commit();
433: $this->_connection->autocommit(true);
434: }
435:
436: 437: 438: 439: 440:
441: protected function _rollBack()
442: {
443: $this->_connect();
444: $this->_connection->rollback();
445: $this->_connection->autocommit(true);
446: }
447:
448: 449: 450: 451: 452: 453: 454:
455: public function setFetchMode($mode)
456: {
457: switch ($mode) {
458: case Zend_Db::FETCH_LAZY:
459: case Zend_Db::FETCH_ASSOC:
460: case Zend_Db::FETCH_NUM:
461: case Zend_Db::FETCH_BOTH:
462: case Zend_Db::FETCH_NAMED:
463: case Zend_Db::FETCH_OBJ:
464: $this->_fetchMode = $mode;
465: break;
466: case Zend_Db::FETCH_BOUND:
467: 468: 469:
470: require_once 'Zend/Db/Adapter/Mysqli/Exception.php';
471: throw new Zend_Db_Adapter_Mysqli_Exception('FETCH_BOUND is not supported yet');
472: break;
473: default:
474: 475: 476:
477: require_once 'Zend/Db/Adapter/Mysqli/Exception.php';
478: throw new Zend_Db_Adapter_Mysqli_Exception("Invalid fetch mode '$mode' specified");
479: }
480: }
481:
482: 483: 484: 485: 486: 487: 488: 489:
490: public function limit($sql, $count, $offset = 0)
491: {
492: $count = intval($count);
493: if ($count <= 0) {
494: 495: 496:
497: require_once 'Zend/Db/Adapter/Mysqli/Exception.php';
498: throw new Zend_Db_Adapter_Mysqli_Exception("LIMIT argument count=$count is not valid");
499: }
500:
501: $offset = intval($offset);
502: if ($offset < 0) {
503: 504: 505:
506: require_once 'Zend/Db/Adapter/Mysqli/Exception.php';
507: throw new Zend_Db_Adapter_Mysqli_Exception("LIMIT argument offset=$offset is not valid");
508: }
509:
510: $sql .= " LIMIT $count";
511: if ($offset > 0) {
512: $sql .= " OFFSET $offset";
513: }
514:
515: return $sql;
516: }
517:
518: 519: 520: 521: 522: 523:
524: public function supportsParameters($type)
525: {
526: switch ($type) {
527: case 'positional':
528: return true;
529: case 'named':
530: default:
531: return false;
532: }
533: }
534:
535: 536: 537: 538: 539:
540: public function getServerVersion()
541: {
542: $this->_connect();
543: $version = $this->_connection->server_version;
544: $major = (int) ($version / 10000);
545: $minor = (int) ($version % 10000 / 100);
546: $revision = (int) ($version % 100);
547: return $major . '.' . $minor . '.' . $revision;
548: }
549: }
550: