[TASK] Use strict comparison for strings
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Persistence / Generic / Storage / Typo3DbBackend.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Persistence\Generic\Storage;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use Doctrine\DBAL\DBALException;
18 use TYPO3\CMS\Backend\Utility\BackendUtility;
19 use TYPO3\CMS\Core\Database\ConnectionPool;
20 use TYPO3\CMS\Core\Database\Query\QueryBuilder;
21 use TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer;
22 use TYPO3\CMS\Core\SingletonInterface;
23 use TYPO3\CMS\Core\Utility\GeneralUtility;
24 use TYPO3\CMS\Core\Utility\MathUtility;
25 use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
26 use TYPO3\CMS\Extbase\DomainObject\AbstractValueObject;
27 use TYPO3\CMS\Extbase\Persistence\Generic\Qom;
28 use TYPO3\CMS\Extbase\Persistence\Generic\Storage\Exception\SqlErrorException;
29 use TYPO3\CMS\Extbase\Persistence\QueryInterface;
30
31 /**
32 * A Storage backend
33 */
34 class Typo3DbBackend implements BackendInterface, SingletonInterface
35 {
36 /**
37 * @var ConnectionPool
38 */
39 protected $connectionPool;
40
41 /**
42 * @var \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper
43 */
44 protected $dataMapper;
45
46 /**
47 * The TYPO3 page repository. Used for language and workspace overlay
48 *
49 * @var \TYPO3\CMS\Frontend\Page\PageRepository
50 */
51 protected $pageRepository;
52
53 /**
54 * A first-level TypoScript configuration cache
55 *
56 * @var array
57 */
58 protected $pageTSConfigCache = [];
59
60 /**
61 * @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
62 */
63 protected $configurationManager;
64
65 /**
66 * @var \TYPO3\CMS\Extbase\Service\CacheService
67 */
68 protected $cacheService;
69
70 /**
71 * @var \TYPO3\CMS\Extbase\Service\EnvironmentService
72 */
73 protected $environmentService;
74
75 /**
76 * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
77 */
78 protected $objectManager;
79
80 /**
81 * @param \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper $dataMapper
82 */
83 public function injectDataMapper(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper $dataMapper)
84 {
85 $this->dataMapper = $dataMapper;
86 }
87
88 /**
89 * @param \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager
90 */
91 public function injectConfigurationManager(ConfigurationManagerInterface $configurationManager)
92 {
93 $this->configurationManager = $configurationManager;
94 }
95
96 /**
97 * @param \TYPO3\CMS\Extbase\Service\CacheService $cacheService
98 */
99 public function injectCacheService(\TYPO3\CMS\Extbase\Service\CacheService $cacheService)
100 {
101 $this->cacheService = $cacheService;
102 }
103
104 /**
105 * @param \TYPO3\CMS\Extbase\Service\EnvironmentService $environmentService
106 */
107 public function injectEnvironmentService(\TYPO3\CMS\Extbase\Service\EnvironmentService $environmentService)
108 {
109 $this->environmentService = $environmentService;
110 }
111
112 /**
113 * @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
114 */
115 public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
116 {
117 $this->objectManager = $objectManager;
118 }
119
120 /**
121 * Constructor. takes the database handle from $GLOBALS['TYPO3_DB']
122 */
123 public function __construct()
124 {
125 $this->connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
126 }
127
128 /**
129 * Adds a row to the storage
130 *
131 * @param string $tableName The database table name
132 * @param array $fieldValues The row to be inserted
133 * @param bool $isRelation TRUE if we are currently inserting into a relation table, FALSE by default
134 * @return int The uid of the inserted row
135 * @throws SqlErrorException
136 */
137 public function addRow($tableName, array $fieldValues, $isRelation = false)
138 {
139 if (isset($fieldValues['uid'])) {
140 unset($fieldValues['uid']);
141 }
142 try {
143 $connection = $this->connectionPool->getConnectionForTable($tableName);
144 $connection->insert($tableName, $fieldValues);
145 } catch (DBALException $e) {
146 throw new SqlErrorException($e->getPrevious()->getMessage(), 1470230766);
147 }
148
149 $uid = $connection->lastInsertId($tableName);
150
151 if (!$isRelation) {
152 $this->clearPageCache($tableName, $uid);
153 }
154 return (int)$uid;
155 }
156
157 /**
158 * Updates a row in the storage
159 *
160 * @param string $tableName The database table name
161 * @param array $fieldValues The row to be updated
162 * @param bool $isRelation TRUE if we are currently inserting into a relation table, FALSE by default
163 * @throws \InvalidArgumentException
164 * @throws SqlErrorException
165 * @return bool
166 */
167 public function updateRow($tableName, array $fieldValues, $isRelation = false)
168 {
169 if (!isset($fieldValues['uid'])) {
170 throw new \InvalidArgumentException('The given row must contain a value for "uid".', 1476045164);
171 }
172
173 $uid = (int)$fieldValues['uid'];
174 unset($fieldValues['uid']);
175
176 try {
177 $this->connectionPool->getConnectionForTable($tableName)
178 ->update($tableName, $fieldValues, ['uid' => $uid]);
179 } catch (DBALException $e) {
180 throw new SqlErrorException($e->getPrevious()->getMessage(), 1470230767);
181 }
182
183 if (!$isRelation) {
184 $this->clearPageCache($tableName, $uid);
185 }
186
187 // always returns true
188 return true;
189 }
190
191 /**
192 * Updates a relation row in the storage.
193 *
194 * @param string $tableName The database relation table name
195 * @param array $fieldValues The row to be updated
196 * @throws \InvalidArgumentException
197 * @return bool
198 * @throws SqlErrorException
199 */
200 public function updateRelationTableRow($tableName, array $fieldValues)
201 {
202 if (!isset($fieldValues['uid_local']) && !isset($fieldValues['uid_foreign'])) {
203 throw new \InvalidArgumentException(
204 'The given fieldValues must contain a value for "uid_local" and "uid_foreign".', 1360500126
205 );
206 }
207
208 $where['uid_local'] = (int)$fieldValues['uid_local'];
209 $where['uid_foreign'] = (int)$fieldValues['uid_foreign'];
210 unset($fieldValues['uid_local']);
211 unset($fieldValues['uid_foreign']);
212
213 if (!empty($fieldValues['tablenames'])) {
214 $where['tablenames'] = $fieldValues['tablenames'];
215 unset($fieldValues['tablenames']);
216 }
217 if (!empty($fieldValues['fieldname'])) {
218 $where['fieldname'] = $fieldValues['fieldname'];
219 unset($fieldValues['fieldname']);
220 }
221
222 try {
223 $this->connectionPool->getConnectionForTable($tableName)
224 ->update($tableName, $fieldValues, $where);
225 } catch (DBALException $e) {
226 throw new SqlErrorException($e->getPrevious()->getMessage(), 1470230768);
227 }
228
229 // always returns true
230 return true;
231 }
232
233 /**
234 * Deletes a row in the storage
235 *
236 * @param string $tableName The database table name
237 * @param array $where An array of where array('fieldname' => value).
238 * @param bool $isRelation TRUE if we are currently manipulating a relation table, FALSE by default
239 * @return bool
240 * @throws SqlErrorException
241 */
242 public function removeRow($tableName, array $where, $isRelation = false)
243 {
244 try {
245 $this->connectionPool->getConnectionForTable($tableName)->delete($tableName, $where);
246 } catch (DBALException $e) {
247 throw new SqlErrorException($e->getPrevious()->getMessage(), 1470230769);
248 }
249
250 if (!$isRelation && isset($where['uid'])) {
251 $this->clearPageCache($tableName, $where['uid']);
252 }
253
254 // always returns true
255 return true;
256 }
257
258 /**
259 * Fetches maximal value for given table column from database.
260 *
261 * @param string $tableName The database table name
262 * @param array $where An array of where array('fieldname' => value).
263 * @param string $columnName column name to get the max value from
264 * @return mixed the max value
265 * @throws SqlErrorException
266 */
267 public function getMaxValueFromTable($tableName, array $where, $columnName)
268 {
269 try {
270 $queryBuilder = $this->connectionPool->getQueryBuilderForTable($tableName);
271 $queryBuilder->getRestrictions()->removeAll();
272 $queryBuilder
273 ->select($columnName)
274 ->from($tableName)
275 ->orderBy($columnName, 'DESC')
276 ->setMaxResults(1);
277
278 foreach ($where as $fieldName => $value) {
279 $queryBuilder->andWhere(
280 $queryBuilder->expr()->eq($fieldName, $queryBuilder->createNamedParameter($value, \PDO::PARAM_STR))
281 );
282 }
283
284 $result = $queryBuilder->execute()->fetchColumn(0);
285 } catch (DBALException $e) {
286 throw new SqlErrorException($e->getPrevious()->getMessage(), 1470230770);
287 }
288 return $result;
289 }
290
291 /**
292 * Fetches row data from the database
293 *
294 * @param string $tableName
295 * @param array $where An array of where array('fieldname' => value).
296 * @return array|bool
297 * @throws SqlErrorException
298 */
299 public function getRowByIdentifier($tableName, array $where)
300 {
301 try {
302 $queryBuilder = $this->connectionPool->getQueryBuilderForTable($tableName);
303 $queryBuilder->getRestrictions()->removeAll();
304 $queryBuilder
305 ->select('*')
306 ->from($tableName);
307
308 foreach ($where as $fieldName => $value) {
309 $queryBuilder->andWhere(
310 $queryBuilder->expr()->eq($fieldName, $queryBuilder->createNamedParameter($value, \PDO::PARAM_STR))
311 );
312 }
313
314 $row = $queryBuilder->execute()->fetch();
315 } catch (DBALException $e) {
316 throw new SqlErrorException($e->getPrevious()->getMessage(), 1470230771);
317 }
318 return $row ?: false;
319 }
320
321 /**
322 * Returns the object data matching the $query.
323 *
324 * @param QueryInterface $query
325 * @return array
326 * @throws SqlErrorException
327 */
328 public function getObjectDataByQuery(QueryInterface $query)
329 {
330 $statement = $query->getStatement();
331 if ($statement instanceof Qom\Statement) {
332 $rows = $this->getObjectDataByRawQuery($statement);
333 } else {
334 $queryBuilder = $this->objectManager->get(Typo3DbQueryParser::class)
335 ->convertQueryToDoctrineQueryBuilder($query);
336 if ($query->getOffset()) {
337 $queryBuilder->setFirstResult($query->getOffset());
338 }
339 if ($query->getLimit()) {
340 $queryBuilder->setMaxResults($query->getLimit());
341 }
342 try {
343 $rows = $queryBuilder->execute()->fetchAll();
344 } catch (DBALException $e) {
345 throw new SqlErrorException($e->getPrevious()->getMessage(), 1472074485);
346 }
347 }
348
349 $rows = $this->doLanguageAndWorkspaceOverlay($query->getSource(), $rows, $query->getQuerySettings());
350 return $rows;
351 }
352
353 /**
354 * Returns the object data using a custom statement
355 *
356 * @param Qom\Statement $statement
357 * @return array
358 * @throws SqlErrorException when the raw SQL statement fails in the database
359 */
360 protected function getObjectDataByRawQuery(Qom\Statement $statement)
361 {
362 $realStatement = $statement->getStatement();
363 $parameters = $statement->getBoundVariables();
364
365 // The real statement is an instance of the Doctrine DBAL QueryBuilder, so fetching
366 // this directly is possible
367 if ($realStatement instanceof QueryBuilder) {
368 try {
369 $result = $realStatement->execute();
370 } catch (DBALException $e) {
371 throw new SqlErrorException($e->getPrevious()->getMessage(), 1472064721);
372 }
373 $rows = $result->fetchAll();
374 } elseif ($realStatement instanceof \Doctrine\DBAL\Statement) {
375 try {
376 $result = $realStatement->execute($parameters);
377 } catch (DBALException $e) {
378 throw new SqlErrorException($e->getPrevious()->getMessage(), 1481281404);
379 }
380 $rows = $result->fetchAll();
381 } elseif ($realStatement instanceof \TYPO3\CMS\Core\Database\PreparedStatement) {
382 GeneralUtility::deprecationLog('Extbase support for Prepared Statements has been deprecated in TYPO3 v8, and will be removed in TYPO3 v9. Use native Doctrine DBAL Statements or QueryBuilder objects.');
383 $realStatement->execute($parameters);
384 $rows = $realStatement->fetchAll();
385
386 $realStatement->free();
387 } else {
388 // Do a real raw query. This is very stupid, as it does not allow to use DBAL's real power if
389 // several tables are on different databases, so this is used with caution and could be removed
390 // in the future
391 try {
392 $connection = $this->connectionPool->getConnectionByName(ConnectionPool::DEFAULT_CONNECTION_NAME);
393 $statement = $connection->executeQuery($realStatement);
394 } catch (DBALException $e) {
395 throw new SqlErrorException($e->getPrevious()->getMessage(), 1472064775);
396 }
397
398 $rows = $statement->fetchAll();
399 }
400
401 return $rows;
402 }
403
404 /**
405 * Returns the number of tuples matching the query.
406 *
407 * @param QueryInterface $query
408 * @throws Exception\BadConstraintException
409 * @return int The number of matching tuples
410 * @throws SqlErrorException
411 */
412 public function getObjectCountByQuery(QueryInterface $query)
413 {
414 if ($query->getConstraint() instanceof Qom\Statement) {
415 throw new \TYPO3\CMS\Extbase\Persistence\Generic\Storage\Exception\BadConstraintException('Could not execute count on queries with a constraint of type TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Qom\\Statement', 1256661045);
416 }
417
418 $queryBuilder = $this->objectManager->get(Typo3DbQueryParser::class)
419 ->convertQueryToDoctrineQueryBuilder($query)
420 ->resetQueryPart('orderBy');
421
422 if (count($queryBuilder->getQueryPart('groupBy')) !== 0) {
423 $source = $queryBuilder->getQueryPart('from')[0];
424 // Tablename is already quoted for the DBMS, we need to treat table and field names separately
425 $tableName = $source['alias'] ?: $source['table'];
426 $fieldName = $queryBuilder->quoteIdentifier('uid');
427 $queryBuilder->resetQueryPart('groupBy')
428 ->selectLiteral(sprintf('COUNT(DISTINCT %s.%s)', $tableName, $fieldName));
429 } else {
430 $queryBuilder->count('*');
431 }
432
433 try {
434 $count = $queryBuilder->execute()->fetchColumn(0);
435 } catch (DBALException $e) {
436 throw new SqlErrorException($e->getPrevious()->getMessage(), 1472074379);
437 }
438 if ($query->getOffset()) {
439 $count -= $query->getOffset();
440 }
441 if ($query->getLimit()) {
442 $count = min($count, $query->getLimit());
443 }
444 return (int)max(0, $count);
445 }
446
447 /**
448 * Checks if a Value Object equal to the given Object exists in the database
449 *
450 * @param AbstractValueObject $object The Value Object
451 * @return mixed The matching uid if an object was found, else FALSE
452 * @throws SqlErrorException
453 */
454 public function getUidOfAlreadyPersistedValueObject(AbstractValueObject $object)
455 {
456 $dataMap = $this->dataMapper->getDataMap(get_class($object));
457 $tableName = $dataMap->getTableName();
458 $queryBuilder = $this->connectionPool->getQueryBuilderForTable($tableName);
459 if ($this->environmentService->isEnvironmentInFrontendMode()) {
460 $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
461 }
462 $whereClause = [];
463 // loop over all properties of the object to exactly set the values of each database field
464 $properties = $object->_getProperties();
465 foreach ($properties as $propertyName => $propertyValue) {
466 // @todo We couple the Backend to the Entity implementation (uid, isClone); changes there breaks this method
467 if ($dataMap->isPersistableProperty($propertyName) && $propertyName !== 'uid' && $propertyName !== 'pid' && $propertyName !== 'isClone') {
468 $fieldName = $dataMap->getColumnMap($propertyName)->getColumnName();
469 if ($propertyValue === null) {
470 $whereClause[] = $queryBuilder->expr()->isNull($fieldName);
471 } else {
472 $whereClause[] = $queryBuilder->expr()->eq($fieldName, $queryBuilder->createNamedParameter($this->dataMapper->getPlainValue($propertyValue)));
473 }
474 }
475 }
476 $queryBuilder
477 ->select('uid')
478 ->from($tableName)
479 ->where(...$whereClause);
480
481 try {
482 $uid = (int)$queryBuilder
483 ->execute()
484 ->fetchColumn(0);
485 if ($uid > 0) {
486 return $uid;
487 } else {
488 return false;
489 }
490 } catch (DBALException $e) {
491 throw new SqlErrorException($e->getPrevious()->getMessage(), 1470231748);
492 }
493 }
494
495 /**
496 * Performs workspace and language overlay on the given row array. The language and workspace id is automatically
497 * detected (depending on FE or BE context). You can also explicitly set the language/workspace id.
498 *
499 * @param Qom\SourceInterface $source The source (selector od join)
500 * @param array $rows
501 * @param \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface $querySettings The TYPO3 CMS specific query settings
502 * @param null|int $workspaceUid
503 * @return array
504 */
505 protected function doLanguageAndWorkspaceOverlay(Qom\SourceInterface $source, array $rows, \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface $querySettings, $workspaceUid = null)
506 {
507 if ($source instanceof Qom\SelectorInterface) {
508 $tableName = $source->getSelectorName();
509 } elseif ($source instanceof Qom\JoinInterface) {
510 $tableName = $source->getRight()->getSelectorName();
511 } else {
512 // No proper source, so we do not have a table name here
513 // we cannot do an overlay and return the original rows instead.
514 return $rows;
515 }
516
517 $pageRepository = $this->getPageRepository();
518 if (is_object($GLOBALS['TSFE'])) {
519 if ($workspaceUid !== null) {
520 $pageRepository->versioningWorkspaceId = $workspaceUid;
521 }
522 } else {
523 if ($workspaceUid === null) {
524 $workspaceUid = $GLOBALS['BE_USER']->workspace;
525 }
526 $pageRepository->versioningWorkspaceId = $workspaceUid;
527 }
528
529 // Fetches the move-placeholder in case it is supported
530 // by the table and if there's only one row in the result set
531 // (applying this to all rows does not work, since the sorting
532 // order would be destroyed and possible limits not met anymore)
533 if (!empty($pageRepository->versioningWorkspaceId)
534 && BackendUtility::isTableWorkspaceEnabled($tableName)
535 && count($rows) === 1
536 ) {
537 $versionId = $pageRepository->versioningWorkspaceId;
538 $queryBuilder = $this->connectionPool->getQueryBuilderForTable($tableName);
539 $queryBuilder->getRestrictions()->removeAll();
540 $movePlaceholder = $queryBuilder->select($tableName . '.*')
541 ->from($tableName)
542 ->where(
543 $queryBuilder->expr()->eq('t3ver_state', $queryBuilder->createNamedParameter(3, \PDO::PARAM_INT)),
544 $queryBuilder->expr()->eq('t3ver_wsid', $queryBuilder->createNamedParameter($versionId, \PDO::PARAM_INT)),
545 $queryBuilder->expr()->eq('t3ver_move_id', $queryBuilder->createNamedParameter($rows[0]['uid'], \PDO::PARAM_INT))
546 )
547 ->setMaxResults(1)
548 ->execute()
549 ->fetch();
550 if (!empty($movePlaceholder)) {
551 $rows = [$movePlaceholder];
552 }
553 }
554
555 $overlaidRows = [];
556 foreach ($rows as $row) {
557 // If current row is a translation select its parent
558 if (isset($tableName) && isset($GLOBALS['TCA'][$tableName])
559 && isset($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])
560 && isset($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'])
561 && $tableName !== 'pages_language_overlay'
562 ) {
563 if (isset($row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']])
564 && $row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']] > 0
565 ) {
566 $queryBuilder = $this->connectionPool->getQueryBuilderForTable($tableName);
567 $queryBuilder->getRestrictions()->removeAll();
568 $row = $queryBuilder->select($tableName . '.*')
569 ->from($tableName)
570 ->where(
571 $queryBuilder->expr()->eq(
572 $tableName . '.uid',
573 $queryBuilder->createNamedParameter(
574 $row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']],
575 \PDO::PARAM_INT
576 )
577 ),
578 $queryBuilder->expr()->eq(
579 $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'],
580 $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
581 )
582 )
583 ->setMaxResults(1)
584 ->execute()
585 ->fetch();
586 }
587 }
588 $pageRepository->versionOL($tableName, $row, true);
589 if ($tableName === 'pages') {
590 $row = $pageRepository->getPageOverlay($row, $querySettings->getLanguageUid());
591 } elseif (isset($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])
592 && $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] !== ''
593 && $tableName !== 'pages_language_overlay'
594 ) {
595 if (in_array($row[$GLOBALS['TCA'][$tableName]['ctrl']['languageField']], [-1, 0])) {
596 $overlayMode = $querySettings->getLanguageMode() === 'strict' ? 'hideNonTranslated' : '';
597 $row = $pageRepository->getRecordOverlay($tableName, $row, $querySettings->getLanguageUid(), $overlayMode);
598 }
599 }
600 if ($row !== null && is_array($row)) {
601 $overlaidRows[] = $row;
602 }
603 }
604 return $overlaidRows;
605 }
606
607 /**
608 * @return \TYPO3\CMS\Frontend\Page\PageRepository
609 */
610 protected function getPageRepository()
611 {
612 if (!$this->pageRepository instanceof \TYPO3\CMS\Frontend\Page\PageRepository) {
613 if ($this->environmentService->isEnvironmentInFrontendMode() && is_object($GLOBALS['TSFE'])) {
614 $this->pageRepository = $GLOBALS['TSFE']->sys_page;
615 } else {
616 $this->pageRepository = GeneralUtility::makeInstance(\TYPO3\CMS\Frontend\Page\PageRepository::class);
617 }
618 }
619
620 return $this->pageRepository;
621 }
622
623 /**
624 * Clear the TYPO3 page cache for the given record.
625 * If the record lies on a page, then we clear the cache of this page.
626 * If the record has no PID column, we clear the cache of the current page as best-effort.
627 *
628 * Much of this functionality is taken from DataHandler::clear_cache() which unfortunately only works with logged-in BE user.
629 *
630 * @param string $tableName Table name of the record
631 * @param int $uid UID of the record
632 * @return void
633 */
634 protected function clearPageCache($tableName, $uid)
635 {
636 $frameworkConfiguration = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
637 if (isset($frameworkConfiguration['persistence']['enableAutomaticCacheClearing']) && $frameworkConfiguration['persistence']['enableAutomaticCacheClearing'] === '1') {
638 } else {
639 // if disabled, return
640 return;
641 }
642 $pageIdsToClear = [];
643 $storagePage = null;
644 $columns = GeneralUtility::makeInstance(ConnectionPool::class)
645 ->getConnectionForTable($tableName)
646 ->getSchemaManager()
647 ->listTableColumns($tableName);
648 if (array_key_exists('pid', $columns)) {
649 $queryBuilder = $this->connectionPool->getQueryBuilderForTable($tableName);
650 $queryBuilder->getRestrictions()->removeAll();
651 $result = $queryBuilder
652 ->select('pid')
653 ->from($tableName)
654 ->where(
655 $queryBuilder->expr()->eq(
656 'uid',
657 $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
658 )
659 )
660 ->execute();
661 if ($row = $result->fetch()) {
662 $storagePage = $row['pid'];
663 $pageIdsToClear[] = $storagePage;
664 }
665 } elseif (isset($GLOBALS['TSFE'])) {
666 // No PID column - we can do a best-effort to clear the cache of the current page if in FE
667 $storagePage = $GLOBALS['TSFE']->id;
668 $pageIdsToClear[] = $storagePage;
669 }
670 if ($storagePage === null) {
671 return;
672 }
673 if (!isset($this->pageTSConfigCache[$storagePage])) {
674 $this->pageTSConfigCache[$storagePage] = BackendUtility::getPagesTSconfig($storagePage);
675 }
676 if (isset($this->pageTSConfigCache[$storagePage]['TCEMAIN.']['clearCacheCmd'])) {
677 $clearCacheCommands = GeneralUtility::trimExplode(',', strtolower($this->pageTSConfigCache[$storagePage]['TCEMAIN.']['clearCacheCmd']), true);
678 $clearCacheCommands = array_unique($clearCacheCommands);
679 foreach ($clearCacheCommands as $clearCacheCommand) {
680 if (MathUtility::canBeInterpretedAsInteger($clearCacheCommand)) {
681 $pageIdsToClear[] = $clearCacheCommand;
682 }
683 }
684 }
685
686 foreach ($pageIdsToClear as $pageIdToClear) {
687 $this->cacheService->getPageIdStack()->push($pageIdToClear);
688 }
689 }
690 }