Overview

Namespaces

  • None
  • RedBeanPHP
    • Adapter
    • BeanHelper
    • Cursor
    • Driver
    • Logger
      • RDefault
    • QueryWriter
    • RedException
    • Repository
    • Util

Classes

  • R
  • RedBean_SimpleModel
  • RedBeanPHP\Adapter\DBAdapter
  • RedBeanPHP\AssociationManager
  • RedBeanPHP\BeanCollection
  • RedBeanPHP\BeanHelper\SimpleFacadeBeanHelper
  • RedBeanPHP\Cursor\NullCursor
  • RedBeanPHP\Cursor\PDOCursor
  • RedBeanPHP\Driver\RPDO
  • RedBeanPHP\DuplicationManager
  • RedBeanPHP\Facade
  • RedBeanPHP\Finder
  • RedBeanPHP\Jsonable
  • RedBeanPHP\LabelMaker
  • RedBeanPHP\Logger\RDefault
  • RedBeanPHP\Logger\RDefault\Debug
  • RedBeanPHP\Observable
  • RedBeanPHP\OODB
  • RedBeanPHP\OODBBean
  • RedBeanPHP\QueryWriter\AQueryWriter
  • RedBeanPHP\QueryWriter\CUBRID
  • RedBeanPHP\QueryWriter\MySQL
  • RedBeanPHP\QueryWriter\PostgreSQL
  • RedBeanPHP\QueryWriter\SQLiteT
  • RedBeanPHP\R
  • RedBeanPHP\Repository
  • RedBeanPHP\Repository\Fluid
  • RedBeanPHP\Repository\Frozen
  • RedBeanPHP\SimpleModel
  • RedBeanPHP\SimpleModelHelper
  • RedBeanPHP\TagManager
  • RedBeanPHP\ToolBox
  • RedBeanPHP\Util\ArrayTool
  • RedBeanPHP\Util\DispenseHelper
  • RedBeanPHP\Util\Dump
  • RedBeanPHP\Util\MultiLoader
  • RedBeanPHP\Util\Transaction

Interfaces

  • RedBeanPHP\Adapter
  • RedBeanPHP\BeanHelper
  • RedBeanPHP\Cursor
  • RedBeanPHP\Driver
  • RedBeanPHP\Logger
  • RedBeanPHP\Observer
  • RedBeanPHP\Plugin
  • RedBeanPHP\QueryWriter

Exceptions

  • RedBeanPHP\RedException
  • RedBeanPHP\RedException\SQL

