This commit is contained in:
2020-03-27 10:13:51 +07:00
commit da1024a5b3
16614 changed files with 3274282 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\conditions;
/**
* Condition that connects two or more SQL expressions with the `AND` operator.
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
class AndCondition extends ConjunctionCondition
{
/**
* Returns the operator that is represented by this condition class, e.g. `AND`, `OR`.
*
* @return string
*/
public function getOperator()
{
return 'AND';
}
}

View File

@@ -0,0 +1,121 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\conditions;
use yii\base\InvalidArgumentException;
use yii\db\ExpressionInterface;
use yii\db\Query;
/**
* Class BetweenColumnCondition represents a `BETWEEN` condition where
* values is between two columns. For example:
*
* ```php
* new BetweenColumnsCondition(42, 'BETWEEN', 'min_value', 'max_value')
* // Will be build to:
* // 42 BETWEEN min_value AND max_value
* ```
*
* And a more complex example:
*
* ```php
* new BetweenColumnsCondition(
* new Expression('NOW()'),
* 'NOT BETWEEN',
* (new Query)->select('time')->from('log')->orderBy('id ASC')->limit(1),
* 'update_time'
* );
*
* // Will be built to:
* // NOW() NOT BETWEEN (SELECT time FROM log ORDER BY id ASC LIMIT 1) AND update_time
* ```
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
class BetweenColumnsCondition implements ConditionInterface
{
/**
* @var string $operator the operator to use (e.g. `BETWEEN` or `NOT BETWEEN`)
*/
private $operator;
/**
* @var mixed the value to compare against
*/
private $value;
/**
* @var string|ExpressionInterface|Query the column name or expression that is a beginning of the interval
*/
private $intervalStartColumn;
/**
* @var string|ExpressionInterface|Query the column name or expression that is an end of the interval
*/
private $intervalEndColumn;
/**
* Creates a condition with the `BETWEEN` operator.
*
* @param mixed the value to compare against
* @param string $operator the operator to use (e.g. `BETWEEN` or `NOT BETWEEN`)
* @param string|ExpressionInterface $intervalStartColumn the column name or expression that is a beginning of the interval
* @param string|ExpressionInterface $intervalEndColumn the column name or expression that is an end of the interval
*/
public function __construct($value, $operator, $intervalStartColumn, $intervalEndColumn)
{
$this->value = $value;
$this->operator = $operator;
$this->intervalStartColumn = $intervalStartColumn;
$this->intervalEndColumn = $intervalEndColumn;
}
/**
* @return string
*/
public function getOperator()
{
return $this->operator;
}
/**
* @return mixed
*/
public function getValue()
{
return $this->value;
}
/**
* @return string|ExpressionInterface|Query
*/
public function getIntervalStartColumn()
{
return $this->intervalStartColumn;
}
/**
* @return string|ExpressionInterface|Query
*/
public function getIntervalEndColumn()
{
return $this->intervalEndColumn;
}
/**
* {@inheritdoc}
* @throws InvalidArgumentException if wrong number of operands have been given.
*/
public static function fromArrayDefinition($operator, $operands)
{
if (!isset($operands[0], $operands[1], $operands[2])) {
throw new InvalidArgumentException("Operator '$operator' requires three operands.");
}
return new static($operands[0], $operator, $operands[1], $operands[2]);
}
}

View File

