Neevo Public API
  • Namespace
  • Class
  • Tree

Namespaces

  • Neevo
    • Nette
  • None
  • PHP

Classes

  • Neevo
  • NeevoBaseStmt
  • NeevoCacheFile
  • NeevoCacheMemcache
  • NeevoCacheMemory
  • NeevoCacheSession
  • NeevoConnection
  • NeevoDriverMySQL
  • NeevoDriverMySQLi
  • NeevoDriverPgSQL
  • NeevoDriverSQLite2
  • NeevoDriverSQLite3
  • NeevoLiteral
  • NeevoLoader
  • NeevoObserverMap
  • NeevoParser
  • NeevoResult
  • NeevoResultIterator
  • NeevoRow
  • NeevoStmt

Interfaces

  • INeevoCache
  • INeevoDriver
  • INeevoObservable
  • INeevoObserver

Exceptions

  • NeevoDriverException
  • NeevoException
  • NeevoImplementationException
  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) 2011 Martin Srank (http://smasty.net)
  9  *
 10  */
 11 
 12 
 13 /**
 14  * Neevo statement abstract base ancestor.
 15  *
 16  * @method NeevoBaseStmt and($expr, $value = true)
 17  * @method NeevoBaseStmt or($expr, $value = true)
 18  * @method NeevoBaseStmt if($condition)
 19  * @method NeevoBaseStmt else()
 20  * @method NeevoBaseStmt end()
 21  *
 22  * @author Martin Srank
 23  * @package Neevo
 24  */
 25 abstract class NeevoBaseStmt implements INeevoObservable {
 26 
 27 
 28     /** @var string */
 29     protected $source;
 30 
 31     /** @var string */
 32     protected $type;
 33 
 34     /** @var int */
 35     protected $limit;
 36 
 37     /** @var int */
 38     protected $offset;
 39 
 40     /** @var array */
 41     protected $conditions = array();
 42 
 43     /** @var array */
 44     protected $sorting = array();
 45 
 46     /** @var float */
 47     protected $time;
 48 
 49     /** @var bool */
 50     protected $performed;
 51 
 52     /** @var NeevoConnection */
 53     protected $connection;
 54 
 55     /** @var array Event type conversion table */
 56     protected static $eventTable = array(
 57         Neevo::STMT_SELECT => INeevoObserver::SELECT,
 58         Neevo::STMT_INSERT => INeevoObserver::INSERT,
 59         Neevo::STMT_UPDATE => INeevoObserver::UPDATE,
 60         Neevo::STMT_DELETE => INeevoObserver::DELETE
 61     );
 62 
 63     /** @var array */
 64     protected $subqueries = array();
 65 
 66     /** @var NeevoObserverMap */
 67     protected $observers;
 68 
 69     /** @var array */
 70     private $stmtConditions = array();
 71 
 72 
 73     /**
 74      * Create statement.
 75      * @param NeevoConnection $connection
 76      * @return void
 77      */
 78     public function __construct(NeevoConnection $connection){
 79         $this->connection = $connection;
 80         $this->observers = new NeevoObserverMap;
 81     }
 82 
 83 
 84     /**
 85      * String representation of object.
 86      * @return string
 87      */
 88     public function __toString(){
 89         return (string) $this->parse();
 90     }
 91 
 92 
 93     /**
 94      * Create clone of object.
 95      * @return void
 96      */
 97     public function __clone(){
 98         $this->resetState();
 99     }
100 
101 
102     /**
103      * @return NeevoBaseStmt fluent interface
104      * @internal
105      * @throws BadMethodCallException
106      * @throws InvalidArgumentException
107      */
108     public function __call($name, $args){
109         $name = strtolower($name);
110 
111         // AND/OR where() glues
112         if(in_array($name, array('and', 'or'))){
113             if($this->validateConditions())
114                 return $this;
115 
116             $this->resetState();
117             if(($count = count($this->conditions)) !== 0)
118                 $this->conditions[$count-1]['glue'] = strtoupper($name);
119             if(count($args) >= 1)
120                 call_user_func_array(array($this, 'where'), $args);
121             return $this;
122         }
123 
124         // Conditional statements
125         elseif(in_array($name, array('if', 'else', 'end'))){
126 
127             // Parameter counts
128             if(count($args) < 1 && $name == 'if')
129                 throw new InvalidArgumentException('Missing argument 1 for '.__CLASS__."::$name().");
130 
131             $conds = & $this->stmtConditions;
132             if($name == 'if')
133                 $conds[] = (bool) $args[0];
134             elseif($name == 'else')
135                 $conds[count($conds)-1] = !end($conds);
136             elseif($name == 'end')
137                 array_pop($conds);
138 
139             return $this;
140 
141         }
142         throw new BadMethodCallException('Call to undefined method '.__CLASS__."::$name()");
143     }
144 
145 
146     /*  ************  Statement clauses  ************  */
147 
148 
149     /**
150      * Set WHERE condition. Accepts infinite arguments.
151      *
152      * More calls append conditions with 'AND' operator. Conditions can also be specified
153      * by calling and() / or() methods the same way as where().
154      * Corresponding operator will be used.
155      *
156      * Accepts associative array in field => value form.
157      * @param string|array|Traversable $expr
158      * @param mixed $value
159      * @return NeevoBaseStmt fluent interface
160      */
161     public function where($expr, $value = true){
162         if((is_array($expr) || $expr instanceof Traversable) && $value === true){
163             foreach($expr as $key => $val)
164                 $this->where($key, $val);
165             return $this;
166         }
167 
168         if($this->validateConditions())
169             return $this;
170 
171         $this->resetState();
172 
173         // Simple format
174         if(strpos($expr, '%') === false){
175             $field = trim($expr);
176             $this->conditions[] = array(
177                 'simple' => true,
178                 'field' => $field,
179                 'value' => $value,
180                 'glue' => 'AND'
181             );
182             if($value instanceof self)
183                 $this->subqueries[] = $value;
184             return $this;
185         }
186 
187         // Format with modifiers
188         $args = func_get_args();
189         array_shift($args);
190         preg_match_all('~%(bin|sub|b|i|f|s|d|a|l)?~i', $expr, $matches);
191         $this->conditions[] = array(
192             'simple' => false,
193             'expr' => $expr,
194             'modifiers' => $matches[0],
195             'types' => $matches[1],
196             'values' => $args,
197             'glue' => 'AND'
198         );
199         foreach($args as $arg){
200             if($arg instanceof self)
201                 $this->subqueries[] = $arg;
202         }
203         return $this;
204     }
205 
206 
207     /**
208      * Define order. More calls append rules.
209      * @param string|array|Traversable $rule
210      * @param string $order Use constants - Neevo::ASC, Neevo::DESC
211      * @return NeevoBaseStmt fluent interface
212      */
213     public function order($rule, $order = null){
214         if($this->validateConditions())
215             return $this;
216 
217         $this->resetState();
218 
219         if(is_array($rule) || $rule instanceof Traversable){
220             foreach($rule as $key => $val){
221                 $this->order($key, $val);
222             }
223             return $this;
224         }
225         $this->sorting[] = array($rule, $order);
226 
227         return $this;
228     }
229 
230 
231     /**
232      * Set LIMIT and OFFSET clauses.
233      * @param int $limit
234      * @param int $offset
235      * @return NeevoBaseStmt fluent interface
236      */
237     public function limit($limit, $offset = null){
238         if($this->validateConditions())
239             return $this;
240 
241         $this->resetState();
242         $this->limit = array($limit,
243             ($offset !== null && $this->type === Neevo::STMT_SELECT) ? $offset : null);
244         return $this;
245     }
246 
247 
248     /**
249      * Randomize order. Removes any other order clause.
250      * @return NeevoBaseStmt fluent interface
251      */
252     public function rand(){
253         if($this->validateConditions())
254             return $this;
255 
256         $this->resetState();
257         $this->connection->getDriver()->randomizeOrder($this);
258         return $this;
259     }
260 
261 
262     /*  ************  Statement manipulation  ************  */
263 
264 
265     /**
266      * Print out syntax highlighted statement.
267      * @param bool $return
268      * @return string|NeevoBaseStmt fluent interface
269      */
270     public function dump($return = false){
271         $sql = PHP_SAPI === 'cli' ? $this->parse() . "\n" : Neevo::highlightSql($this->parse());
272         if(!$return)
273             echo $sql;
274         return $return ? $sql : $this;
275     }
276 
277 
278     /**
279      * Perform the statement.
280      * @return resource|bool
281      */
282     public function run(){
283         if(!$this->performed)
284             $start = microtime(true);
285 
286         $query = $this->performed
287             ? $this->resultSet : $this->connection->getDriver()->runQuery($this->parse());
288 
289         if(!$this->performed)
290             $this->time = microtime(true) - $start;
291 
292         $this->performed = true;
293         $this->resultSet = $query;
294 
295         $this->notifyObservers(isset($this->type)
296             ? self::$eventTable[$this->type] : INeevoObserver::QUERY);
297 
298         return $query;
299     }
300 
301 
302     /**
303      * Perform the statement. Alias for run().
304      * @return resource|bool
305      */
306     public function exec(){
307         return $this->run();
308     }
309 
310 
311     /**
312      * Build the SQL statement from the instance.
313      * @return string The SQL statement
314      * @internal
315      */
316     public function parse(){
317         if($this->hasCircularReferences($this))
318             throw new RuntimeException('Circular reference found, aborting.');
319 
320         $this->connection->connect();
321 
322         $parser = $this->connection->getParser();
323         $instance = new $parser($this);
324         return $instance->parse();
325     }
326 
327 
328     /*   * ***********  INeevoObservable implementation  ************  */
329 
330 
331     /**
332      * Attach given observer to given event.
333      * @param INeevoObserver $observer
334      * @param int $event
335      * @return void
336      */
337     public function attachObserver(INeevoObserver $observer, $event){
338         $this->observers->attach($observer, $event);
339     }
340 
341 
342     /**
343      * Detach given observer.
344      * @param INeevoObserver $observer
345      * @return void
346      */
347     public function detachObserver(INeevoObserver $observer){
348         $this->observers->detach($observer);
349     }
350 
351 
352     /**
353      * Notify all observers attached to given event.
354      * @param type $event
355      * @return void
356      */
357     public function notifyObservers($event){
358         foreach($this->observers as $observer){
359             if($event & $this->observers->getEvent())
360                 $observer->updateStatus($this, $event);
361         }
362     }
363 
364 
365     /*  ************  Getters  ************  */
366 
367 
368     /**
369      * Query execution time.
370      * @return int
371      */
372     public function getTime(){
373         return $this->time;
374     }
375 
376 
377     /**
378      * If query was performed.
379      * @return bool
380      */
381     public function isPerformed(){
382         return $this->performed;
383     }
384 
385 
386     /**
387      * Get full table name (with prefix).
388      * @return string
389      */
390     public function getTable(){
391         $table = str_replace(':', '', $this->source);
392         $prefix = $this->connection->getPrefix();
393         return $prefix . $table;
394     }
395 
396 
397     /**
398      * Statement type.
399      * @return string
400      */
401     public function getType(){
402         return $this->type;
403     }
404 
405 
406     /**
407      * Get LIMIT and OFFSET clauses.
408      * @return array
409      */
410     public function getLimit(){
411         return $this->limit;
412     }
413 
414 
415     /**
416      * Statement WHERE clause.
417      * @return array
418      */
419     public function getConditions(){
420         return $this->conditions;
421     }
422 
423 
424     /**
425      * Statement ORDER BY clause.
426      * @return array
427      */
428     public function getSorting(){
429         return $this->sorting;
430     }
431 
432 
433     /**
434      * Name of the PRIMARY KEY column.
435      * @return string|null
436      */
437     public function getPrimaryKey(){
438         $table = $this->getTable();
439         if(!$table)
440             return null;
441         $key = null;
442         $cached = $this->connection->getCache()->fetch($table . '_primaryKey');
443 
444         if($cached === null){
445             try{
446                 $key = $this->connection->getDriver()->getPrimaryKey($table);
447             } catch(NeevoException $e){
448                 return null;
449             }
450             $this->connection->getCache()->store($table . '_primaryKey', $key);
451             return $key === '' ? null : $key;
452         }
453         return $cached === '' ? null : $cached;
454     }
455 
456 
457     /*  ************  Internal methods  ************  */
458 
459 
460     /**
461      * Get the connection instance.
462      * @return NeevoConnection
463      */
464     public function getConnection(){
465         return $this->connection;
466     }
467 
468 
469     /**
470      * Reset the state of the statement.
471      * @return void
472      */
473     protected function resetState(){
474         $this->performed = false;
475         $this->resultSet = null;
476         $this->time = null;
477     }
478 
479 
480     /**
481      * Validate the current statement condition.
482      * @return bool
483      */
484     protected function validateConditions(){
485         if(empty($this->stmtConditions))
486             return false;
487         foreach($this->stmtConditions as $cond){
488             if($cond) continue;
489             else return true;
490         }
491         return false;
492     }
493 
494 
495     /**
496      * Check the query tree for circular references.
497      * @param NeevoBaseStmt $parent
498      * @param array $visited
499      * @return bool True if circular reference found.
500      */
501     protected function hasCircularReferences($parent, $visited = array()){
502         foreach($parent->subqueries as $child){
503             if(isset($visited[spl_object_hash($child)]))
504                 return true;
505             $visited[spl_object_hash($child)] = true;
506             if($this->hasCircularReferences($child, $visited))
507                 return true;
508             array_pop($visited);
509         }
510         return false;
511     }
512 
513 
514 }
515 
Neevo Public API API documentation generated by ApiGen 2.8.0