| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349 |
- <?php
- declare(strict_types=1);
- /**
- * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
- * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- *
- * Licensed under The MIT License
- * For full copyright and license information, please see the LICENSE.txt
- * Redistributions of files must retain the above copyright notice.
- *
- * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- * @link https://cakephp.org CakePHP(tm) Project
- * @since 4.3.0
- * @license https://opensource.org/licenses/mit-license.php MIT License
- */
- namespace Cake\Database\Expression;
- use Cake\Database\ExpressionInterface;
- use Cake\Database\Query;
- use Cake\Database\Type\ExpressionTypeCasterTrait;
- use Cake\Database\TypeMap;
- use Cake\Database\ValueBinder;
- use Closure;
- use InvalidArgumentException;
- use LogicException;
- /**
- * Represents a SQL when/then clause with a fluid API
- */
- class WhenThenExpression implements ExpressionInterface
- {
- use CaseExpressionTrait;
- use ExpressionTypeCasterTrait;
- /**
- * The names of the clauses that are valid for use with the
- * `clause()` method.
- *
- * @var array<string>
- */
- protected $validClauseNames = [
- 'when',
- 'then',
- ];
- /**
- * The type map to use when using an array of conditions for the
- * `WHEN` value.
- *
- * @var \Cake\Database\TypeMap
- */
- protected $_typeMap;
- /**
- * Then `WHEN` value.
- *
- * @var \Cake\Database\ExpressionInterface|scalar|object|null
- */
- protected $when = null;
- /**
- * The `WHEN` value type.
- *
- * @var array|string|null
- */
- protected $whenType = null;
- /**
- * The `THEN` value.
- *
- * @var \Cake\Database\ExpressionInterface|scalar|object|null
- */
- protected $then = null;
- /**
- * Whether the `THEN` value has been defined, eg whether `then()`
- * has been invoked.
- *
- * @var bool
- */
- protected $hasThenBeenDefined = false;
- /**
- * The `THEN` result type.
- *
- * @var string|null
- */
- protected $thenType = null;
- /**
- * Constructor.
- *
- * @param \Cake\Database\TypeMap|null $typeMap The type map to use when using an array of conditions for the `WHEN`
- * value.
- */
- public function __construct(?TypeMap $typeMap = null)
- {
- if ($typeMap === null) {
- $typeMap = new TypeMap();
- }
- $this->_typeMap = $typeMap;
- }
- /**
- * Sets the `WHEN` value.
- *
- * @param \Cake\Database\ExpressionInterface|object|array|scalar $when The `WHEN` value. When using an array of
- * conditions, it must be compatible with `\Cake\Database\Query::where()`. Note that this argument is _not_
- * completely safe for use with user data, as a user supplied array would allow for raw SQL to slip in! If you
- * plan to use user data, either pass a single type for the `$type` argument (which forces the `$when` value to be
- * a non-array, and then always binds the data), use a conditions array where the user data is only passed on the
- * value side of the array entries, or custom bindings!
- * @param array|string|null $type The when value type. Either an associative array when using array style
- * conditions, or else a string. If no type is provided, the type will be tried to be inferred from the value.
- * @return $this
- * @throws \InvalidArgumentException In case the `$when` argument is neither a non-empty array, nor a scalar value,
- * an object, or an instance of `\Cake\Database\ExpressionInterface`.
- * @throws \InvalidArgumentException In case the `$type` argument is neither an array, a string, nor null.
- * @throws \InvalidArgumentException In case the `$when` argument is an array, and the `$type` argument is neither
- * an array, nor null.
- * @throws \InvalidArgumentException In case the `$when` argument is a non-array value, and the `$type` argument is
- * neither a string, nor null.
- * @see CaseStatementExpression::when() for a more detailed usage explanation.
- */
- public function when($when, $type = null)
- {
- if (
- !(is_array($when) && !empty($when)) &&
- !is_scalar($when) &&
- !is_object($when)
- ) {
- throw new InvalidArgumentException(sprintf(
- 'The `$when` argument must be either a non-empty array, a scalar value, an object, ' .
- 'or an instance of `\%s`, `%s` given.',
- ExpressionInterface::class,
- is_array($when) ? '[]' : get_debug_type($when)
- ));
- }
- if (
- $type !== null &&
- !is_array($type) &&
- !is_string($type)
- ) {
- throw new InvalidArgumentException(sprintf(
- 'The `$type` argument must be either an array, a string, or `null`, `%s` given.',
- get_debug_type($type)
- ));
- }
- if (is_array($when)) {
- if (
- $type !== null &&
- !is_array($type)
- ) {
- throw new InvalidArgumentException(sprintf(
- 'When using an array for the `$when` argument, the `$type` argument must be an ' .
- 'array too, `%s` given.',
- get_debug_type($type)
- ));
- }
- // avoid dirtying the type map for possible consecutive `when()` calls
- $typeMap = clone $this->_typeMap;
- if (
- is_array($type) &&
- count($type) > 0
- ) {
- $typeMap = $typeMap->setTypes($type);
- }
- $when = new QueryExpression($when, $typeMap);
- } else {
- if (
- $type !== null &&
- !is_string($type)
- ) {
- throw new InvalidArgumentException(sprintf(
- 'When using a non-array value for the `$when` argument, the `$type` argument must ' .
- 'be a string, `%s` given.',
- get_debug_type($type)
- ));
- }
- if (
- $type === null &&
- !($when instanceof ExpressionInterface)
- ) {
- $type = $this->inferType($when);
- }
- }
- $this->when = $when;
- $this->whenType = $type;
- return $this;
- }
- /**
- * Sets the `THEN` result value.
- *
- * @param \Cake\Database\ExpressionInterface|object|scalar|null $result The result value.
- * @param string|null $type The result type. If no type is provided, the type will be inferred from the given
- * result value.
- * @return $this
- */
- public function then($result, ?string $type = null)
- {
- if (
- $result !== null &&
- !is_scalar($result) &&
- !(is_object($result) && !($result instanceof Closure))
- ) {
- throw new InvalidArgumentException(sprintf(
- 'The `$result` argument must be either `null`, a scalar value, an object, ' .
- 'or an instance of `\%s`, `%s` given.',
- ExpressionInterface::class,
- get_debug_type($result)
- ));
- }
- $this->then = $result;
- if ($type === null) {
- $type = $this->inferType($result);
- }
- $this->thenType = $type;
- $this->hasThenBeenDefined = true;
- return $this;
- }
- /**
- * Returns the expression's result value type.
- *
- * @return string|null
- * @see WhenThenExpression::then()
- */
- public function getResultType(): ?string
- {
- return $this->thenType;
- }
- /**
- * Returns the available data for the given clause.
- *
- * ### Available clauses
- *
- * The following clause names are available:
- *
- * * `when`: The `WHEN` value.
- * * `then`: The `THEN` result value.
- *
- * @param string $clause The name of the clause to obtain.
- * @return \Cake\Database\ExpressionInterface|object|scalar|null
- * @throws \InvalidArgumentException In case the given clause name is invalid.
- */
- public function clause(string $clause)
- {
- if (!in_array($clause, $this->validClauseNames, true)) {
- throw new InvalidArgumentException(
- sprintf(
- 'The `$clause` argument must be one of `%s`, the given value `%s` is invalid.',
- implode('`, `', $this->validClauseNames),
- $clause
- )
- );
- }
- return $this->{$clause};
- }
- /**
- * @inheritDoc
- */
- public function sql(ValueBinder $binder): string
- {
- if ($this->when === null) {
- throw new LogicException('Case expression has incomplete when clause. Missing `when()`.');
- }
- if (!$this->hasThenBeenDefined) {
- throw new LogicException('Case expression has incomplete when clause. Missing `then()` after `when()`.');
- }
- $when = $this->when;
- if (
- is_string($this->whenType) &&
- !($when instanceof ExpressionInterface)
- ) {
- $when = $this->_castToExpression($when, $this->whenType);
- }
- if ($when instanceof Query) {
- $when = sprintf('(%s)', $when->sql($binder));
- } elseif ($when instanceof ExpressionInterface) {
- $when = $when->sql($binder);
- } else {
- $placeholder = $binder->placeholder('c');
- if (is_string($this->whenType)) {
- $whenType = $this->whenType;
- } else {
- $whenType = null;
- }
- $binder->bind($placeholder, $when, $whenType);
- $when = $placeholder;
- }
- $then = $this->compileNullableValue($binder, $this->then, $this->thenType);
- return "WHEN $when THEN $then";
- }
- /**
- * @inheritDoc
- */
- public function traverse(Closure $callback)
- {
- if ($this->when instanceof ExpressionInterface) {
- $callback($this->when);
- $this->when->traverse($callback);
- }
- if ($this->then instanceof ExpressionInterface) {
- $callback($this->then);
- $this->then->traverse($callback);
- }
- return $this;
- }
- /**
- * Clones the inner expression objects.
- *
- * @return void
- */
- public function __clone()
- {
- if ($this->when instanceof ExpressionInterface) {
- $this->when = clone $this->when;
- }
- if ($this->then instanceof ExpressionInterface) {
- $this->then = clone $this->then;
- }
- }
- }
|