summaryrefslogtreecommitdiffstats
path: root/modules/database
diff options
context:
space:
mode:
Diffstat (limited to 'modules/database')
-rw-r--r--modules/database/classes/database/expression.php31
-rw-r--r--modules/database/classes/database/query.php380
-rw-r--r--modules/database/classes/database/result.php74
-rw-r--r--modules/database/classes/db.php127
-rw-r--r--modules/database/classes/driver/mysql/db.php88
-rw-r--r--modules/database/classes/driver/mysql/query.php199
-rw-r--r--modules/database/classes/driver/mysql/result.php47
-rw-r--r--modules/database/classes/driver/pdo/db.php70
-rw-r--r--modules/database/classes/driver/pdo/query.php8
-rw-r--r--modules/database/classes/driver/pdo/result.php47
10 files changed, 1071 insertions, 0 deletions
diff --git a/modules/database/classes/database/expression.php b/modules/database/classes/database/expression.php
new file mode 100644
index 0000000..03cfd99
--- /dev/null
+++ b/modules/database/classes/database/expression.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * This class allows you to wrap fields or values that you don't want to be escaped
+ * inside the query
+ */
+class Expression_Database{
+
+ /**
+ * Part of query that shoud not be escaped
+ * @var mixed
+ * @access public
+ */
+ public $value;
+
+ /**
+ * Marks a part of query as a database specific expression,
+ * e.g. calls to SQL functions like MAX(), SUBSTR() etc.
+ * Example
+ * <code>
+ * $q->fields(DB::expr('COUNT(*)'));
+ * </code>
+ *
+ * @param mixed $value Part of query that shoud not be escaped
+ * @return Expression_Database
+ * @access public
+ */
+ public function __construct($value){
+ $this->value=$value;
+ }
+} \ No newline at end of file
diff --git a/modules/database/classes/database/query.php b/modules/database/classes/database/query.php
new file mode 100644
index 0000000..ec36b9e
--- /dev/null
+++ b/modules/database/classes/database/query.php
@@ -0,0 +1,380 @@
+<?php
+
+/**
+ * Query builder. It allows building queries by using methods to set specific query parameters.
+ * Database drivers extend this class so that they can generate database specific queries.
+ * The idea is to provide a database agnostic interface to query writing.
+ *
+ * @method mixed table(string $table = null) Set table to query.
+ * Without arguments returns current table, returns self otherwise.
+ *
+ * @method mixed data(array $data = null) Set data for insert or update queries.
+ * Without arguments returns current data, returns self otherwise.
+ *
+ * @method mixed limit(int $limit = null) Set number of rows to return.
+ * Without arguments returns current limit, returns self otherwise.
+ *
+ * @method mixed offset(string $offset = null) Set the offset for the first row in result.
+ * Without arguments returns current offset, returns self otherwise.
+ *
+ * @method mixed groupby(string $groupby = null) A column to group rows by for aggregator functions.
+ * Without arguments returns current groupby argument, returns self otherwise.
+ *
+ * @method mixed type(string $type = null) Set query type. Available types: select, update, insert, delete, count.
+ * Without arguments returns current type argument, returns self otherwise.
+ */
+abstract class Query_Database {
+
+ /**
+ * Array of conditions that rows must meet
+ * @var array
+ * @access protected
+ */
+ protected $_conditions = array();
+
+ /**
+ * Table to query
+ * @var unknown
+ * @access protected
+ */
+ protected $_table;
+
+ /**
+ * Fields to return in the query
+ * @var array
+ * @access protected
+ */
+ protected $_fields;
+
+ /**
+ * Data for row insertion or update
+ * @var unknown
+ * @access protected
+ */
+ protected $_data;
+
+ /**
+ * Query type. Available types: select, update, insert, delete, count
+ * @var string
+ * @access protected
+ */
+ protected $_type;
+
+ /**
+ * Parameters for tables to join
+ * @var array
+ * @access protected
+ */
+ protected $_joins = array();
+
+ /**
+ * Number of rows to return
+ * @var int
+ * @access protected
+ */
+ protected $_limit;
+
+ /**
+ * Offset of the first row
+ * @var int
+ * @access protected
+ */
+ protected $_offset;
+
+ /**
+ * Columns and directions to order by
+ * @var array
+ * @access protected
+ */
+ protected $_orderby = array();
+
+ /**
+ * Database connection
+ * @var DB
+ * @access protected
+ */
+ protected $_db;
+
+ /**
+ * Conditions for aggregator functions
+ * @var array
+ * @access protected
+ */
+ protected $_having=array();
+
+ /**
+ * Column to group by for aggregator functions
+ * @var string
+ * @access protected
+ */
+ protected $_groupby;
+
+ /**
+ * Last alias used on the table
+ * @var string
+ * @access protected
+ */
+ protected $alias = null;
+
+ /**
+ * Methods and type of value they allow that are available via __call
+ * @var array
+ * @access protected
+ */
+ protected $methods = array('table' => 'string','data' => 'array','limit' => 'integer','offset' => 'integer','groupby' => 'string','type' => 'string');
+
+ /**
+ * Generates a query in format tht can be executed on current database implementation
+ *
+ * @access public
+ */
+ public abstract function query();
+
+ /**
+ * Creates a new query
+ *
+ * @param DB $db Database connection
+ * @param string $type Query type. Available types: select, update, insert, delete, count
+ * @return void
+ * @access public
+ */
+ public function __construct($db, $type) {
+ $this->_db=$db;
+ $this->_type=$type;
+ }
+
+ /**
+ * Sets fields to be queried from the database
+ *
+ * @param mixed A single field or an array of them
+ * @return mixed If no parameters are passed returns current array of fields,
+ * otherwise returns self.
+ * @access public
+ */
+ public function fields() {
+ $p = func_get_args();
+ if (empty($p)) {
+ return $this->_fields;
+ }elseif (is_array($p[0])) {
+ $this->_fields=$p[0];
+ }else {
+ $this->_fields=array($p[0]);
+ }
+ return $this;
+ }
+
+ /**
+ * Magic methods to create methods for all generic query parts
+ *
+ * @param string $method Name of the method to call
+ * @param array $args Array of parameters
+ * @return mixed If no arguments are passed returns the current value of the property,
+ * otherwise returns self.
+ * @access public
+ * @throws Exception If method doesn't exist
+ * @throws Exception If value is of incorrect type
+ * @see $methods
+ */
+ public function __call($method, $args) {
+
+ if (isset($this->methods[$method])) {
+
+ $property = '_'.$method;
+
+ if (empty($args))
+ return $this->$property;
+ $val = $args[0];
+ if (is_int($val))
+ $val=(int) $val;
+ if (gettype($val) != $this->methods[$method])
+ throw new Exception("Method '{$method}' only accepts values of type: '{$this->methods[$method]}', '{$val}' was passed");
+ $this->$property = $val;
+ return $this;
+ }
+ throw new Exception("Method '{$method}' doesn't exist.");
+ }
+
+ /**
+ * Executes the query
+ *
+ * @return object Executes current query on its database connection
+ * @access public
+ * @see DB
+ */
+ public function execute() {
+ $query = $this->query();
+ $result = $this->_db->execute($query[0], $query[1]);
+ if ($this->_type == 'count')
+ return $result->current()->count;
+ return $result;
+ }
+
+ /**
+ * Adds a joined table to the query.
+ *
+ * @param string $table Table to join
+ * @param array $conds Conditions to join tables on, same behavior as with where() method
+ * @param string $type Type of join. Defaults to 'left'
+ * @return Query_Database Returns self
+ * @access public
+ * @see where()
+ */
+ public function join($table,$conds,$type='left'){
+ $this->_joins[] = array($table, $type, $this->get_condition_part($conds));
+ return $this;
+ }
+
+ /**
+ * Sets conditions for aggregator functions, same behavior as with where() method
+ *
+ * @return Query_Database Returns self
+ * @access public
+ * @see where()
+ */
+ public function having() {
+ $p = func_get_args();
+ $cond = $this->get_condition_part($p);
+ $this->_having = array_merge($this->_having,$cond);
+ return $this;
+ }
+
+ /**
+ * Adds a column to ordering parameters.
+ *
+ * @param string $column Column to order by
+ * @param string $dir Ordering direction.
+ * @return Query_Database Returns self
+ * @throws Exception If ordering direction isn't DESC or ASC
+ * @access public
+ */
+ public function orderby($column, $dir) {
+ $dir=strtoupper($dir);
+ if ($dir != 'DESC' && $dir != 'ASC')
+ throw new Exception("Invalid sorting direction {$dir} specified");
+ $this->_orderby[]=array($column,$dir);
+ return $this;
+ }
+
+ /**
+ * Sets conditions for the query.
+ * Can be called in many ways, examples:
+ * Shorthand equals condition:
+ * <code>
+ * $q->where('name', 'Tinkerbell')
+ * </code>
+ * Conditions with operator:
+ * <code>
+ * $q->where('id', '>', 3)
+ * </code>
+ * OR logic:
+ * <code>
+ * $q->where('or', array('name', 'Tinkerbell'))
+ * </code>
+ * OR logic with operator
+ * <code>
+ * ->where('or', array('id', '>', 3))
+ * </code>
+ * Arrays represent brackets. e.g
+ * <code>
+ * $q->where('name', 'Tinkerbell')
+ * ->where('or', array(
+ * array('id', '>', 7),
+ * array('id', '<', 15)
+ * );
+ * //Will produce "WHERE `name`='Tinkerbell' OR (`id` > 7 AND `id` < 15)"
+ * </code>
+ * Multiple calls to where() append new conditions to previous ones
+ *
+ * @param mixed $column Column name, logic parameter 'OR' or 'AND' or an array of conditions
+ * @param mixed $operator Condition value, operator or an array of parameters
+ * @param mixed $val Condition value
+ *
+ * @return Query_Database Returns self
+ * @access public
+ */
+ public function where() {
+ $p = func_get_args();
+ $cond = $this->get_condition_part($p);
+ $this->_conditions= array_merge($this->_conditions,$cond);
+
+ return $this;
+ }
+
+ /**
+ * Recursively builds condition arrays for methods like where(), having()
+ *
+ * @param array $p Parameters passed to the method
+ * @return array Array in condition format
+ * @access private
+ * @throws Exception If condition format is incorrect
+ */
+ private function get_condition_part($p) {
+ if (is_string($p[0]) && (strtolower($p[0]) == 'or'||strtolower($p[0]) == 'and') && isset($p[1]) && is_array($p[1])) {
+ $cond = $this->get_condition_part($p[1]);
+ $cond['logic'] = strtolower($p[0]);
+ return $cond;
+ }
+
+ if (is_array($p[0])) {
+ if (count($p) == 1)
+ return $this->get_condition_part($p[0]);
+ $conds = array();
+ foreach($p as $q) {
+ $conds[]=$this->get_condition_part($q);
+ }
+ if (count($conds) == 1)
+ return $conds;
+ return array('logic'=>'and','conditions'=>$conds);
+ }
+
+
+ if ((is_string($p[0]) || get_class($p[0]) == 'Expression_Database') && isset($p[1])) {
+ if (strpos($p[0], '.') === false)
+ $p[0]=$this->lastAlias().'.'.$p[0];
+ return array(
+ 'logic' => 'and',
+ 'conditions'=>array(
+ 'field' => $p[0],
+ 'operator' => isset($p[2])?$p[1]:'=',
+ 'value' => isset($p[2])?$p[2]:$p[1]
+ )
+ );
+ }
+
+ throw new Exception('Incorrect conditional statement passed');
+ }
+
+ /**
+ * Gets last generated alias
+ *
+ * @return string Last generated alias. If no alias were created returns table name.
+ * @access public
+ */
+ public function lastAlias() {
+ if ($this->alias === null)
+ return $this->_table;
+ return 'a'.$this->alias;
+ }
+
+ /**
+ * Generates new alias. Useful for programmatically adding aliases to joins.
+ * Alias is just a letter 'a' with an incremeted number.
+ *
+ * @return string New alias
+ * @access public
+ */
+ public function addAlias() {
+ if ($this->alias === null){
+ $this->alias = 0;
+ }else {
+ $this->alias++;
+ }
+ return $this->lastAlias();
+ }
+
+
+
+
+
+
+} \ No newline at end of file
diff --git a/modules/database/classes/database/result.php b/modules/database/classes/database/result.php
new file mode 100644
index 0000000..ba83f25
--- /dev/null
+++ b/modules/database/classes/database/result.php
@@ -0,0 +1,74 @@
+<?php
+
+/**
+ * Allows to access database results in a unified way and
+ * provides iterator support, so it can be used inside loops like 'foreach'
+ */
+abstract class Result_Database implements Iterator {
+
+ /**
+ * Current row number
+ * @var integer
+ * @access protected
+ */
+ protected $_position = 0;
+
+ /**
+ * Database result object
+ * @var mixed
+ * @access protected
+ */
+ protected $_result;
+
+ /**
+ * Current row
+ * @var object
+ * @access protected
+ */
+ protected $_row;
+
+
+ /**
+ * Returns current row
+ *
+ * @return object Current row in result set
+ * @access public
+ */
+ public function current() {
+ return $this->_row;
+ }
+
+ /**
+ * Gets the number of the current row
+ *
+ * @return integer Row number
+ * @access public
+ */
+ public function key() {
+ return $this->_position;
+ }
+
+ /**
+ * Check if current row exists.
+ *
+ * @return bool True if row exists
+ * @access public
+ */
+ public function valid() {
+ return $this->_row!=null;
+ }
+
+ /**
+ * Returns all rows as array
+ *
+ * @return array Array of rows
+ * @access public
+ */
+ public function as_array() {
+ $arr = array();
+ foreach($this as $row)
+ $arr[] = $row;
+ return $arr;
+ }
+
+} \ No newline at end of file
diff --git a/modules/database/classes/db.php b/modules/database/classes/db.php
new file mode 100644
index 0000000..acbec0f
--- /dev/null
+++ b/modules/database/classes/db.php
@@ -0,0 +1,127 @@
+<?php
+
+/**
+ * Daatabase related functions. Creates connections,
+ * executes queries and returns results. It is also the
+ * generic connectionc class used by drivers.
+ */
+abstract class DB {
+
+ /**
+ * An associative array of connections to databases
+ * @var array
+ * @access private
+ * @static
+ */
+ private static $_instances=array();
+
+ /**
+ * Executes a prepared statement query
+ *
+ * @param string $query A prepared statement query
+ * @param array $params Parameters for the query
+ * @return Result_Database
+ * @access public
+ * @see Result_Database
+ */
+ public abstract function execute($query, $params = array());
+
+ /**
+ * Builds a new Query to the database
+ *
+ * @param string $type Query type. Available types: select, update, insert, delete, count
+ * @return Result_Database
+ * @access public
+ * @see Query_Database
+ */
+ public abstract function build_query($type);
+
+ /**
+ * Gets the id of the last inserted row.
+ *
+ * @return mixed The id of the last inserted row
+ * @access public
+ */
+ public abstract function get_insert_id();
+
+ /**
+ * Executes a named query where parameters are passed as an associative array
+ * Example:
+ * Query: SELECT * FROM fairies where name = :name
+ * Params: array('name'=>'Tinkerbell');
+ *
+ * @param string $query A named query
+ * @param array $params Associative array of parameters
+ * @return Result_Database Current drivers implementation of Result_Database
+ * @access public
+ */
+ public function namedQuery($query, $params=array()) {
+ $bind = array();
+ preg_match_all('#:(\w+)#is', $query, $matches,PREG_SET_ORDER);
+ foreach($matches as $match)
+ if(isset($params[$match[1]])){
+ $query = preg_replace("#{$match[0]}#", '?', $query, 1);
+ $bind[] = $params[$match[1]];
+ }
+ return $this->execute($query,$bind);
+ }
+
+ /**
+ * Returns an Expression_Database representation of the value.
+ * Values wrapped inside Expression_Database are not escaped in queries
+ *
+ * @param mixed $value Value to be wrapped
+ * @return Expression_Database Raw value that will not be escaped during query building
+ * @access public
+ * @static
+ */
+ public static function expr($value){
+ return new Expression_Database($value);
+ }
+
+ /**
+ * Builds a query for specified connection.
+ *
+ * @param string $type Query type. Available types: select,update,insert,delete,count
+ * @param string $config Configuration name of the connection.
+ * Defaults to 'default'.
+ * @return Query_Database Driver implementation of the Query_Database class.
+ * @access public
+ * @static
+ */
+ public static function query($type,$config = 'default') {
+ return DB::instance($config)->build_query($type);
+ }
+
+ /**
+ * Gets the id of the last inserted row
+ *
+ * @param string $config Configuration name of the connection.
+ * Defaults to 'default'.
+ * @return mixed Id of the last inserted row
+ * @access public
+ * @static
+ */
+ public static function insert_id($config = 'default') {
+ return DB::instance($config)->get_insert_id();
+ }
+
+ /**
+ * Gets an instance of a connection to the database
+ *
+ * @param string $config Configuration name of the connection.
+ * Defaults to 'default'.
+ * @return DB Driver implementation of the DB class.
+ * @access public
+ * @static
+ */
+ public static function instance($config='default'){
+ if (!isset(DB::$_instances[$config])) {
+ $driver = Config::get("database.{$config}.driver");
+ $driver="DB_{$driver}_Driver";
+ DB::$_instances[$config] = new $driver($config);
+ }
+ return DB::$_instances[$config];
+ }
+
+} \ No newline at end of file
diff --git a/modules/database/classes/driver/mysql/db.php b/modules/database/classes/driver/mysql/db.php
new file mode 100644
index 0000000..553cfb9
--- /dev/null
+++ b/modules/database/classes/driver/mysql/db.php
@@ -0,0 +1,88 @@
+<?php
+
+/**
+ * Mysqli Database Implementation
+ */
+class DB_Mysql_Driver extends DB{
+
+ /**
+ * Mysqli database connection object
+ * @var mysqli
+ * @access public
+ * @link http://php.net/manual/en/class.mysqli.php
+ */
+ public $conn;
+
+ /**
+ * Initializes database connection
+ *
+ * @param string $config Name of the connection to initialize
+ * @return void
+ * @access public
+ */
+ public function __construct($config) {
+ $this->conn = mysqli_connect(
+ Config::get("database.{$config}.host",'localhost'),
+ Config::get("database.{$config}.user",''),
+ Config::get("database.{$config}.password",''),
+ Config::get("database.{$config}.db")
+ );
+ }
+
+ /**
+ * Builds a new Query implementation
+ *
+ * @param string $type Query type. Available types: select,update,insert,delete,count
+ * @return Query_Mysql_Driver Returns a Mysqli implementation of a Query.
+ * @access public
+ * @see Query_Database
+ */
+ public function build_query($type) {
+ return new Query_Mysql_Driver($this,$type);
+ }
+
+ /**
+ * Gets the id of the last inserted row.
+ *
+ * @return mixed Row id
+ * @access public
+ */
+ public function get_insert_id() {
+ return $this->conn->insert_id;
+ }
+
+ /**
+ * Executes a prepared statement query
+ *
+ * @param string $query A prepared statement query
+ * @param array $params Parameters for the query
+ * @return Result_Mysql_Driver Mysqli implementation of a database result
+ * @access public
+ * @throws Exception If the query resulted in an error
+ * @see Database_Result
+ */
+ public function execute($query, $params = array()) {
+ $cursor = $this->conn->prepare($query);
+ if (!$cursor)
+ throw new Exception("Database error: {$this->conn->error} \n in query:\n{$query}");
+ $types = '';
+ $bind = array();
+ $refs = array();
+ if(!empty($params)){
+ foreach($params as $key=>$param) {
+ $refs[$key]=is_array($param)?$param[0]:$param;
+ $bind[]=&$refs[$key];
+ $types.=is_array($param)?$param[1]:'s';
+ }
+ array_unshift($bind, $types);
+
+ call_user_func_array(array($cursor, 'bind_param'), $bind);
+ }
+ $cursor->execute();
+ $res = $cursor->get_result();
+ if (is_object($res)){
+ $res=new Result_Mysql_Driver($res);
+ }
+ return $res;
+ }
+} \ No newline at end of file
diff --git a/modules/database/classes/driver/mysql/query.php b/modules/database/classes/driver/mysql/query.php
new file mode 100644
index 0000000..9f38cc5
--- /dev/null
+++ b/modules/database/classes/driver/mysql/query.php
@@ -0,0 +1,199 @@
+<?php
+
+/**
+ * Mysqli implementation of the database Query
+ */
+class Query_Mysql_Driver extends Query_Database{
+
+ /**
+ * If a string is passed escapes a field by enclosing it in `` quotes.
+ * If you pass an Expression_Database object the value will be inserted into the query uneascaped
+ *
+ * @param mixed $field Field to be escaped or an Expression_Database object
+ * if the field must not be escaped
+ * @return string Escaped field representation
+ * @access public
+ * @see Expression_Database
+ */
+ public function escape_field($field) {
+ if (is_object($field) && get_class($field) == 'Expression_Database')
+ return $field->value.' ';
+ $field = explode('.', $field);
+ if (count($field) == 1)
+ array_unshift($field,$this->lastAlias());
+ $str = '`'.$field[0].'`.';
+ if (trim($field[1]) == '*')
+ return $str.'* ';
+ return $str."`{$field[1]}` ";
+ }
+
+ /**
+ * Replaces the value with ? and appends it to the parameters array
+ * If you pass an Expression_Database object the value will be inserted into the query uneascaped
+ * @param mixed $val Value to be escaped or an Expression_Database object
+ * if the value must not be escaped
+ * @param array &$params Reference to parameters array
+ * @return string Escaped value representation
+ * @access public
+ */
+ public function escape_value($val,&$params) {
+ if (is_object($val) && get_class($val) == 'Expression_Database')
+ return $val->value.' ';
+ $params[] = $val;
+ return '? ';
+ }
+
+ /**
+ * Builds a query and fills the $params array with parameter values
+ *
+ * @return array An array with a prepared query string and an array of parameters
+ * @access public
+ */
+ public function query() {
+
+ $query = '';
+ $params = array();
+ if ($this->_type == 'insert') {
+ $query.= "INSERT INTO `{$this->_table}`";
+ $columns = '';
+ $values = '';
+ $first = true;
+ foreach($this->_data as $key => $val) {
+ if (!$first) {
+ $values.= ',';
+ $columns.= ',';
+ }else {
+ $first=false;
+ }
+ $columns.= "`{$key}` ";
+ $values.=$this->escape_value($val,$params);
+ }
+ $query.= "({$columns}) VALUES({$values})";
+ }else{
+ if ($this->_type == 'select'){
+ $query.= "SELECT ";
+ if($this->_fields==null){
+ $query.= "* ";
+ }else{
+ $first = true;
+ foreach($this->_fields as $f) {
+ if (!$first) {
+ $query.=", ";
+ }else {
+ $first = false;
+ }
+ $query.="{$this->escape_field($f)} ";
+ }
+ }
+ $query.= "FROM `{$this->_table}` ";
+ }
+ if ($this->_type == 'count') {
+ $query.= "SELECT COUNT(*) as `count` FROM `{$this->_table}` ";
+ }
+ if($this->_type=='delete')
+ $query.= "DELETE FROM `{$this->_table}` ";
+ if($this->_type=='update'){
+ $query.= "UPDATE `{$this->_table}` SET ";
+ $first = true;
+ foreach($this->_data as $key=>$val){
+ if (!$first) {
+ $query.=',';
+ }else {
+ $first=false;
+ }
+ $query.= "`{$key}`=".$this->escape_value($val,$params);
+ }
+ }
+
+ foreach($this->_joins as $join) {
+ $table = $join[0];
+ if (is_array($table)){
+ $table = "`{$table[0]}` as `{$table[1]}`";
+ }else {
+ $table="`{$table}`";
+ }
+ $query.= strtoupper($join[1])." JOIN {$table} ON ".$this->get_condition_query($join[2],$params,true,true);
+ }
+
+ if (!empty($this->_conditions)) {
+ $query.="WHERE ".$this->get_condition_query($this->_conditions,$params,true);
+ }
+ if (($this->_type == 'select' || $this->_type == 'count') && $this->_groupby!=null) {
+ $query.="GROUP BY ".$this->escape_field($this->_groupby);
+ }
+ if (($this->_type == 'select' || $this->_type == 'count') && !empty($this->_having)) {
+ $query.="HAVING ".$this->get_condition_query($this->_having,$params,true);
+ }
+
+ if ($this->_type == 'select' && !empty($this->_orderby)) {
+ $query.="ORDER BY ";
+ $first = true;
+ foreach($this->_orderby as $order) {
+ if (!$first) {
+ $query.=',';
+ }else {
+ $first=false;
+ }
+ $query.= $this->escape_field($order[0]);
+ if (isset($order[1])) {
+ $dir = strtoupper($order[1]);
+ $query.=$dir." ";
+ }
+ }
+ }
+ if($this->_type != 'count'){
+ if ($this->_limit != null)
+ $query.= "LIMIT {$this->_limit} ";
+ if ($this->_offset != null)
+ $query.= "OFFSET {$this->_offset} ";
+ }
+
+ }
+
+ return array($query,$params);
+ }
+
+ /**
+ * Recursively parses conditions array into a query string
+ *
+ * @param array $p Element of the array of conditions
+ * @param array &$params Reference to parameters array
+ * @param boolean $skip_first_operator Flag to skip the first logical operator in a query
+ * to prevent AND or OR to be at the beginning of the query
+ * @param boolean $value_is_field Flag if the the value in the logical operations should
+ * be treated as a field. E.g. for joins where the fields are
+ * compared between themselves and not with actual values
+ * @return string String representation of the conditions
+ * @access public
+ * @throws Exception If condition cannot be parsed
+ */
+ public function get_condition_query($p,&$params,$skip_first_operator,$value_is_field=false) {
+ if (isset($p['field'])) {
+ if ($value_is_field){
+ $param = $this->escape_field($p['value']);
+ }else {
+ $param = $this->escape_value($p['value'],$params);
+ }
+ return $this->escape_field($p['field']).' '.$p['operator'].' '.$param;
+ }
+ if (isset($p['logic'])) {
+ return ($skip_first_operator?'':strtoupper($p['logic'])).' '
+ .$this->get_condition_query($p['conditions'],$params,false,$value_is_field);
+ }
+
+ $conds = '';
+ $skip=$skip_first_operator||(count($p) > 1);
+ foreach($p as $q) {
+ $conds.=$this->get_condition_query($q,$params,$skip,$value_is_field);
+ $skip=false;
+ }
+ if (count($p) > 1 && !$skip_first_operator)
+ return "( ".$conds." ) ";
+ return $conds;
+
+ throw new Exception("Cannot parse condition:\n".var_export($p,true));
+ }
+
+
+
+} \ No newline at end of file
diff --git a/modules/database/classes/driver/mysql/result.php b/modules/database/classes/driver/mysql/result.php
new file mode 100644
index 0000000..0fbb645
--- /dev/null
+++ b/modules/database/classes/driver/mysql/result.php
@@ -0,0 +1,47 @@
+<?php
+
+/**
+ * Database result implementation for Mysqli
+ */
+class Result_Mysql_Driver extends Result_Database {
+
+ /**
+ * Initializes new result object
+ *
+ * @param mysqli_result $result Mysqli Result
+ * @return void
+ * @access public
+ * @link http://php.net/manual/en/class.mysqli-result.php
+ */
+ public function __construct($result) {
+ $this->_result = $result;
+ $this->_row=$this->_result->fetch_object();
+ }
+
+ /**
+ * Throws exception if rewind is attempted.
+ *
+ * @return void
+ * @access public
+ * @throws Exception If rewind is attempted
+ */
+ public function rewind() {
+ if($this->_position!=0)
+ throw new Exception('Mysqli result cannot be rewound for unbuffered queries.');
+ }
+
+ /**
+ * Iterates to the next row in the result set
+ *
+ * @return void
+ * @access public
+ */
+ public function next() {
+
+ $this->_position++;
+ $this->_row=$this->_result->fetch_object();
+ if ($this->_row == null)
+ $this->_result->free();
+ }
+
+} \ No newline at end of file
diff --git a/modules/database/classes/driver/pdo/db.php b/modules/database/classes/driver/pdo/db.php
new file mode 100644
index 0000000..89c7165
--- /dev/null
+++ b/modules/database/classes/driver/pdo/db.php
@@ -0,0 +1,70 @@
+<?php
+
+/**
+ * PDO Database implementation.
+ */
+class DB_PDO_Driver extends DB{
+
+ /**
+ * Connection object
+ * @var PDO
+ * @access public
+ * @link http://php.net/manual/en/class.pdo.php
+ */
+ public $conn;
+
+ /**
+ * Initializes database connection
+ *
+ * @param string $config Name of the connection to initialize
+ * @return void
+ * @access public
+ */
+ public function __construct($config) {
+ $this->conn = new PDO(
+ Config::get("database.{$config}.connection"),
+ Config::get("database.{$config}.user",''),
+ Config::get("database.{$config}.password",'')
+ );
+ }
+
+ /**
+ * Builds a new Query implementation
+ *
+ * @param string $type Query type. Available types: select,update,insert,delete,count
+ * @return Query_PDO_Driver Returns a PDO implementation of a Query.
+ * @access public
+ * @see Query_Database
+ */
+ public function build_query($type) {
+ return new Query_PDO_Driver($this,$type);
+ }
+
+ /**
+ * Gets the id of the last inserted row.
+ *
+ * @return mixed Row id
+ * @access public
+ */
+ public function get_insert_id() {
+ return $this->conn->lastInsertId();
+ }
+
+ /**
+ * Executes a prepared statement query
+ *
+ * @param string $query A prepared statement query
+ * @param array $params Parameters for the query
+ * @return Result_PDO_Driver PDO implementation of a database result
+ * @access public
+ * @throws Exception If the query resulted in an error
+ * @see Database_Result
+ */
+ public function execute($query, $params = array()) {
+ $cursor = $this->conn->prepare($query);
+ if(!$cursor->execute($params))
+ throw new Exception("Database error: ".implode(' ',$this->conn->errorInfo())." \n in query:\n{$query}");
+
+ return new Result_PDO_Driver($cursor);
+ }
+} \ No newline at end of file
diff --git a/modules/database/classes/driver/pdo/query.php b/modules/database/classes/driver/pdo/query.php
new file mode 100644
index 0000000..4a3d7c2
--- /dev/null
+++ b/modules/database/classes/driver/pdo/query.php
@@ -0,0 +1,8 @@
+<?php
+
+/**
+ * PDO Database Query implementation. Mimics Mysql implementation.
+ */
+class Query_PDO_Driver extends Query_Mysql_Driver{
+
+} \ No newline at end of file
diff --git a/modules/database/classes/driver/pdo/result.php b/modules/database/classes/driver/pdo/result.php
new file mode 100644
index 0000000..3270231
--- /dev/null
+++ b/modules/database/classes/driver/pdo/result.php
@@ -0,0 +1,47 @@
+<?php
+
+/**
+ * Database result implementation for PDO
+ */
+class Result_PDO_Driver extends Result_Database {
+
+ /**
+ * Initializes new result object
+ *
+ * @param PDOStatement $stmt PDO Statement
+ * @return void
+ * @access public
+ * @link http://php.net/manual/en/class.pdostatement.php
+ */
+ public function __construct($stmt) {
+ $this->_result = $stmt;
+ $this->_row=$this->_result->fetchObject();
+ }
+
+ /**
+ * Throws exception if rewind is attempted.
+ *
+ * @return void
+ * @access public
+ * @throws Exception If rewind is attempted
+ */
+ public function rewind() {
+ if($this->_position!=0)
+ throw new Exception('PDO statement cannot be rewound for unbuffered queries');
+ }
+
+ /**
+ * Iterates to the next row in the result set
+ *
+ * @return void
+ * @access public
+ */
+ public function next() {
+
+ $this->_position++;
+ $this->_row=$this->_result->fetchObject();
+ if ($this->_row == false)
+ $this->_result->closeCursor();
+ }
+
+} \ No newline at end of file