ReaderAbstract::open()   B
last analyzed

Complexity

Conditions 8
Paths 9

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 8

Importance

Changes 0
Metric Value
dl 0
loc 24
ccs 14
cts 14
cp 1
rs 8.4444
c 0
b 0
f 0
cc 8
nc 9
nop 1
crap 8
1
<?php
2
3
namespace Box\Spout\Reader;
4
5
use Box\Spout\Common\Exception\IOException;
6
use Box\Spout\Common\Helper\GlobalFunctionsHelper;
7
use Box\Spout\Common\Manager\OptionsManagerInterface;
8
use Box\Spout\Reader\Common\Creator\InternalEntityFactoryInterface;
9
use Box\Spout\Reader\Common\Entity\Options;
10
use Box\Spout\Reader\Exception\ReaderNotOpenedException;
11
12
/**
13
 * Class ReaderAbstract
14
 *
15
 * @abstract
16
 */
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 122
    public function __construct(
66
        OptionsManagerInterface $optionsManager,
67
        GlobalFunctionsHelper $globalFunctionsHelper,
68
        InternalEntityFactoryInterface $entityFactory
69
    ) {
70 122
        $this->optionsManager = $optionsManager;
71 122
        $this->globalFunctionsHelper = $globalFunctionsHelper;
72 122
        $this->entityFactory = $entityFactory;
73 122
    }
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 68
    public function setShouldFormatDates($shouldFormatDates)
82
    {
83 68
        $this->optionsManager->setOption(Options::SHOULD_FORMAT_DATES, $shouldFormatDates);
84
85 68
        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 88
    public function setShouldPreserveEmptyRows($shouldPreserveEmptyRows)
95
    {
96 88
        $this->optionsManager->setOption(Options::SHOULD_PRESERVE_EMPTY_ROWS, $shouldPreserveEmptyRows);
97
98 88
        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 111
    public function open($filePath)
110
    {
111 111
        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 106
        if (!$this->isPhpStream($filePath)) {
116
            // we skip the checks if the provided file path points to a PHP stream
117 106
            if (!$this->globalFunctionsHelper->file_exists($filePath)) {
118 3
                throw new IOException("Could not open $filePath for reading! File does not exist.");
119
            }
120 103
            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 102
            $fileRealPath = $this->getFileRealPath($filePath);
127 102
            $this->openReader($fileRealPath);
128 98
            $this->isStreamOpened = true;
129 4
        } catch (\Exception $exception) {
130 4
            throw new IOException("Could not open $filePath for reading! ({$exception->getMessage()})");
131
        }
132 98
    }
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 102
    protected function getFileRealPath($filePath)
142
    {
143 102
        if ($this->isSupportedStreamWrapper($filePath)) {
144 102
            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 111
    protected function getStreamWrapperScheme($filePath)
159
    {
160 111
        $streamScheme = null;
161 111
        if (\preg_match('/^(\w+):\/\//', $filePath, $matches)) {
162 6
            $streamScheme = $matches[1];
163
        }
164
165 111
        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 111
    protected function isStreamWrapper($filePath)
176
    {
177 111
        return ($this->getStreamWrapperScheme($filePath) !== null);
178
    }
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 103
    protected function isSupportedStreamWrapper($filePath)
189
    {
190 103
        $streamScheme = $this->getStreamWrapperScheme($filePath);
191
192 103
        return ($streamScheme !== null) ?
193 2
            \in_array($streamScheme, $this->globalFunctionsHelper->stream_get_wrappers()) :
194 103
            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 106
    protected function isPhpStream($filePath)
204
    {
205 106
        $streamScheme = $this->getStreamWrapperScheme($filePath);
206
207 106
        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 99
    public function getSheetIterator()
217
    {
218 99
        if (!$this->isStreamOpened) {
219 1
            throw new ReaderNotOpenedException('Reader should be opened first.');
220
        }
221
222 98
        return $this->getConcreteSheetIterator();
223
    }
224
225
    /**
226
     * Closes the reader, preventing any additional reading
227
     *
228
     * @return void
229
     */
230 95
    public function close()
231
    {
232 95
        if ($this->isStreamOpened) {
233 95
            $this->closeReader();
234
235 95
            $sheetIterator = $this->getConcreteSheetIterator();
236 95
            if ($sheetIterator) {
237 95
                $sheetIterator->end();
238
            }
239
240 95
            $this->isStreamOpened = false;
241
        }
242 95
    }
243
}
244