@@ -0,0 +1,81 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\conditions;
use yii\db\ExpressionBuilderInterface;
use yii\db\ExpressionBuilderTrait;
use yii\db\ExpressionInterface;
use yii\db\Query;
/**
* Class BetweenColumnsConditionBuilder builds objects of [[BetweenColumnsCondition]]
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
class BetweenColumnsConditionBuilder implements ExpressionBuilderInterface
{
use ExpressionBuilderTrait;
/**
* Method builds the raw SQL from the $expression that will not be additionally
* escaped or quoted.
*
* @param ExpressionInterface|BetweenColumnsCondition $expression the expression to be built.
* @param array $params the binding parameters.
* @return string the raw SQL that will not be additionally escaped or quoted.
*/
public function build(ExpressionInterface $expression, array &$params = [])
{
$operator = $expression->getOperator();
$startColumn = $this->escapeColumnName($expression->getIntervalStartColumn(), $params);
$endColumn = $this->escapeColumnName($expression->getIntervalEndColumn(), $params);
$value = $this->createPlaceholder($expression->getValue(), $params);
return "$value $operator $startColumn AND $endColumn";
}
/**
* Prepares column name to be used in SQL statement.
*
* @param Query|ExpressionInterface|string $columnName
* @param array $params the binding parameters.
* @return string
*/
protected function escapeColumnName($columnName, &$params = [])
{
if ($columnName instanceof Query) {
list($sql, $params) = $this->queryBuilder->build($columnName, $params);
return "($sql)";
} elseif ($columnName instanceof ExpressionInterface) {
return $this->queryBuilder->buildExpression($columnName, $params);
} elseif (strpos($columnName, '(') === false) {
return $this->queryBuilder->db->quoteColumnName($columnName);
}
return $columnName;
}
/**
* Attaches $value to $params array and returns placeholder.
*
* @param mixed $value
* @param array $params passed by reference
* @return string
*/
protected function createPlaceholder($value, &$params)
{
if ($value instanceof ExpressionInterface) {
return $this->queryBuilder->buildExpression($value, $params);
}
return $this->queryBuilder->bindParam($value, $params);
}
}

View File

@@ -0,0 +1,98 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\conditions;
use yii\base\InvalidArgumentException;
/**
* Class BetweenCondition represents a `BETWEEN` condition.
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
class BetweenCondition implements ConditionInterface
{
/**
* @var string $operator the operator to use (e.g. `BETWEEN` or `NOT BETWEEN`)
*/
private $operator;
/**
* @var mixed the column name to the left of [[operator]]
*/
private $column;
/**
* @var mixed beginning of the interval
*/
private $intervalStart;
/**
* @var mixed end of the interval
*/
private $intervalEnd;
/**
* Creates a condition with the `BETWEEN` operator.
*
* @param mixed $column the literal to the left of $operator
* @param string $operator the operator to use (e.g. `BETWEEN` or `NOT BETWEEN`)
* @param mixed $intervalStart beginning of the interval
* @param mixed $intervalEnd end of the interval
*/
public function __construct($column, $operator, $intervalStart, $intervalEnd)
{
$this->column = $column;
$this->operator = $operator;
$this->intervalStart = $intervalStart;
$this->intervalEnd = $intervalEnd;
}
/**
* @return string
*/
public function getOperator()
{
return $this->operator;
}
/**
* @return mixed
*/
public function getColumn()
{
return $this->column;
}
/**
* @return mixed
*/
public function getIntervalStart()
{
return $this->intervalStart;
}
/**
* @return mixed
*/
public function getIntervalEnd()
{
return $this->intervalEnd;
}
/**
* {@inheritdoc}
* @throws InvalidArgumentException if wrong number of operands have been given.
*/
public static function fromArrayDefinition($operator, $operands)
{
if (!isset($operands[0], $operands[1], $operands[2])) {
throw new InvalidArgumentException("Operator '$operator' requires three operands.");
}
return new static($operands[0], $operator, $operands[1], $operands[2]);
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\conditions;
use yii\db\ExpressionBuilderInterface;
use yii\db\ExpressionBuilderTrait;
use yii\db\ExpressionInterface;
/**
* Class BetweenConditionBuilder builds objects of [[BetweenCondition]]
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
class BetweenConditionBuilder implements ExpressionBuilderInterface
{
use ExpressionBuilderTrait;
/**
* Method builds the raw SQL from the $expression that will not be additionally
* escaped or quoted.
*
* @param ExpressionInterface|BetweenCondition $expression the expression to be built.
* @param array $params the binding parameters.
* @return string the raw SQL that will not be additionally escaped or quoted.
*/
public function build(ExpressionInterface $expression, array &$params = [])
{
$operator = $expression->getOperator();
$column = $expression->getColumn();
if (strpos($column, '(') === false) {
$column = $this->queryBuilder->db->quoteColumnName($column);
}
$phName1 = $this->createPlaceholder($expression->getIntervalStart(), $params);
$phName2 = $this->createPlaceholder($expression->getIntervalEnd(), $params);
return "$column $operator $phName1 AND $phName2";
}
/**
* Attaches $value to $params array and returns placeholder.
*
* @param mixed $value
* @param array $params passed by reference
* @return string
*/
protected function createPlaceholder($value, &$params)
{
if ($value instanceof ExpressionInterface) {
return $this->queryBuilder->buildExpression($value, $params);
}
return $this->queryBuilder->bindParam($value, $params);
}
}

