2 /*------------------------------------------------------------
3 * CarbonPHP framework (C) Tom Bell
4 * http://tombell.org.uk
5 *------------------------------------------------------------*/
7 if (!defined('CARBON_PATH'))
9 exit('Direct script access is not allowed.');
12 class Carbon_Database_active_record
extends Carbon_Database_driver
14 protected $ar_select = array();
15 protected $ar_distinct = false;
16 protected $ar_from = array();
17 protected $ar_join = array();
18 protected $ar_where = array();
19 protected $ar_like = array();
20 protected $ar_groupby = array();
21 protected $ar_having = array();
22 protected $ar_limit = false;
23 protected $ar_offset = false;
24 protected $ar_order = false;
25 protected $ar_orderby = array();
26 protected $ar_set = array();
27 protected $ar_wherein = array();
28 protected $ar_aliased_tables = array();
29 protected $ar_store_array = array();
31 protected $ar_caching = false;
32 protected $ar_cache_select = array();
33 protected $ar_cache_from = array();
34 protected $ar_cache_join = array();
35 protected $ar_cache_where = array();
36 protected $ar_cache_like = array();
37 protected $ar_cache_groupby = array();
38 protected $ar_cache_having = array();
39 protected $ar_cache_limit = false;
40 protected $ar_cache_offset = false;
41 protected $ar_cache_order = false;
42 protected $ar_cache_orderby = array();
43 protected $ar_cache_set = array();
45 public function dbprefix($table = '')
49 $this->display_error('database_table_name_required');
52 return $this->dbprefix
. $table;
55 public function select($select = '*', $protected_identifiers = true)
57 if (is_string($select))
59 $select = explode(', ', $select);
62 foreach ($select as $value)
64 $value = trim($value);
66 if ($value != '*' && $protected_identifiers !== false)
68 if (strpos($value, '.') !== false)
70 $value = $this->dbprefix
. $value;
74 $value = $this->database_protect_identifiers($value);
80 $this->ar_select
[] = $value;
82 if ($this->ar_caching
=== true)
84 $this->ar_cache_select
[] = $value;
92 public function select_max($select = '', $alias = '')
94 if (!is_string($select) ||
$select = '')
96 $this->display_error('database_invalid_query');
99 $alias = ($alias != '') ?
$alias : $select;
101 $sql = 'MAX(' . $this->database_protect_identifiers(trim($select)) . ') AS ' . $this->database_protect_identifiers(trim($alias));
103 $this->ar_select
[] = $sql;
105 if ($this->ar_caching
=== true)
107 $this->ar_cache_select
[] = $sql;
113 public function select_min($select = '', $alias = '')
115 if (!is_string($select) ||
$select = '')
117 $this->display_error('database_invalid_query');
120 $alias = ($alias != '') ?
$alias : $select;
122 $sql = 'MIN(' . $this->database_protect_identifiers(trim($select)) . ') AS ' . $this->database_protect_identifiers(trim($alias));
124 $this->ar_select
[] = $sql;
126 if ($this->ar_caching
=== true)
128 $this->ar_cache_select
[] = $sql;
134 public function select_avg($select = '', $alias = '')
136 if (!is_string($select) ||
$select = '')
138 $this->display_error('database_invalid_query');
141 $alias = ($alias != '') ?
$alias : $select;
143 $sql = 'AVG(' . $this->database_protect_identifiers(trim($select)) . ') AS ' . $this->database_protect_identifiers(trim($alias));
145 $this->ar_select
[] = $sql;
147 if ($this->ar_caching
=== true)
149 $this->ar_cache_select
[] = $sql;
155 public function select_sum($select = '', $alias = '')
157 if (!is_string($select) ||
$select = '')
159 $this->display_error('database_invalid_query');
162 $alias = ($alias != '') ?
$alias : $select;
164 $sql = 'SUM(' . $this->database_protect_identifiers(trim($select)) . ') AS ' . $this->database_protect_identifiers(trim($alias));
166 $this->ar_select
[] = $sql;
168 if ($this->ar_caching
=== true)
170 $this->ar_cache_select
[] = $sql;
176 public function distinct($value = true)
178 $this->ar_distinct
= (is_bool($value)) ?
$value : true;
182 public function from($from)
184 foreach ((array) $from as $value)
186 $this->ar_from
[] = $this->database_protect_identifiers($this->database_track_aliases($value));
188 if ($this->ar_caching
=== true)
190 $this->ar_caching_from
[] = $this->database_protect_identifiers($value);
197 public function join($table, $condition, $type = '')
201 $type = strtoupper(trim($type));
203 if (!in_array($type, array('LEFT', 'RIGHT', 'OUTER', 'INNER', 'LEFT OUTER', 'RIGHT OUTER'), true))
215 $this->database_track_aliases($table);
216 $condition = preg_replace('|(' . $this->dbprefix
. ')([\w\.]+)([\W\s]+)|', "$2$3", $condition);
217 $condition = preg_replace('|([\w\.]+)([\W\s]+)(.+)|', $this->dbprefix
. "$1$2" . $this->dbprefix
. "$3", $condition);
220 $join = $type . 'JOIN' . $this->database_protect_identifiers($this->dbprefix
. $table, true) . ' ON ' . $condition;
221 $this->ar_join
[] = $join;
223 if ($this->ar_caching
=== true)
225 $this->ar_cache_join
[] = $join;
231 public function where($key, $value = null, $escape = true)
233 return $this->database_where($key, $value, 'AND ', $escape);
236 public function or_where($key, $value = null, $escape = true)
238 return $this->database_where($key, $value, 'OR ', $escape);
241 private function database_where($key, $value = null, $type = 'AND ', $escape = true)
245 $key = array($key => $value);
248 foreach ($key as $k => $v)
250 $prefix = (count($this->ar_where
) == 0) ?
'' : $type;
252 if (!$this->database_has_operator($k) && is_null($key[$k]))
259 if ($escape === true)
261 if ($this->database_has_operator($k))
263 $k = preg_replace("/([A-Za-z_0-9]+)/", $this->database_protect_identifiers('$1'), $k);
267 $k = $this->database_protect_identifiers($k);
271 if (!$this->database_has_operator($k))
276 $v = ' ' . $this->escape($v);
280 $k = $this->database_protect_identifiers($k, true);
283 $this->ar_where
[] = $prefix . $k . $v;
285 if ($this->ar_caching
=== true)
287 $this->ar_cache_where
[] = $prefix . $k . $v;
294 public function where_in($key = null, $values = null)
296 return $this->database_where_in($key, $values);
299 public function where_in_or($key = null, $values = null)
301 return $this->database_where_in($key, $values, false, 'OR ');
304 public function where_not_in($key = null, $values = null)
306 return $this->database_where_in($key, $values, true);
309 public function or_where_not_in($key = null, $values = null)
311 return $this->database_where_in($key, $values, false, 'OR ');
314 public function database_where_in($key = null, $values = null, $not = false, $type = 'AND ')
316 if ($key === null ||
!is_array($values))
321 $not = ($not) ?
' NOT ' : '';
323 foreach ($values as $value)
325 $this->ar_wherein
[] = $this->escape($value);
328 $prefix = (count($this->ar_where
) == 0) ?
'' : $type;
330 $where_in = $prefix . $this->protect_identifiers($key) . $not . ' IN (' . implode(', ', $this->ar_wherein
) . ') ';
332 $this->ar_where
[] = $where_in;
334 if ($this->ar_caching
=== true)
336 $this->ar_cache_where
[] = $where_in;
339 $this->ar_wherein
= array();
343 public function like($field, $match = '', $side = 'both')
345 return $this->database_like($field, $match, 'AND ', $side);
348 public function not_like($field, $match = '', $side = 'both')
350 return $this->database_like($field, $match, 'AND ', $side, ' NOT');
353 public function or_like($field, $match = '', $side = 'both')
355 return $this->database_like($field, $match, 'ON ', $side);
358 public function or_not_like($field, $match = '', $side = 'both')
360 return $this->database_like($field, $match, 'OR ', $side, 'NOT ');
363 private function database_like($field, $match = '', $type = 'AND ', $side = 'both', $not = '')
365 if (!is_array($field))
367 $field = array($field => $match);
370 foreach ($field as $k => $v)
372 $k = $this->database_protect_identifiers($k);
374 $prefix = (count($this->ar_like
) == 0) ?
'' : $type;
376 $v = $this->escape_string($v);
378 if ($side == 'before')
380 $like_statement = $prefix . " $k $not LIKE '%{$v}'";
382 else if ($side == 'after')
384 $like_statement = $prefix . " $k $not LIKE '{$v}%'";
388 $like_statement = $prefix . " $k $not LIKE '%{$v}%'";
391 $this->ar_like
[] = $like_statement;
393 if ($this->ar_caching
=== true)
395 $this->ar_cache_like
[] = $like_statement;
402 public function group_by($by)
406 $by = explode(',', $by);
409 foreach ($by as $val)
415 $this->ar_groupby
[] = $this->database_protect_identifiers($val);
417 if ($this->ar_caching
=== true)
419 $this->ar_cache_groupby
[] = $this->database_protect_identifiers($val);
427 public function having($key, $value = '')
429 return $this->database_having($key, $value, 'AND ');
432 public function or_having($key, $value = '')
434 return $this->database_having($key, $value, 'ON ');
437 private function database_having($key, $value, $type = 'AND ')
441 $key = array($key => $value);
444 foreach ($key as $k => $v)
446 $prefix = (count($this->ar_having
) == 0) ?
'' : $type;
447 $k = $this->database_protect_identifiers($k);
451 $v = ' ' . $this->escape($v);
454 $this->ar_having
[] = $prefix . $k . $v;
456 if ($this->ar_caching
=== true)
458 $this->ar_cache_having
[] = $prefix . $k . $v;
465 public function order_by($orderby, $direction = '')
467 if (strtolower($direction) == 'random')
470 $direction = $this->random_keyword
;
472 else if (trim($direction) != '')
474 $direction = (in_array(strtoupper(trim($direction)), array('ASC', 'DESC', 'RAND()'), true)) ?
' ' . $direction : ' ASC';
477 $orderby_statement = $this->database_protect_identifiers($orderby, true) . $direction;
478 $this->ar_orderby
[] = $orderby_statement;
480 if ($this->ar_caching
=== true)
482 $this->ar_cache_orderby
[] = $orderby_statement;
488 public function limit($value, $offset = '')
490 $this->ar_limit
= $value;
492 if ($this->ar_caching
=== true)
494 $this->ar_cache_limit
[] = $value;
499 $this->ar_offset
= $offset;
501 if ($this->ar_caching
=== true)
503 $this->ar_cache_offset
[] = $offset;
510 public function offset($value)
512 $this->ar_offset
= $value;
514 if ($this->ar_caching
=== true)
516 $this->ar_cache_offset
[] = $value;
522 public function set($key, $value = '', $escape = true)
524 $key = $this->database_object_to_array($key);
528 $key = array($key => $value);
531 foreach ($key as $k => $v)
533 if ($escape === false)
535 $this->ar_set
[$this->database_protect_identifiers($k)] = $v;
537 if ($this->ar_caching
=== true)
539 $this->ar_cache_set
[$this->database_protect_identifiers($k)] = $v;
544 $this->ar_set
[$this->database_protect_identifiers($k)] = $this->escape($v);
546 if ($this->ar_caching
=== true)
548 $this->ar_cache_set
[$this->database_protect_identifiers($k)] = $this->escape($v);
556 public function get($table = '', $limit = null, $offset = null)
560 $this->database_track_aliases($table);
564 if (!is_null($limit))
566 $this->limit($limit, $offset);
569 $sql = $this->database_compile_select();
571 $result = $this->query($sql);
572 $this->database_reset_select();
577 public function count_all_results($table = '')
581 $this->database_track_aliases($table);
585 $sql = $this->database_compile_select($this->count_string
. $this->database_protect_identifiers('numrows'));
587 $query = $this->query($sql);
588 $this->database_reset_select();
590 if ($query->num_rows() == 0)
595 $row = $query->row();
596 return $row->numrows
;
599 public function get_where($table = '', $where = null, $limit = null, $offset = null)
603 $this->database_track_aliases($table);
607 if (!is_null($where))
609 $this->where($where);
612 if (!is_null($limit))
614 $this->limit($limit, $offset);
617 $sql = $this->database_compile_select();
619 $result = $this->query($sql);
620 $this->database_reset_select();
625 public function insert($table = '', $set = null)
632 if (count($this->ar_set
) == 0)
636 return $this->display_error('database_must_use_set');
644 if (!isset($this->ar_from
[0]))
648 return $this->display_error('database_must_set_table');
654 $table = $this->ar_from
[0];
657 $sql = $this->database_insert($this->database_protect_identifiers($this->dbprefix
. $table), array_keys($this->ar_set
), array_values($this->ar_set
));
659 $this->database_reset_write();
660 return $this->query($sql);
663 public function update($table = '', $set = null, $where = null, $limit = null)
670 if (count($this->ar_set
) == 0)
674 return $this->display_error('database_must_use_set');
682 if (!isset($this->ar_from
[0]))
686 return $this->display_error('database_must_set_table');
692 $table = $this->ar_from
[0];
697 $this->where($where);
702 $this->limit($limit);
706 $sql = $this->database_update($this->database_protect_identifiers($this->dbprefix
. $table), $this->ar_set
, $this->ar_where
, $this->ar_orderby
, $this->ar_limit
);
708 $this->database_reset_write();
709 return $this->query($sql);
712 public function empty_table($table = '')
716 if (!isset($this->ar_from
[0]))
720 return $this->display_error('database_must_set_table');
726 $table = $this->ar_from
[0];
730 $table = $this->database_protect_identifiers($this->dbprefix
. $table);
733 $sql = $this->database_delete($table);
734 $this->database_reset_write();
736 return $this->query($sql);
739 public function truncate($table = '')
743 if (!isset($this->ar_from
[0]))
747 return $this->display_error('database_must_set_table');
753 $table = $this->ar_from
[0];
757 $table = $this->database_protect_identifiers($this->dbprefix
. $table);
760 $sql = $this->database_truncate($table);
761 $this->database_reset_write();
763 return $this->query($sql);
766 public function delete($table = '', $where = '', $limit = null, $reset_data = true)
770 if (!isset($this->ar_from
[0]))
774 return $this->display_error('database_must_set_table');
780 $table = $this->ar_from
[0];
782 else if (is_array($table))
784 foreach ($table as $single_table)
786 $this->delete($single_table, $where, $limit, false);
789 $this->database_reset_write();
794 $table = $this->database_protect_identifiers($this->dbprefix
. $table);
799 $this->where($where);
804 $this->limit($limit);
807 if (count($this->ar_where
) == 0 && count($this->ar_like
) == 0)
811 return $this->display_error('database_del_must_use_where');
817 $sql = $this->database_delete($this->dbprefix
. $table, $this->ar_where
, $this->ar_like
, $this->ar_limit
);
818 $this->database_reset_write();
822 $this->database_reset_write();
825 return $this->query($sql);
828 public function database_has_operator($string)
830 $string = trim($string);
832 if (!preg_match("/(\s|<|>|!|=|is null|is not null)/i", $string))
840 private function database_track_aliases($table)
842 if (strpos($table, ' ') !== false)
844 $table = preg_replace('/ AS /i', ' ', $table);
845 $this->ar_aliased_tables
[] = trim(strrchr($table, ' '));
848 return $this->dbprefix
. $table;
851 private function database_filter_table_aliases($statements)
853 foreach ($statements as $k => $v)
855 foreach ($this->ar_aliased_tables
as $table)
857 $statement = preg_replace('/(\w+\.\w+)/', $this->database_protect_identifiers('$0'), $v);
858 $statement = str_replace(array($this->dbprefix
. $table, '.'), array($table, $this->database_protect_identifiers('.')), $statement);
861 $statements[$k] = $statement;
867 private function database_compile_select($select_override = false)
869 $this->database_merge_cache();
871 $sql = (!$this->ar_distinct
) ?
'SELECT ' : 'SELECT DISTINCT ';
873 $sql .= (count($this->ar_select
) == 0) ?
'*' : implode(', ', $this->ar_select
);
875 if ($select_override !== false)
877 $sql = $select_override;
880 if (count($this->ar_from
) > 0)
883 $sql .= $this->database_from_tables($this->ar_from
);
886 if (count($this->ar_join
) > 0)
890 if (count($this->ar_aliased_table
) > 0 && $this->dbprefix
)
892 $sql .= implode("\n", $this->database_filter_table_aliases($this->ar_join
));
896 $sql .= implode("\n", $this->ar_join
);
900 if (count($this->ar_where
) > 0 ||
count($this->ar_like
) > 0)
905 $sql .= implode("\n", $this->ar_where
);
907 if (count($this->ar_like
) > 0)
909 if (count($this->ar_where
) > 0)
914 $sql .= implode("\n", $this->ar_like
);
917 if (count($this->ar_groupby
) > 0)
919 $sql .= "\nGROUP BY ";
921 if (count($this->ar_aliased_tables
) > 0 && $this->dbprefix
)
923 $sql .= implode(', ', $this->database_filter_table_aliases($this->ar_groupby
));
927 $sql .= implode(', ', $this->ar_groupby
);
931 if (count($this->ar_having
) > 0)
934 $sql .= implode("\n", $this->ar_having
);
937 if (count($this->ar_orderby
) > 0)
939 $sql .= "\nORDER BY ";
940 $sql .= implode(', ', $this->ar_orderby
);
942 if ($this->ar_order
!== false)
944 $sql .= ($this->ar_order
== 'desc') ?
' DESC' : ' ASC';
948 if (is_numeric($this->ar_limit
))
951 $sql = $this->database_limit($sql, $this->ar_limit
, $this->ar_offset
);
957 private function database_object_to_array($object)
959 if (!is_object($object))
966 foreach (get_object_vars($object) as $key => $val)
968 if (!is_object($val) && !is_array($val) && $key != 'c_scaffolding' && $key != 'c_scaff_table')
977 public function start_cache()
979 $this->ar_caching
= true;
982 public function stop_cache()
984 $this->ar_caching
= false;
987 public function flush_cache()
989 $ar_reset_items = array(
990 'ar_cache_select' => array(),
991 'ar_cache_from' => array(),
992 'ar_cache_join' => array(),
993 'ar_cache_where' => array(),
994 'ar_cache_like' => array(),
995 'ar_cache_groupby' => array(),
996 'ar_cache_having' => array(),
997 'ar_cache_orderby' => array(),
998 'ar_cache_set' => array()
1001 $this->database_reset_run($ar_reset_items);
1004 private function database_merge_cache()
1006 $ar_items = array('select', 'from', 'join', 'where', 'like', 'groupby', 'having', 'orderby', 'set');
1008 foreach ($ar_items as $ar_item)
1010 $ar_cache_item = 'ar_cache_' . $ar_item;
1011 $ar_item = 'ar_' . $ar_item;
1012 $this->$ar_item = array_unique(array_merge($this->$ar_item, $this->$ar_cache_item));
1016 private function database_reset_run($ar_reset_items)
1018 foreach ($ar_reset_items as $item => $default_value)
1020 if (!in_array($item, $this->ar_store_array
))
1022 $this->$item = $default_value;
1027 private function database_reset_select()
1029 $ar_reset_items = array(
1030 'ar_select' => array(),
1031 'ar_from' => array(),
1032 'ar_join' => array(),
1033 'ar_where' => array(),
1034 'ar_like' => array(),
1035 'ar_groupby' => array(),
1036 'ar_having' => array(),
1037 'ar_orderby' => array(),
1038 'ar_wherein' => array(),
1039 'ar_aliased_tables' => array(),
1040 'ar_distinct' => false,
1041 'ar_limit' => false,
1042 'ar_offset' => false,
1046 $this->database_reset_run($ar_reset_items);
1049 private function database_reset_write()
1051 $ar_reset_items = array(
1052 'ar_set' => array(),
1053 'ar_from' => array(),
1054 'ar_where' => array(),
1055 'ar_like' => array(),
1056 'ar_orderby' => array(),
1057 'ar_limit' => false,
1061 $this->database_reset_run($ar_reset_items);