Completed
Pull Request — master (#178)
by ignace nyamagana
02:35
created

StreamFilter::getStreamFilterPath()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 13
rs 9.4285
cc 2
eloc 9
nc 2
nop 0
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 InvalidArgumentException;
16
use LogicException;
17
use OutOfBoundsException;
18
19
/**
20
 *  A Trait to ease PHP Stream Filters manipulation
21
 *  with a SplFileObject
22
 *
23
 * @package League.csv
24
 * @since  6.0.0
25
 *
26
 */
27
trait StreamFilter
28
{
29
    /**
30
     * collection of stream filters
31
     *
32
     * @var array
33
     */
34
    protected $stream_filters = [];
35
36
    /**
37
     * Stream filtering mode to apply on all filters
38
     *
39
     * @var int
40
     */
41
    protected $stream_filter_mode = STREAM_FILTER_ALL;
42
43
    /**
44
     *the real path
45
     *
46
     * @var string the real path to the file
47
     *
48
     */
49
    protected $stream_uri;
50
51
    /**
52
     * PHP Stream Filter Regex
53
     *
54
     * @var string
55
     */
56
    protected $stream_regex = ',^
57
        php://filter/
58
        (?P<mode>:?read=|write=)?  # The resource open mode
59
        (?P<filters>.*?)           # The resource registered filters
60
        /resource=(?P<resource>.*) # The resource path
61
        $,ix';
62
63
    /**
64
     * Charset Encoding for the CSV
65
     *
66
     * @var string
67
     */
68
    protected $input_encoding = 'UTF-8';
69
70
    /**
71
     * Gets the source CSV encoding charset
72
     *
73
     * @return string
74
     */
75
    public function getInputEncoding()
76
    {
77
        return $this->input_encoding;
78
    }
79
80
    /**
81
     * Sets the CSV encoding charset
82
     *
83
     * @param string $str
84
     *
85
     * @return static
86
     */
87
    public function setInputEncoding($str)
88
    {
89
        $str = str_replace('_', '-', $str);
90
        $str = filter_var($str, FILTER_SANITIZE_STRING, ['flags' => FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH]);
91
        $str = trim($str);
92
        if ('' === $str) {
93
            throw new InvalidArgumentException('you should use a valid charset');
94
        }
95
        $this->input_encoding = strtoupper($str);
96
97
        return $this;
98
    }
99
100
    /**
101
     * Internal path setter
102
     *
103
     * The path must be an SplFileInfo object
104
     * an object that implements the `__toString` method
105
     * a path to a file
106
     *
107
     * @param \SplFileObject|string $path The file path
108
     */
109
    protected function initStreamFilter($path)
110
    {
111
        $this->stream_filters = [];
112
        if (!is_string($path)) {
113
            $this->stream_uri = null;
114
115
            return;
116
        }
117
118
        if (!preg_match($this->stream_regex, $path, $matches)) {
119
            $this->stream_uri = $path;
120
121
            return;
122
        }
123
        $this->stream_uri = $matches['resource'];
124
        $this->stream_filters = array_map('urldecode', explode('|', $matches['filters']));
125
        $this->stream_filter_mode = $this->fetchStreamModeAsInt($matches['mode']);
126
    }
127
128
    /**
129
     * Get the stream mode
130
     *
131
     * @param string $mode
132
     *
133
     * @return int
134
     */
135
    protected function fetchStreamModeAsInt($mode)
136
    {
137
        $mode = strtolower($mode);
138
        $mode = rtrim($mode, '=');
139
        if ('write' == $mode) {
140
            return STREAM_FILTER_WRITE;
141
        }
142
143
        if ('read' == $mode) {
144
            return STREAM_FILTER_READ;
145
        }
146
147
        return STREAM_FILTER_ALL;
148
    }
149
150
    /**
151
     * Check if the trait methods can be used
152
     *
153
     * @throws LogicException If the API can not be use
154
     */
155
    protected function assertStreamable()
156
    {
157
        if (!is_string($this->stream_uri)) {
158
            throw new LogicException('The stream filter API can not be used');
159
        }
160
    }
161
162
    /**
163
     * Tells whether the stream filter capabilities can be used
164
     *
165
     * @return bool
166
     */
167
    public function isActiveStreamFilter()
168
    {
169
        return is_string($this->stream_uri);
170
    }
171
172
    /**
173
     * stream filter mode Setter
174
     *
175
     * Set the new Stream Filter mode and remove all
176
     * previously attached stream filters
177
     *
178
     * @param int $mode
179
     *
180
     * @throws OutOfBoundsException If the mode is invalid
181
     *
182
     * @return $this
183
     */
184
    public function setStreamFilterMode($mode)
185
    {
186
        $this->assertStreamable();
187
        if (!in_array($mode, [STREAM_FILTER_ALL, STREAM_FILTER_READ, STREAM_FILTER_WRITE])) {
188
            throw new OutOfBoundsException('the $mode should be a valid `STREAM_FILTER_*` constant');
189
        }
190
191
        $this->stream_filter_mode = $mode;
192
        $this->stream_filters = [];
193
194
        return $this;
195
    }
196
197
    /**
198
     * stream filter mode getter
199
     *
200
     * @return int
201
     */
202
    public function getStreamFilterMode()
203
    {
204
        $this->assertStreamable();
205
206
        return $this->stream_filter_mode;
207
    }
208
209
    /**
210
     * append a stream filter
211
     *
212
     * @param string $filter_name a string or an object that implements the '__toString' method
213
     *
214
     * @return $this
215
     */
216
    public function appendStreamFilter($filter_name)
217
    {
218
        $this->assertStreamable();
219
        $this->stream_filters[] = $this->sanitizeStreamFilter($filter_name);
220
221
        return $this;
222
    }
223
224
    /**
225
     * prepend a stream filter
226
     *
227
     * @param string $filter_name a string or an object that implements the '__toString' method
228
     *
229
     * @return $this
230
     */
231
    public function prependStreamFilter($filter_name)
232
    {
233
        $this->assertStreamable();
234
        array_unshift($this->stream_filters, $this->sanitizeStreamFilter($filter_name));
235
236
        return $this;
237
    }
238
239
    /**
240
     * Sanitize the stream filter name
241
     *
242
     * @param string $filter_name the stream filter name
243
     *
244
     * @return string
245
     */
246
    protected function sanitizeStreamFilter($filter_name)
247
    {
248
        return urldecode($this->validateString($filter_name));
0 ignored issues
show
Bug introduced by
It seems like validateString() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
249
    }
250
251
    /**
252
     * Detect if the stream filter is already present
253
     *
254
     * @param string $filter_name
255
     *
256
     * @return bool
257
     */
258
    public function hasStreamFilter($filter_name)
259
    {
260
        $this->assertStreamable();
261
262
        return false !== array_search(urldecode($filter_name), $this->stream_filters, true);
263
    }
264
265
    /**
266
     * Remove a filter from the collection
267
     *
268
     * @param string $filter_name
269
     *
270
     * @return $this
271
     */
272
    public function removeStreamFilter($filter_name)
273
    {
274
        $this->assertStreamable();
275
        $res = array_search(urldecode($filter_name), $this->stream_filters, true);
276
        if (false !== $res) {
277
            unset($this->stream_filters[$res]);
278
        }
279
280
        return $this;
281
    }
282
283
    /**
284
     * Remove all registered stream filter
285
     *
286
     * @return $this
287
     */
288
    public function clearStreamFilter()
289
    {
290
        $this->assertStreamable();
291
        $this->stream_filters = [];
292
293
        return $this;
294
    }
295
296
    /**
297
     * Return the filter path
298
     *
299
     * @return string
300
     */
301
    protected function getStreamFilterPath()
302
    {
303
        $this->assertStreamable();
304
        $filters = $this->getStreamFilters();
305
        if (!$filters) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $filters of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
306
            return $this->stream_uri;
307
        }
308
309
        return 'php://filter/'
310
            .$this->getStreamFilterPrefix()
311
            .implode('|', array_map('urlencode', $filters))
312
            .'/resource='.$this->stream_uri;
313
    }
314
315
    /**
316
     * Return the registered stream filters
317
     *
318
     * @return string[]
319
     */
320
    protected function getStreamFilters()
321
    {
322
        if (STREAM_FILTER_READ === $this->stream_filter_mode
323
            && in_array('convert.iconv.*', stream_get_filters(), true)
324
            && 'UTF-8' !== $this->input_encoding
325
        ) {
326
            $filters = $this->stream_filters;
327
            $filters[] = $this->sanitizeStreamFilter('convert.iconv.'.$this->input_encoding.'/UTF-8//TRANSLIT');
328
            return $filters;
329
        }
330
331
        return $this->stream_filters;
332
    }
333
334
    /**
335
     * Return PHP stream filter prefix
336
     *
337
     * @return string
338
     */
339
    protected function getStreamFilterPrefix()
340
    {
341
        if (STREAM_FILTER_READ == $this->stream_filter_mode) {
342
            return 'read=';
343
        }
344
345
        if (STREAM_FILTER_WRITE == $this->stream_filter_mode) {
346
            return 'write=';
347
        }
348
349
        return '';
350
    }
351
}
352