Total Complexity | 47 |
Total Lines | 275 |
Duplicated Lines | 0 % |
Coverage | 98.89% |
Changes | 0 |
Complex classes like Sample often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Sample, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
24 | class Sample |
||
25 | { |
||
26 | /** |
||
27 | * Returns whether we run on CLI or browser. |
||
28 | 287 | */ |
|
29 | public function isCli(): bool |
||
30 | 287 | { |
|
31 | return PHP_SAPI === 'cli'; |
||
32 | } |
||
33 | |||
34 | /** |
||
35 | * Return the filename currently being executed. |
||
36 | 1 | */ |
|
37 | public function getScriptFilename(): string |
||
38 | 1 | { |
|
39 | return basename($_SERVER['SCRIPT_FILENAME'], '.php'); |
||
40 | } |
||
41 | |||
42 | /** |
||
43 | * Whether we are executing the index page. |
||
44 | 1 | */ |
|
45 | public function isIndex(): bool |
||
48 | } |
||
49 | |||
50 | /** |
||
51 | * Return the page title. |
||
52 | 1 | */ |
|
53 | public function getPageTitle(): string |
||
54 | 1 | { |
|
55 | return $this->isIndex() ? 'PHPSpreadsheet' : $this->getScriptFilename(); |
||
56 | } |
||
57 | |||
58 | /** |
||
59 | * Return the page heading. |
||
60 | 1 | */ |
|
61 | public function getPageHeading(): string |
||
62 | 1 | { |
|
63 | return $this->isIndex() ? '' : '<h1>' . str_replace('_', ' ', $this->getScriptFilename()) . '</h1>'; |
||
64 | } |
||
65 | |||
66 | /** |
||
67 | * Returns an array of all known samples. |
||
68 | * |
||
69 | * @return string[][] [$name => $path] |
||
70 | 1 | */ |
|
71 | public function getSamples(): array |
||
72 | { |
||
73 | 1 | // Populate samples |
|
74 | 1 | $baseDir = realpath(__DIR__ . '/../../../samples'); |
|
75 | if ($baseDir === false) { |
||
76 | // @codeCoverageIgnoreStart |
||
77 | throw new RuntimeException('realpath returned false'); |
||
78 | // @codeCoverageIgnoreEnd |
||
79 | 1 | } |
|
80 | 1 | $directory = new RecursiveDirectoryIterator($baseDir); |
|
81 | 1 | $iterator = new RecursiveIteratorIterator($directory); |
|
82 | $regex = new RegexIterator($iterator, '/^.+\.php$/', RecursiveRegexIterator::GET_MATCH); |
||
83 | 1 | ||
84 | $files = []; |
||
85 | 1 | /** @var string[] $file */ |
|
86 | 1 | foreach ($regex as $file) { |
|
87 | 1 | $file = str_replace(str_replace('\\', '/', $baseDir) . '/', '', str_replace('\\', '/', $file[0])); |
|
88 | 1 | $info = pathinfo($file); |
|
89 | 1 | $category = str_replace('_', ' ', $info['dirname'] ?? ''); |
|
90 | 1 | $name = str_replace('_', ' ', (string) preg_replace('/(|\.php)/', '', $info['filename'])); |
|
91 | 1 | if (!in_array($category, ['.', 'bootstrap', 'templates']) && $name !== 'Header') { |
|
92 | 1 | if (!isset($files[$category])) { |
|
93 | $files[$category] = []; |
||
94 | 1 | } |
|
95 | $files[$category][$name] = $file; |
||
96 | } |
||
97 | } |
||
98 | |||
99 | 1 | // Sort everything |
|
100 | 1 | ksort($files); |
|
101 | 1 | foreach ($files as &$f) { |
|
102 | asort($f); |
||
103 | } |
||
104 | 1 | ||
105 | return $files; |
||
106 | } |
||
107 | |||
108 | /** |
||
109 | * Write documents. |
||
110 | * |
||
111 | * @param string[] $writers |
||
112 | 115 | */ |
|
113 | public function write(Spreadsheet $spreadsheet, string $filename, array $writers = ['Xlsx', 'Xls'], bool $withCharts = false, ?callable $writerCallback = null, bool $resetActiveSheet = true): void |
||
114 | { |
||
115 | 115 | // Set active sheet index to the first sheet, so Excel opens this as the first sheet |
|
116 | 112 | if ($resetActiveSheet) { |
|
117 | $spreadsheet->setActiveSheetIndex(0); |
||
118 | } |
||
119 | |||
120 | 115 | // Write documents |
|
121 | 115 | foreach ($writers as $writerType) { |
|
122 | 115 | $path = $this->getFilename($filename, mb_strtolower($writerType)); |
|
123 | 115 | $writer = IOFactory::createWriter($spreadsheet, $writerType); |
|
124 | 115 | $writer->setIncludeCharts($withCharts); |
|
125 | 7 | if ($writerCallback !== null) { |
|
126 | $writerCallback($writer); |
||
127 | 115 | } |
|
128 | 115 | $callStartTime = microtime(true); |
|
129 | 115 | if (PHP_VERSION_ID >= 80400 && $writer instanceof Dompdf) { |
|
130 | 115 | @$writer->save($path); |
|
2 ignored issues
–
show
|
|||
131 | } else { |
||
132 | $writer->save($path); |
||
133 | } |
||
134 | $this->logWrite($writer, $path, $callStartTime); |
||
135 | if ($this->isCli() === false) { |
||
136 | // @codeCoverageIgnoreStart |
||
137 | 115 | echo '<a href="/download.php?type=' . pathinfo($path, PATHINFO_EXTENSION) . '&name=' . basename($path) . '">Download ' . basename($path) . '</a><br />'; |
|
138 | // @codeCoverageIgnoreEnd |
||
139 | } |
||
140 | 124 | } |
|
141 | |||
142 | 124 | $this->logEndingNotes(); |
|
143 | } |
||
144 | |||
145 | protected function isDirOrMkdir(string $folder): bool |
||
146 | { |
||
147 | return \is_dir($folder) || \mkdir($folder); |
||
148 | 125 | } |
|
149 | |||
150 | 125 | /** |
|
151 | 125 | * Returns the temporary directory and make sure it exists. |
|
152 | 1 | */ |
|
153 | public function getTemporaryFolder(): string |
||
154 | { |
||
155 | 124 | $tempFolder = sys_get_temp_dir() . '/phpspreadsheet'; |
|
156 | if (!$this->isDirOrMkdir($tempFolder)) { |
||
157 | throw new RuntimeException(sprintf('Directory "%s" was not created', $tempFolder)); |
||
158 | } |
||
159 | |||
160 | return $tempFolder; |
||
161 | 122 | } |
|
162 | |||
163 | 122 | /** |
|
164 | * Returns the filename that should be used for sample output. |
||
165 | 122 | */ |
|
166 | public function getFilename(string $filename, string $extension = 'xlsx'): string |
||
167 | { |
||
168 | $originalExtension = pathinfo($filename, PATHINFO_EXTENSION); |
||
169 | |||
170 | return $this->getTemporaryFolder() . '/' . str_replace('.' . $originalExtension, '.' . $extension, basename($filename)); |
||
171 | 7 | } |
|
172 | |||
173 | 7 | /** |
|
174 | 7 | * Return a random temporary file name. |
|
175 | */ |
||
176 | public function getTemporaryFilename(string $extension = 'xlsx'): string |
||
187 | 286 | } |
|
188 | |||
189 | public function log(string $message): void |
||
193 | } |
||
194 | |||
195 | /** |
||
196 | * Render chart as part of running chart samples in browser. |
||
197 | * Charts are not rendered in unit tests, which are command line. |
||
198 | * |
||
199 | * @codeCoverageIgnore |
||
200 | */ |
||
201 | public function renderChart(Chart $chart, string $fileName, ?Spreadsheet $spreadsheet = null): void |
||
202 | { |
||
203 | if ($this->isCli() === true) { |
||
204 | return; |
||
205 | } |
||
206 | Settings::setChartRenderer(MtJpGraphRenderer::class); |
||
207 | |||
208 | $fileName = $this->getFilename($fileName, 'png'); |
||
209 | $title = $chart->getTitle(); |
||
210 | $caption = null; |
||
211 | if ($title !== null) { |
||
212 | $calculatedTitle = $title->getCalculatedTitle($spreadsheet); |
||
213 | if ($calculatedTitle !== null) { |
||
214 | $caption = $title->getCaption(); |
||
215 | $title->setCaption($calculatedTitle); |
||
216 | } |
||
217 | } |
||
218 | |||
219 | try { |
||
220 | $chart->render($fileName); |
||
221 | $this->log('Rendered image: ' . $fileName); |
||
222 | $imageData = @file_get_contents($fileName); |
||
223 | if ($imageData !== false) { |
||
224 | echo '<div><img src="data:image/gif;base64,' . base64_encode($imageData) . '" /></div>'; |
||
225 | } else { |
||
226 | $this->log('Unable to open chart' . PHP_EOL); |
||
227 | } |
||
228 | } catch (Throwable $e) { |
||
229 | $this->log('Error rendering chart: ' . $e->getMessage() . PHP_EOL); |
||
230 | } |
||
231 | if (isset($title, $caption)) { |
||
232 | 87 | $title->setCaption($caption); |
|
233 | } |
||
234 | 87 | Settings::unsetChartRenderer(); |
|
235 | 87 | } |
|
236 | |||
237 | 87 | public function titles(string $category, string $functionName, ?string $description = null): void |
|
238 | { |
||
239 | $this->log(sprintf('%s Functions:', $category)); |
||
240 | 36 | $description === null |
|
241 | ? $this->log(sprintf('Function: %s()', rtrim($functionName, '()'))) |
||
242 | 36 | : $this->log(sprintf('Function: %s() - %s.', rtrim($functionName, '()'), rtrim($description, '.'))); |
|
243 | 36 | } |
|
244 | |||
245 | public function displayGrid(array $matrix): void |
||
246 | 12 | { |
|
247 | $renderer = new TextGrid($matrix, $this->isCli()); |
||
248 | echo $renderer->render(); |
||
249 | } |
||
250 | |||
251 | public function logCalculationResult( |
||
252 | 12 | Worksheet $worksheet, |
|
253 | 12 | string $functionName, |
|
254 | string $formulaCell, |
||
255 | 12 | ?string $descriptionCell = null |
|
256 | 12 | ): void { |
|
257 | if ($descriptionCell !== null) { |
||
258 | $this->log($worksheet->getCell($descriptionCell)->getValueString()); |
||
259 | } |
||
260 | $this->log($worksheet->getCell($formulaCell)->getValueString()); |
||
261 | $this->log(sprintf('%s() Result is ', $functionName) . $worksheet->getCell($formulaCell)->getCalculatedValueString()); |
||
262 | 118 | } |
|
263 | |||
264 | /** |
||
265 | 118 | * Log ending notes. |
|
266 | */ |
||
267 | public function logEndingNotes(): void |
||
268 | { |
||
269 | // Do not show execution time for index |
||
270 | $this->log('Peak memory usage: ' . (memory_get_peak_usage(true) / 1024 / 1024) . 'MB'); |
||
271 | 120 | } |
|
272 | |||
273 | 120 | /** |
|
274 | 120 | * Log a line about the write operation. |
|
275 | 120 | */ |
|
276 | 120 | public function logWrite(IWriter $writer, string $path, float $callStartTime): void |
|
277 | { |
||
278 | 120 | $callEndTime = microtime(true); |
|
279 | 120 | $callTime = $callEndTime - $callStartTime; |
|
280 | $reflection = new ReflectionClass($writer); |
||
281 | 120 | $format = $reflection->getShortName(); |
|
282 | |||
283 | $codePath = $this->isCli() ? $path : "<code>$path</code>"; |
||
284 | $message = "Write {$format} format to {$codePath} in " . sprintf('%.4f', $callTime) . ' seconds'; |
||
285 | |||
286 | $this->log($message); |
||
287 | 15 | } |
|
288 | |||
289 | 15 | /** |
|
290 | 15 | * Log a line about the read operation. |
|
291 | 15 | */ |
|
292 | public function logRead(string $format, string $path, float $callStartTime): void |
||
299 | } |
||
300 | } |
||
301 |
If you suppress an error, we recommend checking for the error condition explicitly: