‪TYPO3CMS  10.4
DocumentationFile.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 Symfony\Component\Finder\Finder;
21 use Symfony\Component\Finder\SplFileInfo;
27 
33 {
37  protected ‪$registry;
38 
42  protected ‪$tagsTotal = [];
43 
50  protected ‪$changelogPath = '';
51 
57  public function ‪__construct(‪Registry ‪$registry = null, $changelogDir = '')
58  {
59  $this->registry = ‪$registry;
60  if ($this->registry === null) {
61  $this->registry = new ‪Registry();
62  }
63  $this->changelogPath = $changelogDir !== '' ? $changelogDir : (string)realpath(‪ExtensionManagementUtility::extPath('core') . 'Documentation/Changelog');
64  $this->changelogPath = str_replace('\\', '/', $this->changelogPath);
65  }
66 
74  public function ‪findDocumentationDirectories(string $path): array
75  {
76  if (strcasecmp($path, $this->changelogPath) < 0 || strpos($path, $this->changelogPath) === false) {
77  throw new \InvalidArgumentException('the given path does not belong to the changelog dir. Aborting', 1537158043);
78  }
79 
80  $currentVersion = (int)explode('.', ‪VersionNumberUtility::getNumericTypo3Version())[0];
81  $versions = range($currentVersion, $currentVersion - 2);
82  $pattern = '(master|' . implode('\.*|', $versions) . '\.*)';
83  ‪$finder = new Finder();
85  ->depth(0)
86  ->sortByName(true)
87  ->name($pattern)
88  ->in($path);
89 
90  $directories = [];
91  foreach (‪$finder->directories() as $directory) {
93  $directories[] = $directory->getBasename();
94  }
95 
96  return $directories;
97  }
98 
106  public function ‪findDocumentationFiles(string $path): array
107  {
108  if (strcasecmp($path, $this->changelogPath) < 0 || strpos($path, $this->changelogPath) === false) {
109  throw new \InvalidArgumentException('the given path does not belong to the changelog dir. Aborting', 1485425530);
110  }
111 
112  $documentationFiles = $this->‪getDocumentationFilesForVersion($path);
113  $this->tagsTotal = $this->‪collectTagTotal($documentationFiles);
114 
115  return $documentationFiles;
116  }
117 
125  public function ‪getListEntry(string $file): array
126  {
127  $entry = [];
128  if (strcasecmp($file, $this->changelogPath) < 0 || strpos($file, $this->changelogPath) === false) {
129  throw new \InvalidArgumentException('the given file does not belong to the changelog dir. Aborting', 1485425531);
130  }
131  $lines = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
132  $lines = is_array($lines) ? $lines : [];
133  $headline = $this->‪extractHeadline($lines);
134  $entry['version'] = ‪PathUtility::basename(‪PathUtility::dirname($file));
135  $entry['headline'] = $headline;
136  $entry['filepath'] = $file;
137  $entry['filename'] = pathinfo($file)['filename'];
138  $entry['tags'] = $this->‪extractTags($lines);
139  $entry['class'] = 'default';
140  foreach ($entry['tags'] as $key => $tag) {
141  if (strpos($tag, 'cat:') === 0) {
142  $substr = substr($tag, 4);
143  $entry['class'] = strtolower($substr);
144  $entry['tags'][$key] = $substr;
145  }
146  }
147  $entry['tagList'] = implode(',', $entry['tags']);
148  $entry['content'] = (string)file_get_contents($file);
149  $entry['parsedContent'] = $this->‪parseContent($entry['content']);
150  $entry['file_hash'] = md5($entry['content']);
151  if ($entry['version'] !== '') {
152  $entry['url']['documentation'] = sprintf(
153  'https://docs.typo3.org/c/typo3/cms-core/main/en-us/Changelog/%s/%s.html',
154  $entry['version'],
155  $entry['filename']
156  );
157  }
158  $issueId = $this->‪parseIssueId($entry['filename']);
159  if ($issueId) {
160  $entry['url']['issue'] = sprintf('https://forge.typo3.org/issues/%s', $issueId);
161  }
162 
163  return [md5($file) => $entry];
164  }
165 
172  protected function ‪isRelevantFile(array $fileInfo): bool
173  {
174  $isRelevantFile = $fileInfo['extension'] === 'rst' && $fileInfo['filename'] !== 'Index';
175  // file might be ignored by users choice
176  if ($isRelevantFile && $this->‪isFileIgnoredByUsersChoice($fileInfo['basename'])) {
177  $isRelevantFile = false;
178  }
179 
180  return $isRelevantFile;
181  }
182 
189  protected function ‪extractTags(array $file): array
190  {
191  $tags = $this->‪extractTagsFromFile($file);
192  // Headline starting with the category like Breaking, Important or Feature
193  $tags[] = $this->‪extractCategoryFromHeadline($file);
194  natcasesort($tags);
195 
196  return $tags;
197  }
198 
206  protected function ‪extractTagsFromFile(array $file): array
207  {
208  foreach ($file as $line) {
209  if (strpos($line, '.. index::') === 0) {
210  $tagString = substr($line, strlen('.. index:: '));
211  return ‪GeneralUtility::trimExplode(',', $tagString, true);
212  }
213  }
214 
215  return [];
216  }
217 
226  protected function ‪extractCategoryFromHeadline(array $lines): string
227  {
228  $headline = $this->‪extractHeadline($lines);
229  if (strpos($headline, ':') !== false) {
230  return 'cat:' . substr($headline, 0, strpos($headline, ':'));
231  }
232 
233  return '';
234  }
235 
242  protected function ‪extractHeadline(array $lines): string
243  {
244  $index = 0;
245  while (strpos($lines[$index], '..') === 0 || strpos($lines[$index], '==') === 0) {
246  $index++;
247  }
248  return trim($lines[$index]);
249  }
250 
256  protected function ‪versionHasDocumentationFiles(string $docDirectory, string $version): bool
257  {
258  $absolutePath = str_replace('\\', '/', $docDirectory) . '/' . $version;
259  ‪$finder = $this->‪getDocumentFinder()->in($absolutePath);
260 
261  return ‪$finder->files()->count() > 0;
262  }
263 
270  protected function ‪getDocumentationFilesForVersion(string $docDirectory): array
271  {
272  $documentationFiles = [[]];
273  $absolutePath = str_replace('\\', '/', $docDirectory);
274  ‪$finder = $this->‪getDocumentFinder()->in($absolutePath);
275 
276  foreach (‪$finder->files() as $file) {
278  $documentationFiles[] = $this->‪getListEntry($file->getPathname());
279  }
280 
281  return array_merge(...$documentationFiles);
282  }
283 
290  protected function ‪collectTagTotal($documentationFiles): array
291  {
292  $tags = [[]];
293  foreach ($documentationFiles as $fileArray) {
294  $tags[] = $fileArray['tags'];
295  }
296 
297  return array_unique(array_merge(...$tags));
298  }
299 
305  public function ‪getTagsTotal(): array
306  {
307  return ‪$this->tagsTotal;
308  }
309 
316  protected function ‪isFileIgnoredByUsersChoice(string $filename): bool
317  {
318  $isFileIgnoredByUsersChoice = false;
319 
320  $ignoredFiles = $this->registry->get('upgradeAnalysisIgnoreFilter', 'ignoredDocumentationFiles');
321  if (is_array($ignoredFiles)) {
322  foreach ($ignoredFiles as $filePath) {
323  if ($filePath !== null && strlen($filePath) > 0) {
324  if (strpos($filePath, $filename) !== false) {
325  $isFileIgnoredByUsersChoice = true;
326  break;
327  }
328  }
329  }
330  }
331  return $isFileIgnoredByUsersChoice;
332  }
333 
339  protected function ‪parseContent(string $rstContent): string
340  {
341  $content = htmlspecialchars($rstContent);
342  $content = (string)preg_replace('/:issue:`([\d]*)`/', '<a href="https://forge.typo3.org/issues/\\1" target="_blank" rel="noreferrer">\\1</a>', $content);
343  $content = (string)preg_replace('/#([\d]*)/', '#<a href="https://forge.typo3.org/issues/\\1" target="_blank" rel="noreferrer">\\1</a>', $content);
344  $content = (string)preg_replace('/(\n([=]*)\n(.*)\n([=]*)\n)/', '', $content, 1);
345  $content = (string)preg_replace('/.. index::(.*)/', '', $content);
346  $content = (string)preg_replace('/.. include::(.*)/', '', $content);
347  return trim($content);
348  }
349 
355  protected function ‪parseIssueId(string $filename): ?string
356  {
357  return ‪GeneralUtility::trimExplode('-', $filename)[1] ?? null;
358  }
359 
363  protected function ‪getDocumentFinder(): Finder
364  {
365  ‪$finder = new Finder();
366  ‪$finder
367  ->depth(0)
368  ->sortByName()
369  ->name('/^(Feature|Breaking|Deprecation|Important)\-\d+.+\.rst$/i');
370 
371  return ‪$finder;
372  }
373 }
‪TYPO3\CMS\Core\Utility\VersionNumberUtility
Definition: VersionNumberUtility.php:25
‪TYPO3\CMS\Core\Utility\VersionNumberUtility\getNumericTypo3Version
‪static string getNumericTypo3Version()
Definition: VersionNumberUtility.php:107
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:24
‪TYPO3\CMS\Install\UpgradeAnalysis
Definition: DocumentationFile.php:18
‪$finder
‪if(PHP_SAPI !=='cli') $finder
Definition: header-comment.php:22
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile
Definition: DocumentationFile.php:33
‪TYPO3\CMS\Core\Utility\PathUtility\dirname
‪static string dirname($path)
Definition: PathUtility.php:186
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\extractTagsFromFile
‪array extractTagsFromFile(array $file)
Definition: DocumentationFile.php:203
‪TYPO3\CMS\Core\Registry
Definition: Registry.php:33
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\isFileIgnoredByUsersChoice
‪bool isFileIgnoredByUsersChoice(string $filename)
Definition: DocumentationFile.php:313
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\versionHasDocumentationFiles
‪bool versionHasDocumentationFiles(string $docDirectory, string $version)
Definition: DocumentationFile.php:253
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\extractHeadline
‪string extractHeadline(array $lines)
Definition: DocumentationFile.php:239
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\$changelogPath
‪string $changelogPath
Definition: DocumentationFile.php:47
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\parseIssueId
‪string null parseIssueId(string $filename)
Definition: DocumentationFile.php:352
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\getTagsTotal
‪array getTagsTotal()
Definition: DocumentationFile.php:302
‪TYPO3\CMS\Core\Utility\PathUtility\basename
‪static string basename($path)
Definition: PathUtility.php:165
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\__construct
‪__construct(Registry $registry=null, $changelogDir='')
Definition: DocumentationFile.php:54
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\getDocumentFinder
‪Finder getDocumentFinder()
Definition: DocumentationFile.php:360
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\findDocumentationFiles
‪array findDocumentationFiles(string $path)
Definition: DocumentationFile.php:103
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility
Definition: ExtensionManagementUtility.php:43
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\extractCategoryFromHeadline
‪string extractCategoryFromHeadline(array $lines)
Definition: DocumentationFile.php:223
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\getDocumentationFilesForVersion
‪array getDocumentationFilesForVersion(string $docDirectory)
Definition: DocumentationFile.php:267
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\$registry
‪Registry $registry
Definition: DocumentationFile.php:36
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static string[] trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:1059
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\parseContent
‪string parseContent(string $rstContent)
Definition: DocumentationFile.php:336
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\collectTagTotal
‪array collectTagTotal($documentationFiles)
Definition: DocumentationFile.php:287
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\extPath
‪static string extPath($key, $script='')
Definition: ExtensionManagementUtility.php:127
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\isRelevantFile
‪bool isRelevantFile(array $fileInfo)
Definition: DocumentationFile.php:169
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\extractTags
‪array extractTags(array $file)
Definition: DocumentationFile.php:186
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:46
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\$tagsTotal
‪array $tagsTotal
Definition: DocumentationFile.php:40
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\getListEntry
‪array getListEntry(string $file)
Definition: DocumentationFile.php:122
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\findDocumentationDirectories
‪string[] findDocumentationDirectories(string $path)
Definition: DocumentationFile.php:71