‪TYPO3CMS  9.5
checkIntegrityCsvFixtures.php
Go to the documentation of this file.
1 #!/usr/bin/env php
2 <?php
3 declare(strict_types = 1);
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 
18 
19 require __DIR__ . '/../../vendor/autoload.php';
20 
21 if (PHP_SAPI !== 'cli') {
22  die('Script must be called from command line.' . chr(10));
23 }
24 
32 {
36  private ‪$fix = false;
37 
41  private ‪$fixAll = false;
42 
43  public function ‪setFix(bool ‪$fix)
44  {
45  $this->fix = ‪$fix;
46  }
47 
48  public function ‪setFixAll(bool ‪$fixAll)
49  {
50  $this->fixAll = ‪$fixAll;
51  }
52 
59  public function ‪execute(): int
60  {
61  $filesToProcess = $this->‪findCsvFixtures();
62  $outputLines = [];
63  ‪$output = new \Symfony\Component\Console\Output\ConsoleOutput();
64 
65  $exitStatus = 0;
67  foreach ($filesToProcess as $csvFixture) {
68  $fullFilePath = $csvFixture->getRealPath();
69  if ($this->fixAll) {
70  $changed = $this->‪fixCsvFile($fullFilePath);
71  if ($changed) {
72  $outputLines[] = 'Changed file "' . $this->‪formatOutputString($this->‪getRelativePath($fullFilePath)) . '"';
73  }
74  continue;
75  }
76  $singleFileScanResult = $this->‪validateCsvFile($fullFilePath);
77  if ($singleFileScanResult !== '') {
78  if ($this->fix) {
79  $this->‪fixCsvFile($fullFilePath);
80  $outputLines[] = 'Fixed file "' . $this->‪formatOutputString($this->‪getRelativePath($fullFilePath)) . '"';
81  } else {
82  $exitStatus = 1;
83  $outputLines[] = 'File "' . $this->‪formatOutputString($this->‪getRelativePath($fullFilePath)) . '"'
84  . ' is not in valid CSV format: ' . $singleFileScanResult;
85  }
86  }
87  }
88  if (!empty($outputLines)) {
89  foreach ($outputLines as $line) {
90  ‪$output->writeln($line);
91  }
92  }
93  return $exitStatus;
94  }
95 
101  private function ‪findCsvFixtures(): \Symfony\Component\Finder\Finder
102  {
103  ‪$finder = new Symfony\Component\Finder\Finder();
104  $csvFixtures = ‪$finder
105  ->files()
106  ->in(__DIR__ . '/../../typo3/sysext/*/Tests/Functional/**')
107  ->name('*.csv');
108  return $csvFixtures;
109  }
110 
118  private function ‪validateCsvFile(string $csvFixture): string
119  {
120  // Load file content into array split by line
121  $lines = file($csvFixture);
122  $columnCount = 0;
123  foreach ($lines as $index => $line) {
124  // count columns in file
125  $columns = str_getcsv($line);
126  if ($columnCount === 0) {
127  $columnCount = count($columns);
128  } else {
129  if (count($columns) !== $columnCount) {
130  // Skip CSV lines with starting with comments
131  if (count($columns) === 1 && strpos($columns[0], '#') === 0) {
132  continue;
133  }
134  return 'Line ' . ($index + 1) . '; Expected column count: ' . $columnCount . '; Actual: ' . count($columns);
135  }
136  }
137  }
138  return '';
139  }
140 
147  private function ‪fixCsvFile(string $csvFixture): bool
148  {
149  $changeNeeded = false;
150  // Load file content into array split by line
151  $lines = file($csvFixture);
152  $neededColumns = 0;
153  $csvLines = [];
154  foreach ($lines as $line) {
155  // Find out how many columns are needed in this file
156  $csvLine = str_getcsv($line);
157  $csvLines[] = $csvLine;
158  foreach ($csvLine as $columnNumber => $columnContent) {
159  if (!empty($columnContent) && $columnNumber + 1 > $neededColumns) {
160  $neededColumns = $columnNumber + 1;
161  }
162  }
163  }
164  foreach ($csvLines as $csvLine) {
165  // Set $changeNeeded to true if this file needs an update and line is not a comment
166  if (count($csvLine) !== $neededColumns && substr($csvLine[0], 0, 2) !== '# ') {
167  $changeNeeded = true;
168  break;
169  }
170  }
171  if ($changeNeeded) {
172  // Update file
173  $fileHandle = fopen($csvFixture, 'w');
174  if (!$fileHandle) {
175  throw new \Exception('Opening file "' . $csvFixture . '" for writing failed.');
176  }
177  foreach ($csvLines as $csvLine) {
178  // Extend / reduce to needed size
179  $csvLine = array_slice(array_pad($csvLine, $neededColumns, ''), 0, $neededColumns);
180  $isComment = false;
181  $line = array_reduce($csvLine, function ($carry, $column) use (&$isComment) {
182  if ($carry === null && substr($column, 0, 2) === '# ') {
183  $isComment = true;
184  $carry .= $column;
185  } elseif ($isComment) {
186  // comment lines are not filled up with comma
187  return $carry;
188  } elseif (empty($column) && $column !== '0') {
189  // No leading comma if first column
190  $carry .= $carry === null ? '' : ',';
191  } elseif (MathUtility::canBeInterpretedAsInteger($column)) {
192  // No leading comma if first column and integer payload
193  $carry .= ($carry === null ? '' : ',') . $column;
194  } else {
195  // No leading comma if first column and string payload and quote " to ""
196  $column = str_replace('"', '""', $column);
197  $carry .= ($carry === null ? '' : ',') . '"' . $column . '"';
198  }
199  return $carry;
200  });
201  fwrite($fileHandle, $line . chr(10));
202  }
203  fclose($fileHandle);
204  }
205  return $changeNeeded;
206  }
207 
208  private function ‪getRelativePath(string $fullPath): string
209  {
210  $pathSegment = str_replace('Build/Scripts', '', __DIR__);
211  return str_replace($pathSegment, '', $fullPath);
212  }
213 
220  private function ‪formatOutputString(string $filename): string
221  {
222  $pattern = '#typo3[\\\\/]sysext[\\\\/](?<extName>[a-z].+?)[\\\\/]Tests[\\\\/]Functional[\\\\/](?<file>.*)#';
223  preg_match_all($pattern, $filename, $matches, PREG_SET_ORDER, 0);
224  if (isset($matches[0])) {
225  return 'EXT:' . $matches[0]['extName'] . ' > ' . $matches[0]['file'];
226  }
227  return $filename;
228  }
229 }
230 
232 ‪$args = getopt('', ['fix', 'fixAll']);
233 if (array_key_exists('fix', ‪$args)) {
234  ‪$cglFixer->setFix(true);
235 }
236 if (array_key_exists('fixAll', ‪$args)) {
237  ‪$cglFixer->setFixAll(true);
238 }
239 exit(‪$cglFixer->execute());
‪$args
‪$args
Definition: checkIntegrityCsvFixtures.php:230
‪checkIntegrityCsvFixtures\formatOutputString
‪string formatOutputString(string $filename)
Definition: checkIntegrityCsvFixtures.php:218
‪$finder
‪$finder
Definition: annotationChecker.php:102
‪checkIntegrityCsvFixtures\getRelativePath
‪getRelativePath(string $fullPath)
Definition: checkIntegrityCsvFixtures.php:206
‪checkIntegrityCsvFixtures\setFix
‪setFix(bool $fix)
Definition: checkIntegrityCsvFixtures.php:41
‪checkIntegrityCsvFixtures\validateCsvFile
‪string validateCsvFile(string $csvFixture)
Definition: checkIntegrityCsvFixtures.php:116
‪checkIntegrityCsvFixtures
Definition: checkIntegrityCsvFixtures.php:32
‪checkIntegrityCsvFixtures\$fix
‪bool $fix
Definition: checkIntegrityCsvFixtures.php:35
‪checkIntegrityCsvFixtures\fixCsvFile
‪bool fixCsvFile(string $csvFixture)
Definition: checkIntegrityCsvFixtures.php:145
‪$cglFixer
‪$cglFixer
Definition: checkIntegrityCsvFixtures.php:229
‪$output
‪$output
Definition: annotationChecker.php:113
‪checkIntegrityCsvFixtures\$fixAll
‪bool $fixAll
Definition: checkIntegrityCsvFixtures.php:39
‪checkIntegrityCsvFixtures\setFixAll
‪setFixAll(bool $fixAll)
Definition: checkIntegrityCsvFixtures.php:46
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:21
‪checkIntegrityCsvFixtures\execute
‪int execute()
Definition: checkIntegrityCsvFixtures.php:57
‪checkIntegrityCsvFixtures\findCsvFixtures
‪Symfony Component Finder Finder findCsvFixtures()
Definition: checkIntegrityCsvFixtures.php:99