FunctionExpression.php 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  11. * @link https://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license https://opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Database\Expression;
  16. use Cake\Database\ExpressionInterface;
  17. use Cake\Database\TypedResultInterface;
  18. use Cake\Database\TypedResultTrait;
  19. use Cake\Database\Type\ExpressionTypeCasterTrait;
  20. use Cake\Database\ValueBinder;
  21. /**
  22. * This class represents a function call string in a SQL statement. Calls can be
  23. * constructed by passing the name of the function and a list of params.
  24. * For security reasons, all params passed are quoted by default unless
  25. * explicitly told otherwise.
  26. */
  27. class FunctionExpression extends QueryExpression implements TypedResultInterface
  28. {
  29. use ExpressionTypeCasterTrait;
  30. use TypedResultTrait;
  31. /**
  32. * The name of the function to be constructed when generating the SQL string
  33. *
  34. * @var string
  35. */
  36. protected $_name;
  37. /**
  38. * Constructor. Takes a name for the function to be invoked and a list of params
  39. * to be passed into the function. Optionally you can pass a list of types to
  40. * be used for each bound param.
  41. *
  42. * By default, all params that are passed will be quoted. If you wish to use
  43. * literal arguments, you need to explicitly hint this function.
  44. *
  45. * ### Examples:
  46. *
  47. * `$f = new FunctionExpression('CONCAT', ['CakePHP', ' rules']);`
  48. *
  49. * Previous line will generate `CONCAT('CakePHP', ' rules')`
  50. *
  51. * `$f = new FunctionExpression('CONCAT', ['name' => 'literal', ' rules']);`
  52. *
  53. * Will produce `CONCAT(name, ' rules')`
  54. *
  55. * @param string $name the name of the function to be constructed
  56. * @param array $params list of arguments to be passed to the function
  57. * If associative the key would be used as argument when value is 'literal'
  58. * @param array $types associative array of types to be associated with the
  59. * passed arguments
  60. * @param string $returnType The return type of this expression
  61. */
  62. public function __construct($name, $params = [], $types = [], $returnType = 'string')
  63. {
  64. $this->_name = $name;
  65. $this->_returnType = $returnType;
  66. parent::__construct($params, $types, ',');
  67. }
  68. /**
  69. * Sets the name of the SQL function to be invoke in this expression.
  70. *
  71. * @param string $name The name of the function
  72. * @return $this
  73. */
  74. public function setName($name)
  75. {
  76. $this->_name = $name;
  77. return $this;
  78. }
  79. /**
  80. * Gets the name of the SQL function to be invoke in this expression.
  81. *
  82. * @return string
  83. */
  84. public function getName()
  85. {
  86. return $this->_name;
  87. }
  88. /**
  89. * Sets the name of the SQL function to be invoke in this expression,
  90. * if no value is passed it will return current name
  91. *
  92. * @deprecated 3.4.0 Use setName()/getName() instead.
  93. * @param string|null $name The name of the function
  94. * @return string|$this
  95. */
  96. public function name($name = null)
  97. {
  98. deprecationWarning(
  99. 'FunctionExpression::name() is deprecated. ' .
  100. 'Use FunctionExpression::setName()/getName() instead.'
  101. );
  102. if ($name !== null) {
  103. return $this->setName($name);
  104. }
  105. return $this->getName();
  106. }
  107. /**
  108. * Adds one or more arguments for the function call.
  109. *
  110. * @param array $params list of arguments to be passed to the function
  111. * If associative the key would be used as argument when value is 'literal'
  112. * @param array $types associative array of types to be associated with the
  113. * passed arguments
  114. * @param bool $prepend Whether to prepend or append to the list of arguments
  115. * @see \Cake\Database\Expression\FunctionExpression::__construct() for more details.
  116. * @return $this
  117. */
  118. public function add($params, $types = [], $prepend = false)
  119. {
  120. $put = $prepend ? 'array_unshift' : 'array_push';
  121. $typeMap = $this->getTypeMap()->setTypes($types);
  122. foreach ($params as $k => $p) {
  123. if ($p === 'literal') {
  124. $put($this->_conditions, $k);
  125. continue;
  126. }
  127. if ($p === 'identifier') {
  128. $put($this->_conditions, new IdentifierExpression($k));
  129. continue;
  130. }
  131. $type = $typeMap->type($k);
  132. if ($type !== null && !$p instanceof ExpressionInterface) {
  133. $p = $this->_castToExpression($p, $type);
  134. }
  135. if ($p instanceof ExpressionInterface) {
  136. $put($this->_conditions, $p);
  137. continue;
  138. }
  139. $put($this->_conditions, ['value' => $p, 'type' => $type]);
  140. }
  141. return $this;
  142. }
  143. /**
  144. * Returns the string representation of this object so that it can be used in a
  145. * SQL query. Note that values condition values are not included in the string,
  146. * in their place placeholders are put and can be replaced by the quoted values
  147. * accordingly.
  148. *
  149. * @param \Cake\Database\ValueBinder $generator Placeholder generator object
  150. * @return string
  151. */
  152. public function sql(ValueBinder $generator)
  153. {
  154. $parts = [];
  155. foreach ($this->_conditions as $condition) {
  156. if ($condition instanceof ExpressionInterface) {
  157. $condition = sprintf('%s', $condition->sql($generator));
  158. } elseif (is_array($condition)) {
  159. $p = $generator->placeholder('param');
  160. $generator->bind($p, $condition['value'], $condition['type']);
  161. $condition = $p;
  162. }
  163. $parts[] = $condition;
  164. }
  165. return $this->_name . sprintf('(%s)', implode(
  166. $this->_conjunction . ' ',
  167. $parts
  168. ));
  169. }
  170. /**
  171. * The name of the function is in itself an expression to generate, thus
  172. * always adding 1 to the amount of expressions stored in this object.
  173. *
  174. * @return int
  175. */
  176. public function count()
  177. {
  178. return 1 + count($this->_conditions);
  179. }
  180. }