View File

@@ -0,0 +1,33 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\conditions;
use yii\base\InvalidParamException;
use yii\db\ExpressionInterface;
/**
* Interface ConditionInterface should be implemented by classes that represent a condition
* in DBAL of framework.
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
interface ConditionInterface extends ExpressionInterface
{
/**
* Creates object by array-definition as described in
* [Query Builder  Operator format](guide:db-query-builder#operator-format) guide article.
*
* @param string $operator operator in uppercase.
* @param array $operands array of corresponding operands
*
* @return $this
* @throws InvalidParamException if input parameters are not suitable for this condition
*/
public static function fromArrayDefinition($operator, $operands);
}

View File

@@ -0,0 +1,53 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\conditions;
/**
* Class ConjunctionCondition
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
abstract class ConjunctionCondition implements ConditionInterface
{
/**
* @var mixed[]
*/
protected $expressions;
/**
* @param mixed $expressions
*/
public function __construct($expressions) // TODO: use variadic params when PHP>5.6
{
$this->expressions = $expressions;
}
/**
* @return mixed[]
*/
public function getExpressions()
{
return $this->expressions;
}
/**
* Returns the operator that is represented by this condition class, e.g. `AND`, `OR`.
* @return string
*/
abstract public function getOperator();
/**
* {@inheritdoc}
*/
public static function fromArrayDefinition($operator, $operands)
{
return new static($operands);
}
}

View File

@@ -0,0 +1,72 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\conditions;
use yii\db\ExpressionBuilderInterface;
use yii\db\ExpressionBuilderTrait;
use yii\db\ExpressionInterface;
/**
* Class ConjunctionConditionBuilder builds objects of abstract class [[ConjunctionCondition]]
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
class ConjunctionConditionBuilder implements ExpressionBuilderInterface
{
use ExpressionBuilderTrait;
/**
* Method builds the raw SQL from the $expression that will not be additionally
* escaped or quoted.
*
* @param ExpressionInterface|ConjunctionCondition $condition the expression to be built.
* @param array $params the binding parameters.
* @return string the raw SQL that will not be additionally escaped or quoted.
*/
public function build(ExpressionInterface $condition, array &$params = [])
{
$parts = $this->buildExpressionsFrom($condition, $params);
if (empty($parts)) {
return '';
}
if (count($parts) === 1) {
return reset($parts);
}
return '(' . implode(") {$condition->getOperator()} (", $parts) . ')';
}
/**
* Builds expressions, that are stored in $condition
*
* @param ExpressionInterface|ConjunctionCondition $condition the expression to be built.
* @param array $params the binding parameters.
* @return string[]
*/
private function buildExpressionsFrom(ExpressionInterface $condition, &$params = [])
{
$parts = [];
foreach ($condition->getExpressions() as $condition) {
if (is_array($condition)) {
$condition = $this->queryBuilder->buildCondition($condition, $params);
}
if ($condition instanceof ExpressionInterface) {
$condition = $this->queryBuilder->buildExpression($condition, $params);
}
if ($condition !== '') {
$parts[] = $condition;
}
}
return $parts;
}
}

View File

@@ -0,0 +1,70 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\conditions;
use yii\base\InvalidArgumentException;
use yii\db\Query;
/**
* Condition that represents `EXISTS` operator.
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
class ExistsCondition implements ConditionInterface
{
/**
* @var string $operator the operator to use (e.g. `EXISTS` or `NOT EXISTS`)
*/
private $operator;
/**
* @var Query the [[Query]] object representing the sub-query.
*/
private $query;
/**
* ExistsCondition constructor.
*
* @param string $operator the operator to use (e.g. `EXISTS` or `NOT EXISTS`)
* @param Query $query the [[Query]] object representing the sub-query.
*/
public function __construct($operator, $query)
{
$this->operator = $operator;
$this->query = $query;
}
/**
* {@inheritdoc}
*/
public static function fromArrayDefinition($operator, $operands)
{
if (!isset($operands[0]) || !$operands[0] instanceof Query) {
throw new InvalidArgumentException('Subquery for EXISTS operator must be a Query object.');
}
return new static($operator, $operands[0]);
}
/**
* @return string
*/
public function getOperator()
{
return $this->operator;
}
/**
* @return Query
*/
public function getQuery()
{
return $this->query;
}
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\conditions;
use yii\db\ExpressionBuilderInterface;
use yii\db\ExpressionBuilderTrait;
use yii\db\ExpressionInterface;
/**
* Class ExistsConditionBuilder builds objects of [[ExistsCondition]]
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
class ExistsConditionBuilder implements ExpressionBuilderInterface
{
use ExpressionBuilderTrait;
/**
* Method builds the raw SQL from the $expression that will not be additionally
* escaped or quoted.
*
* @param ExpressionInterface|ExistsCondition $expression the expression to be built.
* @param array $params the binding parameters.
* @return string the raw SQL that will not be additionally escaped or quoted.
*/
public function build(ExpressionInterface $expression, array &$params = [])
{
$operator = $expression->getOperator();
$query = $expression->getQuery();
$sql = $this->queryBuilder->buildExpression($query, $params);
return "$operator $sql";
}
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\conditions;
/**
* Condition based on column-value pairs.
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
class HashCondition implements ConditionInterface
{
/**
* @var array|null the condition specification.
*/
private $hash;
/**
* HashCondition constructor.
*
* @param array|null $hash
*/
public function __construct($hash)
{
$this->hash = $hash;
}
/**
* @return array|null
*/
public function getHash()
{
return $this->hash;
}
/**
* {@inheritdoc}
*/
public static function fromArrayDefinition($operator, $operands)
{
return new static($operands);
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\conditions;
use yii\db\ExpressionBuilderInterface;
use yii\db\ExpressionBuilderTrait;
use yii\db\ExpressionInterface;
use yii\db\Query;
use yii\helpers\ArrayHelper;
/**
* Class HashConditionBuilder builds objects of [[HashCondition]]
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
class HashConditionBuilder implements ExpressionBuilderInterface
{
use ExpressionBuilderTrait;
/**
* Method builds the raw SQL from the $expression that will not be additionally
* escaped or quoted.
*
* @param ExpressionInterface|HashCondition $expression the expression to be built.
* @param array $params the binding parameters.
* @return string the raw SQL that will not be additionally escaped or quoted.
*/
public function build(ExpressionInterface $expression, array &$params = [])
{
$hash = $expression->getHash();
$parts = [];
foreach ($hash as $column => $value) {
if (ArrayHelper::isTraversable($value) || $value instanceof Query) {
// IN condition
$parts[] = $this->queryBuilder->buildCondition(new InCondition($column, 'IN', $value), $params);
} else {
if (strpos($column, '(') === false) {
$column = $this->queryBuilder->db->quoteColumnName($column);
}
if ($value === null) {
$parts[] = "$column IS NULL";
} elseif ($value instanceof ExpressionInterface) {
$parts[] = "$column=" . $this->queryBuilder->buildExpression($value, $params);
} else {
$phName = $this->queryBuilder->bindParam($value, $params);
$parts[] = "$column=$phName";
}
}
}
return count($parts) === 1 ? $parts[0] : '(' . implode(') AND (', $parts) . ')';
}
}

