1: <?php
2: /**
3: * DataTables PHP libraries.
4: *
5: * PHP libraries for DataTables and DataTables Editor, utilising PHP 5.3+.
6: *
7: * @author SpryMedia
8: * @copyright 2012 SpryMedia ( http://sprymedia.co.uk )
9: * @license http://editor.datatables.net/license DataTables Editor
10: * @link http://editor.datatables.net
11: */
12:
13: namespace DataTables\Database;
14: if (!defined('DATATABLES')) exit();
15:
16: use
17: DataTables,
18: DataTables\Database,
19: DataTables\Database\Query,
20: DataTables\Database\Result;
21:
22:
23: //
24: // This is a stub class that a driver must extend and complete
25: //
26:
27: /**
28: * Perform an individual query on the database.
29: *
30: * The typical pattern for using this class is through the {@link
31: * \DataTables\Database::query} method (and it's 'select', etc short-cuts).
32: * Typically it would not be initialised directly.
33: *
34: * Note that this is a stub class that a driver will extend and complete as
35: * required for individual database types. Individual drivers could add
36: * additional methods, but this is discouraged to ensure that the API is the
37: * same for all database types.
38: */
39: class Query {
40: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
41: * Constructor
42: */
43:
44: /**
45: * Query instance constructor.
46: *
47: * Note that typically instances of this class will be automatically created
48: * through the {@link \DataTables\Database::query} method.
49: * @param Database $db Database instance
50: * @param string $type Query type - 'select', 'insert', 'update' or 'delete'
51: * @param string|string[] $table Tables to operate on - see {@link table}.
52: */
53: public function __construct( $db, $type, $table=null )
54: {
55: $this->_dbcon = $db;
56: $this->_type = $type;
57: $this->table( $table );
58: }
59:
60:
61: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
62: * Private properties
63: */
64:
65: /**
66: * @var string Driver to use
67: * @internal
68: */
69: protected $_type = "";
70:
71: /**
72: * @var resource Database connection
73: * @internal
74: */
75: protected $_dbcon = null;
76:
77: /**
78: * @var array
79: * @internal
80: */
81: protected $_table = array();
82:
83: /**
84: * @var array
85: * @internal
86: */
87: protected $_field = array();
88:
89: /**
90: * @var array
91: * @internal
92: */
93: protected $_bindings = array();
94:
95: /**
96: * @var array
97: * @internal
98: */
99: protected $_where = array();
100:
101: /**
102: * @var array
103: * @internal
104: */
105: protected $_join = array();
106:
107: /**
108: * @var array
109: * @internal
110: */
111: protected $_order = array();
112:
113: /**
114: * @var array
115: * @internal
116: */
117: protected $_noBind = array();
118:
119: /**
120: * @var int
121: * @internal
122: */
123: protected $_limit = null;
124:
125: /**
126: * @var int
127: * @internal
128: */
129: protected $_offset = null;
130:
131: /**
132: * @var string
133: * @internal
134: */
135: protected $_distinct = false;
136:
137: /**
138: * @var string
139: * @internal
140: */
141: protected $_identifier_limiter = '`';
142:
143: /**
144: * @var string
145: * @internal
146: */
147: protected $_field_quote = '\'';
148:
149:
150:
151: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
152: * Static methods
153: */
154:
155: /**
156: * Commit a transaction.
157: * @param \PDO $dbh The Database handle (typically a PDO object, but not always).
158: */
159: public static function commit ( $dbh )
160: {
161: $dbh->commit();
162: }
163:
164: /**
165: * Database connection - override by the database driver.
166: * @param string|array $user User name or all parameters in an array
167: * @param string $pass Password
168: * @param string $host Host name
169: * @param string $db Database name
170: * @return Query
171: */
172: public static function connect ( $user, $pass='', $host='', $port='', $db='', $dsn='' )
173: {
174: return false;
175: }
176:
177:
178: /**
179: * Start a database transaction
180: * @param \PDO $dbh The Database handle (typically a PDO object, but not always).
181: */
182: public static function transaction ( $dbh )
183: {
184: $dbh->beginTransaction();
185: }
186:
187:
188: /**
189: * Rollback the database state to the start of the transaction.
190: * @param \PDO $dbh The Database handle (typically a PDO object, but not always).
191: */
192: public static function rollback ( $dbh )
193: {
194: $dbh->rollBack();
195: }
196:
197:
198: /**
199: * Common helper for the drivers to handle a PDO DSN postfix
200: * @param string $dsn DSN postfix to use
201: * @return Query
202: * @internal
203: */
204: static function dsnPostfix ( $dsn )
205: {
206: if ( ! $dsn ) {
207: return '';
208: }
209:
210: // Add a DSN field separator if not given
211: if ( strpos( $dsn, ';' ) !== 0 ) {
212: return ';'.$dsn;
213: }
214:
215: return $dsn;
216: }
217:
218:
219:
220: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
221: * Public methods
222: */
223:
224: /**
225: * Safely bind an input value to a parameter. This is evaluated when the
226: * query is executed. This allows user input to be safely executed without
227: * risk of an SQL injection attack.
228: *
229: * @param [type] $name Parameter name. This should include a leading colon
230: * @param [type] $value Value to bind
231: * @param [type] $type Data type. See the PHP PDO documentation:
232: * http://php.net/manual/en/pdo.constants.php
233: * @return Query
234: */
235: public function bind ( $name, $value, $type=null )
236: {
237: $this->_bindings[] = array(
238: "name" => $this->_safe_bind( $name ),
239: "value" => $value,
240: "type" => $type
241: );
242:
243: return $this;
244: }
245:
246: /**
247: * Set a distinct flag for a `select` query. Note that this has no effect on
248: * any of the other query types.
249: * @param boolean $dis Optional
250: * @return Query
251: */
252: public function distinct ( $dis )
253: {
254: $this->_distinct = $dis;
255: return $this;
256: }
257:
258: /**
259: * Execute the query.
260: * @param string $sql SQL string to execute (only if _type is 'raw').
261: * @return Result
262: */
263: public function exec ( $sql=null )
264: {
265: $type = strtolower( $this->_type );
266:
267: if ( $type === 'select' ) {
268: return $this->_select();
269: }
270: else if ( $type === 'insert' ) {
271: return $this->_insert();
272: }
273: else if ( $type === 'update' ) {
274: return $this->_update();
275: }
276: else if ( $type === 'delete' ) {
277: return $this->_delete();
278: }
279: else if ( $type === 'raw' ) {
280: return $this->_raw( $sql );
281: }
282:
283: throw new \Exception("Unknown database command or not supported: ".$type, 1);
284: }
285:
286:
287: /**
288: * Get fields.
289: * @param string|string[] $get,... Fields to get - can be specified as
290: * individual fields, an array of fields, a string of comma separated
291: * fields or any combination of those.
292: * @return self
293: */
294: public function get ( $get )
295: {
296: if ( $get === null ) {
297: return $this;
298: }
299:
300: $args = func_get_args();
301:
302: for ( $i=0 ; $i<count($args) ; $i++ ) {
303: // If argument is an array then we loop over and add each using a
304: // recursive call
305: if ( is_array( $args[$i] ) ) {
306: for ( $j=0 ; $j<count($args[$i]) ; $j++ ) {
307: $this->get( $args[$i][$j] );
308: }
309: }
310: else {
311: // String argument so split into pieces and add
312: // TODO - This limits the ability to use functions. Need to
313: // parse the string so it will split when not in a function
314: $fields = explode(",", $args[$i]);
315:
316: for ( $j=0 ; $j<count($fields) ; $j++ ) {
317: $this->_field[] = trim( $fields[$j] );
318: }
319: }
320: }
321:
322: return $this;
323: }
324:
325:
326: /**
327: * Perform a JOIN operation
328: * @param string $table Table name to do the JOIN on
329: * @param string $condition JOIN condition
330: * @param string $type JOIN type
331: * @return self
332: */
333: public function join ( $table, $condition, $type='' )
334: {
335: // Tidy and check we know what the join type is
336: if ($type !== '') {
337: $type = strtoupper(trim($type));
338:
339: if ( ! in_array($type, array('LEFT', 'RIGHT', 'INNER', 'OUTER', 'LEFT OUTER', 'RIGHT OUTER'))) {
340: $type = '';
341: }
342: }
343:
344: // Protect the identifiers
345: if (preg_match('/([\w\.]+)([\W\s]+)(.+)/', $condition, $match))
346: {
347: $match[1] = $this->_protect_identifiers( $match[1] );
348: $match[3] = $this->_protect_identifiers( $match[3] );
349:
350: $condition = $match[1].$match[2].$match[3];
351: }
352:
353: $this->_join[] = $type .' JOIN '. $this->_protect_identifiers($table) .' ON '. $condition .' ';
354:
355: return $this;
356: }
357:
358:
359: /**
360: * Limit the result set to a certain size.
361: * @param int $lim The number of records to limit the result to.
362: * @return self
363: */
364: public function limit ( $lim )
365: {
366: $this->_limit = $lim;
367:
368: return $this;
369: }
370:
371:
372: /**
373: * Set table(s) to perform the query on.
374: * @param string|string[] $table,... Table(s) to use - can be specified as
375: * individual names, an array of names, a string of comma separated
376: * names or any combination of those.
377: * @return self
378: */
379: public function table ( $table )
380: {
381: if ( $table === null ) {
382: return $this;
383: }
384:
385: if ( is_array($table) ) {
386: // Array so loop internally
387: for ( $i=0 ; $i<count($table) ; $i++ ) {
388: $this->table( $table[$i] );
389: }
390: }
391: else {
392: // String based, explode for multiple tables
393: $tables = explode(",", $table);
394:
395: for ( $i=0 ; $i<count($tables) ; $i++ ) {
396: $this->_table[] = $this->_protect_identifiers( trim($tables[$i]) );
397: }
398: }
399:
400: return $this;
401: }
402:
403:
404: /**
405: * Offset the return set by a given number of records (useful for paging).
406: * @param int $off The number of records to offset the result by.
407: * @return self
408: */
409: public function offset ( $off )
410: {
411: $this->_offset = $off;
412:
413: return $this;
414: }
415:
416:
417: /**
418: * Order by
419: * @param string|string[] $order Columns and direction to order by - can
420: * be specified as individual names, an array of names, a string of comma
421: * separated names or any combination of those.
422: * @return self
423: */
424: public function order ( $order )
425: {
426: if ( $order === null ) {
427: return $this;
428: }
429:
430: if ( !is_array($order) ) {
431: $order = explode(",", $order);
432: }
433:
434: for ( $i=0 ; $i<count($order) ; $i++ ) {
435: // Simplify the white-space
436: $order[$i] = trim( preg_replace('/[\t ]+/', ' ', $order[$i]) );
437:
438: // Find the identifier so we don't escape that
439: if ( strpos($order[$i], ' ') !== false ) {
440: $direction = strstr($order[$i], ' ');
441: $identifier = substr($order[$i], 0, - strlen($direction));
442: }
443: else {
444: $direction = '';
445: $identifier = $order[$i];
446: }
447:
448: $this->_order[] = $this->_protect_identifiers( $identifier ).' '.$direction;
449: }
450:
451: return $this;
452: }
453:
454:
455: /**
456: * Set fields to a given value.
457: *
458: * Can be used in two different ways, as set( field, value ) or as an array of
459: * fields to set: set( array( 'fieldName' => 'value', ...) );
460: * @param string|string[] $set Can be given as a single string, when then $val
461: * must be set, or as an array of key/value pairs to be set.
462: * @param string $val When $set is given as a simple string, $set is the field
463: * name and this is the field's value.
464: * @param boolean $bind Should the value be bound or not
465: * @return self
466: */
467: public function set ( $set, $val=null, $bind=true )
468: {
469: if ( $set === null ) {
470: return $this;
471: }
472:
473: if ( !is_array($set) ) {
474: $set = array( $set => $val );
475: }
476:
477: foreach ($set as $key => $value) {
478: $this->_field[] = $key;
479:
480: if ( $bind ) {
481: $this->bind( ':'.$key, $value );
482: }
483: else {
484: $this->_noBind[$key] = $value;
485: }
486: }
487:
488: return $this;
489: }
490:
491:
492: /**
493: * Where query - multiple conditions are bound as ANDs.
494: *
495: * Can be used in two different ways, as where( field, value ) or as an array of
496: * conditions to use: where( array('fieldName', ...), array('value', ...) );
497: * @param string|string[]|callable $key Single field name, or an array of field names.
498: * If given as a function (i.e. a closure), the function is called, passing the
499: * query itself in as the only parameter, so the function can add extra conditions
500: * with parentheses around the additional parameters.
501: * @param string|string[] $value Single field value, or an array of
502: * values. Can be null to search for `IS NULL` or `IS NOT NULL` (depending
503: * on the value of `$op` which should be `=` or `!=`.
504: * @param string $op Condition operator: <, >, = etc
505: * @param boolean $bind Escape the value (true, default) or not (false).
506: * @return self
507: *
508: * @example
509: * The following will produce
510: * `'WHERE name='allan' AND ( location='Scotland' OR location='Canada' )`:
511: *
512: * <code>
513: * $query
514: * ->where( 'name', 'allan' )
515: * ->where( function ($q) {
516: * $q->where( 'location', 'Scotland' );
517: * $q->where( 'location', 'Canada' );
518: * } );
519: * </code>
520: */
521: public function where ( $key, $value=null, $op="=", $bind=true )
522: {
523: if ( $key === null ) {
524: return $this;
525: }
526: else if ( is_callable($key) && is_object($key) ) { // is a closure
527: $this->_where_group( true, 'AND' );
528: $key( $this );
529: $this->_where_group( false, 'OR' );
530: }
531: else {
532: if ( !is_array($key) && is_array($value) ) {
533: for ( $i=0 ; $i<count($value) ; $i++ ) {
534: $this->where( $key, $value[$i], $op, $bind );
535: }
536: return $this;
537: }
538:
539: $this->_where( $key, $value, 'AND ', $op, $bind );
540: }
541:
542: return $this;
543: }
544:
545:
546: /**
547: * Add addition where conditions to the query with an AND operator. An alias
548: * of `where` for naming consistency.
549: *
550: * Can be used in two different ways, as where( field, value ) or as an array of
551: * conditions to use: where( array('fieldName', ...), array('value', ...) );
552: * @param string|string[]|callable $key Single field name, or an array of field names.
553: * If given as a function (i.e. a closure), the function is called, passing the
554: * query itself in as the only parameter, so the function can add extra conditions
555: * with parentheses around the additional parameters.
556: * @param string|string[] $value Single field value, or an array of
557: * values. Can be null to search for `IS NULL` or `IS NOT NULL` (depending
558: * on the value of `$op` which should be `=` or `!=`.
559: * @param string $op Condition operator: <, >, = etc
560: * @param boolean $bind Escape the value (true, default) or not (false).
561: * @return self
562: */
563: public function and_where ( $key, $value=null, $op="=", $bind=true )
564: {
565: return $this->where( $key, $value, $op, $bind );
566: }
567:
568:
569: /**
570: * Add addition where conditions to the query with an OR operator.
571: *
572: * Can be used in two different ways, as where( field, value ) or as an array of
573: * conditions to use: where( array('fieldName', ...), array('value', ...) );
574: * @param string|string[]|callable $key Single field name, or an array of field names.
575: * If given as a function (i.e. a closure), the function is called, passing the
576: * query itself in as the only parameter, so the function can add extra conditions
577: * with parentheses around the additional parameters.
578: * @param string|string[] $value Single field value, or an array of
579: * values. Can be null to search for `IS NULL` or `IS NOT NULL` (depending
580: * on the value of `$op` which should be `=` or `!=`.
581: * @param string $op Condition operator: <, >, = etc
582: * @param boolean $bind Escape the value (true, default) or not (false).
583: * @return self
584: */
585: public function or_where ( $key, $value=null, $op="=", $bind=true )
586: {
587: if ( $key === null ) {
588: return $this;
589: }
590: else if ( is_callable($key) ) {
591: $this->_where_group( true, 'OR' );
592: $key( $this );
593: $this->_where_group( false, 'OR' );
594: }
595: else {
596: if ( !is_array($key) && is_array($value) ) {
597: for ( $i=0 ; $i<count($value) ; $i++ ) {
598: $this->or_where( $key, $value[$i], $op, $bind );
599: }
600: return $this;
601: }
602:
603: $this->_where( $key, $value, 'OR ', $op, $bind );
604: }
605:
606: return $this;
607: }
608:
609:
610: /**
611: * Provide grouping for WHERE conditions. Calling this function with `true`
612: * as the first parameter will open a bracket, and `false` will then close
613: * it.
614: *
615: * @param boolean $inOut `true` to open brackets, `false` to close
616: * @param string $op Conditional operator to use to join to the
617: * preceding condition. Default `AND`.
618: * @return self
619: */
620: public function where_group ( $inOut, $op='AND' )
621: {
622: $this->_where_group( $inOut, $op );
623:
624: return $this;
625: }
626:
627:
628:
629: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
630: * Protected methods
631: */
632:
633: /**
634: * Create a comma separated field list
635: * @param bool $addAlias Flag to add an alias
636: * @return string
637: * @internal
638: */
639: protected function _build_field( $addAlias=false )
640: {
641: $a = array();
642:
643: for ( $i=0 ; $i<count($this->_field) ; $i++ ) {
644: $field = $this->_field[$i];
645:
646: // Keep the name when referring to a table
647: if ( $addAlias && strpos($field, ' as ') !== false && $field !== '*' ) {
648: $split = explode(' as ', $field);
649: $a[] = $this->_protect_identifiers( $split[0] ).' as '.
650: $this->_field_quote. $split[1] .$this->_field_quote;
651: }
652: else if ( $addAlias && strpos($field, ' as ') === false && $field !== '*' ) {
653: $a[] = $this->_protect_identifiers( $field ).' as '.
654: $this->_field_quote. $field .$this->_field_quote;
655: }
656: else {
657: $a[] = $this->_protect_identifiers( $field );
658: }
659: }
660:
661: return ' '.implode(', ', $a).' ';
662: }
663:
664: /**
665: * Create a JOIN statement list
666: * @return string
667: * @internal
668: */
669: protected function _build_join()
670: {
671: return implode(' ', $this->_join);
672: }
673:
674: /**
675: * Create the LIMIT / OFFSET string
676: *
677: * MySQL and Postgres stylee - anything else can have the driver override
678: * @return string
679: * @internal
680: */
681: protected function _build_limit()
682: {
683: $out = '';
684:
685: if ( $this->_limit ) {
686: $out .= ' LIMIT '.$this->_limit;
687: }
688:
689: if ( $this->_offset ) {
690: $out .= ' OFFSET '.$this->_offset;
691: }
692:
693: return $out;
694: }
695:
696: /**
697: * Create the ORDER BY string
698: * @return string
699: * @internal
700: */
701: protected function _build_order()
702: {
703: if ( count( $this->_order ) > 0 ) {
704: return ' ORDER BY '.implode(', ', $this->_order).' ';
705: }
706: return '';
707: }
708:
709: /**
710: * Create a set list
711: * @return string
712: * @internal
713: */
714: protected function _build_set()
715: {
716: $a = array();
717:
718: for ( $i=0 ; $i<count($this->_field) ; $i++ ) {
719: $field = $this->_field[$i];
720:
721: if ( isset( $this->_noBind[ $field ] ) ) {
722: $a[] = $this->_protect_identifiers( $field ) .' = '. $this->_noBind[ $field ];
723: }
724: else {
725: $a[] = $this->_protect_identifiers( $field ) .' = :'. $this->_safe_bind( $field );
726: }
727: }
728:
729: return ' '.implode(', ', $a).' ';
730: }
731:
732: /**
733: * Create the TABLE list
734: * @return string
735: * @internal
736: */
737: protected function _build_table()
738: {
739: return ' '.implode(', ', $this->_table).' ';
740: }
741:
742: /**
743: * Create a bind field value list
744: * @return string
745: * @internal
746: */
747: protected function _build_value()
748: {
749: $a = array();
750:
751: for ( $i=0, $ien=count($this->_field) ; $i<$ien ; $i++ ) {
752: $a[] = ' :'.$this->_safe_bind( $this->_field[$i] );
753: }
754:
755: return ' '.implode(', ', $a).' ';
756: }
757:
758: /**
759: * Create the WHERE statement
760: * @return string
761: * @internal
762: */
763: protected function _build_where()
764: {
765: if ( count($this->_where) === 0 ) {
766: return "";
767: }
768:
769: $condition = "WHERE ";
770:
771: for ( $i=0 ; $i<count($this->_where) ; $i++ ) {
772: if ( $i === 0 ) {
773: // Nothing (simplifies the logic!)
774: }
775: else if ( $this->_where[$i]['group'] === ')' ) {
776: // Nothing
777: }
778: else if ( $this->_where[$i-1]['group'] === '(' ) {
779: // Nothing
780: }
781: else {
782: $condition .= $this->_where[$i]['operator'];
783: }
784:
785: if ( $this->_where[$i]['group'] !== null ) {
786: $condition .= $this->_where[$i]['group'];
787: }
788: else {
789: $condition .= $this->_where[$i]['query'] .' ';
790: }
791: }
792:
793: return $condition;
794: }
795:
796: /**
797: * Create a DELETE statement
798: * @return Result
799: * @internal
800: */
801: protected function _delete()
802: {
803: $this->_prepare(
804: 'DELETE FROM '
805: .$this->_build_table()
806: .$this->_build_where()
807: );
808:
809: return $this->_exec();
810: }
811:
812: /**
813: * Execute the query. Provided by the driver
814: * @return Result
815: * @internal
816: */
817: protected function _exec()
818: {}
819:
820: /**
821: * Create an INSERT statement
822: * @return Result
823: * @internal
824: */
825: protected function _insert()
826: {
827: $this->_prepare(
828: 'INSERT INTO '
829: .$this->_build_table().' ('
830: .$this->_build_field()
831: .') '
832: .'VALUES ('
833: .$this->_build_value()
834: .')'
835: );
836:
837: return $this->_exec();
838: }
839:
840: /**
841: * Prepare the SQL query by populating the bound variables.
842: * Provided by the driver
843: * @return void
844: * @internal
845: */
846: protected function _prepare( $sql )
847: {}
848:
849: /**
850: * Protect field names
851: * @param string $identifier String to be protected
852: * @return string
853: * @internal
854: */
855: protected function _protect_identifiers( $identifier )
856: {
857: $idl = $this->_identifier_limiter;
858:
859: // No escaping character
860: if ( $idl === '' ) {
861: return $identifier;
862: }
863:
864: // Dealing with a function or other expression? Just return immediately
865: if (strpos($identifier, '(') !== FALSE || strpos($identifier, '*') !== FALSE || strpos($identifier, ' ') !== FALSE)
866: {
867: return $identifier;
868: }
869:
870: // Going to be operating on the spaces in strings, to simplify the white-space
871: $identifier = preg_replace('/[\t ]+/', ' ', $identifier);
872:
873: // Find if our identifier has an alias, so we don't escape that
874: if ( strpos($identifier, ' as ') !== false ) {
875: $alias = strstr($identifier, ' as ');
876: $identifier = substr($identifier, 0, - strlen($alias));
877: }
878: else {
879: $alias = '';
880: }
881:
882: $a = explode('.', $identifier);
883: return $idl . implode($idl.'.'.$idl, $a) . $idl . $alias;
884: }
885:
886: /**
887: * Passed in SQL statement
888: * @return Result
889: * @internal
890: */
891: protected function _raw( $sql )
892: {
893: $this->_prepare( $sql );
894:
895: return $this->_exec();
896: }
897:
898: /**
899: * The characters that can be used for the PDO bindValue name are quite
900: * limited (`[a-zA-Z0-9_]+`). We need to abstract this out to allow slightly
901: * more complex expressions including dots for easy aliasing
902: * @param string $name Field name
903: * @return string
904: * @internal
905: */
906: protected function _safe_bind ( $name )
907: {
908: $name = str_replace('.', '_1_', $name);
909: $name = str_replace('-', '_2_', $name);
910:
911: return $name;
912: }
913:
914: /**
915: * Create a SELECT statement
916: * @return Result
917: * @internal
918: */
919: protected function _select()
920: {
921: $this->_prepare(
922: 'SELECT '.($this->_distinct ? 'DISTINCT ' : '')
923: .$this->_build_field( true )
924: .'FROM '.$this->_build_table()
925: .$this->_build_join()
926: .$this->_build_where()
927: .$this->_build_order()
928: .$this->_build_limit()
929: );
930:
931: return $this->_exec();
932: }
933:
934: /**
935: * Create an UPDATE statement
936: * @return Result
937: * @internal
938: */
939: protected function _update()
940: {
941: $this->_prepare(
942: 'UPDATE '
943: .$this->_build_table()
944: .'SET '.$this->_build_set()
945: .$this->_build_where()
946: );
947:
948: return $this->_exec();
949: }
950:
951: /**
952: * Add an individual where condition to the query.
953: * @internal
954: * @param $where
955: * @param null $value
956: * @param string $type
957: * @param string $op
958: * @param bool $bind
959: */
960: protected function _where ( $where, $value=null, $type='AND ', $op="=", $bind=true )
961: {
962: $idl = $this->_identifier_limiter;
963:
964: if ( $where === null ) {
965: return;
966: }
967: else if ( !is_array($where) ) {
968: $where = array( $where => $value );
969: }
970:
971: foreach ($where as $key => $value) {
972: $i = count( $this->_where );
973:
974: if ( $value === null ) {
975: // Null query
976: $this->_where[] = array(
977: 'operator' => $type,
978: 'group' => null,
979: 'field' => $this->_protect_identifiers($key),
980: 'query' => $this->_protect_identifiers($key) .( $op === '=' ?
981: ' IS NULL' :
982: ' IS NOT NULL')
983: );
984: }
985: else if ( $bind ) {
986: // Binding condition (i.e. escape data)
987: $this->_where[] = array(
988: 'operator' => $type,
989: 'group' => null,
990: 'field' => $this->_protect_identifiers($key),
991: 'query' => $this->_protect_identifiers($key) .' '.$op.' '.$this->_safe_bind(':where_'.$i)
992: );
993: $this->bind( ':where_'.$i, $value );
994: }
995: else {
996: // Non-binding condition
997: $this->_where[] = array(
998: 'operator' => $type,
999: 'group' => null,
1000: 'field' => null,
1001: 'query' => $this->_protect_identifiers($key) .' '. $op .' '. $this->_protect_identifiers($value)
1002: );
1003: }
1004: }
1005: }
1006:
1007: /**
1008: * Add parentheses to a where condition
1009: * @return string
1010: * @internal
1011: */
1012: protected function _where_group ( $inOut, $op )
1013: {
1014: $this->_where[] = array(
1015: "group" => $inOut ? '(' : ')',
1016: "operator" => $op
1017: );
1018: }
1019: };
1020:
1021:
1022: