Failed Conditions
Pull Request — master (#4165)
by Owen
15:55
created

Sample::write()   B

Complexity

Conditions 7
Paths 18

Size

Total Lines 30
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 7

Importance

Changes 0
Metric Value
eloc 17
dl 0
loc 30
ccs 15
cts 15
cp 1
rs 8.8333
c 0
b 0
f 0
cc 7
nc 18
nop 6
crap 7
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Helper;
4
5
use PhpOffice\PhpSpreadsheet\Chart\Chart;
6
use PhpOffice\PhpSpreadsheet\Chart\Renderer\MtJpGraphRenderer;
7
use PhpOffice\PhpSpreadsheet\IOFactory;
8
use PhpOffice\PhpSpreadsheet\Settings;
9
use PhpOffice\PhpSpreadsheet\Spreadsheet;
10
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
11
use PhpOffice\PhpSpreadsheet\Writer\IWriter;
12
use PhpOffice\PhpSpreadsheet\Writer\Pdf\Dompdf;
13
use RecursiveDirectoryIterator;
14
use RecursiveIteratorIterator;
15
use RecursiveRegexIterator;
16
use ReflectionClass;
17
use RegexIterator;
18
use RuntimeException;
19
use Throwable;
20
21
/**
22
 * Helper class to be used in sample code.
23
 */
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
46 1
    {
47
        return $this->getScriptFilename() === 'index';
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
Security Best Practice introduced by
It seems like you do not handle an error condition for save(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

130
                /** @scrutinizer ignore-unhandled */ @$writer->save($path);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
Bug introduced by
Are you sure the usage of $writer->save($path) targeting PhpOffice\PhpSpreadsheet\Writer\Pdf\Dompdf::save() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
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
177
    {
178
        $temporaryFilename = tempnam($this->getTemporaryFolder(), 'phpspreadsheet-');
179 7
        if ($temporaryFilename === false) {
180
            // @codeCoverageIgnoreStart
181 7
            throw new RuntimeException('tempnam returned false');
182
            // @codeCoverageIgnoreEnd
183
        }
184 286
        unlink($temporaryFilename);
185
186 286
        return $temporaryFilename . '.' . $extension;
187 286
    }
188
189
    public function log(string $message): void
190
    {
191
        $eol = $this->isCli() ? PHP_EOL : '<br />';
192
        echo ($this->isCli() ? date('H:i:s ') : '') . $message . $eol;
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
293 15
    {
294
        $callEndTime = microtime(true);
295
        $callTime = $callEndTime - $callStartTime;
296
        $message = "Read {$format} format from <code>{$path}</code>  in " . sprintf('%.4f', $callTime) . ' seconds';
297
298
        $this->log($message);
299
    }
300
}
301