当前位置: 代码迷 >> SQL >> 抽取个sql生成器工具 - 摘自 fuelphp1.1 版本
  详细解决方案

抽取个sql生成器工具 - 摘自 fuelphp1.1 版本

热度:298   发布时间:2016-05-05 13:57:58.0
抽取个sql生成器工具 -- 摘自 fuelphp1.1 版本
<?php/** * Sql 创造者类 * * @author 色色 */class SqlCreator {		// 查询类型	const DB_SELECT =  1;	const DB_INSERT =  2;	const DB_UPDATE =  3;	const DB_DELETE =  4;		/**	 * 返回 Expr Sql 创建器对象	 *	 * @param string $expr	 * 	 * @return SqlCreator_Query_Expression	 */	static function newExpr($expr){		return new SqlCreator_Query_Expression($expr);	}		/**	 * 返回 Insert Sql 创建器对象	 *	 * @param Core_DB $dbo	 * @param string $table	 * @param array $columns	 * 	 * @return SqlCreator_Query_Builder_Insert	 */	static function newInsert(Core_DB $dbo,$table = NULL, array $columns = NULL){		return new SqlCreator_Query_Builder_Insert($dbo,$table,$columns);	}		/**	 * 返回 Insert Sql 创建器对象	 *	 * @param Core_DB $dbo	 * @param string $table	 * 	 * @return SqlCreator_Query_Builder_Update	 */	static function newUpdate(Core_DB $dbo,$table = NULL){		return new SqlCreator_Query_Builder_Update($dbo,$table);	}		/**	 * 返回 Delete Sql 创建器对象	 *	 * @param Core_DB $dbo	 * @param string $table	 * 	 * @return SqlCreator_Query_Builder_Delete	 */	static function newDelete(Core_DB $dbo,$table = NULL){		return new SqlCreator_Query_Builder_Delete($dbo,$table);	}		/**	 * 返回 Select Sql 创建器对象	 *	 * @param Core_DB $dbo	 * @param array $columns	 * 	 * @return SqlCreator_Query_Builder_Select	 */	static function newSelect(Core_DB $dbo,array $columns = NULL){		return new SqlCreator_Query_Builder_Select($dbo,$columns);	}		/**	 * 返回 Join Sql 创建器对象	 *	 * @param Core_DB $dbo	 * @param string $table	 * @param string $type  INNER, RIGHT, LEFT, etc	 * 	 * @return SqlCreator_Query_Builder_Join	 */	static function newJoin(Core_DB $dbo,$table, $type = NULL){		return new SqlCreator_Query_Builder_Join($dbo,$table, $type);	}	}class SqlCreatorException extends Exception {}class SqlCreator_Query_Expression{	// Raw expression string	protected $_value;	/**	 * Sets the expression string.	 *	 * $expression = new SqlCreator_Query_Expression('COUNT(users.id)');	 */	function __construct($value)	{		// Set the expression string		$this->_value = $value;	}	/**	 * Get the expression value as a string.	 *	 *     $sql = $expression->value();	 *	 * @return  string	 */	function value()	{		return (string) $this->_value;	}	function __toString()	{		return $this->value();	}}class SqlCreator_Query{		/**	 * @var Core_DB	 */	protected $_dbo = NULL;		/**	 * @var  int  Query type	 */	protected $_type;	/**	 * @var  string  SQL statement	 */	protected $_sql;	/**	 * @var  array  Quoted query parameters	 */	protected $_parameters = array();	/**	 * Creates a new SQL query of the specified type.	 *	 * @param   Core_DB   $dbo	 * @param   string   query string	 * @param   integer  query type	 */	function __construct(Core_DB $dbo,$sql, $type = null)	{		$this->_dbo = $dbo;		$this->_type = $type;		$this->_sql = $sql;	}	final function __toString()	{		return $this->compile();	}	/**	 * Get the type of the query.	 *	 * @return  integer	 */	function type()	{		return $this->_type;	}		/**	 * Set the value of a parameter in the query.	 *	 * @param   string   parameter key to replace	 * @param   mixed    value to use	 * 	 * @return  SqlCreator_Query	 */	function param($param, $value)	{		// Add or overload a new parameter		$this->_parameters[$param] = $value;		return $this;	}	/**	 * Bind a variable to a parameter in the query.	 *	 * @param   string  parameter key to replace	 * @param   mixed   variable to use	 * 	 * @return  SqlCreator_Query	 */	function bind($param, & $var)	{		// Bind a value to a variable		$this->_parameters[$param] =& $var;		return $this;	}	/**	 * Add multiple parameters to the query.	 *	 * @param   array  list of parameters	 * 	 * @return  SqlCreator_Query	 */	function parameters(array $params)	{		// Merge the new parameters in		$this->_parameters = $params + $this->_parameters;		return $this;	}	/**	 * Compile the SQL query and return it. Replaces any parameters with their	 * given values.	 *	 * @param   mixed  Database instance or instance name	 * 	 * @return  string	 */	function compile()	{		// Import the SQL locally		$sql = $this->_sql;		if ( ! empty($this->_parameters))		{			// Quote all of the values			$values = array_map(array($this, '__quote__'), $this->_parameters);			// Replace the values in the SQL			$sql = strtr($sql, $values);		}		return trim($sql);	}		/**	 * Quote a value for an SQL query.	 *	 * Objects passed to this function will be converted to strings.	 * [Database_Expression] objects will use the value of the expression.	 * [Database_Query] objects will be compiled and converted to a sub-query.	 * All other objects will be converted using the `__toString` method.	 *	 * @param   mixed   any value to quote	 * @return  string	 */	function __quote__($value)	{		if ($value === null)		{			return 'null';		}		elseif ($value === true)		{			return "'1'";		}		elseif ($value === false)		{			return "'0'";		}		elseif (is_object($value))		{			if ($value instanceof SqlCreator_Query)			{				// Create a sub-query				return '('.$value->compile().')';			}			elseif ($value instanceof SqlCreator_Query_Expression)			{				// Use a raw expression				return $value->value();			}			else			{				// Convert the object to a string				return $this->__quote__((string) $value);			}		}		elseif (is_array($value))		{			return '('.implode(', ', array_map(array($this, '__quote__'), $value)).')';		}		elseif (is_int($value))		{			return (int) $value;		}		elseif (is_float($value))		{			// Convert to non-locale aware float to prevent possible commas			return sprintf('%F', $value);		}		return $this->_dbo->qstr($value);	}	/**	 * Quote a database table name and adds the table prefix if needed.	 *	 * @param   mixed   table name or array(table, alias)	 * @return  string	 */	function __quote_table__($value)	{		// Assign the table by reference from the value		if (is_array($value))		{			$table =& $value[0];//			// Attach table prefix to alias//			$value[1] = $this->table_prefix().$value[1];		}		else		{			$table =& $value;		}		// deal with the sub-query objects first		if ($table instanceof SqlCreator_Query)		{			// Create a sub-query			$table = '('.$table->compile().')';		}		elseif (is_string($table))		{			if (strpos($table, '.') === false)			{				// Add the table prefix for tables//				$table = $this->__quote_identifier__($this->table_prefix().$table);			}			else			{				// Split the identifier into the individual parts				$parts = explode('.', $table);//				if ($prefix = $this->table_prefix())//				{//					// Get the offset of the table name, 2nd-to-last part//					// This works for databases that can have 3 identifiers (Postgre)//					if (($offset = count($parts)) == 2)//					{//						$offset = 1;//					}//					else//					{//						$offset = $offset - 2;//					}////					// Add the table prefix to the table name//					$parts[$offset] = $prefix.$parts[$offset];//				}				// Quote each of the parts				$table = implode('.', array_map(array($this, '__quote_identifier__'), $parts));			}		}		// process the alias if present		if (is_array($value))		{			// Separate the column and alias			list ($value, $alias) = $value;			return $value.' AS '.$this->__quote_identifier__($alias);		}		else		{			// return the value			return $value;		}	}	/**	 * Quote a database identifier, such as a column name. Adds the	 * table prefix to the identifier if a table name is present.	 *	 * Objects passed to this function will be converted to strings.	 * [Database_Expression] objects will use the value of the expression.	 * [Database_Query] objects will be compiled and converted to a sub-query.	 * All other objects will be converted using the `__toString` method.	 *	 * @param   mixed   any identifier	 * @return  string	 */	function __quote_identifier__($value)	{		if ($value === '*')		{			return $value;		}		elseif (is_object($value))		{			if ($value instanceof SqlCreator_Query)			{				// Create a sub-query				return '('.$value->compile().')';			}			elseif ($value instanceof SqlCreator_Query_Expression)			{				// Use a raw expression				return $value->value();			}			else			{				// Convert the object to a string				return $this->__quote_identifier__((string) $value);			}		}		elseif (is_array($value))		{			// Separate the column and alias			list ($value, $alias) = $value;			return $this->__quote_identifier__($value).' AS '.$this->__quote_identifier__($alias);		}		if (strpos($value, '"') !== false)		{			// Quote the column in FUNC("ident") identifiers			return preg_replace('/"(.+?)"/e', '$this->__quote_identifier__("$1")', $value);		}		elseif (strpos($value, '.') !== false)		{			// Split the identifier into the individual parts			$parts = explode('.', $value);//			if ($prefix = $this->table_prefix())//			{//				// Get the offset of the table name, 2nd-to-last part//				// This works for databases that can have 3 identifiers (Postgre)//				$offset = count($parts) - 2;////				// Add the table prefix to the table name//				$parts[$offset] = $prefix.$parts[$offset];//			}			// Quote each of the parts			return implode('.', array_map(array($this, '__quote_identifier__'), $parts));		}		else		{			return '`'.$value.'`';		}	}}abstract class SqlCreator_Query_Builder extends SqlCreator_Query{	/**	 * Compiles an array of JOIN statements into an SQL partial.	 *	 * @param   array   join statements	 * @return  string	 */	protected function _compile_join(array $joins)	{		$statements = array();		foreach ($joins as $join)		{			// Compile each of the join statements			$statements[] = $join->compile();		}		return implode(' ', $statements);	}	/**	 * Compiles an array of conditions into an SQL partial. Used for WHERE	 * and HAVING.	 *	 * @param   array   condition statements	 * @return  string	 */	protected function _compile_conditions(array $conditions)	{		$last_condition = NULL;		$sql = '';		foreach ($conditions as $group)		{			// Process groups of conditions			foreach ($group as $logic => $condition)			{				if ($condition === '(')				{					if ( ! empty($sql) AND $last_condition !== '(')					{						// Include logic operator						$sql .= ' '.$logic.' ';					}					$sql .= '(';				}				elseif ($condition === ')')				{					$sql .= ')';				}				else				{					if ( ! empty($sql) AND $last_condition !== '(')					{						// Add the logic operator						$sql .= ' '.$logic.' ';					}					// Split the condition					list($column, $op, $value) = $condition;					if ($value === NULL)					{						if ($op === '=')						{							// Convert "val = NULL" to "val IS NULL"							$op = 'IS';						}						elseif ($op === '!=')						{							// Convert "val != NULL" to "valu IS NOT NULL"							$op = 'IS NOT';						}					}					// Database operators are always uppercase					$op = strtoupper($op);					if ($op === 'BETWEEN' AND is_array($value))					{						// BETWEEN always has exactly two arguments						list($min, $max) = $value;						if (is_string($min) AND array_key_exists($min, $this->_parameters))						{							// Set the parameter as the minimum							$min = $this->_parameters[$min];						}						if (is_string($max) AND array_key_exists($max, $this->_parameters))						{							// Set the parameter as the maximum							$max = $this->_parameters[$max];						}						// Quote the min and max value						$value = $this->__quote__($min).' AND '.$this->__quote__($max);					}					else					{						if (is_string($value) AND array_key_exists($value, $this->_parameters))						{							// Set the parameter as the value							$value = $this->_parameters[$value];						}						// Quote the entire value normally						$value = $this->__quote__($value);					}					// Append the statement to the query					$sql .= $this->__quote_identifier__($column).' '.$op.' '.$value;				}				$last_condition = $condition;			}		}		return $sql;	}	/**	 * Compiles an array of set values into an SQL partial. Used for UPDATE.	 *	 * @param   array   updated values	 * @return  string	 */	protected function _compile_set(array $values)	{		$set = array();		foreach ($values as $group)		{			// Split the set			list ($column, $value) = $group;			// Quote the column name			$column = $this->__quote_identifier__($column);			if (is_string($value) AND array_key_exists($value, $this->_parameters))			{				// Use the parameter value				$value = $this->_parameters[$value];			}			$set[$column] = $column.' = '.$this->__quote__($value);		}		return implode(', ', $set);	}	/**	 * Compiles an array of ORDER BY statements into an SQL partial.	 *	 * @param   array   sorting columns	 * @return  string	 */	protected function _compile_order_by(array $columns)	{		$sort = array();		foreach ($columns as $group)		{			list ($column, $direction) = $group;			if ( ! empty($direction))			{				// Make the direction uppercase				$direction = ' '.strtoupper($direction);			}			$sort[] = $this->__quote_identifier__($column).$direction;		}		return 'ORDER BY '.implode(', ', $sort);	}	/**	 * Reset the current builder status.	 *	 * @return  SqlCreator_Query_Builder	 */	abstract function reset();}abstract class SqlCreator_Query_Builder_Where extends SqlCreator_Query_Builder{	// WHERE ...	protected $_where = array();	// ORDER BY ...	protected $_order_by = array();	// LIMIT ...	protected $_limit = NULL;	/**	 * Alias of and_where()	 *	 * @return  SqlCreator_Query_Builder_Where	 */	function where()	{		$args = func_get_args();		return call_user_func_array(array($this, 'and_where'), $args);	}	/**	 * Creates a new "AND WHERE" condition for the query.	 *	 * @param   mixed   column name or array($column, $alias) or object	 * @param   string  logic operator	 * @param   mixed   column value	 * 	 * @return  SqlCreator_Query_Builder_Where	 */	function and_where($column, $op = null, $value = null)	{		if (is_array($column))		{			foreach ($column as $key => $val)			{				if (is_array($val))				{					$this->and_where($val[0], $val[1], $val[2]);				}				else				{					$this->and_where($key, '=', $val);				}			}		}		else		{			if(func_num_args() === 2)			{				$value = $op;				$op = '=';			}			$this->_where[] = array('AND' => array($column, $op, $value));		}		return $this;	}	/**	 * Creates a new "OR WHERE" condition for the query.	 *	 * @param   mixed   column name or array($column, $alias) or object	 * @param   string  logic operator	 * @param   mixed   column value	 * 	 * @return  SqlCreator_Query_Builder_Where	 */	function or_where($column, $op = null, $value = null)	{		if (is_array($column))		{			foreach ($column as $key => $val)			{				if (is_array($val))				{					$this->or_where($val[0], $val[1], $val[2]);				}				else				{					$this->or_where($key, '=', $val);				}			}		}		else		{			if(func_num_args() === 2)			{				$value = $op;				$op = '=';			}			$this->_where[] = array('OR' => array($column, $op, $value));		}		return $this;	}	/**	 * Alias of and_where_open()	 *	 * @return  SqlCreator_Query_Builder_Where	 */	function where_open()	{		return $this->and_where_open();	}	/**	 * Opens a new "AND WHERE (...)" grouping.	 *	 * @return  SqlCreator_Query_Builder_Where	 */	function and_where_open()	{		$this->_where[] = array('AND' => '(');		return $this;	}	/**	 * Opens a new "OR WHERE (...)" grouping.	 *	 * @return  SqlCreator_Query_Builder_Where	 */	function or_where_open()	{		$this->_where[] = array('OR' => '(');		return $this;	}	/**	 * Closes an open "AND WHERE (...)" grouping.	 *	 * @return  SqlCreator_Query_Builder_Where	 */	function where_close()	{		return $this->and_where_close();	}	/**	 * Closes an open "AND WHERE (...)" grouping.	 *	 * @return  SqlCreator_Query_Builder_Where	 */	function and_where_close()	{		$this->_where[] = array('AND' => ')');		return $this;	}	/**	 * Closes an open "OR WHERE (...)" grouping.	 *	 * @return  SqlCreator_Query_Builder_Where	 */	function or_where_close()	{		$this->_where[] = array('OR' => ')');		return $this;	}	/**	 * Applies sorting with "ORDER BY ..."	 *	 * @param   mixed   column name or array($column, $alias) or object	 * @param   string  direction of sorting	 * @return  SqlCreator_Query_Builder_Where	 */	function order_by($column, $direction = NULL)	{		$this->_order_by[] = array($column, $direction);		return $this;	}	/**	 * Return up to "LIMIT ..." results	 *	 * @param   integer  maximum results to return	 * 	 * @return  SqlCreator_Query_Builder_Where	 */	function limit($number)	{		$this->_limit = (int) $number;		return $this;	}}class SqlCreator_Query_Builder_Insert extends SqlCreator_Query_Builder{	// INSERT INTO ...	protected $_table;	// (...)	protected $_columns = array();	// VALUES (...)	protected $_values = array();	/**	 * Set the table and columns for an insert.	 *	 * @param   Core_DB  $dbo	 * @param   string  $table	 * @param   array  $columns	 */	function __construct(Core_DB $dbo,$table = NULL, array $columns = NULL)	{		if ($table)		{						$this->_table = $table;		}		if ($columns)		{			// Set the column names			$this->_columns = $columns;		}		// Start the query with no SQL		return parent::__construct($dbo,'', SqlCreator::DB_INSERT);	}	/**	 * Sets the table to insert into.	 *	 * @param  string  $table	 * @return  SqlCreator_Query_Builder_Insert	 */	function table($table)	{		$this->_table = $table;		return $this;	}	/**	 * Set the columns that will be inserted.	 *	 * @param   array  column names	 * @return  SqlCreator_Query_Builder_Insert	 */	function columns(array $columns)	{		$this->_columns = array_merge($this->_columns, $columns);		return $this;	}	/**	 * Adds or overwrites values. Multiple value sets can be added.	 *	 * @param   array   values list	 * @return  SqlCreator_Query_Builder_Insert	 */	function values(array $values)	{		if ( ! is_array($this->_values))		{			throw new SqlCreatorException('INSERT INTO ... SELECT statements cannot be combined with INSERT INTO ... VALUES');		}		// Get all of the passed values		$values = func_get_args();		$this->_values = array_merge($this->_values, $values);		return $this;	}	/**	 * This is a wrapper function for calling columns() and values().	 *	 * @param	array	column value pairs	 * @return	SqlCreator_Query_Builder_Insert	 */	function set(array $pairs)	{		$this->columns(array_keys($pairs));		$this->values($pairs);		return $this;	}	/**	 * Use a sub-query to for the inserted values.	 *	 * @param   SqlCreator_Query  $query 必须是 SqlCreator::DB_SELECT类型的对象	 * 	 * @return  SqlCreator_Query_Builder_Insert	 */	function select(SqlCreator_Query $query)	{		if ($query->type() !== SqlCreator::DB_SELECT)		{			throw new SqlCreatorException('Only SELECT queries can be combined with INSERT queries');		}		$this->_values = $query;		return $this;	}	/**	 * Compile the SQL query and return it.	 *	 * @return  string	 */	function compile()	{				// Start an insertion query		$query = 'INSERT INTO '.$this->__quote_table__($this->_table);		// Add the column names		$query .= ' ('.implode(', ', array_map(array($this, '__quote_identifier__'), $this->_columns)).') ';		if (is_array($this->_values))		{			// Callback for quoting values			$quote = array($this, '__quote__');			$groups = array();			foreach ($this->_values as $group)			{				foreach ($group as $i => $value)				{					if (is_string($value) AND isset($this->_parameters[$value]))					{						// Use the parameter value						$group[$i] = $this->_parameters[$value];					}				}				$groups[] = '('.implode(', ', array_map($quote, $group)).')';			}			// Add the values			$query .= 'VALUES '.implode(', ', $groups);		}		else		{			// Add the sub-query			$query .= (string) $this->_values;		}		return $query;	}	/**	 * @return  SqlCreator_Query_Builder_Insert	 */	function reset()	{		$this->_table = NULL;		$this->_columns =		$this->_values  = array();		$this->_parameters = array();		return $this;	}}class SqlCreator_Query_Builder_Update extends SqlCreator_Query_Builder_Where{	// UPDATE ...	protected $_table;	// SET ...	protected $_set = array();	// JOIN ...	protected $_join = array();	/**	 * Set the table for a update.	 *	 * @param   string  $table	 */	function __construct(Core_DB $dbo,$table = NULL)	{		if ($table)		{						$this->_table = $table;		}		// Start the query with no SQL		return parent::__construct($dbo,'', SqlCreator::DB_UPDATE);	}	/**	 * Sets the table to update.	 *	 * @param   string  $table	 * @return  SqlCreator_Query_Builder_Update	 */	function table($table)	{		$this->_table = $table;		return $this;	}	/**	 * Set the values to update with an associative array.	 *	 * @param   array   associative (column => value) list	 * @return  SqlCreator_Query_Builder_Update	 */	function set(array $pairs)	{		foreach ($pairs as $column => $value)		{			$this->_set[] = array($column, $value);		}		return $this;	}	/**	 * Set the value of a single column.	 *	 * @param   string  $table	 * @param   mixed  column value	 * @return  SqlCreator_Query_Builder_Update	 */	function value($column, $value)	{		$this->_set[] = array($column, $value);		return $this;	}	/**	 * Compile the SQL query and return it.	 *	 * @return  string	 */	function compile()	{		// Start an update query		$query = 'UPDATE '.$this->__quote_table__($this->_table);		if ( ! empty($this->_join))		{			// Add tables to join			$query .= ' '.$this->_compile_join($this->_join);		}		// Add the columns to update		$query .= ' SET '.$this->_compile_set($this->_set);		if ( ! empty($this->_where))		{			// Add selection conditions			$query .= ' WHERE '.$this->_compile_conditions($this->_where);		}		if ($this->_limit !== NULL)		{			// Add limiting			$query .= ' LIMIT '.$this->_limit;		}		return $query;	}	function reset()	{		$this->_table = NULL;		$this->_set   =		$this->_where = array();		$this->_limit = NULL;		$this->_parameters = array();		return $this;	}	/**	 * Adds addition tables to "JOIN ...".	 *	 * @param   string  $table	 * @param   string  join type (LEFT, RIGHT, INNER, etc)	 * 	 * @return  SqlCreator_Query_Builder_Update	 */	function join($table, $type = NULL)	{		$this->_join[] = $this->_last_join = new SqlCreator_Query_Builder_Join($this->_dbo,$table, $type);		return $this;	}	/**	 * Adds "ON ..." conditions for the last created JOIN statement.	 *	 * @param   mixed   column name or array($column, $alias) or object	 * @param   string  logic operator	 * @param   mixed   column name or array($column, $alias) or object	 * 	 * @return  SqlCreator_Query_Builder_Update	 */	function on($c1, $op, $c2)	{		$this->_last_join->on($c1, $op, $c2);		return $this;	}} class SqlCreator_Query_Builder_Delete extends SqlCreator_Query_Builder_Where{	// DELETE FROM ...	protected $_table;	/**	 * Set the table for a delete.	 *	 * @param   string  $table	 */	function __construct(Core_DB $dbo,$table = NULL)	{		if ($table)		{			$this->_table = $table;		}		// Start the query with no SQL		return parent::__construct($dbo,'', SqlCreator::DB_DELETE);	}	/**	 * Sets the table to delete from.	 *	 * @param   string  $table	 * @return  SqlCreator_Query_Builder_Delete	 */	function table($table)	{		$this->_table = $table;		return $this;	}	/**	 * Compile the SQL query and return it.	 *	 * @param   mixed  SqlCreator instance or instance name	 * @return  string	 */	function compile()	{				// Start a deletion query		$query = 'DELETE FROM '.$this->__quote_table__($this->_table);		if ( ! empty($this->_where))		{			// Add deletion conditions			$query .= ' WHERE '.$this->_compile_conditions($this->_where);		}		if ( ! empty($this->_order_by))		{			// Add sorting			$query .= ' '.$this->_compile_order_by($this->_order_by);		}		if ($this->_limit !== NULL)		{			// Add limiting			$query .= ' LIMIT '.$this->_limit;		}		return $query;	}	function reset()	{		$this->_table = NULL;		$this->_where = array();		$this->_parameters = array();		return $this;	}}class SqlCreator_Query_Builder_Join extends SqlCreator_Query_Builder{	// Type of JOIN	protected $_type;	// JOIN ...	protected $_table;	// ON ...	protected $_on = array();	/**	 * Creates a new JOIN statement for a table. Optionally, the type of JOIN	 * can be specified as the second parameter.	 *	 * @param   mixed   column name or array($column, $alias) or object	 * @param   string  type of JOIN: INNER, RIGHT, LEFT, etc	 */	function __construct(Core_DB $dbo,$table, $type = NULL)	{		$this->_dbo = $dbo;				// Set the table to JOIN on		$this->_table = $table;		if ($type !== NULL)		{			// Set the JOIN type			$this->_type = (string) $type;		}	}	/**	 * Adds a new condition for joining.	 *	 * @param   mixed   column name or array($column, $alias) or object	 * @param   string  logic operator	 * @param   mixed   column name or array($column, $alias) or object	 * @return  SqlCreator_Query_Builder_Join	 */	function on($c1, $op, $c2)	{		$this->_on[] = array($c1, $op, $c2);		return $this;	}	/**	 * Compile the SQL partial for a JOIN statement and return it.	 *	 * @param   mixed  SqlCreator instance or instance name	 * @return  string	 */	function compile()	{		if ($this->_type)		{			$sql = strtoupper($this->_type).' JOIN';		}		else		{			$sql = 'JOIN';		}		// Quote the table name that is being joined		$sql .= ' '.$this->__quote_table__($this->_table).' ON ';		$conditions = array();		foreach ($this->_on as $condition)		{			// Split the condition			list($c1, $op, $c2) = $condition;			if ($op)			{				// Make the operator uppercase and spaced				$op = ' '.strtoupper($op);			}			// Quote each of the identifiers used for the condition			$conditions[] = $this->__quote_identifier__($c1).$op.' '.$this->__quote_identifier__($c2);		}		// Concat the conditions "... AND ..."		$sql .= '('.implode(' AND ', $conditions).')';		return $sql;	}	function reset()	{		$this->_type =		$this->_table = NULL;		$this->_on = array();	}}class SqlCreator_Query_Builder_Select extends SqlCreator_Query_Builder_Where{	// SELECT ...	protected $_select = array();	// DISTINCT	protected $_distinct = FALSE;	// FROM ...	protected $_from = array();	// JOIN ...	protected $_join = array();	// GROUP BY ...	protected $_group_by = array();	// HAVING ...	protected $_having = array();	// OFFSET ...	protected $_offset = NULL;	// The last JOIN statement created	protected $_last_join;	/**	 * Sets the initial columns to select from.	 *	 * @param   array  column list	 */	function __construct(Core_DB $dbo,array $columns = NULL)	{		if ( ! empty($columns))		{			// Set the initial columns			$this->_select = $columns;		}		// Start the query with no actual SQL statement		parent::__construct($dbo,'', SqlCreator::DB_SELECT);	}	/**	 * Enables or disables selecting only unique columns using "SELECT DISTINCT"	 *	 * @param   boolean  enable or disable distinct columns	 * @return  SqlCreator_Query_Builder_Select	 */	function distinct($value = true)	{		$this->_distinct = (bool) $value;		return $this;	}	/**	 * Choose the columns to select from.	 *	 * @param   mixed  column name or array($column, $alias) or object	 * @param   ...	 * @return  SqlCreator_Query_Builder_Select	 */	function select($columns = NULL)	{		$columns = func_get_args();		$this->_select = array_merge($this->_select, $columns);		return $this;	}	/**	 * Choose the columns to select from, using an array.	 *	 * @param   array  list of column names or aliases	 * @return  SqlCreator_Query_Builder_Select	 */	function select_array(array $columns)	{		$this->_select = array_merge($this->_select, $columns);		return $this;	}	/**	 * Choose the tables to select "FROM ..."	 *	 * @param   string  $table	 * @param   ...	 * @return  SqlCreator_Query_Builder_Select	 */	function from($tables)	{		$tables = func_get_args();		$this->_from = array_merge($this->_from, $tables);		return $this;	}	/**	 * Adds addition tables to "JOIN ...".	 *	 * @param   mixed   column name or array($column, $alias) or object	 * @param   string  join type (LEFT, RIGHT, INNER, etc)	 * @return  SqlCreator_Query_Builder_Select	 */	function join($table, $type = NULL)	{		$this->_join[] = $this->_last_join = new SqlCreator_Query_Builder_Join($this->_dbo,$table, $type);		return $this;	}	/**	 * Adds "ON ..." conditions for the last created JOIN statement.	 *	 * @param   mixed   column name or array($column, $alias) or object	 * @param   string  logic operator	 * @param   mixed   column name or array($column, $alias) or object	 * @return  SqlCreator_Query_Builder_Select	 */	function on($c1, $op, $c2)	{		$this->_last_join->on($c1, $op, $c2);		return $this;	}	/**	 * Creates a "GROUP BY ..." filter.	 *	 * @param   mixed   column name or array($column, $alias) or object	 * @return  SqlCreator_Query_Builder_Select	 */	function group_by($columns)	{		$columns = func_get_args();		$this->_group_by = array_merge($this->_group_by, $columns);		return $this;	}	/**	 * Alias of and_having()	 *	 * @param   mixed   column name or array($column, $alias) or object	 * @param   string  logic operator	 * @param   mixed   column value	 * 	 * @return  SqlCreator_Query_Builder_Select	 */	function having($column, $op, $value = NULL)	{		return $this->and_having($column, $op, $value);	}	/**	 * Creates a new "AND HAVING" condition for the query.	 *	 * @param   mixed   column name or array($column, $alias) or object	 * @param   string  logic operator	 * @param   mixed   column value	 * 	 * @return  SqlCreator_Query_Builder_Select	 */	function and_having($column, $op, $value = NULL)	{		$this->_having[] = array('AND' => array($column, $op, $value));		return $this;	}	/**	 * Creates a new "OR HAVING" condition for the query.	 *	 * @param   mixed   column name or array($column, $alias) or object	 * @param   string  logic operator	 * @param   mixed   column value	 * 	 * @return  SqlCreator_Query_Builder_Select	 */	function or_having($column, $op, $value = NULL)	{		$this->_having[] = array('OR' => array($column, $op, $value));		return $this;	}	/**	 * Alias of and_having_open()	 *	 * @return  SqlCreator_Query_Builder_Select	 */	function having_open()	{		return $this->and_having_open();	}	/**	 * Opens a new "AND HAVING (...)" grouping.	 *	 * @return  SqlCreator_Query_Builder_Select	 */	function and_having_open()	{		$this->_having[] = array('AND' => '(');		return $this;	}	/**	 * Opens a new "OR HAVING (...)" grouping.	 *	 * @return  SqlCreator_Query_Builder_Select	 */	function or_having_open()	{		$this->_having[] = array('OR' => '(');		return $this;	}	/**	 * Closes an open "AND HAVING (...)" grouping.	 *	 * @return  SqlCreator_Query_Builder_Select	 */	function having_close()	{		return $this->and_having_close();	}	/**	 * Closes an open "AND HAVING (...)" grouping.	 *	 * @return  SqlCreator_Query_Builder_Select	 */	function and_having_close()	{		$this->_having[] = array('AND' => ')');		return $this;	}	/**	 * Closes an open "OR HAVING (...)" grouping.	 *	 * @return  SqlCreator_Query_Builder_Select	 */	function or_having_close()	{		$this->_having[] = array('OR' => ')');		return $this;	}	/**	 * Start returning results after "OFFSET ..."	 *	 * @param   integer   starting result number	 * 	 * @return  SqlCreator_Query_Builder_Select	 */	function offset($number)	{		$this->_offset = (int) $number;		return $this;	}	/**	 * Compile the SQL query and return it.	 *	 * @return  string	 */	function compile()	{						// Callback to quote identifiers		$quote_ident = array($this, '__quote_identifier__');		// Callback to quote tables		$quote_table = array($this, '__quote_table__');		// Start a selection query		$query = 'SELECT ';		if ($this->_distinct === TRUE)		{			// Select only unique results			$query .= 'DISTINCT ';		}		if (empty($this->_select))		{			// Select all columns			$query .= '*';		}		else		{			// Select all columns			$query .= implode(', ', array_unique(array_map($quote_ident, $this->_select)));		}		if ( ! empty($this->_from))		{			// Set tables to select from			$query .= ' FROM '.implode(', ', array_unique(array_map($quote_table, $this->_from)));		}		if ( ! empty($this->_join))		{			// Add tables to join			$query .= ' '.$this->_compile_join($this->_join);		}		if ( ! empty($this->_where))		{			// Add selection conditions			$query .= ' WHERE '.$this->_compile_conditions($this->_where);		}		if ( ! empty($this->_group_by))		{			// Add sorting			$query .= ' GROUP BY '.implode(', ', array_map($quote_ident, $this->_group_by));		}		if ( ! empty($this->_having))		{			// Add filtering conditions			$query .= ' HAVING '.$this->_compile_conditions($this->_having);		}		if ( ! empty($this->_order_by))		{			// Add sorting			$query .= ' '.$this->_compile_order_by($this->_order_by);		}		if ($this->_limit !== NULL)		{			// Add limiting			$query .= ' LIMIT '.$this->_limit;		}		if ($this->_offset !== NULL)		{			// Add offsets			$query .= ' OFFSET '.$this->_offset;		}		return $query;	}	function reset()	{		$this->_select   =		$this->_from     =		$this->_join     =		$this->_where    =		$this->_group_by =		$this->_having   =		$this->_order_by = array();		$this->_distinct = FALSE;		$this->_limit     =		$this->_offset    =		$this->_last_join = NULL;		$this->_parameters = array();		return $this;	}}
?