Completed
Push — master ( 579725...36331d )
by Renato
07:19
created

src/VCR/Util/StreamProcessor.php (5 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace VCR\Util;
4
5
use VCR\Configuration;
6
use VCR\CodeTransform\AbstractCodeTransform;
7
8
/**
9
 * Implementation adapted from:
10
 * https://github.com/antecedent/patchwork/blob/418a9aae80ca3228d6763a2dc6d9a30ade7a4e7e/lib/Preprocessor/Stream.php
11
 *
12
 * @author     Ignas Rudaitis <[email protected]>
13
 * @author     Adrian Philipp <[email protected]>
14
 * @copyright  2010-2013 Ignas Rudaitis
15
 * @license    http://www.opensource.org/licenses/mit-license.html
16
 * @link       http://antecedent.github.com/patchwork
17
 */
18
class StreamProcessor
19
{
20
    /**
21
     * Constant for a stream which was opened while including a file.
22
     */
23
    const STREAM_OPEN_FOR_INCLUDE = 128;
24
25
    /**
26
     * Stream protocol which is used when registering this wrapper.
27
     */
28
    const PROTOCOL = 'file';
29
30
    /**
31
     * @var Configuration
32
     */
33
    protected static $configuration;
34
35
    /**
36
     * @var AbstractCodeTransform[] $codeTransformers Transformers which have been appended to this stream processor.
37
     */
38
    protected static $codeTransformers = array();
39
40
    /**
41
     * @var resource Resource for the currently opened file.
42
     */
43
    protected $resource;
44
45
    /**
46
     * @link http://www.php.net/manual/en/class.streamwrapper.php#streamwrapper.props.context
47
     * @var resource The current context, or NULL if no context was passed to the caller function.
48
     */
49
    public $context;
50
51
    /**
52
     * @var bool
53
     */
54
    protected $isIntercepting = false;
55
56
    /**
57
     *
58
     * @param Configuration $configuration
59
     */
60 396
    public function __construct(Configuration $configuration = null)
61
    {
62 396
        if ($configuration) {
63 137
            static::$configuration = $configuration;
64 97
        }
65 396
    }
66
67
    /**
68
     * Registers current class as the PHP file stream wrapper.
69
     *
70
     * @return void
71
     */
72 402
    public function intercept()
73
    {
74 402
        if (!$this->isIntercepting) {
75 388
            stream_wrapper_unregister(self::PROTOCOL);
76 388
            $this->isIntercepting = stream_wrapper_register(self::PROTOCOL, __CLASS__);
77 280
        }
78 402
    }
79
80
    /**
81
     * Restores the original file stream wrapper status.
82
     *
83
     * @return void
84
     */
85 298
    public function restore()
86
    {
87 298
        stream_wrapper_restore(self::PROTOCOL);
88 298
    }
89
90
    /**
91
     * Determines that the provided url is member of a url whitelist.
92
     *
93
     * @param string $uri
94
     *
95
     * @return bool True if the specified url is whitelisted, false otherwise.
96
     */
97 186
    protected function isWhitelisted($uri)
98
    {
99 186
        $whiteList = static::$configuration->getWhiteList();
100
101 186
        if (empty($whiteList)) {
102 186
            return true;
103
        }
104
105
        $uri = $this->normalizePath($uri);
106
107
        foreach ($whiteList as $path) {
108
            if (strpos($uri, $path) !== false) {
109
                return true;
110
            }
111
        }
112
113
        return false;
114
    }
115
116
    /**
117
     * Determines that the provided url is member of a url blacklist.
118
     *
119
     * @param string $uri
120
     *
121
     * @return bool True if the provided url is blacklisted, false otherwise.
122
     */
123 186
    protected function isBlacklisted($uri)
124
    {
125 186
        $uri = $this->normalizePath($uri);
126
127 186
        foreach (static::$configuration->getBlackList() as $path) {
128 186
            if (strpos($uri, $path) !== false) {
129 52
                return true;
130
            }
131 138
        }
132
133 181
        return false;
134
    }
135
136
    /**
137
     * Determines that the provided uri leads to a PHP file.
138
     *
139
     * @param string $uri
140
     *
141
     * @return bool
142
     */
143 181
    protected function isPhpFile($uri)
144
    {
145 181
        return pathinfo($uri, PATHINFO_EXTENSION) === 'php';
146
    }
147
148
    /**
149
     *
150
     * @param string $uri
151
     *
152
     * @return bool
153
     */
154 186
    protected function shouldProcess($uri)
155
    {
156 186
        return $this->isWhitelisted($uri) && !$this->isBlacklisted($uri) && $this->isPhpFile($uri);
157
    }
158
159
    /**
160
     * Opens a stream and attaches registered filters.
161
     *
162
     * @param  string  $path       Specifies the URL that was passed to the original function.
163
     * @param  string  $mode       The mode used to open the file, as detailed for fopen().
164
     * @param  integer $options    Holds additional flags set by the streams API.
165
     *                             It can hold one or more of the following values OR'd together.
166
     * @param  string  $openedPath If the path is opened successfully, and STREAM_USE_PATH is set in options,
167
     *                             opened_path should be set to the full path of the file/resource that was
168
     *                             actually opened.
169
     *
170
     * @return boolean Returns TRUE on success or FALSE on failure.
171
     */
172 256
    public function stream_open($path, $mode, $options, &$openedPath)
0 ignored issues
show
The parameter $openedPath is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
173
    {
174 256
        $this->restore();
175
176 256
        if (isset($this->context)) {
177 95
            $this->resource = fopen($path, $mode, $options & STREAM_USE_PATH, $this->context);
178 70
        } else {
179 207
            $this->resource = fopen($path, $mode, $options & STREAM_USE_PATH);
180
        }
181
182 256
        if ($options & self::STREAM_OPEN_FOR_INCLUDE && $this->shouldProcess($path)) {
183 188
            $this->appendFiltersToStream($this->resource);
184 139
        }
185
186 256
        $this->intercept();
187
188 256
        return $this->resource !== false;
189
    }
190
191
    /**
192
     * Close an resource.
193
     *
194
     * @link http://www.php.net/manual/en/streamwrapper.stream-close.php
195
     *
196
     * @return boolean
197
     */
198 242
    public function stream_close()
199
    {
200 242
        return fclose($this->resource);
201
    }
202
203
    /**
204
     * Tests for end-of-file on a file pointer.
205
     *
206
     * @link http://www.php.net/manual/en/streamwrapper.stream-eof.php
207
     *
208
     * @return boolean Should return TRUE if the read/write position is at the end of the stream
209
     *                 and if no more data is available to be read, or FALSE otherwise.
210
     */
211 228
    public function stream_eof()
212
    {
213 228
        return feof($this->resource);
214
    }
215
216
    /**
217
     * Flushes the output.
218
     *
219
     * @link http://www.php.net/manual/en/streamwrapper.stream-flush.php
220
     *
221
     * @return boolean
222
     */
223 165
    public function stream_flush()
224
    {
225 165
        return fflush($this->resource);
226
    }
227
228
    /**
229
     * Read from stream.
230
     *
231
     * @link http://www.php.net/manual/en/streamwrapper.stream-read.php
232
     * @param  int $count How many bytes of data from the current position should be returned.
233
     *
234
     * @return string If there are less than count bytes available, return as many as are available.
235
     *                If no more data is available, return either FALSE or an empty string.
236
     */
237 228
    public function stream_read($count)
238
    {
239 228
        return fread($this->resource, $count);
240
    }
241
242
    /**
243
     * Seeks to specific location in a stream.
244
     *
245
     * @param  integer $offset The stream offset to seek to.
246
     * @param  integer $whence Possible values:
247
     *                         SEEK_SET - Set position equal to offset bytes.
248
     *                         SEEK_CUR - Set position to current location plus offset.
249
     *                         SEEK_END - Set position to end-of-file plus offset.
250
     * @return boolean Return TRUE if the position was updated, FALSE otherwise.
251
     */
252 28
    public function stream_seek($offset, $whence = SEEK_SET)
253
    {
254 28
        return fseek($this->resource, $offset, $whence) === 0;
255
    }
256
257
    /**
258
     * Retrieve information about a file resource.
259
     *
260
     * @link http://www.php.net/manual/en/streamwrapper.stream-stat.php
261
     *
262
     * @return array See stat().
263
     */
264 212
    public function stream_stat()
265
    {
266 212
        return fstat($this->resource);
267
    }
268
269
    /**
270
     * Retrieve the current position of a stream.
271
     *
272
     * This method is called in response to fseek() to determine the current position.
273
     *
274
     * @link http://www.php.net/manual/en/streamwrapper.stream-tell.php
275
     *
276
     * @return integer Should return the current position of the stream.
277
     */
278 28
    public function stream_tell()
279
    {
280 28
        return ftell($this->resource);
281
    }
282
283
    /**
284
     * Retrieve information about a file.
285
     *
286
     * @link http://www.php.net/manual/en/streamwrapper.url-stat.php
287
     *
288
     * @param  string  $path  The file path or URL to stat.
289
     * @param  integer $flags Holds additional flags set by the streams API.
290
     *
291
     * @return integer        Should return as many elements as stat() does.
292
     */
293 123
    public function url_stat($path, $flags)
294
    {
295 123
        $this->restore();
296 123
        if ($flags & STREAM_URL_STAT_QUIET) {
297 109
            set_error_handler(function () {
298
                // Use native error handler
299 51
                return false;
300 109
            });
301 109
            $result = @stat($path);
302 109
            restore_error_handler();
303 80
        } else {
304 14
            $result = stat($path);
305
        }
306 116
        $this->intercept();
307
308 116
        return $result;
309
    }
310
311
    /**
312
     * Close directory handle.
313
     *
314
     * @link http://www.php.net/manual/en/streamwrapper.dir-closedir.php
315
     *
316
     * @return boolean Returns TRUE on success or FALSE on failure.
317
     */
318 7
    public function dir_closedir()
319
    {
320 7
        closedir($this->resource);
321
322 7
        return true;
323
    }
324
325
    /**
326
     * Open directory handle.
327
     *
328
     * @link http://www.php.net/manual/en/streamwrapper.dir-opendir.php
329
     *
330
     * @param  string $path The file path or URL to stat.
331
     *
332
     * @return boolean Returns TRUE on success or FALSE on failure.
333
     */
334 14
    public function dir_opendir($path)
335
    {
336 14
        $this->restore();
337 14
        if (isset($this->context)) {
338
            $this->resource = opendir($path, $this->context);
339
        } else {
340 14
            $this->resource = opendir($path);
341
        }
342 14
        $this->intercept();
343
344 14
        return $this->resource !== false;
345
    }
346
347
    /**
348
     * Read entry from directory handle.
349
     *
350
     * @link http://www.php.net/manual/en/streamwrapper.dir-readdir.php
351
     *
352
     * @return mixed Should return string representing the next filename, or FALSE if there is no next file.
353
     */
354
    public function dir_readdir()
355
    {
356
        return readdir($this->resource);
357
    }
358
359
    /**
360
     * Rewind directory handle.
361
     *
362
     * @link http://www.php.net/manual/en/streamwrapper.dir-rewinddir.php
363
     *
364
     * @return boolean Returns TRUE on success or FALSE on failure.
365
     */
366
    public function dir_rewinddir()
367
    {
368
        rewinddir($this->resource);
369
370
        return true;
371
    }
372
373
    /**
374
     * Create a directory.
375
     *
376
     * @link http://www.php.net/manual/en/streamwrapper.mkdir.php
377
     *
378
     * @param  string  $path       Directory which should be created.
379
     * @param  int     $mode       The value passed to mkdir().
380
     * @param  integer $options    A bitwise mask of values, such as STREAM_MKDIR_RECURSIVE.
381
     *
382
     * @return boolean  Returns TRUE on success or FALSE on failure.
383
     */
384 14
    public function mkdir($path, $mode, $options)
385
    {
386 14
        $this->restore();
387 14
        if (isset($this->context)) {
388 14
            $result = mkdir($path, $mode, $options, $this->context);
389 10
        } else {
390 14
            $result = mkdir($path, $mode, $options);
391
        }
392 14
        $this->intercept();
393
394 14
        return $result;
395
    }
396
397
    /**
398
     * Renames a file or directory.
399
     *
400
     * @link http://www.php.net/manual/en/streamwrapper.rename.php
401
     *
402
     * @param  string $path_from The URL to the current file.
403
     * @param  string $path_to   The URL which the path_from should be renamed to.
404
     *
405
     * @return boolean Returns TRUE on success or FALSE on failure.
406
     */
407 7 View Code Duplication
    public function rename($path_from, $path_to)
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
408
    {
409 7
        $this->restore();
410 7
        if (isset($this->context)) {
411 7
            $result = rename($path_from, $path_to, $this->context);
412 5
        } else {
413 7
            $result = rename($path_from, $path_to);
414
        }
415 7
        $this->intercept();
416
417 7
        return $result;
418
    }
419
420
    /**
421
     * Removes a directory
422
     *
423
     * @link http://www.php.net/manual/en/streamwrapper.rmdir.php
424
     *
425
     * @param  string $path The directory URL which should be removed.
426
     *
427
     * @return boolean Returns TRUE on success or FALSE on failure.
428
     */
429 14 View Code Duplication
    public function rmdir($path)
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
430
    {
431 14
        $this->restore();
432 14
        if (isset($this->context)) {
433 14
            $result = rmdir($path, $this->context);
434 10
        } else {
435 14
            $result = rmdir($path);
436
        }
437 14
        $this->intercept();
438
439 14
        return $result;
440
    }
441
442
    /**
443
     * Retrieve the underlaying resource.
444
     *
445
     * @link http://www.php.net/manual/en/streamwrapper.stream-cast.php
446
     *
447
     * @param  integer $cast_as Can be STREAM_CAST_FOR_SELECT when stream_select() is calling stream_cast() or
448
     *                          STREAM_CAST_AS_STREAM when stream_cast() is called for other uses.
449
     * @return resource         Should return the underlying stream resource used by the wrapper, or FALSE.
450
     */
451
    public function stream_cast($cast_as)
0 ignored issues
show
The parameter $cast_as is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
452
    {
453
        return $this->resource;
454
    }
455
456
    /**
457
     * Advisory file locking.
458
     *
459
     * @link http://www.php.net/manual/en/streamwrapper.stream-lock.php
460
     *
461
     * @param  integer $operation One of the operation constantes.
462
     *
463
     * @return boolean Returns TRUE on success or FALSE on failure.
464
     */
465 7
    public function stream_lock($operation)
466
    {
467 7
        $operation = ($operation === 0 ? LOCK_EX : $operation);
468 7
        return flock($this->resource, $operation);
469
    }
470
471
    /**
472
     * Change stream options.
473
     *
474
     * @codeCoverageIgnore
475
     *
476
     * @param  int $option One of STREAM_OPTION_BLOCKING, STREAM_OPTION_READ_TIMEOUT, STREAM_OPTION_WRITE_BUFFER.
477
     * @param  int $arg1   Depending on option.
478
     * @param  int $arg2   Depending on option.
479
     *
480
     * @return boolean Returns TRUE on success or FALSE on failure. If option is not implemented,
481
     *                 FALSE should be returned.
482
     */
483
    public function stream_set_option($option, $arg1, $arg2)
484
    {
485
        switch ($option) {
486
            case STREAM_OPTION_BLOCKING:
487
                return stream_set_blocking($this->resource, $arg1);
488
            case STREAM_OPTION_READ_TIMEOUT:
489
                return stream_set_timeout($this->resource, $arg1, $arg2);
490
            case STREAM_OPTION_WRITE_BUFFER:
491
                return stream_set_write_buffer($this->resource, $arg1);
492
            case STREAM_OPTION_READ_BUFFER:
493
                return stream_set_read_buffer($this->resource, $arg1);
494
            case STREAM_OPTION_CHUNK_SIZE:
495
                return stream_set_chunk_size($this->resource, $arg1);
496
        }
497
    }
498
499
    /**
500
     * Write to stream.
501
     *
502
     * @throws \BadMethodCallException If called, because this method is not applicable for this stream.
503
     * @link http://www.php.net/manual/en/streamwrapper.stream-write.php
504
     *
505
     * @param  string $data Should be stored into the underlying stream.
506
     *
507
     * @return int
508
     */
509 7
    public function stream_write($data)
510
    {
511 7
        return fwrite($this->resource, $data);
512
    }
513
514
    /**
515
     * Delete a file.
516
     *
517
     * @link http://www.php.net/manual/en/streamwrapper.unlink.php
518
     *
519
     * @param  string $path The file URL which should be deleted.
520
     *
521
     * @return boolean Returns TRUE on success or FALSE on failure.
522
     */
523 12 View Code Duplication
    public function unlink($path)
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
524
    {
525 12
        $this->restore();
526 12
        if (isset($this->context)) {
527 12
            $result = unlink($path, $this->context);
528 8
        } else {
529 5
            $result = unlink($path);
530
        }
531 12
        $this->intercept();
532
533 12
        return $result;
534
    }
535
536
    /**
537
     * Change stream options.
538
     *
539
     * @link http://www.php.net/manual/en/streamwrapper.stream-metadata.php
540
     * @param  string  $path   The file path or URL to set metadata.
541
     * @param  integer $option One of the stream options.
542
     * @param  mixed   $value  Value depending on the option.
543
     *
544
     * @return boolean Returns TRUE on success or FALSE on failure.
545
     */
546 5
    public function stream_metadata($path, $option, $value)
547
    {
548 5
        $this->restore();
549 5
        $result = null;
550
551
        switch ($option) {
552 5
            case STREAM_META_TOUCH:
553 5
                if (empty($value)) {
554 5
                    $result = touch($path);
555 3
                } else {
556 5
                    $result = touch($path, $value[0], $value[1]);
557
                }
558 5
                break;
559 5
            case STREAM_META_OWNER_NAME:
560 5
            case STREAM_META_OWNER:
561 5
                $result = chown($path, $value);
562 5
                break;
563 5
            case STREAM_META_GROUP_NAME:
564 5
            case STREAM_META_GROUP:
565 5
                $result = chgrp($path, $value);
566 5
                break;
567 5
            case STREAM_META_ACCESS:
568 5
                $result = chmod($path, $value);
569 5
                break;
570
        }
571 5
        $this->intercept();
572
573 5
        return $result;
574
    }
575
576
    /**
577
     * Truncate stream.
578
     *
579
     * @link http://www.php.net/manual/en/streamwrapper.stream-truncate.php
580
     *
581
     * @param  integer $new_size The new size.
582
     *
583
     * @return boolean Returns TRUE on success or FALSE on failure.
584
     */
585 5
    public function stream_truncate($new_size)
586
    {
587 5
        return ftruncate($this->resource, $new_size);
588
    }
589
590
    /**
591
     * Adds code transformer to the stream processor.
592
     *
593
     * @param AbstractCodeTransform $codeTransformer
594
     *
595
     * @return void
596
     */
597 151
    public function appendCodeTransformer(AbstractCodeTransform $codeTransformer)
598
    {
599 151
        static::$codeTransformers[$codeTransformer::NAME] = $codeTransformer;
600 151
    }
601
602
    /**
603
     * Removes a code transformer from the stream processor.
604
     *
605
     * @param AbstractCodeTransform $codeTransformer
606
     *
607
     * @return void
608
     */
609
    public function detachCodeTransformer(AbstractCodeTransform $codeTransformer)
610
    {
611
        if (!empty(static::$codeTransformers[$codeTransformer::NAME])) {
612
            unset(static::$codeTransformers[$codeTransformer::NAME]);
613
        }
614
    }
615
616
    /**
617
     * Appends the current set of php_user_filter to the provided stream.
618
     *
619
     * @param resource $stream
620
     */
621 181
    protected function appendFiltersToStream($stream)
622
    {
623 181
        foreach (static::$codeTransformers as $codeTransformer) {
624 181
            stream_filter_append($stream, $codeTransformer::NAME, STREAM_FILTER_READ);
625 134
        }
626 181
    }
627
628
    /**
629
     * Normalizes the path, to always use the slash as directory separator.
630
     *
631
     * @param string $path
632
     *
633
     * @return string
634
     */
635 186
    private function normalizePath($path)
636
    {
637 186
        if (DIRECTORY_SEPARATOR !== '/') {
638
            return str_replace(DIRECTORY_SEPARATOR, '/', $path);
639
        }
640
641 186
        return $path;
642
    }
643
}
644