mirror of https://github.com/IoTcat/ushio-img.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
565 lines
15 KiB
565 lines
15 KiB
4 years ago
|
<?php
|
||
|
/**
|
||
|
* Typecho Blog Platform
|
||
|
*
|
||
|
* @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org)
|
||
|
* @license GNU General Public License 2.0
|
||
|
* @version $Id: DbQuery.php 97 2008-04-04 04:39:54Z magike.net $
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* Typecho数据库查询语句构建类
|
||
|
* 使用方法:
|
||
|
* $query = new Typecho_Db_Query(); //或者使用DB积累的sql方法返回实例化对象
|
||
|
* $query->select('posts', 'post_id, post_title')
|
||
|
* ->where('post_id = %d', 1)
|
||
|
* ->limit(1);
|
||
|
* echo $query;
|
||
|
* 打印的结果将是
|
||
|
* SELECT post_id, post_title FROM posts WHERE 1=1 AND post_id = 1 LIMIT 1
|
||
|
*
|
||
|
*
|
||
|
* @package Db
|
||
|
*/
|
||
|
class Typecho_Db_Query
|
||
|
{
|
||
|
/** 数据库关键字 */
|
||
|
const KEYWORDS = '*PRIMARY|AND|OR|LIKE|BINARY|BY|DISTINCT|AS|IN|IS|NULL';
|
||
|
|
||
|
/**
|
||
|
* 默认字段
|
||
|
*
|
||
|
* @var array
|
||
|
* @access private
|
||
|
*/
|
||
|
private static $_default = array(
|
||
|
'action' => NULL,
|
||
|
'table' => NULL,
|
||
|
'fields' => '*',
|
||
|
'join' => array(),
|
||
|
'where' => NULL,
|
||
|
'limit' => NULL,
|
||
|
'offset' => NULL,
|
||
|
'order' => NULL,
|
||
|
'group' => NULL,
|
||
|
'having' => NULL,
|
||
|
'rows' => array(),
|
||
|
);
|
||
|
|
||
|
/**
|
||
|
* 数据库适配器
|
||
|
*
|
||
|
* @var Typecho_Db_Adapter
|
||
|
*/
|
||
|
private $_adapter;
|
||
|
|
||
|
/**
|
||
|
* 查询语句预结构,由数组构成,方便组合为SQL查询字符串
|
||
|
*
|
||
|
* @var array
|
||
|
*/
|
||
|
private $_sqlPreBuild;
|
||
|
|
||
|
/**
|
||
|
* 前缀
|
||
|
*
|
||
|
* @access private
|
||
|
* @var string
|
||
|
*/
|
||
|
private $_prefix;
|
||
|
|
||
|
/**
|
||
|
* @var array
|
||
|
*/
|
||
|
private $_params = array();
|
||
|
|
||
|
/**
|
||
|
* 构造函数,引用数据库适配器作为内部数据
|
||
|
*
|
||
|
* @param Typecho_Db_Adapter $adapter 数据库适配器
|
||
|
* @param string $prefix 前缀
|
||
|
*/
|
||
|
public function __construct(Typecho_Db_Adapter $adapter, $prefix)
|
||
|
{
|
||
|
$this->_adapter = &$adapter;
|
||
|
$this->_prefix = $prefix;
|
||
|
|
||
|
$this->_sqlPreBuild = self::$_default;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 过滤表前缀,表前缀由table.构成
|
||
|
*
|
||
|
* @param string $string 需要解析的字符串
|
||
|
* @return string
|
||
|
*/
|
||
|
private function filterPrefix($string)
|
||
|
{
|
||
|
return (0 === strpos($string, 'table.')) ? substr_replace($string, $this->_prefix, 0, 6) : $string;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 过滤数组键值
|
||
|
*
|
||
|
* @access private
|
||
|
* @param string $str 待处理字段值
|
||
|
* @return string
|
||
|
*/
|
||
|
private function filterColumn($str)
|
||
|
{
|
||
|
$str = $str . ' 0';
|
||
|
$length = strlen($str);
|
||
|
$lastIsAlnum = false;
|
||
|
$result = '';
|
||
|
$word = '';
|
||
|
$split = '';
|
||
|
$quotes = 0;
|
||
|
|
||
|
for ($i = 0; $i < $length; $i ++) {
|
||
|
$cha = $str[$i];
|
||
|
|
||
|
if (ctype_alnum($cha) || false !== strpos('_*', $cha)) {
|
||
|
if (!$lastIsAlnum) {
|
||
|
if ($quotes > 0 && !ctype_digit($word) && '.' != $split
|
||
|
&& false === strpos(self::KEYWORDS, strtoupper($word))) {
|
||
|
$word = $this->_adapter->quoteColumn($word);
|
||
|
} else if ('.' == $split && 'table' == $word) {
|
||
|
$word = $this->_prefix;
|
||
|
$split = '';
|
||
|
}
|
||
|
|
||
|
$result .= $word . $split;
|
||
|
$word = '';
|
||
|
$quotes = 0;
|
||
|
}
|
||
|
|
||
|
$word .= $cha;
|
||
|
$lastIsAlnum = true;
|
||
|
} else {
|
||
|
|
||
|
if ($lastIsAlnum) {
|
||
|
|
||
|
if (0 == $quotes) {
|
||
|
if (false !== strpos(' ,)=<>.+-*/', $cha)) {
|
||
|
$quotes = 1;
|
||
|
} else if ('(' == $cha) {
|
||
|
$quotes = -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$split = '';
|
||
|
}
|
||
|
|
||
|
$split .= $cha;
|
||
|
$lastIsAlnum = false;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 从参数中合成查询字段
|
||
|
*
|
||
|
* @access private
|
||
|
* @param array $parameters
|
||
|
* @return string
|
||
|
*/
|
||
|
private function getColumnFromParameters(array $parameters)
|
||
|
{
|
||
|
$fields = array();
|
||
|
|
||
|
foreach ($parameters as $value) {
|
||
|
if (is_array($value)) {
|
||
|
foreach ($value as $key => $val) {
|
||
|
$fields[] = $key . ' AS ' . $val;
|
||
|
}
|
||
|
} else {
|
||
|
$fields[] = $value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $this->filterColumn(implode(' , ', $fields));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 转义参数
|
||
|
*
|
||
|
* @param array $values
|
||
|
* @access protected
|
||
|
* @return array
|
||
|
*/
|
||
|
protected function quoteValues(array $values)
|
||
|
{
|
||
|
foreach ($values as &$value) {
|
||
|
if (is_array($value)) {
|
||
|
$value = '(' . implode(',', array_map(array($this, 'quoteValue'), $value)) . ')';
|
||
|
} else {
|
||
|
$value = $this->quoteValue($value);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $values;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 延迟转义
|
||
|
*
|
||
|
* @param $value
|
||
|
* @return string
|
||
|
*/
|
||
|
public function quoteValue($value)
|
||
|
{
|
||
|
$this->_params[] = $value;
|
||
|
return '#param:' . (count($this->_params) - 1) . '#';
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 获取参数
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
public function getParams()
|
||
|
{
|
||
|
return $this->_params;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* set default params
|
||
|
*
|
||
|
* @param array $default
|
||
|
*/
|
||
|
public static function setDefault(array $default)
|
||
|
{
|
||
|
self::$_default = array_merge(self::$_default, $default);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 获取查询字串属性值
|
||
|
*
|
||
|
* @access public
|
||
|
* @param string $attributeName 属性名称
|
||
|
* @return string
|
||
|
*/
|
||
|
public function getAttribute($attributeName)
|
||
|
{
|
||
|
return isset($this->_sqlPreBuild[$attributeName]) ? $this->_sqlPreBuild[$attributeName] : NULL;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 清除查询字串属性值
|
||
|
*
|
||
|
* @access public
|
||
|
* @param string $attributeName 属性名称
|
||
|
* @return Typecho_Db_Query
|
||
|
*/
|
||
|
public function cleanAttribute($attributeName)
|
||
|
{
|
||
|
if (isset($this->_sqlPreBuild[$attributeName])) {
|
||
|
$this->_sqlPreBuild[$attributeName] = self::$_default[$attributeName];
|
||
|
}
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 连接表
|
||
|
*
|
||
|
* @param string $table 需要连接的表
|
||
|
* @param string $condition 连接条件
|
||
|
* @param string $op 连接方法(LEFT, RIGHT, INNER)
|
||
|
* @return Typecho_Db_Query
|
||
|
*/
|
||
|
public function join($table, $condition, $op = Typecho_Db::INNER_JOIN)
|
||
|
{
|
||
|
$this->_sqlPreBuild['join'][] = array($this->filterPrefix($table), $this->filterColumn($condition), $op);
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* AND条件查询语句
|
||
|
*
|
||
|
* @param string $condition 查询条件
|
||
|
* @param mixed $param 条件值
|
||
|
* @return Typecho_Db_Query
|
||
|
*/
|
||
|
public function where()
|
||
|
{
|
||
|
$condition = func_get_arg(0);
|
||
|
$condition = str_replace('?', "%s", $this->filterColumn($condition));
|
||
|
$operator = empty($this->_sqlPreBuild['where']) ? ' WHERE ' : ' AND';
|
||
|
|
||
|
if (func_num_args() <= 1) {
|
||
|
$this->_sqlPreBuild['where'] .= $operator . ' (' . $condition . ')';
|
||
|
} else {
|
||
|
$args = func_get_args();
|
||
|
array_shift($args);
|
||
|
$this->_sqlPreBuild['where'] .= $operator . ' (' . vsprintf($condition, $this->quoteValues($args)) . ')';
|
||
|
}
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* OR条件查询语句
|
||
|
*
|
||
|
* @param string $condition 查询条件
|
||
|
* @param mixed $param 条件值
|
||
|
* @return Typecho_Db_Query
|
||
|
*/
|
||
|
public function orWhere()
|
||
|
{
|
||
|
$condition = func_get_arg(0);
|
||
|
$condition = str_replace('?', "%s", $this->filterColumn($condition));
|
||
|
$operator = empty($this->_sqlPreBuild['where']) ? ' WHERE ' : ' OR';
|
||
|
|
||
|
if (func_num_args() <= 1) {
|
||
|
$this->_sqlPreBuild['where'] .= $operator . ' (' . $condition . ')';
|
||
|
} else {
|
||
|
$args = func_get_args();
|
||
|
array_shift($args);
|
||
|
$this->_sqlPreBuild['where'] .= $operator . ' (' . vsprintf($condition, $this->quoteValues($args)) . ')';
|
||
|
}
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 查询行数限制
|
||
|
*
|
||
|
* @param integer $limit 需要查询的行数
|
||
|
* @return Typecho_Db_Query
|
||
|
*/
|
||
|
public function limit($limit)
|
||
|
{
|
||
|
$this->_sqlPreBuild['limit'] = intval($limit);
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 查询行数偏移量
|
||
|
*
|
||
|
* @param integer $offset 需要偏移的行数
|
||
|
* @return Typecho_Db_Query
|
||
|
*/
|
||
|
public function offset($offset)
|
||
|
{
|
||
|
$this->_sqlPreBuild['offset'] = intval($offset);
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 分页查询
|
||
|
*
|
||
|
* @param integer $page 页数
|
||
|
* @param integer $pageSize 每页行数
|
||
|
* @return Typecho_Db_Query
|
||
|
*/
|
||
|
public function page($page, $pageSize)
|
||
|
{
|
||
|
$pageSize = intval($pageSize);
|
||
|
$this->_sqlPreBuild['limit'] = $pageSize;
|
||
|
$this->_sqlPreBuild['offset'] = (max(intval($page), 1) - 1) * $pageSize;
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 指定需要写入的栏目及其值
|
||
|
*
|
||
|
* @param array $rows
|
||
|
* @return Typecho_Db_Query
|
||
|
*/
|
||
|
public function rows(array $rows)
|
||
|
{
|
||
|
foreach ($rows as $key => $row) {
|
||
|
$this->_sqlPreBuild['rows'][$this->filterColumn($key)] = is_null($row) ? 'NULL' : $this->_adapter->quoteValue($row);
|
||
|
}
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 指定需要写入栏目及其值
|
||
|
* 单行且不会转义引号
|
||
|
*
|
||
|
* @param string $key 栏目名称
|
||
|
* @param mixed $value 指定的值
|
||
|
* @param bool $escape 是否转义
|
||
|
* @return Typecho_Db_Query
|
||
|
*/
|
||
|
public function expression($key, $value, $escape = true)
|
||
|
{
|
||
|
$this->_sqlPreBuild['rows'][$this->filterColumn($key)] = $escape ? $this->filterColumn($value) : $value;
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 排序顺序(ORDER BY)
|
||
|
*
|
||
|
* @param string $orderby 排序的索引
|
||
|
* @param string $sort 排序的方式(ASC, DESC)
|
||
|
* @return Typecho_Db_Query
|
||
|
*/
|
||
|
public function order($orderby, $sort = Typecho_Db::SORT_ASC)
|
||
|
{
|
||
|
$this->_sqlPreBuild['order'] = ' ORDER BY ' . $this->filterColumn($orderby) . (empty($sort) ? NULL : ' ' . $sort);
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 集合聚集(GROUP BY)
|
||
|
*
|
||
|
* @param string $key 聚集的键值
|
||
|
* @return Typecho_Db_Query
|
||
|
*/
|
||
|
public function group($key)
|
||
|
{
|
||
|
$this->_sqlPreBuild['group'] = ' GROUP BY ' . $this->filterColumn($key);
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* HAVING (HAVING)
|
||
|
*
|
||
|
* @return Typecho_Db_Query
|
||
|
*/
|
||
|
public function having()
|
||
|
{
|
||
|
$condition = func_get_arg(0);
|
||
|
$condition = str_replace('?', "%s", $this->filterColumn($condition));
|
||
|
$operator = empty($this->_sqlPreBuild['having']) ? ' HAVING ' : ' AND';
|
||
|
|
||
|
if (func_num_args() <= 1) {
|
||
|
$this->_sqlPreBuild['having'] .= $operator . ' (' . $condition . ')';
|
||
|
} else {
|
||
|
$args = func_get_args();
|
||
|
array_shift($args);
|
||
|
$this->_sqlPreBuild['having'] .= $operator . ' (' . vsprintf($condition, $this->quoteValues($args)) . ')';
|
||
|
}
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 选择查询字段
|
||
|
*
|
||
|
* @access public
|
||
|
* @param mixed $field 查询字段
|
||
|
* @return Typecho_Db_Query
|
||
|
*/
|
||
|
public function select($field = '*')
|
||
|
{
|
||
|
$this->_sqlPreBuild['action'] = Typecho_Db::SELECT;
|
||
|
$args = func_get_args();
|
||
|
|
||
|
$this->_sqlPreBuild['fields'] = $this->getColumnFromParameters($args);
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 查询记录操作(SELECT)
|
||
|
*
|
||
|
* @param string $table 查询的表
|
||
|
* @return Typecho_Db_Query
|
||
|
*/
|
||
|
public function from($table)
|
||
|
{
|
||
|
$this->_sqlPreBuild['table'] = $this->filterPrefix($table);
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 更新记录操作(UPDATE)
|
||
|
*
|
||
|
* @param string $table 需要更新记录的表
|
||
|
* @return Typecho_Db_Query
|
||
|
*/
|
||
|
public function update($table)
|
||
|
{
|
||
|
$this->_sqlPreBuild['action'] = Typecho_Db::UPDATE;
|
||
|
$this->_sqlPreBuild['table'] = $this->filterPrefix($table);
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 删除记录操作(DELETE)
|
||
|
*
|
||
|
* @param string $table 需要删除记录的表
|
||
|
* @return Typecho_Db_Query
|
||
|
*/
|
||
|
public function delete($table)
|
||
|
{
|
||
|
$this->_sqlPreBuild['action'] = Typecho_Db::DELETE;
|
||
|
$this->_sqlPreBuild['table'] = $this->filterPrefix($table);
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 插入记录操作(INSERT)
|
||
|
*
|
||
|
* @param string $table 需要插入记录的表
|
||
|
* @return Typecho_Db_Query
|
||
|
*/
|
||
|
public function insert($table)
|
||
|
{
|
||
|
$this->_sqlPreBuild['action'] = Typecho_Db::INSERT;
|
||
|
$this->_sqlPreBuild['table'] = $this->filterPrefix($table);
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param $query
|
||
|
* @return string
|
||
|
*/
|
||
|
public function prepare($query)
|
||
|
{
|
||
|
$params = $this->_params;
|
||
|
$adapter = $this->_adapter;
|
||
|
|
||
|
return preg_replace_callback("/#param:([0-9]+)#/", function ($matches) use ($params, $adapter) {
|
||
|
if (array_key_exists($matches[1], $params)) {
|
||
|
return $adapter->quoteValue($params[$matches[1]]);
|
||
|
} else {
|
||
|
return $matches[0];
|
||
|
}
|
||
|
}, $query);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 构造最终查询语句
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public function __toString()
|
||
|
{
|
||
|
switch ($this->_sqlPreBuild['action']) {
|
||
|
case Typecho_Db::SELECT:
|
||
|
return $this->_adapter->parseSelect($this->_sqlPreBuild);
|
||
|
case Typecho_Db::INSERT:
|
||
|
return 'INSERT INTO '
|
||
|
. $this->_sqlPreBuild['table']
|
||
|
. '(' . implode(' , ', array_keys($this->_sqlPreBuild['rows'])) . ')'
|
||
|
. ' VALUES '
|
||
|
. '(' . implode(' , ', array_values($this->_sqlPreBuild['rows'])) . ')'
|
||
|
. $this->_sqlPreBuild['limit'];
|
||
|
case Typecho_Db::DELETE:
|
||
|
return 'DELETE FROM '
|
||
|
. $this->_sqlPreBuild['table']
|
||
|
. $this->_sqlPreBuild['where'];
|
||
|
case Typecho_Db::UPDATE:
|
||
|
$columns = array();
|
||
|
if (isset($this->_sqlPreBuild['rows'])) {
|
||
|
foreach ($this->_sqlPreBuild['rows'] as $key => $val) {
|
||
|
$columns[] = "$key = $val";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 'UPDATE '
|
||
|
. $this->_sqlPreBuild['table']
|
||
|
. ' SET ' . implode(' , ', $columns)
|
||
|
. $this->_sqlPreBuild['where'];
|
||
|
default:
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
}
|