Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
80.49% covered (warning)
80.49%
33 / 41
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
OdsExport
80.49% covered (warning)
80.49%
33 / 41
50.00% covered (danger)
50.00%
1 / 2
16.67
0.00% covered (danger)
0.00%
0 / 1
 streamFromSheets
78.95% covered (warning)
78.95%
30 / 38
0.00% covered (danger)
0.00%
0 / 1
15.83
 getSupportedExtensions
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2namespace Apie\Export;
3
4use Apie\Export\Concerns\FlattensValues;
5use Apie\Export\Lists\FileExtensionList;
6use Apie\Export\ValueObjects\FileExtension;
7use Nyholm\Psr7\Stream;
8use OpenSpout\Common\Entity\Row;
9use OpenSpout\Writer\ODS\Writer;
10use Psr\Http\Message\StreamInterface;
11use UnitEnum;
12
13class OdsExport implements ExportInterface
14{
15    use FlattensValues;
16
17    public function streamFromSheets(array $sheets, string $outputFilename = 'export.ods'): StreamInterface
18    {
19        $tempFile = tempnam(sys_get_temp_dir(), 'apie_ods_');
20        if ($tempFile === false) {
21            throw new \RuntimeException('Unable to create temporary file for ODS export.');
22        }
23
24        $writer = new Writer();
25        $writer->setCreator('Apie library');
26        $writer->openToFile($tempFile);
27
28        try {
29            $sheetIndex = 0;
30            foreach ($sheets as $sheetName => $data) {
31                if ($sheetIndex > 0) {
32                    $writer->addNewSheetAndMakeItCurrent();
33                }
34
35                foreach ($data as $row) {
36                    // If row is Traversable or object, try to convert to array
37                    if (is_object($row) && !$row instanceof \Stringable && !$row instanceof UnitEnum) {
38                        if ($row instanceof \Traversable) {
39                            $row = iterator_to_array($row);
40                        } else {
41                            // cast to array will extract properties; prefer that to avoid errors
42                            $row = (array)$row;
43                        }
44                    }
45                    if (!is_array($row)) {
46                        // single value row -> wrap
47                        $row = [$row];
48                    }
49
50                    $converted = [];
51                    foreach ($row as $cell) {
52                        $cell = $this->toSingleValue($cell);
53                        if ($cell === null) {
54                            $converted[] = '';
55                        } else {
56                            $converted[] = $cell;
57                        }
58                    }
59
60                    $writer->addRow(Row::fromValues($converted));
61                }
62
63                $sheetIndex++;
64            }
65
66            $writer->close();
67
68            $source = fopen($tempFile, 'rb');
69            if ($source === false) {
70                throw new \RuntimeException('Unable to read temporary ODS file.');
71            }
72
73            $stream = fopen('php://temp', 'r+');
74            if ($stream === false) {
75                fclose($source);
76                throw new \RuntimeException('Unable to create temporary stream for ODS export.');
77            }
78
79            stream_copy_to_stream($source, $stream);
80            fclose($source);
81            rewind($stream);
82        } finally {
83            @unlink($tempFile);
84        }
85
86        return new Stream($stream);
87    }
88
89    public function getSupportedExtensions(): FileExtensionList
90    {
91        return new FileExtensionList([
92            new FileExtension('ods'),
93        ]);
94    }
95}