View File

@@ -0,0 +1,89 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\conditions;
use yii\base\InvalidArgumentException;
use yii\db\ExpressionInterface;
/**
* Class InCondition represents `IN` condition.
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
class InCondition implements ConditionInterface
{
/**
* @var string $operator the operator to use (e.g. `IN` or `NOT IN`)
*/
private $operator;
/**
* @var string|string[] the column name. If it is an array, a composite `IN` condition
* will be generated.
*/
private $column;
/**
* @var ExpressionInterface[]|string[]|int[] an array of values that [[column]] value should be among.
* If it is an empty array the generated expression will be a `false` value if
* [[operator]] is `IN` and empty if operator is `NOT IN`.
*/
private $values;
/**
* SimpleCondition constructor
*
* @param string|string[] the column name. If it is an array, a composite `IN` condition
* will be generated.
* @param string $operator the operator to use (e.g. `IN` or `NOT IN`)
* @param array an array of values that [[column]] value should be among. If it is an empty array the generated
* expression will be a `false` value if [[operator]] is `IN` and empty if operator is `NOT IN`.
*/
public function __construct($column, $operator, $values)
{
$this->column = $column;
$this->operator = $operator;
$this->values = $values;
}
/**
* @return string
*/
public function getOperator()
{
return $this->operator;
}
/**
* @return mixed
*/
public function getColumn()
{
return $this->column;
}
/**
* @return ExpressionInterface[]|string[]|int[]
*/
public function getValues()
{
return $this->values;
}
/**
* {@inheritdoc}
* @throws InvalidArgumentException if wrong number of operands have been given.
*/
public static function fromArrayDefinition($operator, $operands)
{
if (!isset($operands[0], $operands[1])) {
throw new InvalidArgumentException("Operator '$operator' requires two operands.");
}
return new static($operands[0], $operator, $operands[1]);
}
}

View File

