TYPO3 CMS  TYPO3_8-7
QueryBuilder.php
Go to the documentation of this file.
1 <?php
2 declare(strict_types = 1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
25 
43 {
49  protected $connection;
50 
55 
60 
65 
74  public function __construct(
77  \Doctrine\DBAL\Query\QueryBuilder $concreteQueryBuilder = null,
78  array $additionalRestrictions = null
79  ) {
80  $this->connection = $connection;
81  $this->additionalRestrictions = $additionalRestrictions ?: $GLOBALS['TYPO3_CONF_VARS']['DB']['additionalQueryRestrictions'] ?? [];
82  $this->setRestrictions($restrictionContainer ?: GeneralUtility::makeInstance(DefaultRestrictionContainer::class));
83  $this->concreteQueryBuilder = $concreteQueryBuilder ?: GeneralUtility::makeInstance(\Doctrine\DBAL\Query\QueryBuilder::class, $connection);
84  }
85 
89  public function getRestrictions()
90  {
92  }
93 
98  {
99  foreach ($this->additionalRestrictions as $restrictionClass => $options) {
100  if (empty($options['disabled'])) {
101  $restriction = GeneralUtility::makeInstance($restrictionClass);
102  $restrictionContainer->add($restriction);
103  }
104  }
105  $this->restrictionContainer = $restrictionContainer;
106  }
107 
111  public function resetRestrictions()
112  {
113  $this->setRestrictions(GeneralUtility::makeInstance(DefaultRestrictionContainer::class));
114  }
115 
125  public function expr(): ExpressionBuilder
126  {
127  return $this->connection->getExpressionBuilder();
128  }
129 
136  public function getType(): int
137  {
138  return $this->concreteQueryBuilder->getType();
139  }
140 
146  public function getConnection(): Connection
147  {
148  return $this->connection;
149  }
150 
157  public function getState(): int
158  {
159  return $this->concreteQueryBuilder->getState();
160  }
161 
168  public function getConcreteQueryBuilder(): \Doctrine\DBAL\Query\QueryBuilder
169  {
171  }
172 
178  public function execute()
179  {
180  if ($this->getType() !== \Doctrine\DBAL\Query\QueryBuilder::SELECT) {
181  return $this->concreteQueryBuilder->execute();
182  }
183 
184  // Set additional query restrictions
185  $originalWhereConditions = $this->addAdditionalWhereConditions();
186 
187  $result = $this->concreteQueryBuilder->execute();
188 
189  // Restore the original query conditions in case the user keeps
190  // on modifying the state.
191  $this->concreteQueryBuilder->add('where', $originalWhereConditions, false);
192 
193  return $result;
194  }
195 
204  public function getSQL(): string
205  {
206  if ($this->getType() !== \Doctrine\DBAL\Query\QueryBuilder::SELECT) {
207  return $this->concreteQueryBuilder->getSQL();
208  }
209 
210  // Set additional query restrictions
211  $originalWhereConditions = $this->addAdditionalWhereConditions();
212 
213  $sql = $this->concreteQueryBuilder->getSQL();
214 
215  // Restore the original query conditions in case the user keeps
216  // on modifying the state.
217  $this->concreteQueryBuilder->add('where', $originalWhereConditions, false);
218 
219  return $sql;
220  }
221 
231  public function setParameter($key, $value, int $type = null): QueryBuilder
232  {
233  $this->concreteQueryBuilder->setParameter($key, $value, $type);
234 
235  return $this;
236  }
237 
246  public function setParameters(array $params, array $types = []): QueryBuilder
247  {
248  $this->concreteQueryBuilder->setParameters($params, $types);
249 
250  return $this;
251  }
252 
258  public function getParameters(): array
259  {
260  return $this->concreteQueryBuilder->getParameters();
261  }
262 
270  public function getParameter($key)
271  {
272  return $this->concreteQueryBuilder->getParameter($key);
273  }
274 
280  public function getParameterTypes(): array
281  {
282  return $this->concreteQueryBuilder->getParameterTypes();
283  }
284 
292  public function getParameterType($key)
293  {
294  return $this->concreteQueryBuilder->getParameterType($key);
295  }
296 
304  public function setFirstResult(int $firstResult): QueryBuilder
305  {
306  $this->concreteQueryBuilder->setFirstResult($firstResult);
307 
308  return $this;
309  }
310 
317  public function getFirstResult(): int
318  {
319  return (int)$this->concreteQueryBuilder->getFirstResult();
320  }
321 
329  public function setMaxResults(int $maxResults): QueryBuilder
330  {
331  $this->concreteQueryBuilder->setMaxResults($maxResults);
332 
333  return $this;
334  }
335 
342  public function getMaxResults(): int
343  {
344  return (int)$this->concreteQueryBuilder->getMaxResults();
345  }
346 
359  public function add(string $sqlPartName, string $sqlPart, bool $append = false): QueryBuilder
360  {
361  $this->concreteQueryBuilder->add($sqlPartName, $sqlPart, $append);
362 
363  return $this;
364  }
365 
373  public function count(string $item): QueryBuilder
374  {
375  $countExpr = $this->getConnection()->getDatabasePlatform()->getCountExpression(
376  $item === '*' ? $item : $this->quoteIdentifier($item)
377  );
378  $this->concreteQueryBuilder->select($countExpr);
379 
380  return $this;
381  }
382 
390  public function select(string ...$selects): QueryBuilder
391  {
392  $this->concreteQueryBuilder->select(...$this->quoteIdentifiersForSelect($selects));
393 
394  return $this;
395  }
396 
404  public function addSelect(string ...$selects): QueryBuilder
405  {
406  $this->concreteQueryBuilder->addSelect(...$this->quoteIdentifiersForSelect($selects));
407 
408  return $this;
409  }
410 
420  public function selectLiteral(string ...$selects): QueryBuilder
421  {
422  $this->concreteQueryBuilder->select(...$selects);
423 
424  return $this;
425  }
426 
435  public function addSelectLiteral(string ...$selects): QueryBuilder
436  {
437  $this->concreteQueryBuilder->addSelect(...$selects);
438 
439  return $this;
440  }
441 
453  public function delete(string $delete, string $alias = null): QueryBuilder
454  {
455  $this->concreteQueryBuilder->delete(
456  $this->quoteIdentifier($delete),
457  empty($alias) ? $alias : $this->quoteIdentifier($alias)
458  );
459 
460  return $this;
461  }
462 
472  public function update(string $update, string $alias = null): QueryBuilder
473  {
474  $this->concreteQueryBuilder->update(
475  $this->quoteIdentifier($update),
476  empty($alias) ? $alias : $this->quoteIdentifier($alias)
477  );
478 
479  return $this;
480  }
481 
490  public function insert(string $insert): QueryBuilder
491  {
492  $this->concreteQueryBuilder->insert($this->quoteIdentifier($insert));
493 
494  return $this;
495  }
496 
506  public function from(string $from, string $alias = null): QueryBuilder
507  {
508  $this->concreteQueryBuilder->from(
509  $this->quoteIdentifier($from),
510  empty($alias) ? $alias : $this->quoteIdentifier($alias)
511  );
512 
513  return $this;
514  }
515 
526  public function join(string $fromAlias, string $join, string $alias, string $condition = null): QueryBuilder
527  {
528  $this->concreteQueryBuilder->innerJoin(
529  $this->quoteIdentifier($fromAlias),
530  $this->quoteIdentifier($join),
531  $this->quoteIdentifier($alias),
532  $condition
533  );
534 
535  return $this;
536  }
537 
548  public function innerJoin(string $fromAlias, string $join, string $alias, string $condition = null): QueryBuilder
549  {
550  $this->concreteQueryBuilder->innerJoin(
551  $this->quoteIdentifier($fromAlias),
552  $this->quoteIdentifier($join),
553  $this->quoteIdentifier($alias),
554  $condition
555  );
556 
557  return $this;
558  }
559 
570  public function leftJoin(string $fromAlias, string $join, string $alias, string $condition = null): QueryBuilder
571  {
572  $this->concreteQueryBuilder->leftJoin(
573  $this->quoteIdentifier($fromAlias),
574  $this->quoteIdentifier($join),
575  $this->quoteIdentifier($alias),
576  $condition
577  );
578 
579  return $this;
580  }
581 
592  public function rightJoin(string $fromAlias, string $join, string $alias, string $condition = null): QueryBuilder
593  {
594  $this->concreteQueryBuilder->rightJoin(
595  $this->quoteIdentifier($fromAlias),
596  $this->quoteIdentifier($join),
597  $this->quoteIdentifier($alias),
598  $condition
599  );
600 
601  return $this;
602  }
603 
613  public function set(string $key, $value, bool $createNamedParameter = true): QueryBuilder
614  {
615  $this->concreteQueryBuilder->set(
616  $this->quoteIdentifier($key),
617  $createNamedParameter ? $this->createNamedParameter($value) : $value
618  );
619 
620  return $this;
621  }
622 
630  public function where(...$predicates): QueryBuilder
631  {
632  $this->concreteQueryBuilder->where(...$predicates);
633 
634  return $this;
635  }
636 
647  public function andWhere(...$where): QueryBuilder
648  {
649  $this->concreteQueryBuilder->andWhere(...$where);
650 
651  return $this;
652  }
653 
664  public function orWhere(...$where): QueryBuilder
665  {
666  $this->concreteQueryBuilder->orWhere(...$where);
667 
668  return $this;
669  }
670 
679  public function groupBy(...$groupBy): QueryBuilder
680  {
681  $this->concreteQueryBuilder->groupBy(...$this->quoteIdentifiers($groupBy));
682 
683  return $this;
684  }
685 
693  public function addGroupBy(...$groupBy): QueryBuilder
694  {
695  $this->concreteQueryBuilder->addGroupBy(...$this->quoteIdentifiers($groupBy));
696 
697  return $this;
698  }
699 
709  public function setValue(string $column, $value, bool $createNamedParameter = true): QueryBuilder
710  {
711  $this->concreteQueryBuilder->setValue(
712  $this->quoteIdentifier($column),
713  $createNamedParameter ? $this->createNamedParameter($value) : $value
714  );
715 
716  return $this;
717  }
718 
728  public function values(array $values, bool $createNamedParameters = true): QueryBuilder
729  {
730  if ($createNamedParameters === true) {
731  foreach ($values as &$value) {
732  $value = $this->createNamedParameter($value);
733  }
734  }
735 
736  $this->concreteQueryBuilder->values($this->quoteColumnValuePairs($values));
737 
738  return $this;
739  }
740 
749  public function having(...$having): QueryBuilder
750  {
751  $this->concreteQueryBuilder->having(...$having);
752  return $this;
753  }
754 
763  public function andHaving(...$having): QueryBuilder
764  {
765  $this->concreteQueryBuilder->andHaving(...$having);
766 
767  return $this;
768  }
769 
778  public function orHaving(...$having): QueryBuilder
779  {
780  $this->concreteQueryBuilder->orHaving(...$having);
781 
782  return $this;
783  }
784 
794  public function orderBy(string $fieldName, string $order = null): QueryBuilder
795  {
796  $this->concreteQueryBuilder->orderBy($this->connection->quoteIdentifier($fieldName), $order);
797 
798  return $this;
799  }
800 
809  public function addOrderBy(string $fieldName, string $order = null): QueryBuilder
810  {
811  $this->concreteQueryBuilder->addOrderBy($this->connection->quoteIdentifier($fieldName), $order);
812 
813  return $this;
814  }
815 
823  public function getQueryPart(string $queryPartName)
824  {
825  return $this->concreteQueryBuilder->getQueryPart($queryPartName);
826  }
827 
833  public function getQueryParts(): array
834  {
835  return $this->concreteQueryBuilder->getQueryParts();
836  }
837 
845  public function resetQueryParts(array $queryPartNames = null): QueryBuilder
846  {
847  $this->concreteQueryBuilder->resetQueryParts($queryPartNames);
848 
849  return $this;
850  }
851 
859  public function resetQueryPart($queryPartName): QueryBuilder
860  {
861  $this->concreteQueryBuilder->resetQueryPart($queryPartName);
862 
863  return $this;
864  }
865 
872  public function __toString(): string
873  {
874  return $this->getSQL();
875  }
876 
894  public function createNamedParameter($value, int $type = \PDO::PARAM_STR, string $placeHolder = null): string
895  {
896  return $this->concreteQueryBuilder->createNamedParameter($value, $type, $placeHolder);
897  }
898 
912  public function createPositionalParameter($value, int $type = \PDO::PARAM_STR): string
913  {
914  return $this->concreteQueryBuilder->createPositionalParameter($value, $type);
915  }
916 
924  public function escapeLikeWildcards(string $value): string
925  {
926  return addcslashes($value, '_%');
927  }
928 
937  public function quote($input, int $type = null)
938  {
939  return $this->getConnection()->quote($input, $type);
940  }
941 
952  public function quoteIdentifier(string $identifier): string
953  {
954  return $this->getConnection()->quoteIdentifier($identifier);
955  }
956 
966  public function quoteIdentifiers(array $input): array
967  {
968  return $this->getConnection()->quoteIdentifiers($input);
969  }
970 
983  public function quoteIdentifiersForSelect(array $input): array
984  {
985  foreach ($input as &$select) {
986  list($fieldName, $alias, $suffix) = array_pad(
988  ' AS ',
989  str_ireplace(' as ', ' AS ', $select),
990  true,
991  3
992  ),
993  3,
994  null
995  );
996  if (!empty($suffix)) {
997  throw new \InvalidArgumentException(
998  'QueryBuilder::quoteIdentifiersForSelect() could not parse the input "' . $input . '"',
999  1461170686
1000  );
1001  }
1002 
1003  // The SQL * operator must not be quoted. As it can only occur either by itself
1004  // or preceded by a tablename (tablename.*) check if the last character of a select
1005  // expression is the * and quote only prepended table name. In all other cases the
1006  // full expression is being quoted.
1007  if (substr($fieldName, -2) === '.*') {
1008  $select = $this->quoteIdentifier(substr($fieldName, 0, -2)) . '.*';
1009  } elseif ($fieldName !== '*') {
1010  $select = $this->quoteIdentifier($fieldName);
1011  }
1012 
1013  // Quote the alias for the current fieldName, if given
1014  if (!empty($alias)) {
1015  $select .= ' AS ' . $this->quoteIdentifier($alias);
1016  }
1017  }
1018  return $input;
1019  }
1020 
1031  public function quoteColumnValuePairs(array $input): array
1032  {
1033  return $this->getConnection()->quoteColumnValuePairs($input);
1034  }
1035 
1043  protected function unquoteSingleIdentifier(string $identifier): string
1044  {
1045  $identifier = trim($identifier);
1046  $platform = $this->getConnection()->getDatabasePlatform();
1047  if ($platform instanceof SQLServerPlatform) {
1048  // mssql quotes identifiers with [ and ], not a single character
1049  $identifier = ltrim($identifier, '[');
1050  $identifier = rtrim($identifier, ']');
1051  } else {
1052  $quoteChar = $platform->getIdentifierQuoteCharacter();
1053  $identifier = trim($identifier, $quoteChar);
1054  $identifier = str_replace($quoteChar . $quoteChar, $quoteChar, $identifier);
1055  }
1056  return $identifier;
1057  }
1058 
1067  protected function getQueriedTables(): array
1068  {
1069  $queriedTables = [];
1070 
1071  // Loop through all FROM tables
1072  foreach ($this->getQueryPart('from') as $from) {
1073  $tableName = $this->unquoteSingleIdentifier($from['table']);
1074  $tableAlias = isset($from['alias']) ? $this->unquoteSingleIdentifier($from['alias']) : $tableName;
1075  $queriedTables[$tableAlias] = $tableName;
1076  }
1077 
1078  // Loop through all JOIN tables
1079  foreach ($this->getQueryPart('join') as $fromTable => $joins) {
1080  foreach ($joins as $join) {
1081  $tableName = $this->unquoteSingleIdentifier($join['joinTable']);
1082  $tableAlias = isset($join['joinAlias']) ? $this->unquoteSingleIdentifier($join['joinAlias']) : $tableName;
1083  $queriedTables[$tableAlias] = $tableName;
1084  }
1085  }
1086 
1087  return $queriedTables;
1088  }
1089 
1097  protected function addAdditionalWhereConditions()
1098  {
1099  $originalWhereConditions = $this->concreteQueryBuilder->getQueryPart('where');
1100  $expression = $this->restrictionContainer->buildExpression($this->getQueriedTables(), $this->expr());
1101  // This check would be obsolete, as the composite expression would not add empty expressions anyway
1102  // But we keep it here to only clone the previous state, in case we really will change it.
1103  // Once we remove this state preserving functionality, we can remove the count check here
1104  // and just add the expression to the query builder.
1105  if ($expression->count() > 0) {
1106  if ($originalWhereConditions instanceof CompositeExpression) {
1107  // Save the original query conditions so we can restore
1108  // them after the query has been built.
1109  $originalWhereConditions = clone $originalWhereConditions;
1110  }
1111  $this->concreteQueryBuilder->andWhere($expression);
1112  }
1113 
1114  return $originalWhereConditions;
1115  }
1116 
1121  public function __clone()
1122  {
1123  $this->concreteQueryBuilder = clone $this->concreteQueryBuilder;
1124  $this->restrictionContainer = clone $this->restrictionContainer;
1125  }
1126 }
setRestrictions(QueryRestrictionContainerInterface $restrictionContainer)
from(string $from, string $alias=null)
update(string $update, string $alias=null)
rightJoin(string $fromAlias, string $join, string $alias, string $condition=null)
createNamedParameter($value, int $type=\PDO::PARAM_STR, string $placeHolder=null)
addOrderBy(string $fieldName, string $order=null)
set(string $key, $value, bool $createNamedParameter=true)
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
setParameters(array $params, array $types=[])
static makeInstance($className,... $constructorArguments)
values(array $values, bool $createNamedParameters=true)
add(string $sqlPartName, string $sqlPart, bool $append=false)
setValue(string $column, $value, bool $createNamedParameter=true)
innerJoin(string $fromAlias, string $join, string $alias, string $condition=null)
orderBy(string $fieldName, string $order=null)
__construct(Connection $connection, QueryRestrictionContainerInterface $restrictionContainer=null, \Doctrine\DBAL\Query\QueryBuilder $concreteQueryBuilder=null, array $additionalRestrictions=null)
leftJoin(string $fromAlias, string $join, string $alias, string $condition=null)
delete(string $delete, string $alias=null)
resetQueryParts(array $queryPartNames=null)
setParameter($key, $value, int $type=null)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
join(string $fromAlias, string $join, string $alias, string $condition=null)
createPositionalParameter($value, int $type=\PDO::PARAM_STR)