Neevo Public API
  • Namespace
  • Class
  • Tree

Namespaces

  • Neevo
    • Cache
    • Drivers
    • Nette
  • PHP

Classes

  • MySQLDriver
  • MySQLiDriver
  • PDODriver
  • PgSQLDriver
  • SQLite2Driver
  • SQLite3Driver
  1 <?php
  2 /**
  3  * Neevo - Tiny database layer for PHP. (http://neevo.smasty.net)
  4  *
  5  * This source file is subject to the MIT license that is bundled
  6  * with this package in the file license.txt.
  7  *
  8  * Copyright (c) 2012 Smasty (http://smasty.net)
  9  *
 10  */
 11 
 12 namespace Neevo\Drivers;
 13 
 14 use DateTime;
 15 use InvalidArgumentException;
 16 use Neevo\BaseStatement;
 17 use Neevo\Connection;
 18 use Neevo\DriverException;
 19 use Neevo\DriverInterface;
 20 use Neevo\ImplementationException;
 21 use Neevo\Manager;
 22 use Neevo\Parser;
 23 use PDO;
 24 use PDOException;
 25 use PDOStatement;
 26 
 27 
 28 /**
 29  * Neevo PDO driver (PHP extension 'pdo') Experimental!
 30  *
 31  * Driver configuration:
 32  * - dsn => Driver-specific DSN
 33  * - username
 34  * - password
 35  * - options (array) => Driver-specific options for {@see PDO::__construct}
 36  * - resource (instance of PDO) => Existing PDO connection
 37  * - lazy, table_prefix... => see {@see Neevo\Connection}
 38  *
 39  * @author Smasty
 40  */
 41 class PDODriver extends Parser implements DriverInterface {
 42 
 43 
 44     /** @var PDO */
 45     private $resource;
 46 
 47     /** @var string */
 48     private $driverName;
 49 
 50     /** @var int */
 51     private $affectedRows;
 52 
 53 
 54     /**
 55      * Checks for required PHP extension.
 56      * @throws DriverException
 57      */
 58     public function __construct(BaseStatement $statement = null){
 59         if(!extension_loaded("pdo"))
 60             throw new DriverException("Cannot instantiate Neevo PDO driver - PHP extension 'pdo' not loaded.");
 61         if($statement instanceof BaseStatement)
 62             parent::__construct($statement);
 63     }
 64 
 65 
 66     /**
 67      * Creates connection to database.
 68      * @param array $config Configuration options
 69      * @throws DriverException
 70      */
 71     public function connect(array $config){
 72         Connection::alias($config, 'resource', 'pdo');
 73 
 74         // Defaults
 75         $defaults = array(
 76             'dsn' => null,
 77             'resource' => null,
 78             'username' => null,
 79             'password' => null,
 80             'options' => array(),
 81         );
 82 
 83         $config += $defaults;
 84 
 85         // Connect
 86         if($config['resource'] instanceof PDO)
 87             $this->resource = $config['resource'];
 88         else try{
 89             $this->resource = new PDO($config['dsn'], $config['username'], $config['password'], $config['options']);
 90         } catch(PDOException $e){
 91             throw new DriverException($e->getMessage(), $e->getCode());
 92         }
 93 
 94         if(!$this->resource)
 95             throw new DriverException('Connection failed.');
 96 
 97         $this->driverName = $this->resource->getAttribute(PDO::ATTR_DRIVER_NAME);
 98     }
 99 
100 
101     /**
102      * Closes the connection.
103      */
104     public function closeConnection(){
105         @$this->resource = null;
106     }
107 
108 
109     /**
110      * Frees memory used by given result set.
111      * @param PDOStatement $resultSet
112      * @return bool
113      */
114     public function freeResultSet($resultSet){
115         return $resultSet = null;
116     }
117 
118 
119     /**
120      * Executes given SQL statement.
121      * @param string $queryString
122      * @return PDOStatement|bool
123      * @throws DriverException
124      */
125     public function runQuery($queryString){
126 
127         $cmd = strtoupper(substr(trim($queryString), 0, 6));
128         static $list = array('UPDATE' => 1, 'DELETE' => 1, 'INSERT' => 1, 'REPLAC' => 1);
129         $this->affectedRows = false;
130 
131         if(isset($list[$cmd])){
132             $this->affectedRows = $this->resource->exec($queryString);
133 
134             if($this->affectedRows === false){
135                 $error = $this->resource->errorInfo();
136                 throw new DriverException("SQLSTATE[$error[0]]: $error[2]", $error[1], $queryString);
137             } else
138                 return true;
139         }
140 
141         $result = $this->resource->query($queryString);
142 
143         if($result === false){
144             $error = $this->resource->errorInfo();
145             throw new DriverException("SQLSTATE[$error[0]]: $error[2]", $error[1], $queryString);
146         }else
147             return $result;
148     }
149 
150 
151     /**
152      * Begins a transaction if supported.
153      * @param string $savepoint
154      */
155     public function beginTransaction($savepoint = null){
156         if(!$this->resource->beginTransaction()){
157             $error = $this->resource->errorInfo();
158             throw new DriverException("SQLSTATE[$error[0]]: $error[2]", $error[1]);
159         }
160     }
161 
162 
163     /**
164      * Commits statements in a transaction.
165      * @param string $savepoint
166      */
167     public function commit($savepoint = null){
168         if(!$this->resource->commit()){
169             $error = $this->resource->errorInfo();
170             throw new DriverException("SQLSTATE[$error[0]]: $error[2]", $error[1]);
171         }
172     }
173 
174 
175     /**
176      * Rollbacks changes in a transaction.
177      * @param string $savepoint
178      */
179     public function rollback($savepoint = null){
180         if(!$this->resource->rollBack()){
181             $error = $this->resource->errorInfo();
182             throw new DriverException("SQLSTATE[$error[0]]: $error[2]", $error[1]);
183         }
184     }
185 
186 
187     /**
188      * Fetches row from given result set as an associative array.
189      * @param PDOStatement $resultSet
190      * @return array
191      */
192     public function fetch($resultSet){
193         return $resultSet->fetch(PDO::FETCH_ASSOC);
194     }
195 
196 
197     /**
198      * Moves internal result pointer.
199      * @param PDOStatement $resultSet
200      * @param int
201      * @return bool
202      * @throws ImplementationException
203      */
204     public function seek($resultSet, $offset){
205         throw new ImplementationException('Cannot seek on unbuffered result.');
206     }
207 
208 
209     /**
210      * Returns the ID generated in the INSERT statement.
211      * @return int
212      */
213     public function getInsertId(){
214         return $this->resource->lastInsertId();
215     }
216 
217 
218     /**
219      * Randomizes result order.
220      * @param BaseStatement $statement
221      */
222     public function randomizeOrder(BaseStatement $statement){
223         switch($this->driverName){
224             case 'mysql':
225             case 'pgsql':
226                 $random = 'RAND()';
227 
228             case 'sqlite':
229             case 'sqlite2':
230                 $random = 'RANDOM()';
231 
232             case 'odbc':
233                 $random = 'Rnd(id)';
234 
235             case 'oci':
236                 $random = 'dbms_random.value';
237 
238             case 'mssql':
239                 $random = 'NEWID()';
240         }
241         $statement->order($random);
242     }
243 
244 
245     /**
246      * Returns the number of rows in the given result set.
247      * @param PDOStatement $resultSet
248      * @return int
249      */
250     public function getNumRows($resultSet){
251         $resultSet->rowCount();
252     }
253 
254 
255     /**
256      * Returns the number of affected rows in previous operation.
257      * @return int
258      */
259     public function getAffectedRows(){
260         return $this->affectedRows;
261     }
262 
263 
264     /**
265      * Escapes given value.
266      * @param mixed $value
267      * @param string $type
268      * @return mixed
269      * @throws InvalidArgumentException
270      */
271     public function escape($value, $type){
272         switch($type){
273             case Manager::BOOL:
274                 return $this->resource->quote($value, PDO::PARAM_BOOL);
275 
276             case Manager::TEXT:
277                 return $this->resource->quote($value, PDO::PARAM_STR);
278 
279             case Manager::IDENTIFIER:
280                 switch($this->driverName){
281                     case 'mysql':
282                         return str_replace('`*`', '*', '`' . str_replace('.', '`.`', str_replace('`', '``', $value)) . '`');
283 
284                     case 'pgsql':
285                         return '"' . str_replace('.', '"."', str_replace('"', '""', $value)) . '"';
286 
287                     case 'sqlite':
288                     case 'sqlite2':
289                         return str_replace('[*]', '*', '[' . str_replace('.', '].[', $value) . ']');
290 
291                     case 'odbc':
292                     case 'oci':
293                     case 'mssql':
294                         return '[' . str_replace(array('[', ']'), array('[[', ']]'), $value) . ']';
295 
296                     default:
297                         return $value;
298                 }
299 
300             case Manager::BINARY:
301                 return $this->resource->quote($value, PDO::PARAM_LOB);
302 
303             case Manager::DATETIME:
304                 return ($value instanceof DateTime) ? $value->format("'Y-m-d H:i:s'") : date("'Y-m-d H:i:s'", $value);
305 
306             default:
307                 throw new InvalidArgumentException('Unsupported data type.');
308                 break;
309         }
310     }
311 
312 
313     /**
314      * Decodes given value.
315      * @param mixed $value
316      * @param string $type
317      * @return mixed
318      * @throws InvalidArgumentException
319      */
320     public function unescape($value, $type){
321         if($type === Manager::BINARY)
322             return $value;
323         throw new InvalidArgumentException('Unsupported data type.');
324     }
325 
326 
327     /**
328      * Returns the PRIMARY KEY column for given table.
329      *
330      * Not supported by all PDO drivers.
331      * @param string $table
332      * @throws ImplementationException
333      */
334     public function getPrimaryKey($table, $resultSet = null){
335         if($resultSet instanceof PDOStatement){
336             $i = 0;
337             while($col = $resultSet->getColumnMeta($i++)){
338                 if(in_array('primary_key', $col['flags']))
339                     $primaryKey = $col['name'];
340             }
341         }
342         if(isset($primaryKey))
343             return $primaryKey;
344         throw new ImplementationException;
345     }
346 
347 
348     /**
349      * Returns types of columns in given result set.
350      *
351      * Not supported by all PDO drivers.
352      * @param PDOStatement $resultset
353      * @param string $table
354      * @throws ImplementationException
355      */
356     public function getColumnTypes($resultSet, $table){
357         $types = array();
358         $i = 0;
359         while($col = $resultSet->getColumnMeta($i++))
360             $types[$col['name']] = strtolower($col['native_type']);
361 
362         if(empty($types))
363             throw new ImplementationException;
364         return $types;
365     }
366 
367 
368     /**
369      * Parses UPDATE statement.
370      * @return string
371      */
372     protected function parseUpdateStmt(){
373         $sql = parent::parseUpdateStmt();
374         if($this->driverName === 'mysql')
375             return $this->applyLimit($sql . $this->clauses[3]);
376         return $sql;
377     }
378 
379 
380     /**
381      * Parses DELETE statement.
382      * @return string
383      */
384     protected function parseDeleteStmt(){
385         $sql = parent::parseDeleteStmt();
386         if($this->driverName === 'mysql')
387             return $this->applyLimit($sql . $this->clauses[3]);
388         return $sql;
389     }
390 
391 
392     /**
393      * Applies LIMIT/OFFSET to SQL command.
394      * @param string $sql SQL command
395      * @return string
396      * @throws DriverException
397      */
398     protected function applyLimit($sql){
399         list($limit, $offset) = $this->stmt->getLimit();
400         if((int) $limit < 1 && (int) $offset < 1)
401             return $sql;
402 
403         switch($this->stmt->getConnection()->getDriver()->getDriverName()){
404             case 'mysql':
405             case 'pgsql':
406             case 'sqlite':
407             case 'sqlite2':
408                 if((int) $limit > 0){
409                     $sql .= "\nLIMIT " . (int) $limit;
410                     if((int) $offset > 0)
411                         $sql .= ' OFFSET ' . (int) $offset;
412                 }
413                 return $sql;
414 
415             case 'odbc':
416             case 'mssql':
417                 if($offset < 1)
418                     return 'SELECT TOP ' . (int) $limit . " *\nFROM (\n\t"
419                          . implode("\n\t", explode("\n", $sql)) . "\n)";
420 
421             default:
422                 throw new DriverException('PDO or selected driver does not allow apllying limit or offset.');
423         }
424     }
425 
426 
427     /**
428      * Returns PDO instance.
429      * @return PDO
430      */
431     public function getResource(){
432         return $this->resource;
433     }
434 
435 
436     /**
437      * Returns PDO driver name.
438      * @return string
439      */
440     public function getDriverName(){
441         return $this->driverName;
442     }
443 
444 
445 }
446 
Neevo Public API API documentation generated by ApiGen 2.8.0