StreamResource   F
last analyzed

Complexity

Total Complexity 75

Size/Duplication

Total Lines 932
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 94.44%

Importance

Changes 0
Metric Value
dl 0
loc 932
ccs 221
cts 234
cp 0.9444
rs 2.068
c 0
b 0
f 0
wmc 75
lcom 1
cbo 2

41 Methods

Rating   Name   Duplication   Size   Complexity  
A __invoke() 0 4 1
A __construct() 0 26 3
A __destruct() 0 4 1
A connect() 0 26 3
A disconnect() 0 8 2
A setUri() 0 22 5
A setMode() 0 22 3
A setBaseMode() 0 10 2
A setIsPlus() 0 7 2
A setIsBinary() 0 9 2
A setIsText() 0 9 2
A setUseIncludePath() 0 7 1
A setContext() 0 13 4
A setContextResource() 0 16 4
A setContextOptions() 0 15 3
A setContextParams() 0 10 2
A getContextOptions() 0 10 3
A getContextParams() 0 4 1
A getContext() 0 12 2
A getHandle() 0 8 3
A isConnected() 0 4 1
A getUri() 0 4 1
A getMode() 0 9 1
A isBinary() 0 4 1
A isText() 0 4 1
A isLazy() 0 4 1
A getUseIncludePath() 0 4 1
A isReadable() 0 4 1
A isWritable() 0 4 1
A isCursorPositionedAtBeginning() 0 4 1
A isCursorPositionedAtEnd() 0 4 1
A isTruncated() 0 4 1
A attemptsFileCreation() 0 4 1
A rejectsExistingFiles() 0 4 1
A appendsWriteOps() 0 4 1
A initWithResource() 0 18 2
A updateAccess() 0 7 1
A setLazy() 0 9 2
A updateContext() 0 16 2
A assertNotConnected() 0 6 2
A assertValidWrapper() 0 6 2

How to fix   Complexity   

Complex Class

Complex classes like StreamResource often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use StreamResource, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * CSVelte: Slender, elegant CSV for PHP
5
 * Inspired by Python's CSV module and Frictionless Data and the W3C's CSV
6
 * standardization efforts, CSVelte was written in an effort to take all the
7
 * suck out of working with CSV.
8
 *
9
 * @version   {version}
10
 * @copyright Copyright (c) 2016 Luke Visinoni <[email protected]>
11
 * @author    Luke Visinoni <[email protected]>
12
 * @license   https://github.com/deni-zen/csvelte/blob/master/LICENSE The MIT License (MIT)
13
 */
14
namespace CSVelte\IO;
15
16
use CSVelte\Contract\Streamable;
17
use CSVelte\Exception\IOException;
18
use InvalidArgumentException;
19
20
/**
21
 * Stream Resource.
22
 *
23
 * Represents a stream resource connection. May be open or closed. This allows
24
 * me to provide a nice, clean, easy-to-use interface for opening stream
25
 * resources in a particular mode as well as to lazy-open a stream.
26
 *
27
 * @package    CSVelte
28
 * @subpackage CSVelte\IO
29
 *
30
 * @copyright  (c) 2016, Luke Visinoni <[email protected]>
31
 * @author     Luke Visinoni <[email protected]>
32
 *
33
 * @since      v0.2.1
34
 */
