IdentifierQuoter.php 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  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;
  16. use Cake\Database\Expression\FieldInterface;
  17. use Cake\Database\Expression\IdentifierExpression;
  18. use Cake\Database\Expression\OrderByExpression;
  19. /**
  20. * Contains all the logic related to quoting identifiers in a Query object
  21. *
  22. * @internal
  23. */
  24. class IdentifierQuoter
  25. {
  26. /**
  27. * The driver instance used to do the identifier quoting
  28. *
  29. * @var \Cake\Database\Driver
  30. */
  31. protected $_driver;
  32. /**
  33. * Constructor
  34. *
  35. * @param \Cake\Database\Driver $driver The driver instance used to do the identifier quoting
  36. */
  37. public function __construct(Driver $driver)
  38. {
  39. $this->_driver = $driver;
  40. }
  41. /**
  42. * Iterates over each of the clauses in a query looking for identifiers and
  43. * quotes them
  44. *
  45. * @param \Cake\Database\Query $query The query to have its identifiers quoted
  46. * @return \Cake\Database\Query
  47. */
  48. public function quote(Query $query)
  49. {
  50. $binder = $query->getValueBinder();
  51. $query->setValueBinder(false);
  52. if ($query->type() === 'insert') {
  53. $this->_quoteInsert($query);
  54. } elseif ($query->type() === 'update') {
  55. $this->_quoteUpdate($query);
  56. } else {
  57. $this->_quoteParts($query);
  58. }
  59. $query->traverseExpressions([$this, 'quoteExpression']);
  60. $query->setValueBinder($binder);
  61. return $query;
  62. }
  63. /**
  64. * Quotes identifiers inside expression objects
  65. *
  66. * @param \Cake\Database\ExpressionInterface $expression The expression object to walk and quote.
  67. * @return void
  68. */
  69. public function quoteExpression($expression)
  70. {
  71. if ($expression instanceof FieldInterface) {
  72. $this->_quoteComparison($expression);
  73. return;
  74. }
  75. if ($expression instanceof OrderByExpression) {
  76. $this->_quoteOrderBy($expression);
  77. return;
  78. }
  79. if ($expression instanceof IdentifierExpression) {
  80. $this->_quoteIdentifierExpression($expression);
  81. return;
  82. }
  83. }
  84. /**
  85. * Quotes all identifiers in each of the clauses of a query
  86. *
  87. * @param \Cake\Database\Query $query The query to quote.
  88. * @return void
  89. */
  90. protected function _quoteParts($query)
  91. {
  92. foreach (['distinct', 'select', 'from', 'group'] as $part) {
  93. $contents = $query->clause($part);
  94. if (!is_array($contents)) {
  95. continue;
  96. }
  97. $result = $this->_basicQuoter($contents);
  98. if (!empty($result)) {
  99. $query->{$part}($result, true);
  100. }
  101. }
  102. $joins = $query->clause('join');
  103. if ($joins) {
  104. $joins = $this->_quoteJoins($joins);
  105. $query->join($joins, [], true);
  106. }
  107. }
  108. /**
  109. * A generic identifier quoting function used for various parts of the query
  110. *
  111. * @param array $part the part of the query to quote
  112. * @return array
  113. */
  114. protected function _basicQuoter($part)
  115. {
  116. $result = [];
  117. foreach ((array)$part as $alias => $value) {
  118. $value = !is_string($value) ? $value : $this->_driver->quoteIdentifier($value);
  119. $alias = is_numeric($alias) ? $alias : $this->_driver->quoteIdentifier($alias);
  120. $result[$alias] = $value;
  121. }
  122. return $result;
  123. }
  124. /**
  125. * Quotes both the table and alias for an array of joins as stored in a Query
  126. * object
  127. *
  128. * @param array $joins The joins to quote.
  129. * @return array
  130. */
  131. protected function _quoteJoins($joins)
  132. {
  133. $result = [];
  134. foreach ($joins as $value) {
  135. $alias = null;
  136. if (!empty($value['alias'])) {
  137. $alias = $this->_driver->quoteIdentifier($value['alias']);
  138. $value['alias'] = $alias;
  139. }
  140. if (is_string($value['table'])) {
  141. $value['table'] = $this->_driver->quoteIdentifier($value['table']);
  142. }
  143. $result[$alias] = $value;
  144. }
  145. return $result;
  146. }
  147. /**
  148. * Quotes the table name and columns for an insert query
  149. *
  150. * @param \Cake\Database\Query $query The insert query to quote.
  151. * @return void
  152. */
  153. protected function _quoteInsert($query)
  154. {
  155. list($table, $columns) = $query->clause('insert');
  156. $table = $this->_driver->quoteIdentifier($table);
  157. foreach ($columns as &$column) {
  158. if (is_scalar($column)) {
  159. $column = $this->_driver->quoteIdentifier($column);
  160. }
  161. }
  162. $query->insert($columns)->into($table);
  163. }
  164. /**
  165. * Quotes the table name for an update query
  166. *
  167. * @param \Cake\Database\Query $query The update query to quote.
  168. * @return void
  169. */
  170. protected function _quoteUpdate($query)
  171. {
  172. $table = $query->clause('update')[0];
  173. if (is_string($table)) {
  174. $query->update($this->_driver->quoteIdentifier($table));
  175. }
  176. }
  177. /**
  178. * Quotes identifiers in expression objects implementing the field interface
  179. *
  180. * @param \Cake\Database\Expression\FieldInterface $expression The expression to quote.
  181. * @return void
  182. */
  183. protected function _quoteComparison(FieldInterface $expression)
  184. {
  185. $field = $expression->getField();
  186. if (is_string($field)) {
  187. $expression->setField($this->_driver->quoteIdentifier($field));
  188. } elseif (is_array($field)) {
  189. $quoted = [];
  190. foreach ($field as $f) {
  191. $quoted[] = $this->_driver->quoteIdentifier($f);
  192. }
  193. $expression->setField($quoted);
  194. } elseif ($field instanceof ExpressionInterface) {
  195. $this->quoteExpression($field);
  196. }
  197. }
  198. /**
  199. * Quotes identifiers in "order by" expression objects
  200. *
  201. * Strings with spaces are treated as literal expressions
  202. * and will not have identifiers quoted.
  203. *
  204. * @param \Cake\Database\Expression\OrderByExpression $expression The expression to quote.
  205. * @return void
  206. */
  207. protected function _quoteOrderBy(OrderByExpression $expression)
  208. {
  209. $expression->iterateParts(function ($part, &$field) {
  210. if (is_string($field)) {
  211. $field = $this->_driver->quoteIdentifier($field);
  212. return $part;
  213. }
  214. if (is_string($part) && strpos($part, ' ') === false) {
  215. return $this->_driver->quoteIdentifier($part);
  216. }
  217. return $part;
  218. });
  219. }
  220. /**
  221. * Quotes identifiers in "order by" expression objects
  222. *
  223. * @param \Cake\Database\Expression\IdentifierExpression $expression The identifiers to quote.
  224. * @return void
  225. */
  226. protected function _quoteIdentifierExpression(IdentifierExpression $expression)
  227. {
  228. $expression->setIdentifier(
  229. $this->_driver->quoteIdentifier($expression->getIdentifier())
  230. );
  231. }
  232. }