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

StreamFilter::assertStreamable()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 3
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 9.0.0
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
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
     * Charset Encoding for the CSV
64
     *
65
     * @var string
66
     */
67
    protected $input_encoding = 'UTF-8';
68
69
    /**
70
     * Gets the source CSV encoding charset
71
     *
72
     * @return string
73
     */
74
    public function getInputEncoding()
75
    {
76
        return $this->input_encoding;
77
    }
78
79
    /**
80
     * Sets the CSV encoding charset
81
     *
82
     * @param string $str
83
     *
84
     * @return static
85
     */
86
    public function setInputEncoding($str)
87
    {
88
        $str = str_replace('_', '-', $str);
89
        $str = filter_var($str, FILTER_SANITIZE_STRING, ['flags' => FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH]);
90
        $str = trim($str);
91
        if ('' === $str) {
92
            throw new InvalidArgumentException('you should use a valid charset');
93
        }
94
        $this->input_encoding = strtoupper($str);
95
96
        return $this;
97
    }
98
99
    /**
100
     * stream filter mode getter
101
     *
102
     * @return int
103
     */
104
    public function getStreamFilterMode()
105
    {
106
        $this->assertStreamable();
107
108
        return $this->stream_filter_mode;
109
    }
110
111
    /**
112
     * Internal path setter
113
     *
114
     * The path must be an SplFileInfo object
115
     * an object that implements the `__toString` method
116
     * a path to a file
117
     *
118
     * @param \SplFileObject|string $path The file path
119
     */
120
    protected function initStreamFilter($path)
121
    {
122
        $this->stream_filters = [];
123
        if (!is_string($path)) {
124
            $this->stream_uri = null;
125
            return;
126
        }
127
128
        if (!preg_match($this->stream_regex, $path, $matches)) {
129
            $this->stream_uri = $path;
130
            return;
131
        }
132
133
        $this->stream_uri = $matches['resource'];
134
        $this->stream_filters = [];
135
        $filter_mode = $this->fetchStreamModeAsInt($matches['mode']);
136
        if (in_array($filter_mode, [STREAM_FILTER_ALL, $this->stream_filter_mode])) {
137
            $this->stream_filters = array_map('urldecode', explode('|', $matches['filters']));
138
        }
139
    }
140
141
    /**
142
     * Get the stream mode
143
     *
144
     * @param string $mode
145
     *
146
     * @return int
147
     */
148
    protected function fetchStreamModeAsInt($mode)
149
    {
150
        $mode = strtolower($mode);
151
        $mode = rtrim($mode, '=');
152
        if ('write' == $mode) {
153
            return STREAM_FILTER_WRITE;
154
        }
155
156
        if ('read' == $mode) {
157
            return STREAM_FILTER_READ;
158
        }
159
160
        return STREAM_FILTER_ALL;
161
    }
162
163
    /**
164
     * Check if the trait methods can be used
165
     *
166
     * @throws LogicException If the API can not be use
167
     */
168
    protected function assertStreamable()
169
    {
170
        if (!is_string($this->stream_uri)) {
171
            throw new LogicException('The stream filter API can not be used');
172
        }
173
    }
174
175
    /**
176
     * Tells whether the stream filter capabilities can be used
177
     *
178
     * @return bool
179
     */
180
    public function isActiveStreamFilter()
181
    {
182
        return is_string($this->stream_uri);
183
    }
184
185
    /**
186
     * append a stream filter
187
     *
188
     * @param string $filter_name a string or an object that implements the '__toString' method
189
     *
190
     * @return $this
191
     */
192
    public function appendStreamFilter($filter_name)
193
    {
194
        $this->assertStreamable();
195
        $this->stream_filters[] = $this->sanitizeStreamFilter($filter_name);
196
197
        return $this;
198
    }
199
200
    /**
201
     * prepend a stream filter
202
     *
203
     * @param string $filter_name a string or an object that implements the '__toString' method
204
     *
205
     * @return $this
206
     */
207
    public function prependStreamFilter($filter_name)
208
    {
209
        $this->assertStreamable();
210
        array_unshift($this->stream_filters, $this->sanitizeStreamFilter($filter_name));
211
212
        return $this;
213
    }
214
215
    /**
216
     * Sanitize the stream filter name
217
     *
218
     * @param string $filter_name the stream filter name
219
     *
220
     * @return string
221
     */
222
    protected function sanitizeStreamFilter($filter_name)
223
    {
224
        return urldecode($this->validateString($filter_name));
225
    }
226
227
    /**
228
     * validate a string
229
     *
230
     * @param mixed $str the value to evaluate as a string
231
     *
232
     * @throws InvalidArgumentException if the submitted data can not be converted to string
233
     *
234
     * @return string
235
     */
236
    abstract protected function validateString($str);
237
238
    /**
239
     * Return the filter path
240
     *
241
     * @return string
242
     */
243
    protected function getStreamFilterPath()
244
    {
245
        $this->assertStreamable();
246
        $filters = $this->getStreamFilters();
247
        if (empty($filters)) {
248
            return $this->stream_uri;
249
        }
250
251
        return 'php://filter/'
252
            .$this->getStreamFilterPrefix()
253
            .implode('|', array_map('urlencode', $filters))
254
            .'/resource='.$this->stream_uri;
255
    }
256
257
    /**
258
     * Return the registered stream filters
259
     *
260
     * @return string[]
261
     */
262
    protected function getStreamFilters()
263
    {
264
        if (STREAM_FILTER_READ === $this->stream_filter_mode
265
            && in_array('convert.iconv.*', stream_get_filters(), true)
266
            && 'UTF-8' !== $this->input_encoding
267
        ) {
268
            $filters = $this->stream_filters;
269
            $filters[] = $this->sanitizeStreamFilter('convert.iconv.'.$this->input_encoding.'/UTF-8//TRANSLIT');
270
            return $filters;
271
        }
272
273
        return $this->stream_filters;
274
    }
275
276
    /**
277
     * Return PHP stream filter prefix
278
     *
279
     * @return string
280
     */
281
    protected function getStreamFilterPrefix()
282
    {
283
        if (STREAM_FILTER_READ === $this->stream_filter_mode) {
284
            return 'read=';
285
        }
286
287
        return 'write=';
288
    }
289
}
290