1: <?php
2:
3: namespace RedBeanPHP\Driver;
4:
5: use RedBeanPHP\Driver as Driver;
6: use RedBeanPHP\Logger as Logger;
7: use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
8: use RedBeanPHP\RedException as RedException;
9: use RedBeanPHP\RedException\SQL as SQL;
10: use RedBeanPHP\Logger\RDefault as RDefault;
11: use RedBeanPHP\PDOCompatible as PDOCompatible;
12: use RedBeanPHP\Cursor\PDOCursor as PDOCursor;
13:
14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28:
29: class RPDO implements Driver
30: {
31: 32: 33:
34: protected $max;
35:
36: 37: 38:
39: protected $dsn;
40:
41: 42: 43:
44: protected $loggingEnabled = FALSE;
45:
46: 47: 48:
49: protected $logger = NULL;
50:
51: 52: 53:
54: protected $pdo;
55:
56: 57: 58:
59: protected $affectedRows;
60:
61: 62: 63:
64: protected $resultArray;
65:
66: 67: 68:
69: protected $connectInfo = array();
70:
71: 72: 73:
74: protected $isConnected = FALSE;
75:
76: 77: 78:
79: protected $flagUseStringOnlyBinding = FALSE;
80:
81: 82: 83:
84: protected $queryCounter = 0;
85:
86: 87: 88:
89: protected $mysqlEncoding = '';
90:
91: 92: 93: 94: 95: 96: 97: 98: 99: 100:
101: protected function bindParams( $statement, $bindings )
102: {
103: foreach ( $bindings as $key => &$value ) {
104: if ( is_integer( $key ) ) {
105: if ( is_null( $value ) ) {
106: $statement->bindValue( $key + 1, NULL, \PDO::PARAM_NULL );
107: } elseif ( !$this->flagUseStringOnlyBinding && AQueryWriter::canBeTreatedAsInt( $value ) && abs( $value ) <= $this->max ) {
108: $statement->bindParam( $key + 1, $value, \PDO::PARAM_INT );
109: } else {
110: $statement->bindParam( $key + 1, $value, \PDO::PARAM_STR );
111: }
112: } else {
113: if ( is_null( $value ) ) {
114: $statement->bindValue( $key, NULL, \PDO::PARAM_NULL );
115: } elseif ( !$this->flagUseStringOnlyBinding && AQueryWriter::canBeTreatedAsInt( $value ) && abs( $value ) <= $this->max ) {
116: $statement->bindParam( $key, $value, \PDO::PARAM_INT );
117: } else {
118: $statement->bindParam( $key, $value, \PDO::PARAM_STR );
119: }
120: }
121: }
122: }
123:
124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137:
138: protected function runQuery( $sql, $bindings, $options = array() )
139: {
140: $this->connect();
141: if ( $this->loggingEnabled && $this->logger ) {
142: $this->logger->log( $sql, $bindings );
143: }
144: try {
145: if ( strpos( 'pgsql', $this->dsn ) === 0 ) {
146: if ( defined( '\PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT' ) ) {
147: $statement = $this->pdo->prepare( $sql, array( \PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT => TRUE ) );
148: } else {
149: $statement = $this->pdo->prepare( $sql );
150: }
151: } else {
152: $statement = $this->pdo->prepare( $sql );
153: }
154: $this->bindParams( $statement, $bindings );
155: $statement->execute();
156: $this->queryCounter ++;
157: $this->affectedRows = $statement->rowCount();
158: if ( $statement->columnCount() ) {
159: $fetchStyle = ( isset( $options['fetchStyle'] ) ) ? $options['fetchStyle'] : NULL;
160: if ( isset( $options['noFetch'] ) && $options['noFetch'] ) {
161: $this->resultArray = array();
162: return $statement;
163: }
164: $this->resultArray = $statement->fetchAll( $fetchStyle );
165: if ( $this->loggingEnabled && $this->logger ) {
166: $this->logger->log( 'resultset: ' . count( $this->resultArray ) . ' rows' );
167: }
168: } else {
169: $this->resultArray = array();
170: }
171: } catch ( \PDOException $e ) {
172:
173:
174: $err = $e->getMessage();
175: if ( $this->loggingEnabled && $this->logger ) $this->logger->log( 'An error occurred: ' . $err );
176: $exception = new SQL( $err, 0 );
177: $exception->setSQLState( $e->getCode() );
178: throw $exception;
179: }
180: }
181:
182: 183: 184: 185: 186: 187: 188: 189:
190: protected function setEncoding()
191: {
192: $driver = $this->pdo->getAttribute( \PDO::ATTR_DRIVER_NAME );
193: $version = floatval( $this->pdo->getAttribute( \PDO::ATTR_SERVER_VERSION ) );
194: if ($driver === 'mysql') {
195: $encoding = ($version >= 5.5) ? 'utf8mb4' : 'utf8';
196: $this->pdo->setAttribute(\PDO::MYSQL_ATTR_INIT_COMMAND, 'SET NAMES '.$encoding );
197: $this->pdo->exec(' SET NAMES '. $encoding);
198: $this->mysqlEncoding = $encoding;
199: }
200: }
201:
202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215:
216: public function __construct( $dsn, $user = NULL, $pass = NULL )
217: {
218: if ( is_object( $dsn ) ) {
219: $this->pdo = $dsn;
220: $this->isConnected = TRUE;
221: $this->setEncoding();
222: $this->pdo->setAttribute( \PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION );
223: $this->pdo->setAttribute( \PDO::ATTR_DEFAULT_FETCH_MODE,\PDO::FETCH_ASSOC );
224:
225: $this->dsn = $this->getDatabaseType();
226: } else {
227: $this->dsn = $dsn;
228: $this->connectInfo = array( 'pass' => $pass, 'user' => $user );
229: }
230:
231:
232: if ( ( strpos( $this->dsn, 'sqlite' ) === 0 && PHP_MAJOR_VERSION === 5 && PHP_MINOR_VERSION === 3 ) || defined('HHVM_VERSION') || $this->dsn === 'test-sqlite-53' ) {
233: $this->max = 2147483647;
234: } elseif ( strpos( $this->dsn, 'cubrid' ) === 0 ) {
235: $this->max = 2147483647;
236: } else {
237: $this->max = PHP_INT_MAX;
238: }
239: }
240:
241: 242: 243: 244: 245:
246: public function getMysqlEncoding()
247: {
248: return $this->mysqlEncoding;
249: }
250:
251: 252: 253: 254: 255: 256: 257: 258: 259:
260: public function setUseStringOnlyBinding( $yesNo )
261: {
262: $this->flagUseStringOnlyBinding = (boolean) $yesNo;
263: }
264:
265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276:
277: public function setMaxIntBind( $max )
278: {
279: if ( !is_integer( $max ) ) throw new RedException( 'Parameter has to be integer.' );
280: $oldMax = $this->max;
281: $this->max = $max;
282: return $oldMax;
283: }
284:
285: 286: 287: 288: 289: 290: 291: 292: 293:
294: public function connect()
295: {
296: if ( $this->isConnected ) return;
297: try {
298: $user = $this->connectInfo['user'];
299: $pass = $this->connectInfo['pass'];
300: $this->pdo = new \PDO(
301: $this->dsn,
302: $user,
303: $pass
304: );
305: $this->setEncoding();
306: $this->pdo->setAttribute( \PDO::ATTR_STRINGIFY_FETCHES, TRUE );
307:
308: $this->pdo->setAttribute( \PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION );
309: $this->pdo->setAttribute( \PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC );
310: $this->isConnected = TRUE;
311: } catch ( \PDOException $exception ) {
312: $matches = array();
313: $dbname = ( preg_match( '/dbname=(\w+)/', $this->dsn, $matches ) ) ? $matches[1] : '?';
314: throw new \PDOException( 'Could not connect to database (' . $dbname . ').', $exception->getCode() );
315: }
316: }
317:
318: 319: 320: 321: 322: 323: 324: 325: 326: 327: 328:
329: public function setPDO( \PDO $pdo ) {
330: $this->pdo = $pdo;
331: }
332:
333: 334: 335:
336: public function GetAll( $sql, $bindings = array() )
337: {
338: $this->runQuery( $sql, $bindings );
339: return $this->resultArray;
340: }
341:
342: 343: 344:
345: public function GetAssocRow( $sql, $bindings = array() )
346: {
347: $this->runQuery( $sql, $bindings, array(
348: 'fetchStyle' => \PDO::FETCH_ASSOC
349: )
350: );
351: return $this->resultArray;
352: }
353:
354: 355: 356:
357: public function GetCol( $sql, $bindings = array() )
358: {
359: $rows = $this->GetAll( $sql, $bindings );
360: $cols = array();
361: if ( $rows && is_array( $rows ) && count( $rows ) > 0 ) {
362: foreach ( $rows as $row ) {
363: $cols[] = array_shift( $row );
364: }
365: }
366:
367: return $cols;
368: }
369:
370: 371: 372:
373: public function GetOne( $sql, $bindings = array() )
374: {
375: $arr = $this->GetAll( $sql, $bindings );
376: $res = NULL;
377: if ( !is_array( $arr ) ) return NULL;
378: if ( count( $arr ) === 0 ) return NULL;
379: $row1 = array_shift( $arr );
380: if ( !is_array( $row1 ) ) return NULL;
381: if ( count( $row1 ) === 0 ) return NULL;
382: $col1 = array_shift( $row1 );
383: return $col1;
384: }
385:
386: 387: 388: 389: 390: 391: 392: 393: 394:
395: public function GetCell( $sql, $bindings = array() )
396: {
397: return $this->GetOne( $sql, $bindings );
398: }
399:
400: 401: 402:
403: public function GetRow( $sql, $bindings = array() )
404: {
405: $arr = $this->GetAll( $sql, $bindings );
406: return array_shift( $arr );
407: }
408:
409: 410: 411:
412: public function Execute( $sql, $bindings = array() )
413: {
414: $this->runQuery( $sql, $bindings );
415: return $this->affectedRows;
416: }
417:
418: 419: 420:
421: public function GetInsertID()
422: {
423: $this->connect();
424:
425: return (int) $this->pdo->lastInsertId();
426: }
427:
428: 429: 430:
431: public function GetCursor( $sql, $bindings = array() )
432: {
433: $statement = $this->runQuery( $sql, $bindings, array( 'noFetch' => TRUE ) );
434: $cursor = new PDOCursor( $statement, \PDO::FETCH_ASSOC );
435: return $cursor;
436: }
437:
438: 439: 440:
441: public function Affected_Rows()
442: {
443: $this->connect();
444: return (int) $this->affectedRows;
445: }
446:
447: 448: 449: 450: 451: 452: 453: 454: 455: 456:
457: public function setDebugMode( $tf, $logger = NULL )
458: {
459: $this->connect();
460: $this->loggingEnabled = (bool) $tf;
461: if ( $this->loggingEnabled and !$logger ) {
462: $logger = new RDefault();
463: }
464: $this->setLogger( $logger );
465: }
466:
467: 468: 469: 470: 471: 472: 473: 474:
475: public function setLogger( Logger $logger )
476: {
477: $this->logger = $logger;
478: }
479:
480: 481: 482: 483: 484: 485:
486: public function getLogger()
487: {
488: return $this->logger;
489: }
490:
491: 492: 493:
494: public function StartTrans()
495: {
496: $this->connect();
497: $this->pdo->beginTransaction();
498: }
499:
500: 501: 502:
503: public function CommitTrans()
504: {
505: $this->connect();
506: $this->pdo->commit();
507: }
508:
509: 510: 511:
512: public function FailTrans()
513: {
514: $this->connect();
515: $this->pdo->rollback();
516: }
517:
518: 519: 520: 521: 522: 523: 524:
525: public function getDatabaseType()
526: {
527: $this->connect();
528:
529: return $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME );
530: }
531:
532: 533: 534: 535: 536:
537: public function getDatabaseVersion()
538: {
539: $this->connect();
540: return $this->pdo->getAttribute(\PDO::ATTR_CLIENT_VERSION );
541: }
542:
543: 544: 545: 546: 547:
548: public function getPDO()
549: {
550: $this->connect();
551: return $this->pdo;
552: }
553:
554: 555: 556: 557: 558:
559: public function close()
560: {
561: $this->pdo = NULL;
562: $this->isConnected = FALSE;
563: }
564:
565: 566: 567: 568: 569:
570: public function isConnected()
571: {
572: return $this->isConnected && $this->pdo;
573: }
574:
575: 576: 577: 578: 579: 580: 581:
582: public function setEnableLogging( $enable )
583: {
584: $this->loggingEnabled = (boolean) $enable;
585: }
586:
587: 588: 589: 590: 591:
592: public function resetCounter()
593: {
594: $this->queryCounter = 0;
595: return $this;
596: }
597:
598: 599: 600: 601: 602:
603: public function getQueryCount()
604: {
605: return $this->queryCounter;
606: }
607:
608: 609: 610: 611: 612: 613: 614: 615: 616:
617: public function getIntegerBindingMax()
618: {
619: return $this->max;
620: }
621: }
622: