1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10:
11:
12:
13: require_once dirname(__FILE__) . '/mysql.reflector.php';
14:
15:
16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37:
38: class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
39: {
40: const ERROR_ACCESS_DENIED = 1045;
41: const ERROR_DUPLICATE_ENTRY = 1062;
42: const ERROR_DATA_TRUNCATED = 1265;
43:
44:
45: private $connection;
46:
47:
48: private $resultSet;
49:
50:
51: private $buffered;
52:
53:
54:
55: 56: 57:
58: public function __construct()
59: {
60: if (!extension_loaded('mysqli')) {
61: throw new DibiNotSupportedException("PHP extension 'mysqli' is not loaded.");
62: }
63: }
64:
65:
66:
67: 68: 69: 70: 71:
72: public function connect(array &$config)
73: {
74: mysqli_report(MYSQLI_REPORT_OFF);
75: if (isset($config['resource'])) {
76: $this->connection = $config['resource'];
77:
78: } else {
79:
80: if (!isset($config['charset'])) $config['charset'] = 'utf8';
81: if (!isset($config['username'])) $config['username'] = ini_get('mysqli.default_user');
82: if (!isset($config['password'])) $config['password'] = ini_get('mysqli.default_pw');
83: if (!isset($config['socket'])) $config['socket'] = ini_get('mysqli.default_socket');
84: if (!isset($config['port'])) $config['port'] = NULL;
85: if (!isset($config['host'])) {
86: $host = ini_get('mysqli.default_host');
87: if ($host) {
88: $config['host'] = $host;
89: $config['port'] = ini_get('mysqli.default_port');
90: } else {
91: $config['host'] = NULL;
92: $config['port'] = NULL;
93: }
94: }
95:
96: $foo = & $config['flags'];
97: $foo = & $config['database'];
98:
99: $this->connection = mysqli_init();
100: if (isset($config['options'])) {
101: if (is_scalar($config['options'])) {
102: $config['flags'] = $config['options'];
103: trigger_error(__CLASS__ . ": configuration item 'options' must be array; for constants MYSQLI_CLIENT_* use 'flags'.", E_USER_NOTICE);
104: } else {
105: foreach ((array) $config['options'] as $key => $value) {
106: mysqli_options($this->connection, $key, $value);
107: }
108: }
109: }
110: @mysqli_real_connect($this->connection, (empty($config['persistent']) ? '' : 'p:') . $config['host'], $config['username'], $config['password'], $config['database'], $config['port'], $config['socket'], $config['flags']);
111:
112: if ($errno = mysqli_connect_errno()) {
113: throw new DibiDriverException(mysqli_connect_error(), $errno);
114: }
115: }
116:
117: if (isset($config['charset'])) {
118: $ok = FALSE;
119: if (version_compare(PHP_VERSION , '5.1.5', '>=')) {
120:
121: $ok = @mysqli_set_charset($this->connection, $config['charset']);
122: }
123: if (!$ok) {
124: $this->query("SET NAMES '$config[charset]'");
125: }
126: }
127:
128: if (isset($config['sqlmode'])) {
129: $this->query("SET sql_mode='$config[sqlmode]'");
130: }
131:
132: $this->query("SET time_zone='" . date('P') . "'");
133:
134: $this->buffered = empty($config['unbuffered']);
135: }
136:
137:
138:
139: 140: 141: 142:
143: public function disconnect()
144: {
145: mysqli_close($this->connection);
146: }
147:
148:
149:
150: 151: 152: 153: 154: 155:
156: public function query($sql)
157: {
158: $res = @mysqli_query($this->connection, $sql, $this->buffered ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT);
159:
160: if (mysqli_errno($this->connection)) {
161: throw new DibiDriverException(mysqli_error($this->connection), mysqli_errno($this->connection), $sql);
162:
163: } elseif (is_object($res)) {
164: return $this->createResultDriver($res);
165: }
166: }
167:
168:
169:
170: 171: 172: 173:
174: public function getInfo()
175: {
176: $res = array();
177: preg_match_all('#(.+?): +(\d+) *#', mysqli_info($this->connection), $matches, PREG_SET_ORDER);
178: if (preg_last_error()) throw new DibiPcreException;
179:
180: foreach ($matches as $m) {
181: $res[$m[1]] = (int) $m[2];
182: }
183: return $res;
184: }
185:
186:
187:
188: 189: 190: 191:
192: public function getAffectedRows()
193: {
194: return mysqli_affected_rows($this->connection);
195: }
196:
197:
198:
199: 200: 201: 202:
203: public function getInsertId($sequence)
204: {
205: return mysqli_insert_id($this->connection);
206: }
207:
208:
209:
210: 211: 212: 213: 214: 215:
216: public function begin($savepoint = NULL)
217: {
218: $this->query($savepoint ? "SAVEPOINT $savepoint" : 'START TRANSACTION');
219: }
220:
221:
222:
223: 224: 225: 226: 227: 228:
229: public function commit($savepoint = NULL)
230: {
231: $this->query($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT');
232: }
233:
234:
235:
236: 237: 238: 239: 240: 241:
242: public function rollback($savepoint = NULL)
243: {
244: $this->query($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : 'ROLLBACK');
245: }
246:
247:
248:
249: 250: 251: 252:
253: public function getResource()
254: {
255: return $this->connection;
256: }
257:
258:
259:
260: 261: 262: 263:
264: public function getReflector()
265: {
266: return new DibiMySqlReflector($this);
267: }
268:
269:
270:
271: 272: 273: 274: 275:
276: public function createResultDriver(mysqli_result $resource)
277: {
278: $res = clone $this;
279: $res->resultSet = $resource;
280: return $res;
281: }
282:
283:
284:
285:
286:
287:
288:
289: 290: 291: 292: 293: 294: 295:
296: public function escape($value, $type)
297: {
298: switch ($type) {
299: case dibi::TEXT:
300: return "'" . mysqli_real_escape_string($this->connection, $value) . "'";
301:
302: case dibi::BINARY:
303: return "_binary'" . mysqli_real_escape_string($this->connection, $value) . "'";
304:
305: case dibi::IDENTIFIER:
306: return '`' . str_replace('`', '``', $value) . '`';
307:
308: case dibi::BOOL:
309: return $value ? 1 : 0;
310:
311: case dibi::DATE:
312: return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value);
313:
314: case dibi::DATETIME:
315: return $value instanceof DateTime ? $value->format("'Y-m-d H:i:s'") : date("'Y-m-d H:i:s'", $value);
316:
317: default:
318: throw new InvalidArgumentException('Unsupported type.');
319: }
320: }
321:
322:
323:
324: 325: 326: 327: 328: 329:
330: public function escapeLike($value, $pos)
331: {
332: $value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_");
333: return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
334: }
335:
336:
337:
338: 339: 340: 341: 342: 343: 344:
345: public function unescape($value, $type)
346: {
347: if ($type === dibi::BINARY) {
348: return $value;
349: }
350: throw new InvalidArgumentException('Unsupported type.');
351: }
352:
353:
354:
355: 356: 357: 358: 359: 360: 361:
362: public function applyLimit(&$sql, $limit, $offset)
363: {
364: if ($limit < 0 && $offset < 1) return;
365:
366:
367: $sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit)
368: . ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
369: }
370:
371:
372:
373:
374:
375:
376:
377: 378: 379: 380:
381: public function __destruct()
382: {
383: $this->resultSet && @$this->free();
384: }
385:
386:
387:
388: 389: 390: 391:
392: public function getRowCount()
393: {
394: if (!$this->buffered) {
395: throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
396: }
397: return mysqli_num_rows($this->resultSet);
398: }
399:
400:
401:
402: 403: 404: 405: 406:
407: public function fetch($assoc)
408: {
409: return mysqli_fetch_array($this->resultSet, $assoc ? MYSQLI_ASSOC : MYSQLI_NUM);
410: }
411:
412:
413:
414: 415: 416: 417: 418: 419:
420: public function seek($row)
421: {
422: if (!$this->buffered) {
423: throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
424: }
425: return mysqli_data_seek($this->resultSet, $row);
426: }
427:
428:
429:
430: 431: 432: 433:
434: public function free()
435: {
436: mysqli_free_result($this->resultSet);
437: $this->resultSet = NULL;
438: }
439:
440:
441:
442: 443: 444: 445:
446: public function getResultColumns()
447: {
448: static $types;
449: if (empty($types)) {
450: $consts = get_defined_constants(TRUE);
451: foreach ($consts['mysqli'] as $key => $value) {
452: if (strncmp($key, 'MYSQLI_TYPE_', 12) === 0) {
453: $types[$value] = substr($key, 12);
454: }
455: }
456: $types[MYSQLI_TYPE_TINY] = $types[MYSQLI_TYPE_SHORT] = $types[MYSQLI_TYPE_LONG] = 'INT';
457: }
458:
459: $count = mysqli_num_fields($this->resultSet);
460: $columns = array();
461: for ($i = 0; $i < $count; $i++) {
462: $row = (array) mysqli_fetch_field_direct($this->resultSet, $i);
463: $columns[] = array(
464: 'name' => $row['name'],
465: 'table' => $row['orgtable'],
466: 'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
467: 'nativetype' => $types[$row['type']],
468: 'vendor' => $row,
469: );
470: }
471: return $columns;
472: }
473:
474:
475:
476: 477: 478: 479:
480: public function getResultResource()
481: {
482: return $this->resultSet;
483: }
484:
485: }
486: