‪TYPO3CMS  ‪main
MissingRelationsCommand.php
Go to the documentation of this file.
1 <?php
2 
3 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 
19 
20 use Doctrine\DBAL\Exception\TableNotFoundException;
21 use Symfony\Component\Console\Command\Command;
22 use Symfony\Component\Console\Input\InputInterface;
23 use Symfony\Component\Console\Input\InputOption;
24 use Symfony\Component\Console\Output\OutputInterface;
25 use Symfony\Component\Console\Style\SymfonyStyle;
27 use TYPO3\CMS\Backend\Utility\BackendUtility;
35 
47 class ‪MissingRelationsCommand extends Command
48 {
49  public function ‪__construct(
50  private readonly ‪ConnectionPool $connectionPool,
51  private readonly ‪ReferenceIndex $referenceIndex,
52  ) {
53  parent::__construct();
54  }
55 
59  public function ‪configure()
60  {
61  $this
62  ->setHelp('
63 Assumptions:
64 - a perfect integrity of the reference index table (always update the reference index table before using this tool!)
65 - all database references to check are integers greater than zero
66 - does not check if a referenced record is inside an offline branch, another workspace etc. which could make the reference useless in reality or otherwise question integrity
67 Records may be missing for these reasons (except software bugs):
68 - someone deleted the record which is technically not an error although it might be a mistake that someone did so.
69 - after flushing published versions and/or deleted-flagged records a number of new missing references might appear; those were pointing to records just flushed.
70 
71 An automatic repair is only possible for managed references are (not for soft references), for
72 offline versions records and non-existing records. If you just want to list them, use the --dry-run option.
73 The references in this case are removed.
74 
75 If the option "--dry-run" is not set, all managed files (TCA/FlexForm attachments) will silently remove the references
76 to non-existing and offline version records.
77 All soft references with relations to non-existing records, offline versions and deleted records
78 require manual fix if you consider it an error.
79 
80 Manual repair suggestions:
81 - For soft references you should investigate each case and edit the content accordingly.
82 - References to deleted records can theoretically be removed since a deleted record cannot be selected and hence
83 your website should not be affected by removal of the reference. On the other hand it does not hurt to ignore it
84 for now. To have this automatically fixed you must first flush the deleted records after which remaining
85 references will appear as pointing to Non Existing Records and can now be removed with the automatic fix.
86 
87 If you want to get more detailed information, use the --verbose option.')
88  ->addOption(
89  'dry-run',
90  null,
91  InputOption::VALUE_NONE,
92  'If this option is set, the references will not be removed, but just the output which references would be deleted are shown'
93  )
94  ->addOption(
95  'update-refindex',
96  null,
97  InputOption::VALUE_NONE,
98  'Setting this option automatically updates the reference index and does not ask on command line. Alternatively, use -n to avoid the interactive mode'
99  );
100  }
101 
108  protected function ‪execute(InputInterface $input, OutputInterface ‪$output): int
109  {
110  // Make sure the _cli_ user is loaded
112 
113  $io = new SymfonyStyle($input, ‪$output);
114  $io->title($this->getDescription());
115 
116  $dryRun = $input->hasOption('dry-run') && (bool)$input->getOption('dry-run') !== false;
117 
118  // Update the reference index
119  $this->‪updateReferenceIndex($input, $io);
120 
121  $results = $this->‪findRelationsToNonExistingRecords();
122 
123  // Display soft references to non-existing records
124  if ($io->isVerbose() && count($results['nonExistingRecordsInSoftReferenceRelations'])) {
125  $io->note([
126  'Found ' . count($results['nonExistingRecordsInSoftReferenceRelations']) . ' non-existing records that are still being soft-referenced in the following locations.',
127  'These relations cannot be removed automatically and need manual repair.',
128  ]);
129  $io->listing($results['nonExistingRecordsInSoftReferenceRelations']);
130  }
131 
132  // Display soft references to offline version records
133  // These records are offline versions having a pid=-1 and references should never occur directly to their uids.
134  if ($io->isVerbose() && count($results['offlineVersionRecordsInSoftReferenceRelations'])) {
135  $io->note([
136  'Found ' . count($results['offlineVersionRecordsInSoftReferenceRelations']) . ' soft-references pointing to offline versions, which should never be referenced directly.',
137  'These relations cannot be removed automatically and need manual repair.',
138  ]);
139  $io->listing($results['offlineVersionRecordsInSoftReferenceRelations']);
140  }
141 
142  // Display references to deleted records
143  // These records are deleted with a flag but references are still pointing at them.
144  // Keeping the references is useful if you undelete the referenced records later, otherwise the references
145  // are lost completely when the deleted records are flushed at some point. Notice that if those records listed
146  // are themselves deleted (marked with "DELETED") it is not a problem.
147  if ($io->isVerbose() && count($results['deletedRecords'])) {
148  $io->note([
149  'Found ' . count($results['deletedRecords']) . ' references pointing to deleted records.',
150  'Keeping the references is useful if you undelete the referenced records later, otherwise the references' .
151  'are lost completely when the deleted records are flushed at some point. Notice that if those records listed' .
152  'are themselves deleted (marked with "DELETED") it is not a problem.',
153  ]);
154  $io->listing($results['deletedRecords']);
155  }
156 
157  // soft references which link to deleted records
158  if ($io->isVerbose() && count($results['deletedRecordsInSoftReferenceRelations'])) {
159  $io->note([
160  'Found ' . count($results['deletedRecordsInSoftReferenceRelations']) . ' soft references pointing to deleted records.',
161  'Keeping the references is useful if you undelete the referenced records later, otherwise the references' .
162  'are lost completely when the deleted records are flushed at some point. Notice that if those records listed' .
163  'are themselves deleted (marked with "DELETED") it is not a problem.',
164  ]);
165  $io->listing($results['deletedRecordsInSoftReferenceRelations']);
166  }
167 
168  // Find missing references
169  if (count($results['offlineVersionRecords']) || count($results['nonExistingRecords'])) {
170  $io->note([
171  'Found ' . count($results['nonExistingRecords']) . ' references to non-existing records ' .
172  'and ' . count($results['offlineVersionRecords']) . ' references directly linked to offline versions.',
173  ]);
174 
176  $results['offlineVersionRecords'],
177  $results['nonExistingRecords'],
178  $dryRun,
179  $io
180  );
181  $io->success('All references were updated accordingly.');
182  } else {
183  $io->success('Nothing to do, no missing relations found. Everything is in place.');
184  }
185  return Command::SUCCESS;
186  }
187 
194  protected function ‪updateReferenceIndex(InputInterface $input, SymfonyStyle $io): void
195  {
196  // Check for reference index to update
197  $io->note('Finding missing records referenced by TYPO3 requires a clean reference index (sys_refindex)');
198  if ($input->hasOption('update-refindex') && $input->getOption('update-refindex')) {
199  $updateReferenceIndex = true;
200  } elseif ($input->isInteractive()) {
201  $updateReferenceIndex = $io->confirm('Should the reference index be updated right now?', false);
202  } else {
203  $updateReferenceIndex = false;
204  }
205 
206  // Update the reference index
207  if ($updateReferenceIndex) {
208  $progressListener = GeneralUtility::makeInstance(ReferenceIndexProgressListener::class);
209  $progressListener->initialize($io);
210  $this->referenceIndex->updateIndex(false, $progressListener);
211  } else {
212  $io->writeln('Reference index is assumed to be up to date, continuing.');
213  }
214  }
215 
219  protected function ‪findRelationsToNonExistingRecords(): array
220  {
221  $deletedRecords = [];
222  $deletedRecordsInSoftReferenceRelations = [];
223  $nonExistingRecords = [];
224  $nonExistingRecordsInSoftReferenceRelations = [];
225  $offlineVersionRecords = [];
226  $offlineVersionRecordsInSoftReferenceRelations = [];
227 
228  // Select DB relations from reference table
229  $queryBuilder = $this->connectionPool->getQueryBuilderForTable('sys_refindex');
230  $rowIterator = $queryBuilder
231  ->select('ref_uid', 'ref_table', 'softref_key', 'hash', 'tablename', 'recuid', 'field', 'flexpointer')
232  ->from('sys_refindex')
233  ->where(
234  $queryBuilder->expr()->gt('ref_uid', $queryBuilder->createNamedParameter(0, ‪Connection::PARAM_INT))
235  )
236  ->executeQuery();
237 
238  $existingRecords = [];
239  while ($rec = $rowIterator->fetchAssociative()) {
240  $isSoftReference = !empty($rec['softref_key']);
241  $idx = $rec['ref_table'] . ':' . $rec['ref_uid'];
242  // Get referenced record:
243  if (!isset($existingRecords[$idx])) {
244  $queryBuilder = $this->connectionPool
245  ->getQueryBuilderForTable($rec['ref_table']);
246  $queryBuilder->getRestrictions()->removeAll();
247 
248  $selectFields = ['uid', 'pid'];
249  if (isset(‪$GLOBALS['TCA'][$rec['ref_table']]['ctrl']['delete'])) {
250  $selectFields[] = ‪$GLOBALS['TCA'][$rec['ref_table']]['ctrl']['delete'];
251  }
252  if (BackendUtility::isTableWorkspaceEnabled($rec['ref_table'])) {
253  $selectFields[] = 't3ver_oid';
254  $selectFields[] = 't3ver_wsid';
255  }
256 
257  try {
258  $existingRecords[$idx] = $queryBuilder
259  ->select(...$selectFields)
260  ->from($rec['ref_table'])
261  ->where(
262  $queryBuilder->expr()->eq(
263  'uid',
264  $queryBuilder->createNamedParameter($rec['ref_uid'], ‪Connection::PARAM_INT)
265  )
266  )
267  ->executeQuery()
268  ->fetchAssociative();
269  } catch (TableNotFoundException $tableNotFoundException) {
270  // noop
271  }
272  }
273  // Compile info string for location of reference:
274  $infoString = $this->‪formatReferenceIndexEntryToString($rec);
275  // Handle missing file:
276  if ($existingRecords[$idx]['uid'] ?? false) {
277  // Record exists, but is a reference to an offline version
278  if ((int)($existingRecords[$idx]['t3ver_oid'] ?? 0) > 0) {
279  if ($isSoftReference) {
280  $offlineVersionRecordsInSoftReferenceRelations[] = $infoString;
281  } else {
282  $offlineVersionRecords[$idx][$rec['hash']] = $infoString;
283  }
284  // reference to a deleted record
285  } elseif (isset(‪$GLOBALS['TCA'][$rec['ref_table']]['ctrl']['delete']) && $existingRecords[$idx][‪$GLOBALS['TCA'][$rec['ref_table']]['ctrl']['delete']]) {
286  if ($isSoftReference) {
287  $deletedRecordsInSoftReferenceRelations[] = $infoString;
288  } else {
289  $deletedRecords[] = $infoString;
290  }
291  }
292  } else {
293  if ($isSoftReference) {
294  $nonExistingRecordsInSoftReferenceRelations[] = $infoString;
295  } else {
296  $nonExistingRecords[$idx][$rec['hash']] = $infoString;
297  }
298  }
299  }
300 
301  return [
302  // Non-existing records to which there are references (managed)
303  // These references can safely be removed since there is no record found in the database at all.
304  'nonExistingRecords' => ‪ArrayUtility::sortByKeyRecursive($nonExistingRecords),
305  // Non-existing records to which there are references (softref)
306  'nonExistingRecordsInSoftReferenceRelations' => ‪ArrayUtility::sortByKeyRecursive($nonExistingRecordsInSoftReferenceRelations),
307  // Offline version records (managed)
308  // These records are offline versions having a pid=-1 and references should never occur directly to their uids.
309  'offlineVersionRecords' => ‪ArrayUtility::sortByKeyRecursive($offlineVersionRecords),
310  // Offline version records (softref)
311  'offlineVersionRecordsInSoftReferenceRelations' => ‪ArrayUtility::sortByKeyRecursive($offlineVersionRecordsInSoftReferenceRelations),
312  // Deleted-flagged records (managed)
313  // These records are deleted with a flag but references are still pointing at them.
314  // Keeping the references is useful if you undelete the referenced records later, otherwise the references
315  // are lost completely when the deleted records are flushed at some point. Notice that if those records listed
316  // are themselves deleted (marked with "DELETED") it is not a problem.
317  'deletedRecords' => ‪ArrayUtility::sortByKeyRecursive($deletedRecords),
318  // Deleted-flagged records (softref)
319  'deletedRecordsInSoftReferenceRelations' => ‪ArrayUtility::sortByKeyRecursive($deletedRecordsInSoftReferenceRelations),
320  ];
321  }
322 
332  array $offlineVersionRecords,
333  array $nonExistingRecords,
334  bool $dryRun,
335  SymfonyStyle $io
336  ): void {
337  // Remove references to offline records
338  foreach ($offlineVersionRecords as $fileName => $references) {
339  if ($io->isVeryVerbose()) {
340  $io->writeln('Removing references in offline versions which there are references pointing towards.');
341  }
342  foreach ($references as $hash => $recordReference) {
343  $this->‪removeReference($hash, $recordReference, $dryRun, $io);
344  }
345  }
346 
347  // Remove references to non-existing records
348  foreach ($nonExistingRecords as $fileName => $references) {
349  if ($io->isVeryVerbose()) {
350  $io->writeln('Removing references to non-existing records.');
351  }
352  foreach ($references as $hash => $recordReference) {
353  $this->‪removeReference($hash, $recordReference, $dryRun, $io);
354  }
355  }
356  }
357 
361  protected function ‪removeReference(string $hash, string $recordReference, bool $dryRun, SymfonyStyle $io): void
362  {
363  $io->writeln('Removing reference in record "' . $recordReference . '" (Hash: ' . $hash . ')');
364  if ($dryRun) {
365  return;
366  }
367  $error = $this->‪setReferenceValue($hash);
368  if ($error) {
369  $io->error('ReferenceIndex::setReferenceValue() reported "' . $error . '"');
370  }
371  }
372 
376  protected function ‪formatReferenceIndexEntryToString(array ‪$record): string
377  {
378  return ‪$record['tablename']
379  . ':' . ‪$record['recuid']
380  . ':' . ‪$record['field']
381  . (‪$record['flexpointer'] ? ':' . ‪$record['flexpointer'] : '')
382  . (‪$record['softref_key'] ? ':' . ‪$record['softref_key'] . ' (Soft Reference) ' : '');
383  }
384 
400  protected function ‪setReferenceValue(string $hash): string|bool
401  {
402  $queryBuilder = $this->connectionPool->getQueryBuilderForTable('sys_refindex');
403  $queryBuilder->getRestrictions()->removeAll();
404 
405  // Get current index from Database
406  $referenceRecord = $queryBuilder
407  ->select('*')
408  ->from('sys_refindex')
409  ->where(
410  $queryBuilder->expr()->eq('hash', $queryBuilder->createNamedParameter($hash))
411  )
412  ->setMaxResults(1)
413  ->executeQuery()
414  ->fetchAssociative();
415 
416  // Check if reference existed.
417  if (!is_array($referenceRecord)) {
418  return 'ERROR: No reference record with hash="' . $hash . '" was found!';
419  }
420  if (empty(‪$GLOBALS['TCA'][$referenceRecord['tablename']])) {
421  return 'ERROR: Table "' . $referenceRecord['tablename'] . '" was not in TCA!';
422  }
423 
424  // Get that record from database
425  $queryBuilder = $this->connectionPool->getQueryBuilderForTable($referenceRecord['tablename']);
426  $queryBuilder->getRestrictions()->removeAll();
427  ‪$record = $queryBuilder
428  ->select('*')
429  ->from($referenceRecord['tablename'])
430  ->where(
431  $queryBuilder->expr()->eq(
432  'uid',
433  $queryBuilder->createNamedParameter($referenceRecord['recuid'], ‪Connection::PARAM_INT)
434  )
435  )
436  ->setMaxResults(1)
437  ->executeQuery()
438  ->fetchAssociative();
439 
440  if (is_array(‪$record)) {
441  // Get relation for single field from record
442  $recordRelations = $this->referenceIndex->getRelations((string)$referenceRecord['tablename'], ‪$record, 0);
443  $fieldRelation = $recordRelations[$referenceRecord['field']] ?? null;
444  if ($fieldRelation) {
445  // Initialize data array that is to be sent to DataHandler afterwards:
446  $dataArray = [];
447  // Based on type
448  switch ((string)($fieldRelation['type'] ?? '')) {
449  case 'db':
450  $error = $this->‪setReferenceValue_dbRels($referenceRecord, $fieldRelation['itemArray'], $dataArray);
451  if ($error) {
452  return $error;
453  }
454  break;
455  case 'flex':
456  // DB references in FlexForms
457  if (is_array($fieldRelation['flexFormRels']['db'][$referenceRecord['flexpointer']])) {
458  $error = $this->‪setReferenceValue_dbRels($referenceRecord, $fieldRelation['flexFormRels']['db'][$referenceRecord['flexpointer']], $dataArray, $referenceRecord['flexpointer']);
459  if ($error) {
460  return $error;
461  }
462  }
463  // Soft references in FlexForms
464  if ($referenceRecord['softref_key'] && is_array($fieldRelation['flexFormRels']['softrefs'][$referenceRecord['flexpointer']]['keys'][$referenceRecord['softref_key']])) {
465  $error = $this->‪setReferenceValue_softreferences($referenceRecord, $fieldRelation['flexFormRels']['softrefs'][$referenceRecord['flexpointer']], $dataArray, $referenceRecord['flexpointer']);
466  if ($error) {
467  return $error;
468  }
469  }
470  break;
471  }
472  // Soft references in the field:
473  if ($referenceRecord['softref_key'] && is_array($fieldRelation['softrefs']['keys'][$referenceRecord['softref_key']])) {
474  $error = $this->‪setReferenceValue_softreferences($referenceRecord, $fieldRelation['softrefs'], $dataArray);
475  if ($error) {
476  return $error;
477  }
478  }
479  // Data Array, now ready to be sent to DataHandler, execute CMD array:
480  $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
481  $dataHandler->dontProcessTransformations = true;
482  $dataHandler->bypassWorkspaceRestrictions = true;
483  // Otherwise this may lead to permission issues if user is not admin
484  $dataHandler->bypassAccessCheckForRecords = true;
485  // Check has been done previously that there is a backend user which is Admin and also in live workspace
486  $dataHandler->start($dataArray, []);
487  $dataHandler->process_datamap();
488  // Return errors if any:
489  if (!empty($dataHandler->errorLog)) {
490  return LF . 'DataHandler:' . implode(LF . 'DataHandler:', $dataHandler->errorLog);
491  }
492  }
493  }
494  return false;
495  }
496 
504  protected function ‪setReferenceValue_dbRels(array $refRec, array $itemArray, array &$dataArray, string $flexPointer = ''): string|bool
505  {
506  if ((int)$itemArray[$refRec['sorting']]['id'] === (int)$refRec['ref_uid'] && (string)$itemArray[$refRec['sorting']]['table'] === (string)$refRec['ref_table']) {
507  // Setting or removing value:
508  // Remove value:
509  unset($itemArray[$refRec['sorting']]);
510  // Traverse and compile new list of records:
511  $saveValue = [];
512  foreach ($itemArray as $pair) {
513  $saveValue[] = $pair['table'] . '_' . $pair['id'];
514  }
515  // Set in data array:
516  if ($flexPointer) {
517  $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'] = ‪ArrayUtility::setValueByPath(
518  [],
519  substr($flexPointer, 0, -1),
520  implode(',', $saveValue)
521  );
522  } else {
523  $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']] = implode(',', $saveValue);
524  }
525  } else {
526  return 'ERROR: table:id pair "' . $refRec['ref_table'] . ':' . $refRec['ref_uid'] . '" did not match that of the record ("' . $itemArray[$refRec['sorting']]['table'] . ':' . $itemArray[$refRec['sorting']]['id'] . '") in sorting index "' . $refRec['sorting'] . '"';
527  }
528  return false;
529  }
530 
538  protected function ‪setReferenceValue_softreferences(array $refRec, array $softref, array &$dataArray, string $flexPointer = ''): string|bool
539  {
540  if (!is_array($softref['keys'][$refRec['softref_key']][$refRec['softref_id']])) {
541  return 'ERROR: Soft reference parser key "' . $refRec['softref_key'] . '" or the index "' . $refRec['softref_id'] . '" was not found.';
542  }
543  // Set new value:
544  $softref['keys'][$refRec['softref_key']][$refRec['softref_id']]['subst']['tokenValue'] = '';
545  // Traverse softreferences and replace in tokenized content to rebuild it with new value inside:
546  foreach ($softref['keys'] as $sfIndexes) {
547  foreach ($sfIndexes as $data) {
548  $softref['tokenizedContent'] = str_replace('{softref:' . $data['subst']['tokenID'] . '}', $data['subst']['tokenValue'], $softref['tokenizedContent']);
549  }
550  }
551  // Set in data array:
552  if (!str_contains($softref['tokenizedContent'], '{softref:')) {
553  if ($flexPointer) {
554  $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'] = ‪ArrayUtility::setValueByPath(
555  [],
556  substr($flexPointer, 0, -1),
557  $softref['tokenizedContent']
558  );
559  } else {
560  $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']] = $softref['tokenizedContent'];
561  }
562  } else {
563  return 'ERROR: After substituting all found soft references there were still soft reference tokens in the text. (theoretically this does not have to be an error if the string "{softref:" happens to be in the field for another reason.)';
564  }
565  return false;
566  }
567 }
‪TYPO3\CMS\Core\DataHandling\DataHandler
Definition: DataHandler.php:95
‪TYPO3\CMS\Lowlevel\Command\MissingRelationsCommand\configure
‪configure()
Definition: MissingRelationsCommand.php:59
‪TYPO3\CMS\Lowlevel\Command\MissingRelationsCommand\__construct
‪__construct(private readonly ConnectionPool $connectionPool, private readonly ReferenceIndex $referenceIndex,)
Definition: MissingRelationsCommand.php:49
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT
‪const PARAM_INT
Definition: Connection.php:50
‪TYPO3\CMS\Backend\Command\ProgressListener\ReferenceIndexProgressListener
Definition: ReferenceIndexProgressListener.php:30
‪TYPO3\CMS\Lowlevel\Command\MissingRelationsCommand\setReferenceValue
‪setReferenceValue(string $hash)
Definition: MissingRelationsCommand.php:400
‪TYPO3\CMS\Core\Database\ReferenceIndex
Definition: ReferenceIndex.php:40
‪TYPO3\CMS\Lowlevel\Command\MissingRelationsCommand\setReferenceValue_softreferences
‪setReferenceValue_softreferences(array $refRec, array $softref, array &$dataArray, string $flexPointer='')
Definition: MissingRelationsCommand.php:538
‪TYPO3\CMS\Lowlevel\Command\MissingRelationsCommand\findRelationsToNonExistingRecords
‪findRelationsToNonExistingRecords()
Definition: MissingRelationsCommand.php:219
‪TYPO3\CMS\Core\Utility\ArrayUtility\sortByKeyRecursive
‪static array sortByKeyRecursive(array $array)
Definition: ArrayUtility.php:338
‪TYPO3\CMS\Lowlevel\Command\MissingRelationsCommand\formatReferenceIndexEntryToString
‪formatReferenceIndexEntryToString(array $record)
Definition: MissingRelationsCommand.php:376
‪TYPO3\CMS\Lowlevel\Command\MissingRelationsCommand\setReferenceValue_dbRels
‪setReferenceValue_dbRels(array $refRec, array $itemArray, array &$dataArray, string $flexPointer='')
Definition: MissingRelationsCommand.php:504
‪TYPO3\CMS\Lowlevel\Command\MissingRelationsCommand\updateReferenceIndex
‪updateReferenceIndex(InputInterface $input, SymfonyStyle $io)
Definition: MissingRelationsCommand.php:194
‪TYPO3\CMS\Lowlevel\Command\MissingRelationsCommand\execute
‪execute(InputInterface $input, OutputInterface $output)
Definition: MissingRelationsCommand.php:108
‪TYPO3\CMS\Lowlevel\Command\MissingRelationsCommand\removeReferencesToMissingRecords
‪removeReferencesToMissingRecords(array $offlineVersionRecords, array $nonExistingRecords, bool $dryRun, SymfonyStyle $io)
Definition: MissingRelationsCommand.php:331
‪TYPO3\CMS\Webhooks\Message\$record
‪identifier readonly int readonly array $record
Definition: PageModificationMessage.php:36
‪TYPO3\CMS\Lowlevel\Command\MissingRelationsCommand
Definition: MissingRelationsCommand.php:48
‪$output
‪$output
Definition: annotationChecker.php:119
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:39
‪TYPO3\CMS\Core\Utility\ArrayUtility
Definition: ArrayUtility.php:26
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Core\Bootstrap
Definition: Bootstrap.php:64
‪TYPO3\CMS\Lowlevel\Command
Definition: CleanFlexFormsCommand.php:18
‪TYPO3\CMS\Core\Utility\ArrayUtility\setValueByPath
‪static array setValueByPath(array $array, string|array|\ArrayAccess $path, mixed $value, string $delimiter='/')
Definition: ArrayUtility.php:261
‪TYPO3\CMS\Lowlevel\Command\MissingRelationsCommand\removeReference
‪removeReference(string $hash, string $recordReference, bool $dryRun, SymfonyStyle $io)
Definition: MissingRelationsCommand.php:361
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:48
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:51
‪TYPO3\CMS\Core\Core\Bootstrap\initializeBackendAuthentication
‪static initializeBackendAuthentication()
Definition: Bootstrap.php:529