123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- <?php
- /**
- * 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 3.0.0
- * @license https://opensource.org/licenses/mit-license.php MIT License
- */
- namespace Cake\Database\Expression;
- use Cake\Database\ExpressionInterface;
- use Cake\Database\ValueBinder;
- /**
- * This expression represents SQL fragments that are used for comparing one tuple
- * to another, one tuple to a set of other tuples or one tuple to an expression
- */
- class TupleComparison extends Comparison
- {
- /**
- * Constructor
- *
- * @param string|array|\Cake\Database\ExpressionInterface $fields the fields to use to form a tuple
- * @param array|\Cake\Database\ExpressionInterface $values the values to use to form a tuple
- * @param array $types the types names to use for casting each of the values, only
- * one type per position in the value array in needed
- * @param string $conjunction the operator used for comparing field and value
- */
- public function __construct($fields, $values, $types = [], $conjunction = '=')
- {
- parent::__construct($fields, $values, $types, $conjunction);
- $this->_type = (array)$types;
- }
- /**
- * Convert the expression into a SQL fragment.
- *
- * @param \Cake\Database\ValueBinder $generator Placeholder generator object
- * @return string
- */
- public function sql(ValueBinder $generator)
- {
- $template = '(%s) %s (%s)';
- $fields = [];
- $originalFields = $this->getField();
- if (!is_array($originalFields)) {
- $originalFields = [$originalFields];
- }
- foreach ($originalFields as $field) {
- $fields[] = $field instanceof ExpressionInterface ? $field->sql($generator) : $field;
- }
- $values = $this->_stringifyValues($generator);
- $field = implode(', ', $fields);
- return sprintf($template, $field, $this->_operator, $values);
- }
- /**
- * Returns a string with the values as placeholders in a string to be used
- * for the SQL version of this expression
- *
- * @param \Cake\Database\ValueBinder $generator The value binder to convert expressions with.
- * @return string
- */
- protected function _stringifyValues($generator)
- {
- $values = [];
- $parts = $this->getValue();
- if ($parts instanceof ExpressionInterface) {
- return $parts->sql($generator);
- }
- foreach ($parts as $i => $value) {
- if ($value instanceof ExpressionInterface) {
- $values[] = $value->sql($generator);
- continue;
- }
- $type = $this->_type;
- $multiType = is_array($type);
- $isMulti = $this->isMulti();
- $type = $multiType ? $type : str_replace('[]', '', $type);
- $type = $type ?: null;
- if ($isMulti) {
- $bound = [];
- foreach ($value as $k => $val) {
- $valType = $multiType ? $type[$k] : $type;
- $bound[] = $this->_bindValue($generator, $val, $valType);
- }
- $values[] = sprintf('(%s)', implode(',', $bound));
- continue;
- }
- $valType = $multiType && isset($type[$i]) ? $type[$i] : $type;
- $values[] = $this->_bindValue($generator, $value, $valType);
- }
- return implode(', ', $values);
- }
- /**
- * Registers a value in the placeholder generator and returns the generated
- * placeholder
- *
- * @param \Cake\Database\ValueBinder $generator The value binder
- * @param mixed $value The value to bind
- * @param string $type The type to use
- * @return string generated placeholder
- */
- protected function _bindValue($generator, $value, $type)
- {
- $placeholder = $generator->placeholder('tuple');
- $generator->bind($placeholder, $value, $type);
- return $placeholder;
- }
- /**
- * Traverses the tree of expressions stored in this object, visiting first
- * expressions in the left hand side and then the rest.
- *
- * Callback function receives as its only argument an instance of an ExpressionInterface
- *
- * @param callable $callable The callable to apply to sub-expressions
- * @return void
- */
- public function traverse(callable $callable)
- {
- foreach ($this->getField() as $field) {
- $this->_traverseValue($field, $callable);
- }
- $value = $this->getValue();
- if ($value instanceof ExpressionInterface) {
- $callable($value);
- $value->traverse($callable);
- return;
- }
- foreach ($value as $i => $val) {
- if ($this->isMulti()) {
- foreach ($val as $v) {
- $this->_traverseValue($v, $callable);
- }
- } else {
- $this->_traverseValue($val, $callable);
- }
- }
- }
- /**
- * Conditionally executes the callback for the passed value if
- * it is an ExpressionInterface
- *
- * @param mixed $value The value to traverse
- * @param callable $callable The callable to use when traversing
- * @return void
- */
- protected function _traverseValue($value, $callable)
- {
- if ($value instanceof ExpressionInterface) {
- $callable($value);
- $value->traverse($callable);
- }
- }
- /**
- * Determines if each of the values in this expressions is a tuple in
- * itself
- *
- * @return bool
- */
- public function isMulti()
- {
- return in_array(strtolower($this->_operator), ['in', 'not in']);
- }
- }
|