Completed
Pull Request — master (#178)
by ignace nyamagana
40:39
created

StreamFilter::prependStreamFilter()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 7
rs 9.4285
cc 1
eloc 4
nc 1
nop 1
1
<?php
2
/**
3
* This file is part of the League.csv library
4
*
5
* @license http://opensource.org/licenses/MIT
6
* @link https://github.com/thephpleague/csv/
7
* @version 8.1.1
8
* @package League.csv
9
*
10
* For the full copyright and license information, please view the LICENSE
11
* file that was distributed with this source code.
12
*/
13
namespace League\Csv\Config;
14
15
use LogicException;
16
use OutOfBoundsException;
17
18
/**
19
 *  A Trait to ease PHP Stream Filters manipulation
20
 *  with a SplFileObject
21
 *
22
 * @package League.csv
23
 * @since  6.0.0
24
 *
25
 */
26
trait StreamFilter
27
{
28
    /**
29
     * collection of stream filters
30
     *
31
     * @var array
32
     */
33
    protected $stream_filters = [];
34
35
    /**
36
     * Stream filtering mode to apply on all filters
37
     *
38
     * @var int
39
     */
40
    protected $stream_filter_mode = STREAM_FILTER_ALL;
41
42
    /**
43
     *the real path
44
     *
45
     * @var string the real path to the file
46
     *
47
     */
48
    protected $stream_uri;
49
50
    /**
51
     * PHP Stream Filter Regex
52
     *
53
     * @var string
54
     */
55
    protected $stream_regex = ',^
56
        php://filter/
57
        (?P<mode>:?read=|write=)?  # The resource open mode
58
        (?P<filters>.*?)           # The resource registered filters
59
        /resource=(?P<resource>.*) # The resource path
60
        $,ix';
61
62
    /**
63
     * Internal path setter
64
     *
65
     * The path must be an SplFileInfo object
66
     * an object that implements the `__toString` method
67
     * a path to a file
68
     *
69
     * @param \SplFileObject|string $path The file path
70
     */
71
    protected function initStreamFilter($path)
72
    {
73
        $this->stream_filters = [];
74
        if (!is_string($path)) {
75
            $this->stream_uri = null;
76
77
            return;
78
        }
79
80
        if (!preg_match($this->stream_regex, $path, $matches)) {
81
            $this->stream_uri = $path;
82
83
            return;
84
        }
85
        $this->stream_uri = $matches['resource'];
86
        $this->stream_filters = array_map('urldecode', explode('|', $matches['filters']));
87
        $this->stream_filter_mode = $this->fetchStreamModeAsInt($matches['mode']);
88
    }
89
90
    /**
91
     * Get the stream mode
92
     *
93
     * @param string $mode
94
     *
95
     * @return int
96
     */
97
    protected function fetchStreamModeAsInt($mode)
98
    {
99
        $mode = strtolower($mode);
100
        $mode = rtrim($mode, '=');
101
        if ('write' == $mode) {
102
            return STREAM_FILTER_WRITE;
103
        }
104
105
        if ('read' == $mode) {
106
            return STREAM_FILTER_READ;
107
        }
108
109
        return STREAM_FILTER_ALL;
110
    }
111
112
    /**
113
     * Check if the trait methods can be used
114
     *
115
     * @throws LogicException If the API can not be use
116
     */
117
    protected function assertStreamable()
118
    {
119
        if (!is_string($this->stream_uri)) {
120
            throw new LogicException('The stream filter API can not be used');
121
        }
122
    }
123
124
    /**
125
     * Tells whether the stream filter capabilities can be used
126
     *
127
     * @return bool
128
     */
129
    public function isActiveStreamFilter()
130
    {
131
        return is_string($this->stream_uri);
132
    }
133
134
    /**
135
     * stream filter mode Setter
136
     *
137
     * Set the new Stream Filter mode and remove all
138
     * previously attached stream filters
139
     *
140
     * @param int $mode
141
     *
142
     * @throws OutOfBoundsException If the mode is invalid
143
     *
144
     * @return $this
145
     */
146
    public function setStreamFilterMode($mode)
147
    {
148
        $this->assertStreamable();
149
        if (!in_array($mode, [STREAM_FILTER_ALL, STREAM_FILTER_READ, STREAM_FILTER_WRITE])) {
150
            throw new OutOfBoundsException('the $mode should be a valid `STREAM_FILTER_*` constant');
151
        }
152
153
        $this->stream_filter_mode = $mode;
154
        $this->stream_filters = [];
155
156
        return $this;
157
    }
158
159
    /**
160
     * stream filter mode getter
161
     *
162
     * @return int
163
     */
164
    public function getStreamFilterMode()
165
    {
166
        $this->assertStreamable();
167
168
        return $this->stream_filter_mode;
169
    }
170
171
    /**
172
     * append a stream filter
173
     *
174
     * @param string $filter_name a string or an object that implements the '__toString' method
175
     *
176
     * @return $this
177
     */
178
    public function appendStreamFilter($filter_name)
179
    {
180
        $this->assertStreamable();
181
        $this->stream_filters[] = $this->sanitizeStreamFilter($filter_name);
182
183
        return $this;
184
    }
185
186
    /**
187
     * prepend a stream filter
188
     *
189
     * @param string $filter_name a string or an object that implements the '__toString' method
190
     *
191
     * @return $this
192
     */
193
    public function prependStreamFilter($filter_name)
194
    {
195
        $this->assertStreamable();
196
        array_unshift($this->stream_filters, $this->sanitizeStreamFilter($filter_name));
197
198
        return $this;
199
    }
200
201
    /**
202
     * Sanitize the stream filter name
203
     *
204
     * @param string $filter_name the stream filter name
205
     *
206
     * @return string
207
     */
208
    protected function sanitizeStreamFilter($filter_name)
209
    {
210
        return urldecode($this->validateString($filter_name));
211
    }
212
213
    /**
214
     * @inheritdoc
215
     */
216
    abstract public function validateString($str);
217
218
    /**
219
     * Detect if the stream filter is already present
220
     *
221
     * @param string $filter_name
222
     *
223
     * @return bool
224
     */
225
    public function hasStreamFilter($filter_name)
226
    {
227
        $this->assertStreamable();
228
229
        return false !== array_search(urldecode($filter_name), $this->stream_filters, true);
230
    }
231
232
    /**
233
     * Remove a filter from the collection
234
     *
235
     * @param string $filter_name
236
     *
237
     * @return $this
238
     */
239
    public function removeStreamFilter($filter_name)
240
    {
241
        $this->assertStreamable();
242
        $res = array_search(urldecode($filter_name), $this->stream_filters, true);
243
        if (false !== $res) {
244
            unset($this->stream_filters[$res]);
245
        }
246
247
        return $this;
248
    }
249
250
    /**
251
     * Remove all registered stream filter
252
     *
253
     * @return $this
254
     */
255
    public function clearStreamFilter()
256
    {
257
        $this->assertStreamable();
258
        $this->stream_filters = [];
259
260
        return $this;
261
    }
262
263
    /**
264
     * Return the filter path
265
     *
266
     * @return string
267
     */
268
    protected function getStreamFilterPath()
269
    {
270
        $this->assertStreamable();
271
        if (!$this->stream_filters) {
272
            return $this->stream_uri;
273
        }
274
275
        return 'php://filter/'
276
            .$this->getStreamFilterPrefix()
277
            .implode('|', array_map('urlencode', $this->stream_filters))
278
            .'/resource='.$this->stream_uri;
279
    }
280
281
    /**
282
     * Return PHP stream filter prefix
283
     *
284
     * @return string
285
     */
286
    protected function getStreamFilterPrefix()
287
    {
288
        if (STREAM_FILTER_READ == $this->stream_filter_mode) {
289
            return 'read=';
290
        }
291
292
        if (STREAM_FILTER_WRITE == $this->stream_filter_mode) {
293
            return 'write=';
294
        }
295
296
        return '';
297
    }
298
}
299