1 | <?php |
||
17 | abstract class ReaderAbstract implements ReaderInterface |
||
18 | { |
||
19 | /** @var bool Indicates whether the stream is currently open */ |
||
20 | protected $isStreamOpened = false; |
||
21 | |||
22 | /** @var InternalEntityFactoryInterface Factory to create entities */ |
||
23 | protected $entityFactory; |
||
24 | |||
25 | /** @var \Box\Spout\Common\Helper\GlobalFunctionsHelper Helper to work with global functions */ |
||
26 | protected $globalFunctionsHelper; |
||
27 | |||
28 | /** @var OptionsManagerInterface Writer options manager */ |
||
29 | protected $optionsManager; |
||
30 | |||
31 | /** |
||
32 | * Returns whether stream wrappers are supported |
||
33 | * |
||
34 | * @return bool |
||
35 | */ |
||
36 | abstract protected function doesSupportStreamWrapper(); |
||
37 | |||
38 | /** |
||
39 | * Opens the file at the given file path to make it ready to be read |
||
40 | * |
||
41 | * @param string $filePath Path of the file to be read |
||
42 | * @return void |
||
43 | */ |
||
44 | abstract protected function openReader($filePath); |
||
45 | |||
46 | /** |
||
47 | * Returns an iterator to iterate over sheets. |
||
48 | * |
||
49 | * @return IteratorInterface To iterate over sheets |
||
50 | */ |
||
51 | abstract protected function getConcreteSheetIterator(); |
||
52 | |||
53 | /** |
||
54 | * Closes the reader. To be used after reading the file. |
||
55 | * |
||
56 | * @return ReaderAbstract |
||
57 | */ |
||
58 | abstract protected function closeReader(); |
||
59 | |||
60 | /** |
||
61 | * @param OptionsManagerInterface $optionsManager |
||
62 | * @param GlobalFunctionsHelper $globalFunctionsHelper |
||
63 | * @param InternalEntityFactoryInterface $entityFactory |
||
64 | */ |
||
65 | 110 | public function __construct( |
|
66 | OptionsManagerInterface $optionsManager, |
||
67 | GlobalFunctionsHelper $globalFunctionsHelper, |
||
68 | InternalEntityFactoryInterface $entityFactory |
||
69 | ) { |
||
70 | 110 | $this->optionsManager = $optionsManager; |
|
71 | 110 | $this->globalFunctionsHelper = $globalFunctionsHelper; |
|
72 | 110 | $this->entityFactory = $entityFactory; |
|
73 | 110 | } |
|
74 | |||
75 | /** |
||
76 | * Sets whether date/time values should be returned as PHP objects or be formatted as strings. |
||
77 | * |
||
78 | * @param bool $shouldFormatDates |
||
79 | * @return ReaderAbstract |
||
80 | */ |
||
81 | 66 | public function setShouldFormatDates($shouldFormatDates) |
|
82 | { |
||
83 | 66 | $this->optionsManager->setOption(Options::SHOULD_FORMAT_DATES, $shouldFormatDates); |
|
84 | |||
85 | 66 | return $this; |
|
86 | } |
||
87 | |||
88 | /** |
||
89 | * Sets whether empty rows should be returned or skipped. |
||
90 | * |
||
91 | * @param bool $shouldPreserveEmptyRows |
||
92 | * @return ReaderAbstract |
||
93 | */ |
||
94 | 86 | public function setShouldPreserveEmptyRows($shouldPreserveEmptyRows) |
|
95 | { |
||
96 | 86 | $this->optionsManager->setOption(Options::SHOULD_PRESERVE_EMPTY_ROWS, $shouldPreserveEmptyRows); |
|
97 | |||
98 | 86 | return $this; |
|
99 | } |
||
100 | |||
101 | /** |
||
102 | * Prepares the reader to read the given file. It also makes sure |
||
103 | * that the file exists and is readable. |
||
104 | * |
||
105 | * @param string $filePath Path of the file to be read |
||
106 | * @throws \Box\Spout\Common\Exception\IOException If the file at the given path does not exist, is not readable or is corrupted |
||
107 | * @return void |
||
108 | */ |
||
109 | 109 | public function open($filePath) |
|
110 | { |
||
111 | 109 | if ($this->isStreamWrapper($filePath) && (!$this->doesSupportStreamWrapper() || !$this->isSupportedStreamWrapper($filePath))) { |
|
112 | 5 | throw new IOException("Could not open $filePath for reading! Stream wrapper used is not supported for this type of file."); |
|
113 | } |
||
114 | |||
115 | 104 | if (!$this->isPhpStream($filePath)) { |
|
116 | // we skip the checks if the provided file path points to a PHP stream |
||
117 | 104 | if (!$this->globalFunctionsHelper->file_exists($filePath)) { |
|
118 | 3 | throw new IOException("Could not open $filePath for reading! File does not exist."); |
|
119 | } |
||
120 | 101 | if (!$this->globalFunctionsHelper->is_readable($filePath)) { |
|
121 | 1 | throw new IOException("Could not open $filePath for reading! File is not readable."); |
|
122 | } |
||
123 | } |
||
124 | |||
125 | try { |
||
126 | 100 | $fileRealPath = $this->getFileRealPath($filePath); |
|
127 | 100 | $this->openReader($fileRealPath); |
|
128 | 96 | $this->isStreamOpened = true; |
|
129 | 4 | } catch (\Exception $exception) { |
|
130 | 4 | throw new IOException("Could not open $filePath for reading! ({$exception->getMessage()})"); |
|
131 | } |
||
132 | 96 | } |
|
133 | |||
134 | /** |
||
135 | * Returns the real path of the given path. |
||
136 | * If the given path is a valid stream wrapper, returns the path unchanged. |
||
137 | * |
||
138 | * @param string $filePath |
||
139 | * @return string |
||
140 | */ |
||
141 | 100 | protected function getFileRealPath($filePath) |
|
142 | { |
||
143 | 100 | if ($this->isSupportedStreamWrapper($filePath)) { |
|
144 | 100 | return $filePath; |
|
145 | } |
||
146 | |||
147 | // Need to use realpath to fix "Can't open file" on some Windows setup |
||
148 | return realpath($filePath); |
||
149 | } |
||
150 | |||
151 | /** |
||
152 | * Returns the scheme of the custom stream wrapper, if the path indicates a stream wrapper is used. |
||
153 | * For example, php://temp => php, s3://path/to/file => s3... |
||
154 | * |
||
155 | * @param string $filePath Path of the file to be read |
||
156 | * @return string|null The stream wrapper scheme or NULL if not a stream wrapper |
||
157 | */ |
||
158 | 109 | protected function getStreamWrapperScheme($filePath) |
|
159 | { |
||
160 | 109 | $streamScheme = null; |
|
161 | 109 | if (preg_match('/^(\w+):\/\//', $filePath, $matches)) { |
|
162 | 6 | $streamScheme = $matches[1]; |
|
163 | } |
||
164 | |||
165 | 109 | return $streamScheme; |
|
166 | } |
||
167 | |||
168 | /** |
||
169 | * Checks if the given path is an unsupported stream wrapper |
||
170 | * (like local path, php://temp, mystream://foo/bar...). |
||
171 | * |
||
172 | * @param string $filePath Path of the file to be read |
||
173 | * @return bool Whether the given path is an unsupported stream wrapper |
||
174 | */ |
||
175 | 109 | protected function isStreamWrapper($filePath) |
|
179 | |||
180 | /** |
||
181 | * Checks if the given path is an supported stream wrapper |
||
182 | * (like php://temp, mystream://foo/bar...). |
||
183 | * If the given path is a local path, returns true. |
||
184 | * |
||
185 | * @param string $filePath Path of the file to be read |
||
186 | * @return bool Whether the given path is an supported stream wrapper |
||
187 | */ |
||
188 | 101 | protected function isSupportedStreamWrapper($filePath) |
|
189 | { |
||
190 | 101 | $streamScheme = $this->getStreamWrapperScheme($filePath); |
|
191 | |||
192 | 101 | return ($streamScheme !== null) ? |
|
193 | 2 | in_array($streamScheme, $this->globalFunctionsHelper->stream_get_wrappers()) : |
|
194 | 101 | true; |
|
195 | } |
||
196 | |||
197 | /** |
||
198 | * Checks if a path is a PHP stream (like php://output, php://memory, ...) |
||
199 | * |
||
200 | * @param string $filePath Path of the file to be read |
||
201 | * @return bool Whether the given path maps to a PHP stream |
||
202 | */ |
||
203 | 104 | protected function isPhpStream($filePath) |
|
204 | { |
||
205 | 104 | $streamScheme = $this->getStreamWrapperScheme($filePath); |
|
206 | |||
207 | 104 | return ($streamScheme === 'php'); |
|
208 | } |
||
209 | |||
210 | /** |
||
211 | * Returns an iterator to iterate over sheets. |
||
212 | * |
||
213 | * @throws \Box\Spout\Reader\Exception\ReaderNotOpenedException If called before opening the reader |
||
214 | * @return \Iterator To iterate over sheets |
||
215 | */ |
||
216 | 97 | public function getSheetIterator() |
|
217 | { |
||
218 | 97 | if (!$this->isStreamOpened) { |
|
219 | 1 | throw new ReaderNotOpenedException('Reader should be opened first.'); |
|
220 | } |
||
221 | |||
222 | 96 | return $this->getConcreteSheetIterator(); |
|
223 | } |
||
224 | |||
225 | /** |
||
226 | * Closes the reader, preventing any additional reading |
||
227 | * |
||
228 | * @return void |
||
229 | */ |
||
230 | 92 | public function close() |
|
243 | } |
||
244 |