Functions

  • array_flatten
  • dmp
  • EID
  • genslots
  • Overview
  • Namespace
  • Class
  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:  * PDO Driver
 16:  * This Driver implements the RedBean Driver API.
 17:  * for RedBeanPHP. This is the standard / default database driver
 18:  * for RedBeanPHP.
 19:  *
 20:  * @file    RedBeanPHP/PDO.php
 21:  * @author  Gabor de Mooij and the RedBeanPHP Community, Desfrenes
 22:  * @license BSD/GPLv2
 23:  *
 24:  * @copyright
 25:  * copyright (c) Desfrenes & Gabor de Mooij and the RedBeanPHP community
 26:  * This source file is subject to the BSD/GPLv2 License that is bundled
 27:  * with this source code in the file license.txt.
 28:  */
 29: class RPDO implements Driver
 30: {
 31:     /**
 32:      * @var integer
 33:      */
 34:     protected $max;
 35: 
 36:     /**
 37:      * @var string
 38:      */
 39:     protected $dsn;
 40: 
 41:     /**
 42:      * @var boolean
 43:      */
 44:     protected $loggingEnabled = FALSE;
 45: 
 46:     /**
 47:      * @var Logger
 48:      */
 49:     protected $logger = NULL;
 50: 
 51:     /**
 52:      * @var PDO
 53:      */
 54:     protected $pdo;
 55: 
 56:     /**
 57:      * @var integer
 58:      */
 59:     protected $affectedRows;
 60: 
 61:     /**
 62:      * @var integer
 63:      */
 64:     protected $resultArray;
 65: 
 66:     /**
 67:      * @var array
 68:      */
 69:     protected $connectInfo = array();
 70: 
 71:     /**
 72:      * @var boolean
 73:      */
 74:     protected $isConnected = FALSE;
 75: 
 76:     /**
 77:      * @var bool
 78:      */
 79:     protected $flagUseStringOnlyBinding = FALSE;
 80: 
 81:     /**
 82:      * @var integer
 83:      */
 84:     protected $queryCounter = 0;
 85: 
 86:     /**
 87:      * @var string
 88:      */
 89:     protected $mysqlEncoding = '';
 90: 
 91:     /**
 92:      * Binds parameters. This method binds parameters to a PDOStatement for
 93:      * Query Execution. This method binds parameters as NULL, INTEGER or STRING
 94:      * and supports both named keys and question mark keys.
 95:      *
 96:      * @param PDOStatement $statement PDO Statement instance
 97:      * @param array        $bindings  values that need to get bound to the statement
 98:      *
 99:      * @return void
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:      * This method runs the actual SQL query and binds a list of parameters to the query.
126:      * slots. The result of the query will be stored in the protected property
127:      * $rs (always array). The number of rows affected (result of rowcount, if supported by database)
128:      * is stored in protected property $affectedRows. If the debug flag is set
129:      * this function will send debugging output to screen buffer.
130:      *
131:      * @param string $sql      the SQL string to be send to database server
132:      * @param array  $bindings the values that need to get bound to the query slots
133:      * @param array  $options
134:      *
135:      * @return mixed
136:      * @throws SQL
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:             //Unfortunately the code field is supposed to be int by default (php)
173:             //So we need a property to convey the SQL State code.
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:      * Try to fix MySQL character encoding problems.
184:      * MySQL < 5.5 does not support proper 4 byte unicode but they
185:      * seem to have added it with version 5.5 under a different label: utf8mb4.
186:      * We try to select the best possible charset based on your version data.
187:      *
188:      * @return void
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 ); //on every re-connect
197:             $this->pdo->exec(' SET NAMES '. $encoding); //also for current connection
198:             $this->mysqlEncoding = $encoding;
199:         }
200:     }
201: 
202:     /**
203:      * Constructor. You may either specify dsn, user and password or
204:      * just give an existing PDO connection.
205:      *
206:      * Examples:
207:      *    $driver = new RPDO($dsn, $user, $password);
208:      *    $driver = new RPDO($existingConnection);
209:      *
210:      * @param string|object $dsn  database connection string
211:      * @param string        $user optional, usename to sign in
212:      * @param string        $pass optional, password for connection login
213:      *
214:      * @return void
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:             // make sure that the dsn at least contains the type
225:             $this->dsn = $this->getDatabaseType();
226:         } else {
227:             $this->dsn = $dsn;
228:             $this->connectInfo = array( 'pass' => $pass, 'user' => $user );
229:         }
230: 
231:         //PHP 5.3 PDO SQLite has a bug with large numbers:
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; //otherwise you get -2147483648 ?! demonstrated in build #603 on Travis.
234:         } elseif ( strpos( $this->dsn, 'cubrid' ) === 0 ) {
235:             $this->max = 2147483647; //bindParam in pdo_cubrid also fails...
236:         } else {
237:             $this->max = PHP_INT_MAX; //the normal value of course (makes it possible to use large numbers in LIMIT clause)
238:         }
239:     }
240: 
241:     /**
242:      * Returns the best possible encoding for MySQL based on version data.
243:      *
244:      * @return string
245:      */
246:     public function getMysqlEncoding()
247:     {
248:         return $this->mysqlEncoding;
249:     }
250: 
251:     /**
252:      * Whether to bind all parameters as strings.
253:      * If set to TRUE this will cause all integers to be bound as STRINGS.
254:      * This will NOT affect NULL values.
255:      *
256:      * @param boolean $yesNo pass TRUE to bind all parameters as strings.
257:      *
258:      * @return void
259:      */
260:     public function setUseStringOnlyBinding( $yesNo )
261:     {
262:         $this->flagUseStringOnlyBinding = (boolean) $yesNo;
263:     }
264: 
265:     /**
266:      * Sets the maximum value to be bound as integer, normally
267:      * this value equals PHP's MAX INT constant, however sometimes
268:      * PDO driver bindings cannot bind large integers as integers.
269:      * This method allows you to manually set the max integer binding
270:      * value to manage portability/compatibility issues among different
271:      * PHP builds. This method will return the old value.
272:      *
273:      * @param integer $max maximum value for integer bindings
274:      *
275:      * @return integer
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:      * Establishes a connection to the database using PHP\PDO
287:      * functionality. If a connection has already been established this
288:      * method will simply return directly. This method also turns on
289:      * UTF8 for the database and PDO-ERRMODE-EXCEPTION as well as
290:      * PDO-FETCH-ASSOC.
291:      *
292:      * @return void
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:             //cant pass these as argument to constructor, CUBRID driver does not understand...
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:      * Directly sets PDO instance into driver.
320:      * This method might improve performance, however since the driver does
321:      * not configure this instance terrible things may happen... only use
322:      * this method if you are an expert on RedBeanPHP, PDO and UTF8 connections and
323:      * you know your database server VERY WELL.
324:      *
325:      * @param PDO $pdo PDO instance
326:      *
327:      * @return void
328:      */
329:     public function setPDO( \PDO $pdo ) {
330:         $this->pdo = $pdo;
331:     }
332: 
333:     /**
334:      * @see Driver::GetAll
335:      */
336:     public function GetAll( $sql, $bindings = array() )
337:     {
338:         $this->runQuery( $sql, $bindings );
339:         return $this->resultArray;
340:     }
341: 
342:     /**
343:      * @see Driver::GetAssocRow
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:      * @see Driver::GetCol
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:      * @see Driver::GetOne
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:      * Alias for getOne().
388:      * Backward compatibility.
389:      *
390:      * @param string $sql      SQL
391:      * @param array  $bindings bindings
392:      *
393:      * @return mixed
394:      */
395:     public function GetCell( $sql, $bindings = array() )
396:     {
397:         return $this->GetOne( $sql, $bindings );
398:     }
399: 
400:     /**
401:      * @see Driver::GetRow
402:      */
403:     public function GetRow( $sql, $bindings = array() )
404:     {
405:         $arr = $this->GetAll( $sql, $bindings );
406:         return array_shift( $arr );
407:     }
408: 
409:     /**
410:      * @see Driver::Excecute
411:      */
412:     public function Execute( $sql, $bindings = array() )
413:     {
414:         $this->runQuery( $sql, $bindings );
415:         return $this->affectedRows;
416:     }
417: 
418:     /**
419:      * @see Driver::GetInsertID
420:      */
421:     public function GetInsertID()
422:     {
423:         $this->connect();
424: 
425:         return (int) $this->pdo->lastInsertId();
426:     }
427: 
428:     /**
429:      * @see Driver::GetCursor
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:      * @see Driver::Affected_Rows
440:      */
441:     public function Affected_Rows()
442:     {
443:         $this->connect();
444:         return (int) $this->affectedRows;
445:     }
446: 
447:     /**
448:      * Toggles debug mode. In debug mode the driver will print all
449:      * SQL to the screen together with some information about the
450:      * results.
451:      *
452:      * @param boolean $trueFalse turn on/off
453:      * @param Logger  $logger    logger instance
454:      *
455:      * @return void
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:      * Injects Logger object.
469:      * Sets the logger instance you wish to use.
470:      *
471:      * @param Logger $logger the logger instance to be used for logging
472:      *
473:      * @return void
474:      */
475:     public function setLogger( Logger $logger )
476:     {
477:         $this->logger = $logger;
478:     }
479: 
480:     /**
481:      * Gets Logger object.
482:      * Returns the currently active Logger instance.
483:      *
484:      * @return Logger
485:      */
486:     public function getLogger()
487:     {
488:         return $this->logger;
489:     }
490: 
491:     /**
492:      * @see Driver::StartTrans
493:      */
494:     public function StartTrans()
495:     {
496:         $this->connect();
497:         $this->pdo->beginTransaction();
498:     }
499: 
500:     /**
501:      * @see Driver::CommitTrans
502:      */
503:     public function CommitTrans()
504:     {
505:         $this->connect();
506:         $this->pdo->commit();
507:     }
508: 
509:     /**
510:      * @see Driver::FailTrans
511:      */
512:     public function FailTrans()
513:     {
514:         $this->connect();
515:         $this->pdo->rollback();
516:     }
517: 
518:     /**
519:      * Returns the name of database driver for PDO.
520:      * Uses the PDO attribute DRIVER NAME to obtain the name of the
521:      * PDO driver.
522:      *
523:      * @return string
524:      */
525:     public function getDatabaseType()
526:     {
527:         $this->connect();
528: 
529:         return $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME );
530:     }
531: 
532:     /**
533:      * Returns the version number of the database.
534:      *
535:      * @return mixed
536:      */
537:     public function getDatabaseVersion()
538:     {
539:         $this->connect();
540:         return $this->pdo->getAttribute(\PDO::ATTR_CLIENT_VERSION );
541:     }
542: 
543:     /**
544:      * Returns the underlying PHP PDO instance.
545:      *
546:      * @return PDO
547:      */
548:     public function getPDO()
549:     {
550:         $this->connect();
551:         return $this->pdo;
552:     }
553: 
554:     /**
555:      * Closes database connection by destructing PDO.
556:      *
557:      * @return void
558:      */
559:     public function close()
560:     {
561:         $this->pdo         = NULL;
562:         $this->isConnected = FALSE;
563:     }
564: 
565:     /**
566:      * Returns TRUE if the current PDO instance is connected.
567:      *
568:      * @return boolean
569:      */
570:     public function isConnected()
571:     {
572:         return $this->isConnected && $this->pdo;
573:     }
574: 
575:     /**
576:      * Toggles logging, enables or disables logging.
577:      *
578:      * @param boolean $enable TRUE to enable logging
579:      *
580:      * @return self
581:      */
582:     public function setEnableLogging( $enable )
583:     {
584:         $this->loggingEnabled = (boolean) $enable;
585:     }
586: 
587:     /**
588:      * Resets the internal Query Counter.
589:      *
590:      * @return self
591:      */
592:     public function resetCounter()
593:     {
594:         $this->queryCounter = 0;
595:         return $this;
596:     }
597: 
598:     /**
599:      * Returns the number of SQL queries processed.
600:      *
601:      * @return integer
602:      */
603:     public function getQueryCount()
604:     {
605:         return $this->queryCounter;
606:     }
607: 
608:     /**
609:      * Returns the maximum value treated as integer parameter
610:      * binding.
611:      *
612:      * This method is mainly for testing purposes but it can help
613:      * you solve some issues relating to integer bindings.
614:      *
615:      * @return integer
616:      */
617:     public function getIntegerBindingMax()
618:     {
619:         return $this->max;
620:     }
621: }
622: 
API documentation generated by ApiGen