@@ -0,0 +1,172 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\conditions;
use yii\db\ExpressionBuilderInterface;
use yii\db\ExpressionBuilderTrait;
use yii\db\ExpressionInterface;
use yii\db\Query;
/**
* Class InConditionBuilder builds objects of [[InCondition]]
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
class InConditionBuilder implements ExpressionBuilderInterface
{
use ExpressionBuilderTrait;
/**
* Method builds the raw SQL from the $expression that will not be additionally
* escaped or quoted.
*
* @param ExpressionInterface|InCondition $expression the expression to be built.
* @param array $params the binding parameters.
* @return string the raw SQL that will not be additionally escaped or quoted.
*/
public function build(ExpressionInterface $expression, array &$params = [])
{
$operator = $expression->getOperator();
$column = $expression->getColumn();
$values = $expression->getValues();
if ($column === []) {
// no columns to test against
return $operator === 'IN' ? '0=1' : '';
}
if ($values instanceof Query) {
return $this->buildSubqueryInCondition($operator, $column, $values, $params);
}
if (!is_array($values) && !$values instanceof \Traversable) {
// ensure values is an array
$values = (array) $values;
}
if ($column instanceof \Traversable || ((is_array($column) || $column instanceof \Countable) && count($column) > 1)) {
return $this->buildCompositeInCondition($operator, $column, $values, $params);
}
if (is_array($column)) {
$column = reset($column);
}
$sqlValues = $this->buildValues($expression, $values, $params);
if (empty($sqlValues)) {
return $operator === 'IN' ? '0=1' : '';
}
if (strpos($column, '(') === false) {
$column = $this->queryBuilder->db->quoteColumnName($column);
}
if (count($sqlValues) > 1) {
return "$column $operator (" . implode(', ', $sqlValues) . ')';
}
$operator = $operator === 'IN' ? '=' : '<>';
return $column . $operator . reset($sqlValues);
}
/**
* Builds $values to be used in [[InCondition]]
*
* @param ConditionInterface|InCondition $condition
* @param array $values
* @param array $params the binding parameters
* @return array of prepared for SQL placeholders
*/
protected function buildValues(ConditionInterface $condition, $values, &$params)
{
$sqlValues = [];
$column = $condition->getColumn();
foreach ($values as $i => $value) {
if (is_array($value) || $value instanceof \ArrayAccess) {
$value = isset($value[$column]) ? $value[$column] : null;
}
if ($value === null) {
$sqlValues[$i] = 'NULL';
} elseif ($value instanceof ExpressionInterface) {
$sqlValues[$i] = $this->queryBuilder->buildExpression($value, $params);
} else {
$sqlValues[$i] = $this->queryBuilder->bindParam($value, $params);
}
}
return $sqlValues;
}
/**
* Builds SQL for IN condition.
*
* @param string $operator
* @param array|string $columns
* @param Query $values
* @param array $params
* @return string SQL
*/
protected function buildSubqueryInCondition($operator, $columns, $values, &$params)
{
$sql = $this->queryBuilder->buildExpression($values, $params);
if (is_array($columns)) {
foreach ($columns as $i => $col) {
if (strpos($col, '(') === false) {
$columns[$i] = $this->queryBuilder->db->quoteColumnName($col);
}
}
return '(' . implode(', ', $columns) . ") $operator $sql";
}
if (strpos($columns, '(') === false) {
$columns = $this->queryBuilder->db->quoteColumnName($columns);
}
return "$columns $operator $sql";
}
/**
* Builds SQL for IN condition.
*
* @param string $operator
* @param array|\Traversable $columns
* @param array $values
* @param array $params
* @return string SQL
*/
protected function buildCompositeInCondition($operator, $columns, $values, &$params)
{
$vss = [];
foreach ($values as $value) {
$vs = [];
foreach ($columns as $column) {
if (isset($value[$column])) {
$vs[] = $this->queryBuilder->bindParam($value[$column], $params);
} else {
$vs[] = 'NULL';
}
}
$vss[] = '(' . implode(', ', $vs) . ')';
}
if (empty($vss)) {
return $operator === 'IN' ? '0=1' : '';
}
$sqlColumns = [];
foreach ($columns as $i => $column) {
$sqlColumns[] = strpos($column, '(') === false ? $this->queryBuilder->db->quoteColumnName($column) : $column;
}
return '(' . implode(', ', $sqlColumns) . ") $operator (" . implode(', ', $vss) . ')';
}
}

View File

@@ -0,0 +1,78 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\conditions;
use yii\base\InvalidArgumentException;
/**
* Class LikeCondition represents a `LIKE` condition.
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
class LikeCondition extends SimpleCondition
{
/**
* @var array|false map of chars to their replacements, false if characters should not be escaped
* or either null or empty array if escaping is condition builder responsibility.
* By default it's set to `null`.
*/
protected $escapingReplacements;
/**
* @param string $column the column name.
* @param string $operator the operator to use (e.g. `LIKE`, `NOT LIKE`, `OR LIKE` or `OR NOT LIKE`)
* @param string[]|string $value single value or an array of values that $column should be compared with.
* If it is an empty array the generated expression will be a `false` value if operator is `LIKE` or `OR LIKE`
* and empty if operator is `NOT LIKE` or `OR NOT LIKE`.
*/
public function __construct($column, $operator, $value)
{
parent::__construct($column, $operator, $value);
}
/**
* This method allows to specify how to escape special characters in the value(s).
*
* @param array an array of mappings from the special characters to their escaped counterparts.
* You may use `false` or an empty array to indicate the values are already escaped and no escape
* should be applied. Note that when using an escape mapping (or the third operand is not provided),
* the values will be automatically enclosed within a pair of percentage characters.
*/
public function setEscapingReplacements($escapingReplacements)
{
$this->escapingReplacements = $escapingReplacements;
}
/**
* @return array|false
*/
public function getEscapingReplacements()
{
return $this->escapingReplacements;
}
/**
* {@inheritdoc}
* @throws InvalidArgumentException if wrong number of operands have been given.
*/
public static function fromArrayDefinition($operator, $operands)
{
if (!isset($operands[0], $operands[1])) {
throw new InvalidArgumentException("Operator '$operator' requires two operands.");
}
$condition = new static($operands[0], $operator, $operands[1]);
if (isset($operands[2])) {
$condition->escapingReplacements = $operands[2];
}
return $condition;
}
}

