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