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