View File

@@ -0,0 +1,114 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\conditions;
use yii\base\InvalidArgumentException;
use yii\db\ExpressionBuilderInterface;
use yii\db\ExpressionBuilderTrait;
use yii\db\ExpressionInterface;
/**
* Class LikeConditionBuilder builds objects of [[LikeCondition]]
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
class LikeConditionBuilder implements ExpressionBuilderInterface
{
use ExpressionBuilderTrait;
/**
* @var array map of chars to their replacements in LIKE conditions.
* By default it's configured to escape `%`, `_` and `\` with `\`.
*/
protected $escapingReplacements = [
'%' => '\%',
'_' => '\_',
'\\' => '\\\\',
];
/**
* @var string|null character used to escape special characters in LIKE conditions.
* By default it's assumed to be `\`.
*/
protected $escapeCharacter;
/**
* Method builds the raw SQL from the $expression that will not be additionally
* escaped or quoted.
*
* @param ExpressionInterface|LikeCondition $expression the expression to be built.
* @param array $params the binding parameters.
* @return string the raw SQL that will not be additionally escaped or quoted.
*/
public function build(ExpressionInterface $expression, array &$params = [])
{
$operator = $expression->getOperator();
$column = $expression->getColumn();
$values = $expression->getValue();
$escape = $expression->getEscapingReplacements();
if ($escape === null || $escape === []) {
$escape = $this->escapingReplacements;
}
list($andor, $not, $operator) = $this->parseOperator($operator);
if (!is_array($values)) {
$values = [$values];
}
if (empty($values)) {
return $not ? '' : '0=1';
}
if (strpos($column, '(') === false) {
$column = $this->queryBuilder->db->quoteColumnName($column);
}
$escapeSql = $this->getEscapeSql();
$parts = [];
foreach ($values as $value) {
if ($value instanceof ExpressionInterface) {
$phName = $this->queryBuilder->buildExpression($value, $params);
} else {
$phName = $this->queryBuilder->bindParam(empty($escape) ? $value : ('%' . strtr($value, $escape) . '%'), $params);
}
$parts[] = "{$column} {$operator} {$phName}{$escapeSql}";
}
return implode($andor, $parts);
}
/**
* @return string
*/
private function getEscapeSql()
{
if ($this->escapeCharacter !== null) {
return " ESCAPE '{$this->escapeCharacter}'";
}
return '';
}
/**
* @param string $operator
* @return array
*/
protected function parseOperator($operator)
{
if (!preg_match('/^(AND |OR |)(((NOT |))I?LIKE)/', $operator, $matches)) {
throw new InvalidArgumentException("Invalid operator '$operator'.");
}
$andor = ' ' . (!empty($matches[1]) ? $matches[1] : 'AND ');
$not = !empty($matches[3]);
$operator = $matches[2];
return [$andor, $not, $operator];
}
}

View File

@@ -0,0 +1,56 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\conditions;
use yii\base\InvalidArgumentException;
/**
* Condition that inverts passed [[condition]].
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
class NotCondition implements ConditionInterface
{
/**
* @var mixed the condition to be negated
*/
private $condition;
/**
* NotCondition constructor.
*
* @param mixed $condition the condition to be negated
*/
public function __construct($condition)
{
$this->condition = $condition;
}
/**
* @return mixed
*/
public function getCondition()
{
return $this->condition;
}
/**
* {@inheritdoc}
* @throws InvalidArgumentException if wrong number of operands have been given.
*/
public static function fromArrayDefinition($operator, $operands)
{
if (count($operands) !== 1) {
throw new InvalidArgumentException("Operator '$operator' requires exactly one operand.");
}
return new static(array_shift($operands));
}
}

View File

