TupleComparison.php 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://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. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Database\Expression;
  16. use Cake\Database\ExpressionInterface;
  17. use Cake\Database\ValueBinder;
  18. /**
  19. * This expression represents SQL fragments that are used for comparing one tuple
  20. * to another, one tuple to a set of other tuples or one tuple to an expression
  21. *
  22. * @internal
  23. */
  24. class TupleComparison extends Comparison
  25. {
  26. /**
  27. * Constructor
  28. *
  29. * @param string $fields the fields to use to form a tuple
  30. * @param array|ExpressionInterface $values the values to use to form a tuple
  31. * @param array $types the types names to use for casting each of the values, only
  32. * one type per position in the value array in needed
  33. * @param string $conjunction the operator used for comparing field and value
  34. */
  35. public function __construct($fields, $values, $types = [], $conjunction = '=')
  36. {
  37. parent::__construct($fields, $values, $types, $conjunction);
  38. $this->_type = (array)$types;
  39. }
  40. /**
  41. * Convert the expression into a SQL fragment.
  42. *
  43. * @param \Cake\Database\ValueBinder $generator Placeholder generator object
  44. * @return string
  45. */
  46. public function sql(ValueBinder $generator)
  47. {
  48. $template = '(%s) %s (%s)';
  49. $fields = [];
  50. $originalFields = $this->getField();
  51. if (!is_array($originalFields)) {
  52. $originalFields = [$originalFields];
  53. }
  54. foreach ($originalFields as $field) {
  55. $fields[] = $field instanceof ExpressionInterface ? $field->sql($generator) : $field;
  56. }
  57. $values = $this->_stringifyValues($generator);
  58. $field = implode(', ', $fields);
  59. return sprintf($template, $field, $this->_operator, $values);
  60. }
  61. /**
  62. * Returns a string with the values as placeholders in a string to be used
  63. * for the SQL version of this expression
  64. *
  65. * @param \Cake\Database\ValueBinder $generator The value binder to convert expressions with.
  66. * @return string
  67. */
  68. protected function _stringifyValues($generator)
  69. {
  70. $values = [];
  71. $parts = $this->getValue();
  72. if ($parts instanceof ExpressionInterface) {
  73. return $parts->sql($generator);
  74. }
  75. foreach ($parts as $i => $value) {
  76. if ($value instanceof ExpressionInterface) {
  77. $values[] = $value->sql($generator);
  78. continue;
  79. }
  80. $type = $this->_type;
  81. $multiType = is_array($type);
  82. $isMulti = $this->isMulti();
  83. $type = $multiType ? $type : str_replace('[]', '', $type);
  84. $type = $type ?: null;
  85. if ($isMulti) {
  86. $bound = [];
  87. foreach ($value as $k => $val) {
  88. $valType = $multiType ? $type[$k] : $type;
  89. $bound[] = $this->_bindValue($generator, $val, $valType);
  90. }
  91. $values[] = sprintf('(%s)', implode(',', $bound));
  92. continue;
  93. }
  94. $valType = $multiType && isset($type[$i]) ? $type[$i] : $type;
  95. $values[] = $this->_bindValue($generator, $value, $valType);
  96. }
  97. return implode(', ', $values);
  98. }
  99. /**
  100. * Registers a value in the placeholder generator and returns the generated
  101. * placeholder
  102. *
  103. * @param \Cake\Database\ValueBinder $generator The value binder
  104. * @param mixed $value The value to bind
  105. * @param string $type The type to use
  106. * @return string generated placeholder
  107. */
  108. protected function _bindValue($generator, $value, $type)
  109. {
  110. $placeholder = $generator->placeholder('tuple');
  111. $generator->bind($placeholder, $value, $type);
  112. return $placeholder;
  113. }
  114. /**
  115. * Traverses the tree of expressions stored in this object, visiting first
  116. * expressions in the left hand side and then the rest.
  117. *
  118. * Callback function receives as its only argument an instance of an ExpressionInterface
  119. *
  120. * @param callable $callable The callable to apply to sub-expressions
  121. * @return void
  122. */
  123. public function traverse(callable $callable)
  124. {
  125. foreach ($this->getField() as $field) {
  126. $this->_traverseValue($field, $callable);
  127. }
  128. $value = $this->getValue();
  129. if ($value instanceof ExpressionInterface) {
  130. $callable($value);
  131. $value->traverse($callable);
  132. return;
  133. }
  134. foreach ($value as $i => $value) {
  135. if ($this->isMulti()) {
  136. foreach ($value as $v) {
  137. $this->_traverseValue($v, $callable);
  138. }
  139. } else {
  140. $this->_traverseValue($value, $callable);
  141. }
  142. }
  143. }
  144. /**
  145. * Conditionally executes the callback for the passed value if
  146. * it is an ExpressionInterface
  147. *
  148. * @param mixed $value The value to traverse
  149. * @param callable $callable The callable to use when traversing
  150. * @return void
  151. */
  152. protected function _traverseValue($value, $callable)
  153. {
  154. if ($value instanceof ExpressionInterface) {
  155. $callable($value);
  156. $value->traverse($callable);
  157. }
  158. }
  159. /**
  160. * Determines if each of the values in this expressions is a tuple in
  161. * itself
  162. *
  163. * @return bool
  164. */
  165. public function isMulti()
  166. {
  167. return in_array(strtolower($this->_operator), ['in', 'not in']);
  168. }
  169. }