35
class StreamResource
36
{
37
    /**
38
     * Available base access modes.
39
     *
40
     * @var string base access mode must be one of these letters
41
     */
42
    protected static $bases = 'rwaxc';
43
44
    /**
45
     * Hash of readable/writable stream open mode types.
46
     *
47
     * Mercilessly stolen from:
48
     * https://github.com/guzzle/streams/blob/master/src/Stream.php
49
     *
50
     * My kudos and sincere thanks go out to Michael Dowling and Graham Campbell
51
     * of the guzzle/streams PHP package. Thanks for the inspiration (in some cases)
52
     * and the not suing me for outright theft (in this case).
53
     *
54
     * @var array Hash of readable and writable stream types
55
     *
56
     * @todo I think I can get rid of this by simply checking whether base is a
57
     *     particular letter OR plus is present... try it
58
     * @todo Why are x and c (alone) not even on either of these lists?
59
     *       I just figured out why... readable and writable default to false. So
60
     *       only modes that change that default behavior are listed here
61
     */
62
    protected static $readWriteHash = [
63
        'read' => [
64
            'r'   => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true,
65
            'rb'  => true, 'w+b' => true, 'r+b' => true, 'x+b' => true,
66
            'c+b' => true, 'rt' => true, 'w+t' => true, 'r+t' => true,
67
            'x+t' => true, 'c+t' => true, 'a+' => true,
68
        ],
69
        'write' => [
70
            'w'   => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true,
71
            'c+'  => true, 'wb' => true, 'w+b' => true, 'r+b' => true,
72
            'x+b' => true, 'c+b' => true, 'w+t' => true, 'r+t' => true,
73
            'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true,
74
        ],
75
    ];
76
77
    /**
78
     * Stream URI.
79
     *
80
     * Contains the stream URI to connect to.
81
     *
82
     * @var string The stream uri
83
     */
84
    protected $uri;
85
86
    /**
87
     * Stream resource handle.
88
     *
89
     * Contains the underlying stream resource handle (if there is one).
90
     * Otherwise it will be null.
91
     *
92
     * @var resource The stream resource handle
93
     */
94
    protected $conn;
95
96
    /**
97
     * Lazy open switch.
98
     *
99
     * Determines whether the actual fopen for this resource should be delayed
100
     * until an I/O operation is performed.
101
     *
102
     * @var bool True if connection is lazy
103
     */
104
    protected $lazy;
105
106
    /**
107
     * Extra context to open the resource with.
108
     *
109
     * An associative array of context options and parameters.
110
     *
111
     * @var array An associative array of stream context options and params
112
     *
113
     * @see http://php.net/manual/en/stream.contexts.php
114
     */
115
    protected $context = [
116
        'options' => [],
117
        'params'  => [],
118
    ];
119
120
    /**
121
     * Context resource handle.
122
     *
123
     * Holds a context resource handle object for $this->context
124
     *
125
     * @var resource The context resource handle
126
     */
127
    protected $crh;
128
129
    /**
130
     * Should fopen use include path?
131
     *
132
     * @var bool True if fopen should use the include path to find potential files
133
     */
134
    protected $useIncludePath;
135
136
    /**
137
     * Base open mode.
138
     *
139
     * @var string A single character for base open mode (r, w, a, x or c)
140
     */
141
    protected $base = '';
142
143
    /**
144
     * Plus reading or plus writing.
145
     *
146
     * @var string Either a plus or an empty string
147
     */
148
    protected $plus = '';
149
150
    /**
151
     * Binary or text flag.
152
     *
153
     * @var string Either "b" or "t" for binary or text
154
     */
155
    protected $flag = '';
156
157
    /**
158
     * Does access mode string indicate readability?
159
     *
160
     * @var bool Whether access mode indicates readability
161
     */
162
    protected $readable = false;
163
164
    /**
165
     * Does access mode string indicate writability.
166
     *
167
     * @var bool Whether access mode indicates writability
168
     */
169
    protected $writable = false;
170
171
    /**
172
     * Resource constructor.
173
     *
174
     * Instantiates a stream resource. If lazy is set to true, the connection
175
     * is delayed until the first call to getResource().
176
     *
177
     * @param string|resource|object $uri              The URI to connect to OR a stream resource handle
178
     * @param string                 $mode             The connection mode
179
     * @param bool                   $lazy             Whether connection should be deferred until an I/O
180
     *                                                 operation is requested (such as read or write) on the attached stream
181
     * @param bool|null              $use_include_path
182
     * @param array|null             $context_options
183
     * @param array|null             $context_params
184
     *
185
     * @todo Does stream_get_meta_data belong in Stream or Resource?
186
     */
187 103
    public function __construct(
188
        $uri,
189
        $mode = null,
190
        $lazy = null,
191
        $use_include_path = null,
192
        $context_options = null,
193
        $context_params = null
194
    ) {
195
        // first, check if we're wrapping an existing stream resource
196 103
        if (is_resource($uri)) {
197 6
            $this->initWithResource($uri);
198
199 5
            return;
200
        }
201
//            throw new InvalidArgumentException("Argument one for " . __METHOD__ . " must be a URI or a stream resource.");
202
203
        // ok we're opening a new stream resource handle
204 97
        $this->setUri($uri)
0 ignored issues
show
Bug introduced by Luke Visinoni
It seems like $uri defined by parameter $uri on line 188 can also be of type object or resource; however, CSVelte\IO\StreamResource::setUri() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
205 95
             ->setMode($mode)
206 94
             ->setLazy($lazy)
207 94
             ->setUseIncludePath($use_include_path)
208 94
             ->setContext($context_options, $context_params);
209 94
        if (!$this->isLazy()) {
210 4
            $this->connect();
211 3
        }
212 93
    }
213
214
    /**
215
     * Class destructor.
216
     */
217 95
    public function __destruct()
218
    {
219 95
        $this->disconnect();
220 95
    }
221
222
    /**
223
     * Invoke magic method.
224
     *
225
     * Creates and returns a Stream object for this resource
226
     *
227
     * @return Streamable A stream for this resource
228
     */
229 7
    public function __invoke()
230
    {
231 7
        return new Stream($this);
232
    }
233
234
    /**
235
     * Connect (open connection) to file/stream.
236
     *
237
     * File open is (by default) delayed until the user explicitly calls connect()
238
     * or they request the resource handle with getHandle().
239
     *
240
     * @throws \CSVelte\Exception\IOException if connection fails
241
     *
242
     * @return bool True if connection was successful
243
     */
244 82
    public function connect()
245
    {
246 82
        if (!$this->isConnected()) {
247 82
            $e          = null;
248 82
            $errhandler = function () use (&$e) {
249 4
                $e = new IOException(sprintf(
250 4
                    'Could not open connection for %s using mode %s',
251 4
                    $this->getUri(),
252 4
                    $this->getMode()
253 4
                ), IOException::ERR_STREAM_CONNECTION_FAILED);
254 82
            };
255 82
            set_error_handler($errhandler->bindTo($this));
256 82
            $this->conn = fopen(
257 82
                $this->getUri(),
258 82
                $this->getMode(),
259 82
                $this->getUseIncludePath(),
260 82
                $this->getContext()
261 82
            );
262 82
            restore_error_handler();
263 82
            if ($e) {
264 4
                throw $e;
265
            }
266 78
        }
267
268 78
        return $this->isConnected();
269
    }
270
271
    /**
272
     * Close connection.
273
     *
274
     * Close the connection to this stream (if open).
275
     *
276
     * @return bool|null Whether close was successful, or null if already closed
277
     */
278 95
    public function disconnect()
279
    {
280 95
        if ($this->isConnected()) {
281 83
            return fclose($this->conn);
282
        }
283
        // return null if nothing to close
284 84
        return null;
285
    }
286
287
    /**
288
     * Set stream URI.
289
     *
290
     * Set the stream URI. Can only be set if the connection isn't open yet.
291
     * If you try to set the URI on an open resource, an IOException will be thrown
292
     *
293
     * @param string $uri The URI for this stream resource to open
294
     *
295
     * @throws \InvalidArgumentException      if not a valid stream uri
296
     * @throws \CSVelte\Exception\IOException if stream has already been opened
297
     *
298
     * @return $this
299
     *
300
     * @todo I'm pretty sure that the parse_url function is too restrictive. It
301
     *     will reject URIs that are perfectly valid.
302
     */
303 102
    public function setUri($uri)
304
    {
305 102
        $this->assertNotConnected(__METHOD__);
306
307 102
        if (is_object($uri) && method_exists($uri, '__toString')) {
308
            $uri = (string) $uri;
309
        }
310
311 102
        if (!is_string($uri)) {
312 2
            throw new InvalidArgumentException(sprintf(
313 2
                'Not a valid stream uri, expected "string", got: "%s"',
314 2
                gettype($uri)
315 2
            ));
316
        }
317
318 100
        $uri = (string) $uri;
319 100
        if (parse_url($uri)) {
320 100
            $this->uri = $uri;
321
322 100
            return $this;
323
        }
324
    }
325
326
    /**
327
     * Set the fopen mode.
328
     *
329
     * Thank you to GitHub user "binsoul" whose AccessMode class inspired this
330
     * Also thanks to the author(s) of Guzzle streams implementation, where the
331
     * readwritehash idea came from. Both libraries are MIT licensed, so my
332
     * merciless theft of their code is alright.
333
     *
334
     * @param string $mode A 1-3 character string determining open mode
335
     *
336
     * @throws \InvalidArgumentException      if not a valid stream access mode
337
     * @throws \CSVelte\Exception\IOException if stream has already been opened
338
     *
339
     * @return $this
340
     *
341
     * @see http://php.net/manual/en/function.fopen.php
342
     * @see https://github.com/binsoul/io-stream/blob/master/src/AccessMode.php
343
     * @see https://raw.githubusercontent.com/guzzle/streams/master/src/Stream.php
344
     *
345
     * @todo convert $mode to lower case and test it
346
     */
347 100
    public function setMode($mode = null)
348
    {
349 100
        $this->assertNotConnected(__METHOD__);
350 100
        if (is_null($mode)) {
351 53
            $mode = 'r+b';
352 53
        }
353
354 100
        $mode = substr($mode, 0, 3);
355 100
        $rest = substr($mode, 1);
356
357 100
        $base = substr($mode, 0, 1);
358 100
        $plus = (strpos($rest, '+') !== false) ? '+' : '';
359 100
        $flag = trim($rest, '+');
360
361 100
        $this->flag = '';
362 100
        $this->setBaseMode($base)
363 99
             ->setIsPlus($plus == '+')
364 99
             ->setIsText($flag == 't')
365 99
             ->setIsBinary($flag == 'b');
366
367 99
        return $this;
368
    }
369
370
    /**
371
     * Set base access mode character.
372
     *
373
     * @param string $base The base mode character (must be one of "rwaxc")
374
     *
375
     * @throws \InvalidArgumentException      If passed invalid base char
376
     * @throws \CSVelte\Exception\IOException if stream has already been opened
377
     *
378
     * @return $this
379
     */
380 100
    public function setBaseMode($base)
381
    {
382 100
        $this->assertNotConnected(__METHOD__);
383 100
        if (strpos(self::$bases, $base) === false) {
384 1
            throw new InvalidArgumentException("\"{$base}\" is not a valid base stream access mode.");
385
        }
386 99
        $this->base = $base;
387
388 99
        return $this->updateAccess();
389
    }
390
391
    /**
392
     * Set plus mode.
393
     *
394
     * @param bool $isPlus Whether base access mode should include the + sign
395
     *
396
     * @throws \CSVelte\Exception\IOException if stream has already been opened
397
     *
398
     * @return $this
399
     */
400 99
    public function setIsPlus($isPlus)
401
    {
402 99
        $this->assertNotConnected(__METHOD__);
403 99
        $this->plus = $isPlus ? '+' : '';
404
405 99
        return $this->updateAccess();
406
    }
407
408
    /**
409
     * Set binary-safe mode.
410
     *
411
     * @param bool $isBinary Whether binary safe mode or not
412
     *
413
     * @throws \CSVelte\Exception\IOException if stream has already been opened
414
     *
415
     * @return $this
416
     */
417 99
    public function setIsBinary($isBinary)
418
    {
419 99
        $this->assertNotConnected(__METHOD__);
420 99
        if ($isBinary) {
421 72
            $this->flag = 'b';
422 72
        }
423
424 99
        return $this;
425
    }
426
427
    /**
428
     * Set text mode.
429
     *
430
     * @param bool $isText Whether text mode or not
431
     *
432
     * @throws \CSVelte\Exception\IOException if stream has already been opened
433
     *
434
     * @return $this
435
     */
436 99
    public function setIsText($isText)
437
    {
438 99
        $this->assertNotConnected(__METHOD__);
439 99
        if ($isText) {
440 1
            $this->flag = 't';
441 1
        }
442
443 99
        return $this;
444
    }
445
446
    /**
447
     * Set use include path flag.
448
     *
449
     * Sets whether or not fopen should search the include path for files. Can
450
     * only be set if resource isn't open already. If called when resource is
451
     * already open an exception will be thrown.
452
     *
453
     * @param bool $use_include_path Whether to search include path for files
454
     *
455
     * @throws \CSVelte\Exception\IOException
456
     *
457
     * @return $this
458
     */
459 94
    public function setUseIncludePath($use_include_path)
460
    {
461 94
        $this->assertNotConnected(__METHOD__);
462 94
        $this->useIncludePath = (bool) $use_include_path;
463
464 94
        return $this;
465
    }
466
467
    /**
468
     * Set stream context options and params.
469
     *
470
     * Sets arrays of stream context options and params. Check out the URI below
471
     * for more on stream contexts.
472
     *
473
     * @param array|null $options Stream context options
474
     * @param array|null $params  Stream Context params
475
     *
476
     * @return $this
477
     *
478
     * @see http://php.net/manual/en/stream.contexts.php
479
     */
480 94
    public function setContext($options = null, $params = null)
481
    {
482 94
        if (is_array($options)) {
483 2
            foreach ($options as $wrap => $opts) {
484 2
                $this->setContextOptions($opts, $wrap);
485 2
            }
486 2
        }
487 94
        if (!is_null($params)) {
488 2
            $this->setContextParams($params);
489 2
        }
490
491 94
        return $this;
492
    }
493
494
    /**
495
     * Set context resource directly.
496
     *
497
     * @param resource|null $context Stream context resource to set directly
498
     *
499
     * @return $this
500
     *
501
     * @see http://php.net/manual/en/function.stream-context-create.php
502
     *
503
     * @todo Need to write a unit test for passing this method a null value
504
     */
505 80
    public function setContextResource($context)
506
    {
507 80
        if (!is_null($context)) {
508 10
            if (!is_resource($context) || get_resource_type($context) != 'stream-context') {
509 4
                throw new InvalidArgumentException(sprintf(
510 4
                    'Invalid argument for %s. Expecting resource of type "stream-context" but got: "%s"',
511 4
                    __METHOD__,
512 4
                    gettype($context)
513 4
                ));
514
            }
515
            // don't need to call updateContext() because its already a context resource
516 6
            $this->crh = $context;
517 6
        }
518
519 76
        return $this;
520
    }
521
522
    /**
523
     * Set context options.
524
     *
525
     * Sets stream context options for this stream resource.
526
     *
527
     * @param array  $options An array of stream context options
528
     * @param string $wrapper The wrapper these options belong to (if no wrapper
529
     *                        argument, then $options should be an associative array with key being
530
     *                        a wrapper name and value being its options)
531
     *
532
     * @throws \InvalidArgumentException if passed invalid options or wrapper
533
     *
534
     * @return $this
535
     *
536
     * @see http://php.net/manual/en/stream.contexts.php
537
     */
538 2
    public function setContextOptions($options, $wrapper = null)
539
    {
540 2
        if (is_array($options)) {
541 2
            if (is_null($wrapper)) {
542
                $this->context['options'] = $options;
543
            } else {
544 2
                $this->assertValidWrapper($wrapper);
545 2
                $this->context['options'][$wrapper] = $options;
546
            }
547 2
            $this->updateContext();
548
549 2
            return $this;
550
        }
551
        throw new InvalidArgumentException('Context options must be an array, got: ' . gettype($options));
552
    }
553
554
    /**
555
     * Set context params.
556
     *
557
     * Set the context params for this stream resource.
558
     *
559
     * @param array $params An array of stream resource params
560
     *
561
     * @throws \InvalidArgumentException if passed invalid params
562
     *
563
     * @return $this
564
     *
565
     * @see http://php.net/manual/en/stream.contexts.php
566
     */
567 2
    public function setContextParams($params)
568
    {
569 2
        if (is_array($params)) {
570 2
            $this->context['params'] = $params;
571 2
            $this->updateContext();
572
573 2
            return $this;
574
        }
575
        throw new InvalidArgumentException('Context parameters must be an array, got: ' . gettype($params));
576
    }
577
578
    /**
579
     * Get context options for this stream resource.
580
     *
581
     * Returns the stream context options for this stream resource. Either all
582
     * options for all wrappers, or just the options for the specified wrapper.
583
     *
584
     * @param string $wrapper If present, return options only for this wrapper
585
     *
586
     * @throws \InvalidArgumentException if the wrapper doesn't exist
587
     *
588
     * @return array Context options (either all or for specified wrapper)
589
     */
590 82
    public function getContextOptions($wrapper = null)
591
    {
592 82
        if (is_null($wrapper)) {
593 82
            return $this->context['options'];
594
        }
595
        $this->assertValidWrapper($wrapper);
596
        if (isset($this->context['options'][$wrapper])) {
597
            return $this->context['options'][$wrapper];
598
        }
599
    }
600
601
    /**
602
     * Get context params for this stream resource.
603
     *
604
     * Returns the stream context params for this stream resource.
605
     *
606
     * @return array Context params for this stream resource
607
     */
608 82
    public function getContextParams()
609
    {
610 82
        return $this->context['params'];
611
    }
612
613
    /**
614
     * Get stream context resource.
615
     *
616
     * @return resource|null The stream context resource
617
     */
618 86
    public function getContext()
619
    {
620
        // if context resource hasn't been created, create one
621 86
        if (is_null($this->crh)) {
622 82
            $this->crh = stream_context_create(
623 82
                $this->getContextOptions(),
624 82
                $this->getContextParams()
625 82
            );
626 82
        }
627
        // return context resource handle
628 86
        return $this->crh;
629
    }
630
631
    /**
632
     * Retrieve underlying stream resource handle.
633
     *
634
     * An accessor method for the underlying stream resource object. Also triggers
635
     * stream connection if in lazy open mode. Because this method may potentially
636
     * call the connect() method, it is possible that it may throw an exception
637
     * if there is some issue with opening the stream.
638
     *
639
     * @throws \CSVelte\Exception\IOException
640
     *
641
     * @return resource The underlying stream resource handle
642
     */
643 69
    public function getHandle()
644
    {
645 69
        if (!$this->isConnected() && $this->isLazy()) {
646 7
            $this->connect();
647 6
        }
648
649 68
        return $this->conn;
650
    }
651
652
    /**
653
     * Is the stream connection open?
654
     *
655
     * Tells you whether this stream resource is open or not.
656
     *
657
     * @return bool Whether the stream is open
658
     */
659 102
    public function isConnected()
660
    {
661 102
        return is_resource($this->conn);
662
    }
663
664
    /**
665
     * Get the stream URI.
666
     *
667
     * Accessor method for stream URI.
668
     *
669
     * @return string The stream URI
670
     */
671 82
    public function getUri()
672
    {
673 82
        return $this->uri;
674
    }
675
676
    /**
677
     * Get the access mode.
678
     *
679
     * Tells you what the access mode is. This is the short string of characters
680
     * that you would pass to the fopen function to tell it how to open a file/stream
681
     *
682
     * @return string The file/stream access mode
683
     *
684
     * @see http://php.net/manual/en/function.fopen.php
685
     */
686 99
    public function getMode()
687
    {
688 99
        return sprintf(
689 99
            '%s%s%s',
690 99
            $this->base,
691 99
            $this->plus,
692 99
            $this->flag
693 99
        );
694
    }
695
696
    /**
697
     * Is access mode binary-safe?
698
     *
699
     * @return bool Whether binary-safe flag is set
700
     */
701 4
    public function isBinary()
702
    {
703 4
        return $this->flag == 'b';
704
    }
705
706
    /**
707
     * Is stream connected in text mode?
708
     *
709
     * @return bool Whether text mode flag is set
710
     */
711 3
    public function isText()
712
    {
713 3
        return $this->flag == 't';
714
    }
715
716
    /**
717
     * Is this a lazy open resource?
718
     *
719
     * @return bool Whether this is a lazily-opened resource
720
     */
721 94
    public function isLazy()
722
    {
723 94
        return $this->lazy;
724
    }
725
726
    /**
727
     * Should fopen search include path?
728
     *
729
     * @return bool Whether fopen should search include path for files
730
     */
731 82
    public function getUseIncludePath()
732
    {
733 82
        return $this->useIncludePath;
734
    }
735
736
    /**
737
     * Does the access mode string indicate readability?
738
     *
739
     * Readable, in this context, only refers to the manner in which this stream
740
     * resource was opened (if it even is opened yet). It is no indicator about
741
     * whether or not the underlying stream actually supports read operations.
742
     * It simply refers to the access mode string passed to it by the user.
743
     *
744
     * @return bool Whether access mode indicates readability
745
     */
746 54
    public function isReadable()
747
    {
748 54
        return $this->readable;
749
    }
750
751
    /**
752
     * Does the access mode string indicate writability?
753
     *
754
     * Writable, in this context, only refers to the manner in which this stream
755
     * resource was opened (if it even is opened yet). It is no indicator about
756
     * whether or not the underlying stream actually supports write operations.
757
     * It simply refers to the access mode string passed to it by the user.
758
     *
759
     * @return bool Whether access mode indicates writability
760
     */
761 21
    public function isWritable()
762
    {
763 21
        return $this->writable;
764
    }
765
766
    /**
767
     * Is cursor positioned at the beginning of stream?
768
     *
769
     * Returns true if this stream resource's access mode positions the internal
770
     * cursor at the beginning of the stream.
771
     *
772
     * @return bool Whether cursor positioned at beginning of stream
773
     */
774 2
    public function isCursorPositionedAtBeginning()
775
    {
776 2
        return $this->base != 'a';
777
    }
778
779
    /**
780
     * Is cursor positioned at the end of stream?
781
     *
782
     * Returns true if this stream resource's access mode positions the internal
783
     * cursor at the end of the stream.
784
     *
785
     * @return bool Whether cursor positioned at end of stream
786
     */
787 2
    public function isCursorPositionedAtEnd()
788
    {
789 2
        return $this->base == 'a';
790
    }
791
792
    /**
793
     * Is content truncated to zero-length on opening?
794
     *
795
     * Returns true if this stream resource's access mode indicates truncation of
796
     * stream content to zero-length upon opening.
797
     *
798
     * @return bool Whether stream content is truncated on opening
799
     */
800 3
    public function isTruncated()
801
    {
802 3
        return $this->base == 'w';
803
    }
804
805
    /**
806
     * Does stream access mode indicate file creation?
807
     *
808
     * Returns true if this stream's access mode implies that PHP will attempt to
809
     * create a file if none exists.
810
     *
811
     * @return bool Whether PHP should attempt to create file at $uri
812
     */
813 1
    public function attemptsFileCreation()
814
    {
815 1
        return $this->base != 'r';
816
    }
817
818
    /**
819
     * Does stream access mode indicate the rejection of existing files?
820
     *
821
     * Returns true if this stream's access mode implies that PHP will fail to
822
     * open a file if it already exists.
823
     *
824
     * @return bool Whether PHP should attempt to create file at $uri
825
     */
826 1
    public function rejectsExistingFiles()
827
    {
828 1
        return $this->base == 'x';
829
    }
830
831
    /**
832
     * Are write operations appended to the end of the stream?
833
     *
834
     * Returns true if write operations are appended to the end of the stream
835
     * regardless of the position of the read cursor.
836
     *
837
     * @return bool Whether write operations ore always appended
838
     */
839 1
    public function appendsWriteOps()
840
    {
841 1
        return $this->base == 'w';
842
    }
843
844
    /**
845
     * Initialize resource with PHP resource variable.
846
     *
847
     * Uses a PHP resource variable to initialize this class.
848
     *
849
     * @param resource $handle The stream resource to initialize
850
     *
851
     * @return bool
852
     */
853 6
    protected function initWithResource($handle)
854
    {
855 6
        if (($resource_type = get_resource_type($handle)) != ($exp_resource_type = 'stream')) {
856 1
            throw new InvalidArgumentException(sprintf(
857 1
                'Invalid stream resource type for %s, expected "%s", got: "%s"',
858 1
                __METHOD__,
859 1
                $exp_resource_type,
860
                $resource_type
861 1
            ));
862
        }
863
        // set all this manually
864 5
        $meta = stream_get_meta_data($handle);
865 5
        $this->setUri($meta['uri'])
866 5
            ->setMode($meta['mode']);
867 5
        $this->conn = $handle;
868
869 5
        return true;
870
    }
871
872
    /**
873
     * Update access parameters.
874
     *
875
     * After changing any of the access mode parameters, this method must be
876
     * called in order for readable and writable to stay accurate.
877
     *
878
     * @return $this
879
     */
880 99
    protected function updateAccess()
881
    {
882 99
        $this->readable = isset(self::$readWriteHash['read'][$this->getMode()]);
883 99
        $this->writable = isset(self::$readWriteHash['write'][$this->getMode()]);
884
885 99
        return $this;
886
    }
887
888
    /**
889
     * Set lazy flag.
890
     *
891
     * Set the lazy flag, which tells the class whether to defer the connection
892
     * until the user specifically requests it.
893
     *
894
     * @param bool|null Whether or not to "lazily" open the stream
895
     * @param mixed $lazy
896
     *
897
     * @return $this
898
     */
899 94
    protected function setLazy($lazy)
900
    {
901 94
        if (is_null($lazy)) {
902 87
            $lazy = true;
903 87
        }
904 94
        $this->lazy               = (bool) $lazy;
905
906 94
        return $this;
907
    }
908
909
    /**
910
     * Update the stream context.
911
     *
912
     * After setting/updating stream context options and/or params, this method
913
     * must be called in order to update the stream context resource.
914
     *
915
     * @return $this
916
     */
917 2
    protected function updateContext()
918
    {
919
        // if already connected, set the options on the context resource
920
        // otherwise, it will be set at connection time
921 2
        if ($this->isConnected()) {
922
            // set options and params on existing stream resource
923 1
            stream_context_set_params(
924 1
                $this->getContext(),
925 1
                $this->getContextParams() + [
926 1
                    'options' => $this->getContextOptions(),
927
                ]
928 1
            );
929 1
        }
930
931 2
        return $this;
932
    }
933
934
    /**
935
     * Assert that stream resource is not open.
936
     *
937
     * Used internally to ensure that stream is not open, since some methods should
938
     * only be called on unopened stream resources.
939
     *
940
     * @param string $method The method that is asserting
941
     *
942
     * @throws IOException if stream is open
943
     */
944 102
    protected function assertNotConnected($method)
945
    {
946 102
        if ($this->isConnected()) {
947
            throw new IOException("Cannot perform this operation on a stream once it has already been opened: {$method}", IOException::ERR_STREAM_ALREADY_OPEN);
948
        }
949 102
    }
950
951
    /**
952
     * Assert that given wrapper is a valid, registered stream wrapper.
953
     *
954
     * Used internally to ensure that a given stream wrapper is valid and available
955
     *
956
     * @param string $name The name of the stream wrapper
957
     *
958
     * @throws \InvalidArgumentException if wrapper doesn't exist
959
     */
960 2
    protected function assertValidWrapper($name)
961
    {
962 2
        if (!in_array($name, stream_get_wrappers())) {
963
            throw new InvalidArgumentException("{$name} is not a known stream wrapper.");
964
        }
965 2
    }
966
}
967