@@ -0,0 +1,51 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\conditions;
use yii\db\ExpressionBuilderInterface;
use yii\db\ExpressionBuilderTrait;
use yii\db\ExpressionInterface;
/**
* Class NotConditionBuilder builds objects of [[NotCondition]]
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
class NotConditionBuilder implements ExpressionBuilderInterface
{
use ExpressionBuilderTrait;
/**
* Method builds the raw SQL from the $expression that will not be additionally
* escaped or quoted.
*
* @param ExpressionInterface|NotCondition $expression the expression to be built.
* @param array $params the binding parameters.
* @return string the raw SQL that will not be additionally escaped or quoted.
*/
public function build(ExpressionInterface $expression, array &$params = [])
{
$operand = $expression->getCondition();
if ($operand === '') {
return '';
}
$expession = $this->queryBuilder->buildCondition($operand, $params);
return "{$this->getNegationOperator()} ($expession)";
}
/**
* @return string
*/
protected function getNegationOperator()
{
return 'NOT';
}
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\conditions;
/**
* Condition that connects two or more SQL expressions with the `AND` operator.
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
class OrCondition extends ConjunctionCondition
{
/**
* Returns the operator that is represented by this condition class, e.g. `AND`, `OR`.
*
* @return string
*/
public function getOperator()
{
return 'OR';
}
}

View File

@@ -0,0 +1,84 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\conditions;
use yii\base\InvalidArgumentException;
/**
* Class SimpleCondition represents a simple condition like `"column" operator value`.
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
class SimpleCondition implements ConditionInterface
{
/**
* @var string $operator the operator to use. Anything could be used e.g. `>`, `<=`, etc.
*/
private $operator;
/**
* @var mixed the column name to the left of [[operator]]
*/
private $column;
/**
* @var mixed the value to the right of the [[operator]]
*/
private $value;
/**
* SimpleCondition constructor
*
* @param mixed $column the literal to the left of $operator
* @param string $operator the operator to use. Anything could be used e.g. `>`, `<=`, etc.
* @param mixed $value the literal to the right of $operator
*/
public function __construct($column, $operator, $value)
{
$this->column = $column;
$this->operator = $operator;
$this->value = $value;
}
/**
* @return string
*/
public function getOperator()
{
return $this->operator;
}
/**
* @return mixed
*/
public function getColumn()
{
return $this->column;
}
/**
* @return mixed
*/
public function getValue()
{
return $this->value;
}
/**
* {@inheritdoc}
* @throws InvalidArgumentException if wrong number of operands have been given.
*/
public static function fromArrayDefinition($operator, $operands)
{
if (count($operands) !== 2) {
throw new InvalidArgumentException("Operator '$operator' requires two operands.");
}
return new static($operands[0], $operator, $operands[1]);
}
}

View File

@@ -0,0 +1,54 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\conditions;
use yii\db\ExpressionBuilderInterface;
use yii\db\ExpressionBuilderTrait;
use yii\db\ExpressionInterface;
use yii\db\Query;
/**
* Class NotConditionBuilder builds objects of [[SimpleCondition]]
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
class SimpleConditionBuilder implements ExpressionBuilderInterface
{
use ExpressionBuilderTrait;
/**
* Method builds the raw SQL from the $expression that will not be additionally
* escaped or quoted.
*
* @param ExpressionInterface|SimpleCondition $expression the expression to be built.
* @param array $params the binding parameters.
* @return string the raw SQL that will not be additionally escaped or quoted.
*/
public function build(ExpressionInterface $expression, array &$params = [])
{
$operator = $expression->getOperator();
$column = $expression->getColumn();
$value = $expression->getValue();
if (strpos($column, '(') === false) {
$column = $this->queryBuilder->db->quoteColumnName($column);
}
if ($value === null) {
return "$column $operator NULL";
}
if ($value instanceof ExpressionInterface) {
return "$column $operator {$this->queryBuilder->buildExpression($value, $params)}";
}
$phName = $this->queryBuilder->bindParam($value, $params);
return "$column $operator $phName";
}
}