Completed
Push — master ( 0cbce9...ce80be )
by ignace nyamagana
8s
created

DataPath::getUriComponent()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 4
Bugs 0 Features 0
Metric Value
c 4
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
/**
4
 * League.Uri (http://uri.thephpleague.com)
5
 *
6
 * @package   League.uri
7
 * @author    Ignace Nyamagana Butera <[email protected]>
8
 * @copyright 2013-2015 Ignace Nyamagana Butera
9
 * @license   https://github.com/thephpleague/uri/blob/master/LICENSE (MIT License)
10
 * @version   4.1.1
11
 * @link      https://github.com/thephpleague/uri/
12
 */
13
namespace League\Uri\Components;
14
15
use InvalidArgumentException;
16
use League\Uri\Interfaces\DataPath as DataPathInterface;
17
use RuntimeException;
18
use SplFileObject;
19
20
/**
21
 * Value object representing a URI path component.
22
 *
23
 * @package League.uri
24
 * @author  Ignace Nyamagana Butera <[email protected]>
25
 * @since   4.0.0
26
 */
27
class DataPath extends AbstractComponent implements DataPathInterface
28
{
29
    use PathTrait;
30
31
    const DEFAULT_MIMETYPE = 'text/plain';
32
33
    const DEFAULT_PARAMETER = 'charset=us-ascii';
34
35
    const BINARY_PARAMETER = 'base64';
36
37
    const REGEXP_MIMETYPE = ',^\w+/[-.\w]+(?:\+[-.\w]+)?$,';
38
39
    /**
40
     * The mediatype mimetype
41
     *
42
     * @var string
43
     */
44
    protected $mimetype;
45
46
    /**
47
     * The mediatype parameters
48
     *
49
     * @var string[]
50
     */
51
    protected $parameters;
52
53
    /**
54
     * Is the Document bas64 encoded
55
     *
56
     * @var bool
57
     */
58
    protected $isBinaryData;
59
60
    /**
61
     * The document string representation
62
     *
63
     * @var string
64
     */
65
    protected $document;
66
67
    /**
68
     * @inheritdoc
69
     */
70
    protected static $invalidCharactersRegex = ',[?#],';
71
72
    /**
73
     * new instance
74
     *
75
     * @param string $path the component value
76
     */
77 131
    public function __construct($path = '')
78
    {
79 131
        $path = $this->validateString($path);
80 131
        if ('' === $path) {
81 5
            $path = static::DEFAULT_MIMETYPE.';'.static::DEFAULT_PARAMETER.',';
82 3
        }
83 131
        $this->setComponentProperties($path);
84 107
    }
85
86
    /**
87
     * Set Data Path properties
88
     *
89
     * @param string $path
90
     */
91 131
    protected function setComponentProperties($path)
92
    {
93 131
        $this->assertValidComponent($path);
94 116
        $parts = explode(',', $path, 2);
95 116
        $mediatype = array_shift($parts);
96 116
        $this->document = (string) array_shift($parts);
97 116
        $mimetype = static::DEFAULT_MIMETYPE;
98 116
        $parameters = static::DEFAULT_PARAMETER;
99 116
        if ('' !== $mediatype) {
100 107
            $mediatype = explode(';', $mediatype, 2);
101 107
            $mimetype = array_shift($mediatype);
102 107
            $parameters = (string) array_shift($mediatype);
103 71
        }
104 116
        $this->mimetype = $this->filterMimeType($mimetype);
105 113
        $this->parameters = $this->filterParameters($parameters);
106 110
        if ($this->isBinaryData) {
107 60
            $this->validateDocument();
108 38
        }
109 107
    }
110
111
    /**
112
     * @inheritdoc
113
     */
114 131
    protected function assertValidComponent($path)
115
    {
116 131
        parent::assertValidComponent($path);
117 131
        if (!mb_detect_encoding($path, 'US-ASCII', true)
118 129
            || false === strpos($path, ',')
119 127
            || false !== strpos($path, '\n')
120 87
        ) {
121 15
            throw new InvalidArgumentException(
122 15
                sprintf('The submitted path `%s` is invalid according to RFC2937', $path)
123 10
            );
124
        }
125 116
    }
126
127
    /**
128
     * Filter the mimeType property
129
     *
130
     * @param string $mimetype
131
     *
132
     * @throws InvalidArgumentException If the mimetype is invalid
133
     *
134
     * @return string
135
     */
136 116
    protected function filterMimeType($mimetype)
137
    {
138 116
        if (!preg_match(static::REGEXP_MIMETYPE, $mimetype)) {
139 3
            throw new InvalidArgumentException(sprintf('invalid mimeType, `%s`', $mimetype));
140
        }
141
142 113
        return $mimetype;
143
    }
144
145
    /**
146
     * Extract and set the binary flag from the parameters if it exists
147
     *
148
     * @param string $parameters
149
     *
150
     * @throws InvalidArgumentException If the mediatype parameters contain invalid data
151
     *
152
     * @return string[]
153
     */
154 113
    protected function filterParameters($parameters)
155
    {
156 113
        $this->isBinaryData = false;
157 113
        if ('' == $parameters) {
158 6
            return [static::DEFAULT_PARAMETER];
159
        }
160
161 110
        if (preg_match(',(;|^)'.static::BINARY_PARAMETER.'$,', $parameters, $matches)) {
162 63
            $parameters = mb_substr($parameters, 0, - strlen($matches[0]));
163 63
            $this->isBinaryData = true;
164 42
        }
165
166 110
        $params = array_filter(explode(';', $parameters));
167 110
        if (!empty(array_filter($params, [$this, 'validateParameter']))) {
168 12
            throw new InvalidArgumentException(sprintf('invalid mediatype parameters, `%s`', $parameters));
169
        }
170
171 107
        return $params;
172
    }
173
174
    /**
175
     * Validate mediatype parameter
176
     *
177
     * @param string $parameter a mediatype parameter
178
     *
179
     * @return bool
180
     */
181 104
    protected function validateParameter($parameter)
182
    {
183 104
        $properties = explode('=', $parameter);
184
185 104
        return 2 != count($properties) || mb_strtolower($properties[0], 'UTF-8') == static::BINARY_PARAMETER;
186
    }
187
188
    /**
189
     * Validate the path document string representation
190
     *
191
     * @throws InvalidArgumentException If the data is invalid
192
     */
193 60
    protected function validateDocument()
194
    {
195 60
        $res = base64_decode($this->document, true);
196 60
        if (false === $res || $this->document !== base64_encode($res)) {
197 3
            throw new InvalidArgumentException('The path data is invalid');
198
        }
199 57
    }
200
201
    /**
202
     * Retrieves the data string.
203
     *
204
     * Retrieves the data part of the path. If no data part is provided return
205
     * a empty string
206
     *
207
     * @return string
208
     */
209 18
    public function getData()
210
    {
211 18
        return $this->document;
212
    }
213
214
    /**
215
     * Tells whether the data is binary safe encoded
216
     *
217
     * @return bool
218
     */
219 27
    public function isBinaryData()
220
    {
221 27
        return $this->isBinaryData;
222
    }
223
224
    /**
225
     * Retrieve the data mime type associated to the URI.
226
     *
227
     * If no mimetype is present, this method MUST return the default mimetype 'text/plain'.
228
     *
229
     * @see http://tools.ietf.org/html/rfc2397#section-2
230
     *
231
     * @return string The URI scheme.
232
     */
233 30
    public function getMimeType()
234
    {
235 30
        return $this->mimetype;
236
    }
237
238
    /**
239
     * Retrieve the parameters associated with the Mime Type of the URI.
240
     *
241
     * If no parameters is present, this method MUST return the default parameter 'charset=US-ASCII'.
242
     *
243
     * @see http://tools.ietf.org/html/rfc2397#section-2
244
     *
245
     * @return string The URI scheme.
246
     */
247 95
    public function getParameters()
248
    {
249 95
        return implode(';', $this->parameters);
250
    }
251
252
    /**
253
     * Retrieve the mediatype associated with the URI.
254
     *
255
     * If no mediatype is present, this method MUST return the default parameter 'text/plain;charset=US-ASCII'.
256
     *
257
     * @see http://tools.ietf.org/html/rfc2397#section-3
258
     *
259
     * @return string The URI scheme.
260
     */
261 15
    public function getMediaType()
262
    {
263 15
        return $this->getMimeType().';'.$this->getParameters();
264
    }
265
266
    /**
267
     * Save the data to a specific file
268
     *
269
     * @param string $path The path to the file where to save the data
270
     * @param string $mode The mode parameter specifies the type of access you require to the stream.
271
     *
272
     * @throws RuntimeException if the path is not reachable
273
     *
274
     * @return SplFileObject
275
     */
276 9
    public function save($path, $mode = 'w')
277
    {
278 9
        $file = new SplFileObject($path, $mode);
279 6
        $data = $this->isBinaryData ? base64_decode($this->document) : rawurldecode($this->document);
280 6
        $file->fwrite($data);
281
282 6
        return $file;
283
    }
284
285
    /**
286
     * Returns the instance string representation
287
     * with its optional URI delimiters
288
     *
289
     * @return string
290
     */
291 57
    public function getUriComponent()
292
    {
293 57
        return $this->__toString();
294
    }
295
296
    /**
297
     * Returns the instance string representation; If the
298
     * instance is not defined an empty string is returned
299
     *
300
     * @return string
301
     */
302 71
    public function __toString()
303
    {
304 71
        return $this->format(
305 71
            $this->mimetype,
306 71
            $this->getParameters(),
307 71
            $this->isBinaryData,
308 71
            $this->document
309 47
        );
310
    }
311
312
    /**
313
     * Format the DataURI string
314
     *
315
     * @param string $mimetype
316
     * @param string $parameters
317
     * @param bool   $isBinaryData
318
     * @param string $data
319
     *
320
     * @return string
321
     */
322 104
    protected static function format($mimetype, $parameters, $isBinaryData, $data)
323
    {
324 104
        if ('' != $parameters) {
325 86
            $parameters = ';'.$parameters;
326 57
        }
327
328 104
        if ($isBinaryData) {
329 60
            $parameters .= ';'.static::BINARY_PARAMETER;
330 40
        }
331
332 104
        return static::encodePath($mimetype.$parameters.','.$data);
333
    }
334
335
    /**
336
     * Returns an instance where the data part is base64 encoded
337
     *
338
     * This method MUST retain the state of the current instance, and return
339
     * an instance where the data part is base64 encoded
340
     *
341
     * @return static
342
     */
343 12
    public function toBinary()
344
    {
345 12
        if ($this->isBinaryData) {
346 3
            return $this;
347
        }
348
349 9
        return new static($this->format(
350 9
            $this->mimetype,
351 9
            $this->getParameters(),
352 9
            !$this->isBinaryData,
353 9
            base64_encode(rawurldecode($this->document))
354 6
        ));
355
    }
356
357
    /**
358
     * Returns an instance where the data part is url encoded following RFC3986 rules
359
     *
360
     * This method MUST retain the state of the current instance, and return
361
     * an instance where the data part is url encoded
362
     *
363
     * @return static
364
     */
365 12
    public function toAscii()
366
    {
367 12
        if (!$this->isBinaryData) {
368 3
            return $this;
369
        }
370
371 9
        return new static($this->format(
372 9
            $this->mimetype,
373 9
            $this->getParameters(),
374 9
            !$this->isBinaryData,
375 9
            rawurlencode(base64_decode($this->document))
376 6
        ));
377
    }
378
379
    /**
380
     * Return an instance with the specified mediatype parameters.
381
     *
382
     * This method MUST retain the state of the current instance, and return
383
     * an instance that contains the specified mediatype parameters.
384
     *
385
     * Users must provide encoded characters.
386
     *
387
     * An empty parameters value is equivalent to removing the parameter.
388
     *
389
     * @param string $parameters The mediatype parameters to use with the new instance.
390
     *
391
     * @throws InvalidArgumentException for invalid query strings.
392
     *
393
     * @return static A new instance with the specified mediatype parameters.
394
     */
395 21
    public function withParameters($parameters)
396
    {
397 21
        if ($parameters == $this->getParameters()) {
398 3
            return $this;
399
        }
400
401 18
        if (preg_match(',(;|^)'.static::BINARY_PARAMETER.'$,', $parameters)) {
402 3
            throw new InvalidArgumentException('The parameter data is invalid');
403
        }
404
405 15
        return new static($this->format(
406 15
            $this->mimetype,
407 10
            $parameters,
408 15
            $this->isBinaryData,
409 15
            $this->document
410 10
        ));
411
    }
412
413
    /**
414
     * Create a new instance from a file path
415
     *
416
     * @param string $path
417
     *
418
     * @throws RuntimeException If the File is not readable
419
     *
420
     * @return static
421
     */
422 54
    public static function createFromPath($path)
423
    {
424 54
        if (!is_readable($path)) {
425 18
            throw new RuntimeException(sprintf('The specified file `%s` is not readable', $path));
426
        }
427
428 36
        return new static(static::format(
429 36
            str_replace(' ', '', (new \finfo(FILEINFO_MIME))->file($path)),
430 36
            '',
431 36
            true,
432 24
            base64_encode(file_get_contents($path))
433 24
        ));
434
    }
435
436
    /**
437
     * @inheritdoc
438
     */
439 3
    public static function __set_state(array $properties)
440
    {
441 3
        return new static(static::format(
442 3
            $properties['mimetype'],
443 3
            implode(';', $properties['parameters']),
444 3
            $properties['isBinaryData'],
445 3
            $properties['document']
446 2
        ));
447
    }
448
}
449