2 declare(strict_types = 1);
18 use PhpParser\NodeTraverser;
19 use PhpParser\NodeVisitor\NameResolver;
20 use PhpParser\ParserFactory;
21 use Psr\Http\Message\ResponseInterface;
22 use Psr\Http\Message\ServerRequestInterface;
23 use Symfony\Component\Finder\Finder;
24 use Symfony\Component\Finder\SplFileInfo;
35 use TYPO3\CMS\Core\Package\PackageManager;
66 use TYPO3\CMS\Install\Service\CoreVersionService;
97 $this->packageManager =
$packageManager ?? GeneralUtility::makeInstance(PackageManager::class);
108 'class' => ArrayDimensionMatcher::class,
109 'configurationFile' =>
'EXT:install/Configuration/ExtensionScanner/Php/ArrayDimensionMatcher.php',
112 'class' => ArrayGlobalMatcher::class,
113 'configurationFile' =>
'EXT:install/Configuration/ExtensionScanner/Php/ArrayGlobalMatcher.php',
116 'class' => ClassConstantMatcher::class,
117 'configurationFile' =>
'EXT:install/Configuration/ExtensionScanner/Php/ClassConstantMatcher.php',
120 'class' => ClassNameMatcher::class,
121 'configurationFile' =>
'EXT:install/Configuration/ExtensionScanner/Php/ClassNameMatcher.php',
124 'class' => ConstantMatcher::class,
125 'configurationFile' =>
'EXT:install/Configuration/ExtensionScanner/Php/ConstantMatcher.php',
128 'class' => ConstructorArgumentMatcher::class,
129 'configurationFile' =>
'EXT:install/Configuration/ExtensionScanner/Php/ConstructorArgumentMatcher.php',
132 'class' => PropertyAnnotationMatcher::class,
133 'configurationFile' =>
'EXT:install/Configuration/ExtensionScanner/Php/PropertyAnnotationMatcher.php',
136 'class' => MethodAnnotationMatcher::class,
137 'configurationFile' =>
'EXT:install/Configuration/ExtensionScanner/Php/MethodAnnotationMatcher.php',
140 'class' => FunctionCallMatcher::class,
141 'configurationFile' =>
'EXT:install/Configuration/ExtensionScanner/Php/FunctionCallMatcher.php',
144 'class' => InterfaceMethodChangedMatcher::class,
145 'configurationFile' =>
'EXT:install/Configuration/ExtensionScanner/Php/InterfaceMethodChangedMatcher.php',
148 'class' => MethodArgumentDroppedMatcher::class,
149 'configurationFile' =>
'EXT:install/Configuration/ExtensionScanner/Php/MethodArgumentDroppedMatcher.php',
152 'class' => MethodArgumentDroppedStaticMatcher::class,
153 'configurationFile' =>
'EXT:install/Configuration/ExtensionScanner/Php/MethodArgumentDroppedStaticMatcher.php',
156 'class' => MethodArgumentRequiredMatcher::class,
157 'configurationFile' =>
'EXT:install/Configuration/ExtensionScanner/Php/MethodArgumentRequiredMatcher.php',
160 'class' => MethodArgumentRequiredStaticMatcher::class,
161 'configurationFile' =>
'EXT:install/Configuration/ExtensionScanner/Php/MethodArgumentRequiredStaticMatcher.php',
164 'class' => MethodArgumentUnusedMatcher::class,
165 'configurationFile' =>
'EXT:install/Configuration/ExtensionScanner/Php/MethodArgumentUnusedMatcher.php',
168 'class' => MethodCallMatcher::class,
169 'configurationFile' =>
'EXT:install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php',
172 'class' => MethodCallStaticMatcher::class,
173 'configurationFile' =>
'EXT:install/Configuration/ExtensionScanner/Php/MethodCallStaticMatcher.php',
176 'class' => PropertyExistsStaticMatcher::class,
177 'configurationFile' =>
'EXT:install/Configuration/ExtensionScanner/Php/PropertyExistsStaticMatcher.php'
180 'class' => PropertyProtectedMatcher::class,
181 'configurationFile' =>
'EXT:install/Configuration/ExtensionScanner/Php/PropertyProtectedMatcher.php',
184 'class' => PropertyPublicMatcher::class,
185 'configurationFile' =>
'EXT:install/Configuration/ExtensionScanner/Php/PropertyPublicMatcher.php',
195 public function cardsAction(ServerRequestInterface $request): ResponseInterface
201 'html' => $view->render(),
215 'success' => $this->coreUpdateService->activateVersion($this->coreUpdateGetVersionToHandle($request)),
216 'status' => $this->coreUpdateService->getMessages(),
230 'success' => $this->coreUpdateService->checkPreConditions($this->coreUpdateGetVersionToHandle($request)),
231 'status' => $this->coreUpdateService->getMessages(),
245 'success' => $this->coreUpdateService->downloadVersion($this->coreUpdateGetVersionToHandle($request)),
246 'status' => $this->coreUpdateService->getMessages(),
261 $view->assignMultiple([
269 'html' => $view->render(),
286 if ($this->coreVersionService->isInstalledVersionAReleasedVersion()) {
287 $versionMaintenanceWindow = $this->coreVersionService->getMaintenanceWindow();
288 $renderVersionInformation =
false;
290 if (!$versionMaintenanceWindow->isSupportedByCommunity() && !$versionMaintenanceWindow->isSupportedByElts()) {
292 'title' =>
'Outdated version',
293 'message' =>
'The currently installed TYPO3 version ' . $this->coreVersionService->getInstalledVersion() .
' does not receive any further updates, please consider upgrading to a supported version!',
296 $renderVersionInformation =
true;
298 $currentVersion = $this->coreVersionService->getInstalledVersion();
299 $isCurrentVersionElts = $this->coreVersionService->isCurrentInstalledVersionElts();
300 $latestRelease = $this->coreVersionService->getYoungestPatchRelease();
302 $availableReleases = [];
303 if ($this->coreVersionService->isPatchReleaseSuitableForUpdate($latestRelease)) {
304 $availableReleases[] = $latestRelease;
306 if (!$latestRelease->isElts()) {
307 $action = [
'title' =>
'Update now to version ' . $latestRelease->getVersion(),
'action' =>
'updateRegular'];
310 if (!$versionMaintenanceWindow->isSupportedByCommunity()) {
311 if ($latestRelease->isElts()) {
313 $latestCommunityDrivenRelease = $this->coreVersionService->getYoungestCommunityPatchRelease();
314 if ($this->coreVersionService->isPatchReleaseSuitableForUpdate($latestCommunityDrivenRelease)) {
315 $availableReleases[] = $latestCommunityDrivenRelease;
316 $action = [
'title' =>
'Update now to version ' . $latestCommunityDrivenRelease->getVersion(),
'action' =>
'updateRegular'];
318 } elseif (!$isCurrentVersionElts) {
324 'title' =>
'ELTS will be available soon',
325 'message' => sprintf(
'The currently installed TYPO3 version %s doesn\'t receive any community-driven updates anymore, consider subscribing to Extended Long Term Support (ELTS) releases. Please read the information below.', $currentVersion),
328 $renderVersionInformation =
true;
332 if ($availableReleases === []) {
334 'title' =>
'Up to date',
335 'message' =>
'There are no TYPO3 updates available.',
339 foreach ($availableReleases as $availableRelease) {
340 $isUpdateSecurityRelevant = $this->coreVersionService->isUpdateSecurityRelevant($availableRelease);
341 $versionString = $availableRelease->getVersion();
342 if ($availableRelease->isElts()) {
343 $versionString .=
' ELTS';
346 if ($isUpdateSecurityRelevant) {
347 $title = ($availableRelease->isElts() ?
'ELTS ' :
'') .
'Security update available!';
348 $message = sprintf(
'The currently installed version is %s, update to security relevant released version %s is available.', $currentVersion, $versionString);
351 $title = ($availableRelease->isElts() ?
'ELTS ' :
'') .
'Update available!';
352 $message = sprintf(
'Currently installed version is %s, update to regular released version %s is available.', $currentVersion, $versionString);
356 if ($availableRelease->isElts()) {
357 if ($isCurrentVersionElts) {
358 $message .=
' Please visit my.typo3.org to download the release in your ELTS area.';
360 $message .=
' ' . sprintf(
'The currently installed TYPO3 version %s doesn\'t receive any community-driven updates anymore, consider subscribing to Extended Long Term Support (ELTS) releases. Please read the information below.', $currentVersion);
363 $renderVersionInformation =
true;
368 'message' => $message,
369 'severity' => $severity,
375 if ($renderVersionInformation) {
376 $supportedMajorReleases = $this->coreVersionService->getSupportedMajorReleases();
377 $supportMessages = [];
378 if (!empty($supportedMajorReleases[
'community'])) {
379 $supportMessages[] = sprintf(
'Currently community-supported TYPO3 versions: %s (more information at https://get.typo3.org).', implode(
', ', $supportedMajorReleases[
'community']));
381 if (!empty($supportedMajorReleases[
'elts'])) {
382 $supportMessages[] = sprintf(
'Currently supported TYPO3 ELTS versions: %s (more information at https://typo3.com/elts).', implode(
', ', $supportedMajorReleases[
'elts']));
386 'title' =>
'TYPO3 Version information',
387 'message' => implode(
' ', $supportMessages),
392 foreach ($messages as $message) {
393 $messageQueue->enqueue(
new FlashMessage($message[
'message'], $message[
'title'], $message[
'severity']));
398 'Current version is a development version and can not be updated',
404 'status' => $messageQueue,
406 if (isset($action)) {
407 $responseData[
'action'] = $action;
422 'success' => $this->coreUpdateService->moveVersion($this->coreUpdateGetVersionToHandle($request)),
423 'status' => $this->coreUpdateService->getMessages(),
437 'success' => $this->coreUpdateService->unpackVersion($this->coreUpdateGetVersionToHandle($request)),
438 'status' => $this->coreUpdateService->getMessages(),
452 'success' => $this->coreUpdateService->verifyFileChecksum($this->coreUpdateGetVersionToHandle($request)),
453 'status' => $this->coreUpdateService->getMessages(),
467 $view->assignMultiple([
468 'extensionCompatTesterLoadExtLocalconfToken' => $formProtection->generateToken(
'installTool',
'extensionCompatTesterLoadExtLocalconf'),
469 'extensionCompatTesterLoadExtTablesToken' => $formProtection->generateToken(
'installTool',
'extensionCompatTesterLoadExtTables'),
470 'extensionCompatTesterUninstallToken' => $formProtection->generateToken(
'installTool',
'extensionCompatTesterUninstallExtension'),
475 'html' => $view->render(),
487 $brokenExtensions = [];
488 foreach ($this->packageManager->getActivePackages() as $package) {
491 }
catch (\Throwable $e) {
492 $brokenExtensions[] = [
493 'name' => $package->getPackageKey(),
494 'isProtected' => $package->isProtected()
499 'brokenExtensions' => $brokenExtensions,
500 ], empty($brokenExtensions) ? 200 : 500);
511 $brokenExtensions = [];
512 $activePackages = $this->packageManager->getActivePackages();
513 foreach ($activePackages as $package) {
517 foreach ($activePackages as $package) {
520 }
catch (\Throwable $e) {
521 $brokenExtensions[] = [
522 'name' => $package->getPackageKey(),
523 'isProtected' => $package->isProtected()
528 'brokenExtensions' => $brokenExtensions,
529 ], empty($brokenExtensions) ? 200 : 500);
541 $extension = $request->getParsedBody()[
'install'][
'extension'];
542 if (empty($extension)) {
543 throw new \RuntimeException(
544 'No extension given',
552 GeneralUtility::makeInstance(ClearCacheService::class)->clearAll();
553 GeneralUtility::makeInstance(OpcodeCacheService::class)->clearAllActive();
556 'Extension "' . $extension .
'" unloaded.',
570 'status' => $messageQueue,
585 $view->assignMultiple([
586 'extensionScannerExtensionList' => $extensionsInTypo3conf,
587 'extensionScannerFilesToken' => $formProtection->generateToken(
'installTool',
'extensionScannerFiles'),
588 'extensionScannerScanFileToken' => $formProtection->generateToken(
'installTool',
'extensionScannerScanFile'),
589 'extensionScannerMarkFullyScannedRestFilesToken' => $formProtection->generateToken(
'installTool',
'extensionScannerMarkFullyScannedRestFiles'),
593 'html' => $view->render(),
606 $extension = $request->getParsedBody()[
'install'][
'extension'];
608 if (empty($extension) || !GeneralUtility::isAllowedAbsPath($extensionBasePath)) {
609 throw new \RuntimeException(
610 'Path to extension ' . $extension .
' not allowed.',
614 if (!is_dir($extensionBasePath)) {
615 throw new \RuntimeException(
616 'Extension path ' . $extensionBasePath .
' does not exist or is no directory.',
622 $files =
$finder->files()->in($extensionBasePath)->name(
'*.php')->sortByName();
624 $relativeFileNames = [];
625 foreach ($files as $file) {
627 $relativeFileNames[] = GeneralUtility::fixWindowsFilePath($file->getRelativePathname());
631 'files' => $relativeFileNames,
646 $foundRestFileHashes = (array)$request->getParsedBody()[
'install'][
'hashes'];
649 $registry->removeAllByNamespace(
'extensionScannerNotAffected');
655 $restFiles =
$finder->files()->in($restFilesBasePath);
656 $fullyScannedRestFilesNotAffected = [];
657 foreach ($restFiles as $restFile) {
660 if (strpos($restFile->getRelativePath(),
'8') === 0) {
665 $parsedRestFile = array_pop($documentationFile->getListEntry(str_replace(
668 realpath($restFile->getPathname())
670 if (!in_array($parsedRestFile[
'file_hash'], $foundRestFileHashes,
true)
671 && in_array(
'FullyScanned', $parsedRestFile[
'tags'],
true)
673 $fullyScannedRestFilesNotAffected[] = $parsedRestFile[
'file_hash'];
676 foreach ($fullyScannedRestFilesNotAffected as $fileHash) {
677 $registry->set(
'extensionScannerNotAffected', $fileHash, $fileHash);
681 'markedAsNotAffected' => count($fullyScannedRestFilesNotAffected),
694 $extension = $request->getParsedBody()[
'install'][
'extension'];
696 if (empty($extension) || !GeneralUtility::isAllowedAbsPath($extensionBasePath)) {
697 throw new \RuntimeException(
698 'Path to extension ' . $extension .
' not allowed.',
702 if (!is_dir($extensionBasePath)) {
703 throw new \RuntimeException(
704 'Extension path ' . $extensionBasePath .
' does not exist or is no directory.',
708 $file = $request->getParsedBody()[
'install'][
'file'];
709 $absoluteFilePath = $extensionBasePath .
'/' . $file;
710 if (empty($file) || !GeneralUtility::isAllowedAbsPath($absoluteFilePath)) {
711 throw new \RuntimeException(
712 'Path to file ' . $file .
' of extension ' . $extension .
' not allowed.',
716 if (!is_file($absoluteFilePath)) {
717 throw new \RuntimeException(
718 'File ' . $file .
' not found or is not a file.',
723 $parser = (
new ParserFactory())->create(ParserFactory::PREFER_PHP7);
725 $statements =
$parser->parse(file_get_contents($absoluteFilePath));
731 $traverser =
new NodeTraverser();
732 $traverser->addVisitor(
new NameResolver());
733 $statements = $traverser->traverse($statements);
736 $traverser =
new NodeTraverser();
741 $traverser->addVisitor($statistics);
745 $matchers = $matcherFactory->createAll($this->matchers);
747 $traverser->addVisitor($matcher);
750 $traverser->traverse($statements);
756 $matches[] = $matcher->getMatches();
758 $matches = array_merge(...$matches);
763 $preparedMatches = [];
764 foreach ($matches as $match) {
766 $preparedHit[
'uniqueId'] = str_replace(
'.',
'', uniqid((
string)mt_rand(),
true));
767 $preparedHit[
'message'] = $match[
'message'];
768 $preparedHit[
'line'] = $match[
'line'];
769 $preparedHit[
'indicator'] = $match[
'indicator'];
771 $preparedHit[
'restFiles'] = [];
772 foreach ($match[
'restFiles'] as $fileName) {
774 $restFileLocation =
$finder->files()->in($restFilesBasePath)->name($fileName);
775 if ($restFileLocation->count() !== 1) {
776 throw new \RuntimeException(
777 'ResT file ' . $fileName .
' not found or multiple files found.',
781 foreach ($restFileLocation as $restFile) {
783 $restFileLocation = $restFile->getPathname();
786 $parsedRestFile = array_pop($documentationFile->getListEntry(str_replace(
789 realpath($restFileLocation)
791 $version = GeneralUtility::trimExplode(DIRECTORY_SEPARATOR, $restFileLocation);
794 $parsedRestFile[
'version'] = array_pop($version);
795 $parsedRestFile[
'uniqueId'] = str_replace(
'.',
'', uniqid((
string)mt_rand(),
true));
796 $preparedHit[
'restFiles'][] = $parsedRestFile;
798 $preparedMatches[] = $preparedHit;
802 'matches' => $preparedMatches,
803 'isFileIgnored' => $statistics->isFileIgnored(),
804 'effectiveCodeLines' => $statistics->getNumberOfEffectiveCodeLines(),
805 'ignoredLines' => $statistics->getNumberOfIgnoredLines(),
819 $loadTcaService = GeneralUtility::makeInstance(LoadTcaService::class);
820 $loadTcaService->loadExtensionTablesWithoutMigration();
822 foreach ($this->packageManager->getActivePackages() as $package) {
825 $extensionKey = $package->getPackageKey();
826 $extTablesPath = $package->getPackagePath() .
'ext_tables.php';
827 if (@file_exists($extTablesPath)) {
828 $loadTcaService->loadSingleExtTablesFile($extensionKey);
830 if ($newTca !== $baseTca) {
842 'status' => $messageQueue,
843 'html' => $view->render(),
857 GeneralUtility::makeInstance(LoadTcaService::class)->loadExtensionTablesWithoutMigration();
858 $tcaMigration = GeneralUtility::makeInstance(TcaMigration::class);
860 $tcaMessages = $tcaMigration->getMessages();
861 foreach ($tcaMessages as $tcaMessage) {
870 'status' => $messageQueue,
871 'html' => $view->render(),
886 $view->assignMultiple([
887 'upgradeDocsMarkReadToken' => $formProtection->generateToken(
'installTool',
'upgradeDocsMarkRead'),
888 'upgradeDocsUnmarkReadToken' => $formProtection->generateToken(
'installTool',
'upgradeDocsUnmarkRead'),
889 'upgradeDocsVersions' => $documentationDirectories,
893 'html' => $view->render(),
905 $version = $request->getQueryParams()[
'install'][
'version'] ??
'';
910 $view->assignMultiple([
911 'upgradeDocsFiles' => $documentationFiles[
'normalFiles'],
912 'upgradeDocsReadFiles' => $documentationFiles[
'readFiles'],
913 'upgradeDocsNotAffectedFiles' => $documentationFiles[
'notAffectedFiles'],
917 'html' => $view->render(),
930 $filePath = $request->getParsedBody()[
'install'][
'ignoreFile'];
931 $fileHash = md5_file($filePath);
932 $registry->set(
'upgradeAnalysisIgnoredFiles', $fileHash, $filePath);
947 $filePath = $request->getParsedBody()[
'install'][
'ignoreFile'];
948 $fileHash = md5_file($filePath);
949 $registry->remove(
'upgradeAnalysisIgnoredFiles', $fileHash);
966 $needsUpdate =
false;
968 $adds = $upgradeWizardsService->getBlockingDatabaseAdds();
977 'needsUpdate' => $needsUpdate,
992 $upgradeWizardsService->addMissingTablesAndFields();
996 'Added missing database fields and tables'
1000 'status' => $messages,
1012 $upgradeWizardsService->setDatabaseCharsetUtf8();
1016 'Default connection database has been set to utf8'
1020 'status' => $messages,
1032 $result = !$upgradeWizardsService->isDatabaseCharsetUtf8();
1035 'needsUpdate' => $result,
1048 $wizardsDone = $upgradeWizardsService->listOfWizardsDone();
1049 $rowUpdatersDone = $upgradeWizardsService->listOfRowUpdatersDone();
1051 if (empty($wizardsDone) && empty($rowUpdatersDone)) {
1054 'No wizards are marked as done'
1059 'status' => $messages,
1060 'wizardsDone' => $wizardsDone,
1061 'rowUpdatersDone' => $rowUpdatersDone,
1076 $identifier = $request->getParsedBody()[
'install'][
'identifier'];
1077 $messages = $upgradeWizardsService->executeWizard($identifier);
1080 'status' => $messages,
1095 $identifier = $request->getParsedBody()[
'install'][
'identifier'];
1096 $result = $upgradeWizardsService->getWizardUserInput($identifier);
1100 'userInput' => $result,
1114 $wizards = $upgradeWizardsService->getUpgradeWizardsList();
1118 'wizards' => $wizards,
1131 $wizardToBeMarkedAsUndoneIdentifier = $request->getParsedBody()[
'install'][
'identifier'];
1133 $result = $upgradeWizardsService->markWizardUndone($wizardToBeMarkedAsUndoneIdentifier);
1137 'Wizard has been marked undone'
1141 'Wizard has not been marked undone',
1148 'status' => $messages,
1162 $view->assignMultiple([
1163 'upgradeWizardsMarkUndoneToken' => $formProtection->generateToken(
'installTool',
'upgradeWizardsMarkUndone'),
1164 'upgradeWizardsInputToken' => $formProtection->generateToken(
'installTool',
'upgradeWizardsInput'),
1165 'upgradeWizardsExecuteToken' => $formProtection->generateToken(
'installTool',
'upgradeWizardsExecute'),
1169 'html' => $view->render(),
1180 $this->coreUpdateService = GeneralUtility::makeInstance(CoreUpdateService::class);
1181 $this->coreVersionService = GeneralUtility::makeInstance(CoreVersionService::class);
1182 if (!$this->coreUpdateService->isCoreUpdateEnabled()) {
1183 throw new \RuntimeException(
1184 'Core Update disabled in this environment',
1202 $type = $request->getQueryParams()[
'install'][
'type'];
1203 if (!isset($type) || empty($type)) {
1204 throw new \RuntimeException(
1205 'Type must be set to either "regular" or "development"',
1209 return $this->coreVersionService->getYoungestCommunityPatchRelease();
1220 $extLocalconfPath = $package->
getPackagePath() .
'ext_localconf.php';
1225 global $TYPO3_CONF_VARS;
1226 if (@file_exists($extLocalconfPath)) {
1230 $_EXTCONF =
$GLOBALS[
'TYPO3_CONF_VARS'][
'EXT'][
'extConf'][$_EXTKEY] ??
null;
1231 require $extLocalconfPath;
1246 global $T3_SERVICES, $T3_VAR, $TYPO3_CONF_VARS;
1247 global $TBE_MODULES, $TBE_MODULES_EXT, $TCA;
1248 global $PAGES_TYPES, $TBE_STYLES;
1252 if (@file_exists($extTablesPath)) {
1255 $_EXTCONF =
$GLOBALS[
'TYPO3_CONF_VARS'][
'EXT'][
'extConf'][$_EXTKEY] ??
null;
1256 require $extTablesPath;
1266 $documentationDirectories = $documentationFileService->findDocumentationDirectories(
1269 return array_reverse($documentationDirectories);
1281 $documentationFiles = $documentationFileService->findDocumentationFiles(
1285 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(
'sys_registry');
1286 $filesMarkedAsRead = $queryBuilder
1288 ->from(
'sys_registry')
1290 $queryBuilder->expr()->eq(
1292 $queryBuilder->createNamedParameter(
'upgradeAnalysisIgnoredFiles', \PDO::PARAM_STR)
1297 $hashesMarkedAsRead = [];
1298 foreach ($filesMarkedAsRead as $file) {
1299 $hashesMarkedAsRead[] = $file[
'entry_key'];
1302 $fileMarkedAsNotAffected = $queryBuilder
1304 ->from(
'sys_registry')
1306 $queryBuilder->expr()->eq(
1308 $queryBuilder->createNamedParameter(
'extensionScannerNotAffected', \PDO::PARAM_STR)
1313 $hashesMarkedAsNotAffected = [];
1314 foreach ($fileMarkedAsNotAffected as $file) {
1315 $hashesMarkedAsNotAffected[] = $file[
'entry_key'];
1319 $notAffectedFiles = [];
1320 foreach ($documentationFiles as $fileId => $fileData) {
1321 if (in_array($fileData[
'file_hash'], $hashesMarkedAsRead,
true)) {
1322 $readFiles[$fileId] = $fileData;
1323 unset($documentationFiles[$fileId]);
1324 } elseif (in_array($fileData[
'file_hash'], $hashesMarkedAsNotAffected,
true)) {
1325 $notAffectedFiles[$fileId] = $fileData;
1326 unset($documentationFiles[$fileId]);
1331 'normalFiles' => $documentationFiles,
1332 'readFiles' => $readFiles,
1333 'notAffectedFiles' => $notAffectedFiles,
1346 $fileContent = file($file, FILE_IGNORE_NEW_LINES);
1348 if (isset($fileContent[$lineNumber - 1])) {
1349 $line = trim($fileContent[$lineNumber - 1]);
1362 if ($version !==
'master' && !preg_match(
'/^\d+.\d+(?:.(?:\d+|x))?$/', $version)) {
1363 throw new \InvalidArgumentException(
'Given version "' . $version .
'" is invalid', 1537209128);