Passed
Pull Request — master (#183)
by Luke
03:12
created

StreamResource::isReadable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * CSVelte: Slender, elegant CSV for PHP
4
 *
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
 * @copyright Copyright (c) 2018 Luke Visinoni
10
 * @author    Luke Visinoni <[email protected]>
11
 * @license   See LICENSE file (MIT license)
12
 */
13
namespace CSVelte\IO;
14
15
use CSVelte\Exception\IOException;
16
use InvalidArgumentException;
17
18
/**
19
 * Stream Resource.
20
 *
21
 * Represents a stream resource connection. May be open or closed. This allows
22
 * me to provide a nice, clean, easy-to-use interface for opening stream
23
 * resources in a particular mode as well as to lazy-open a stream.
24
 *
25
 * @since v0.2.1
26
 * @todo  Annoyingly, this class is currently receiving an "F" from my code analysis tool because of its supposed
27
 *        complexity. I believe the high number of methods is the issue. Despite its high number of methods, I don't
28
 *        feel like this class is overly complex. I would like to find a way to configure the tool to ignore this.
29
 */
30
class StreamResource
31
{
32
    /**
33
     * Available base access modes.
34
     *
35
     * @var string base access mode must be one of these letters
36
     */
37
    protected static $bases = 'rwaxc';
38
39
    /**
40
     * Hash of readable/writable stream open mode types.
41
     *
42
     * Mercilessly stolen from:
43
     * https://github.com/guzzle/streams/blob/master/src/Stream.php
44
     *
45
     * My kudos and sincere thanks go out to Michael Dowling and Graham Campbell
46
     * of the guzzle/streams PHP package. Thanks for the inspiration (in some cases)
47
     * and the not suing me for outright theft (in this case).
48
     *
49
     * @var array Hash of readable and writable stream types
50
     *
51
     * @todo I think I can get rid of this by simply checking whether base is a
52
     *     particular letter OR plus is present... try it
53
     * @todo Why are x and c (alone) not even on either of these lists?
54
     *       I just figured out why... readable and writable default to false. So
55
     *       only modes that change that default behavior are listed here
56
     */
57
    protected static $readWriteHash = [
58
        'read' => [
59
            'r'   => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true,
60
            'rb'  => true, 'w+b' => true, 'r+b' => true, 'x+b' => true,
61
            'c+b' => true, 'rt' => true, 'w+t' => true, 'r+t' => true,
62
            'x+t' => true, 'c+t' => true, 'a+' => true,
63
        ],
64
        'write' => [
65
            'w'   => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true,
66
            'c+'  => true, 'wb' => true, 'w+b' => true, 'r+b' => true,
67
            'x+b' => true, 'c+b' => true, 'w+t' => true, 'r+t' => true,
68
            'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true,
69
        ],
70
    ];
71
72
    /**
73
     * Stream URI.
74
     *
75
     * Contains the stream URI to connect to.
76
     *
77
     * @var string The stream uri
78
     */
79
    protected $uri;
80
81
    /**
82
     * Stream resource handle.
83
     *
84
     * Contains the underlying stream resource handle (if there is one).
85
     * Otherwise it will be null.
86
     *
87
     * @var resource The stream resource handle
88
     */
89
    protected $conn;
90
91
    /**
92
     * Lazy open switch.
93
     *
94
     * Determines whether the actual fopen for this resource should be delayed
95
     * until an I/O operation is performed.
96
     *
97
     * @var bool True if connection is lazy
98
     */
99
    protected $lazy;
100
101
    /**
102
     * Extra context to open the resource with.
103
     *
104
     * An associative array of context options and parameters.
105
     *
106
     * @var array An associative array of stream context options and params
107
     *
108
     * @see http://php.net/manual/en/stream.contexts.php
109
     */
110
    protected $context = [
111
        'options' => [],
112
        'params'  => [],
113
    ];
114
115
    /**
116
     * Context resource handle.
117
     *
118
     * Holds a context resource handle object for $this->context
119
     *
120
     * @var resource The context resource handle
121
     */
122
    protected $crh;
123
124
    /**
125
     * Should fopen use include path?
126
     *
127
     * @var bool True if fopen should use the include path to find potential files
128
     */
129
    protected $useIncludePath;
130
131
    /**
132
     * Base open mode.
133
     *
134
     * @var string A single character for base open mode (r, w, a, x or c)
135
     */
136
    protected $base = '';
137
138
    /**
139
     * Plus reading or plus writing.
140
     *
141
     * @var string Either a plus or an empty string
142
     */
143
    protected $plus = '';
144
145
    /**
146
     * Binary or text flag.
147
     *
148
     * @var string Either "b" or "t" for binary or text
149
     */
150
    protected $flag = '';
151
152
    /**
153
     * Does access mode string indicate readability?
154
     *
155
     * @var bool Whether access mode indicates readability
156
     */
157
    protected $readable = false;
158
159
    /**
160
     * Does access mode string indicate writability.
161
     *
162
     * @var bool Whether access mode indicates writability
163
     */
164
    protected $writable = false;
165
166
    /**
167
     * Resource constructor.
168
     *
169
     * Instantiates a stream resource. If lazy is set to true, the connection
170
     * is delayed until the first call to getResource().
171
     *
172
     * @param string|resource|object $uri              The URI to connect to OR a stream resource handle
173
     * @param string                 $mode             The connection mode
174
     * @param bool                   $lazy             Whether connection should be deferred until an I/O
175
     *                                                 operation is requested (such as read or write) on the attached stream
176
     * @param bool|null              $use_include_path
177
     * @param array|null             $context_options
178
     * @param array|null             $context_params
179
     *
180
     * @todo Does stream_get_meta_data belong in Stream or Resource?
181
     */
182 80
    public function __construct(
183
        $uri,
184
        $mode = null,
185
        $lazy = null,
186
        $use_include_path = null,
187
        $context_options = null, // this should just be an array -> $context
188
        $context_params = null
189
    ) {
190
        // first, check if we're wrapping an existing stream resource
191 80
        if (is_resource($uri)) {
192 22
            $this->initWithResource($uri);
193
194 21
            return;
195
        }
196
197
        // ok we're opening a new stream resource handle
198 58
        $this->setUri($uri)
0 ignored issues
show
Bug introduced by
It seems like $uri defined by parameter $uri on line 183 can also be of type resource; however, CSVelte\IO\StreamResource::setUri() does only seem to accept string|object, 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...
199 56
            ->setMode($mode)
200 55
            ->setLazy($lazy)
201 55
            ->setUseIncludePath($use_include_path)
202 55
            ->setContext($context_options, $context_params);
203 55
        if (!$this->isLazy()) {
204 3
            $this->connect();
205 2
        }
206 54
    }
207
208
    /**
209
     * Class destructor.
210
     */
211 72
    public function __destruct()
212
    {
213 72
        $this->disconnect();
214 72
    }
215
216
    /**
217
     * Invoke magic method.
218
     *
219
     * Creates and returns a Stream object for this resource
220
     *
221
     * @todo Should add a getStream() method and return $this->>getStream() from this instead
222
     *
223
     * @return Stream A stream for this resource
224
     */
225 3
    public function __invoke()
226
    {
227 3
        return new Stream($this);
228
    }
229
230
    /**
231
     * Connect (open connection) to file/stream.
232
     *
233
     * File open is (by default) delayed until the user explicitly calls connect()
234
     * or they request the resource handle with getHandle().
235
     *
236
     * @throws IOException if connection fails
237
     *
238
     * @return bool True if connection was successful
239
     */
240 47
    public function connect()
241
    {
242 47
        if (!$this->isConnected()) {
243
            /** @var IOException $e */
244 47
            $e          = null;
245 47
            $errhandler = function () use (&$e) {
246 4
                $e = new IOException(sprintf(
247 4
                    'Could not open connection for %s using mode %s',
248 4
                    $this->getUri(),
249 4
                    $this->getMode()
250 4
                ), IOException::ERR_STREAM_CONNECTION_FAILED);
251 47
            };
252 47
            set_error_handler($errhandler->bindTo($this));
253 47
            $this->conn = fopen(
254 47
                $this->getUri(),
255 47
                $this->getMode(),
256 47
                $this->getUseIncludePath(),
257 47
                $this->getContext()
258 47
            );
259 47
            restore_error_handler();
260 47
            if ($e) {
261 4
                throw $e;
262
            }
263 43
        }
264
265 43
        return $this->isConnected();
266
    }
267
268
    /**
269
     * Close connection.
270
     *
271
     * Close the connection to this stream (if open).
272
     *
273
     * @return bool|null Whether close was successful, or null if already closed
274
     */
275 72
    public function disconnect()
276
    {
277 72
        if ($this->isConnected()) {
278 64
            return fclose($this->conn);
279
        }
280
        // return null if nothing to close
281 61
        return null;
282
    }
283
284
    /**
285
     * Set stream URI.
286
     *
287
     * Set the stream URI. Can only be set if the connection isn't open yet.
288
     * If you try to set the URI on an open resource, an IOException will be thrown
289
     *
290
     * @param string|object $uri The URI for this stream resource to open
291
     *
292
     * @throws \InvalidArgumentException      if not a valid stream uri
293
     * @throws \CSVelte\Exception\IOException if stream has already been opened
294
     *
295
     * @return $this
296
     *
297
     * @todo I'm pretty sure that the parse_url function is too restrictive. It
298
     *     will reject URIs that are perfectly valid.
299
     */
300 79
    public function setUri($uri)
301
    {
302 79
        $this->assertNotConnected(__METHOD__);
303
304 79
        if (is_object($uri) && method_exists($uri, '__toString')) {
305 1
            $uri = (string) $uri;
306 1
        }
307
308 79
        if (!is_string($uri)) {
309 2
            throw new InvalidArgumentException(sprintf(
310 2
                'Not a valid stream uri, expected "string", got: "%s"',
311 2
                gettype($uri)
312 2
            ));
313
        }
314
315 77
        $uri = (string) $uri;
316 77
        if (parse_url($uri)) {
317 77
            $this->uri = $uri;
318
319 77
            return $this;
320
        }
321
    }
322
323
    /**
324
     * Set the fopen mode.
325
     *
326
     * Thank you to GitHub user "binsoul" whose AccessMode class inspired this
327
     * Also thanks to the author(s) of Guzzle streams implementation, where the
328
     * readwritehash idea came from. Both libraries are MIT licensed, so my
329
     * merciless theft of their code is alright.
330
     *
331
     * @param string $mode A 1-3 character string determining open mode
332
     *
333
     * @throws \InvalidArgumentException      if not a valid stream access mode
334
     * @throws \CSVelte\Exception\IOException if stream has already been opened
335
     *
336
     * @return $this
337
     *
338
     * @see http://php.net/manual/en/function.fopen.php
339
     * @see https://github.com/binsoul/io-stream/blob/master/src/AccessMode.php
340
     * @see https://raw.githubusercontent.com/guzzle/streams/master/src/Stream.php
341
     *
342
     * @todo convert $mode to lower case and test it
343
     */
344 77
    public function setMode($mode = null)
345
    {
346 77
        $this->assertNotConnected(__METHOD__);
347 77
        if (is_null($mode)) {
348 32
            $mode = 'r+b';
349 32
        }
350
351 77
        $mode = substr($mode, 0, 3);
352 77
        $rest = substr($mode, 1);
353
354 77
        $base = substr($mode, 0, 1);
355 77
        $plus = (strpos($rest, '+') !== false) ? '+' : '';
356 77
        $flag = trim($rest, '+');
357
358 77
        $this->flag = '';
359 77
        $this->setBaseMode($base)
360 76
            ->setIsPlus($plus == '+')
361 76
            ->setIsText($flag == 't')
362 76
            ->setIsBinary($flag == 'b');
363
364 76
        return $this;
365
    }
366
367
    /**
368
     * Set base access mode character.
369
     *
370
     * @param string $base The base mode character (must be one of "rwaxc")
371
     *
372
     * @throws \InvalidArgumentException      If passed invalid base char
373
     * @throws \CSVelte\Exception\IOException if stream has already been opened
374
     *
375
     * @return $this
376
     */
377 77
    public function setBaseMode($base)
378
    {
379 77
        $this->assertNotConnected(__METHOD__);
380 77
        if (strpos(self::$bases, $base) === false) {
381 1
            throw new InvalidArgumentException("\"{$base}\" is not a valid base stream access mode.");
382
        }
383 76
        $this->base = $base;
384
385 76
        return $this->updateAccess();
386
    }
387
388
    /**
389
     * Set plus mode.
390
     *
391
     * @param bool $isPlus Whether base access mode should include the + sign
392
     *
393
     * @throws \CSVelte\Exception\IOException if stream has already been opened
394
     *
395
     * @return $this
396
     */
397 76
    public function setIsPlus($isPlus)
398
    {
399 76
        $this->assertNotConnected(__METHOD__);
400 76
        $this->plus = $isPlus ? '+' : '';
401
402 76
        return $this->updateAccess();
403
    }
404
405
    /**
406
     * Set binary-safe mode.
407
     *
408
     * @param bool $isBinary Whether binary safe mode or not
409
     *
410
     * @throws \CSVelte\Exception\IOException if stream has already been opened
411
     *
412
     * @return $this
413
     */
414 76
    public function setIsBinary($isBinary)
415
    {
416 76
        $this->assertNotConnected(__METHOD__);
417 76
        if ($isBinary) {
418 49
            $this->flag = 'b';
419 49
        }
420
421 76
        return $this;
422
    }
423
424
    /**
425
     * Set text mode.
426
     *
427
     * @param bool $isText Whether text mode or not
428
     *
429
     * @throws \CSVelte\Exception\IOException if stream has already been opened
430
     *
431
     * @return $this
432
     */
433 76
    public function setIsText($isText)
434
    {
435 76
        $this->assertNotConnected(__METHOD__);
436 76
        if ($isText) {
437 1
            $this->flag = 't';
438 1
        }
439
440 76
        return $this;
441
    }
442
443
    /**
444
     * Set use include path flag.
445
     *
446
     * Sets whether or not fopen should search the include path for files. Can
447
     * only be set if resource isn't open already. If called when resource is
448
     * already open an exception will be thrown.
449
     *
450
     * @param bool $use_include_path Whether to search include path for files
451
     *
452
     * @throws \CSVelte\Exception\IOException
453
     *
454
     * @return $this
455
     */
456 55
    public function setUseIncludePath($use_include_path)
457
    {
458 55
        $this->assertNotConnected(__METHOD__);
459 55
        $this->useIncludePath = (bool) $use_include_path;
460
461 55
        return $this;
462
    }
463
464
    /**
465
     * Set stream context options and params.
466
     *
467
     * Sets arrays of stream context options and params. Check out the URI below
468
     * for more on stream contexts.
469
     *
470
     * @param array|null $options Stream context options
471
     * @param array|null $params  Stream Context params
472
     *
473
     * @return $this
474
     *
475
     * @see http://php.net/manual/en/stream.contexts.php
476
     */
477 55
    public function setContext($options = null, $params = null)
478
    {
479 55
        if (is_array($options)) {
480 2
            foreach ($options as $wrap => $opts) {
481 2
                $this->setContextOptions($opts, $wrap);
482 2
            }
483 2
        }
484 55
        if (!is_null($params)) {
485 2
            $this->setContextParams($params);
486 2
        }
487
488 55
        return $this;
489
    }
490
491
    /**
492
     * Set context resource directly.
493
     *
494
     * @param resource|null $context Stream context resource to set directly
495
     *
496
     * @return $this
497
     *
498
     * @see http://php.net/manual/en/function.stream-context-create.php
499
     *
500
     * @todo Need to write a unit test for passing this method a null value
501
     */
502 41
    public function setContextResource($context)
503
    {
504 41
        if (!is_null($context)) {
505 6
            if (!is_resource($context) || get_resource_type($context) != 'stream-context') {
506 4
                throw new InvalidArgumentException(sprintf(
507 4
                    'Invalid argument for %s. Expecting resource of type "stream-context" but got: "%s"',
508 4
                    __METHOD__,
509 4
                    gettype($context)
510 4
                ));
511
            }
512
            // don't need to call updateContext() because its already a context resource
513 2
            $this->crh = $context;
514 2
        }
515
516 37
        return $this;
517
    }
518
519
    /**
520
     * Set context options.
521
     *
522
     * Sets stream context options for this stream resource.
523
     *
524
     * @param array  $options An array of stream context options
525
     * @param string $wrapper The wrapper these options belong to (if no wrapper
526
     *                        argument, then $options should be an associative array with key being
527
     *                        a wrapper name and value being its options)
528
     *
529
     * @throws \InvalidArgumentException if passed invalid options or wrapper
530
     *
531
     * @return $this
532
     *
533
     * @see http://php.net/manual/en/stream.contexts.php
534
     */
535 2
    public function setContextOptions($options, $wrapper = null)
536
    {
537 2
        if (is_array($options)) {
538 2
            if (is_null($wrapper)) {
539
                $this->context['options'] = $options;
540
            } else {
541 2
                $this->assertValidWrapper($wrapper);
542 2
                $this->context['options'][$wrapper] = $options;
543
            }
544 2
            $this->updateContext();
545
546 2
            return $this;
547
        }
548
        throw new InvalidArgumentException('Context options must be an array, got: ' . gettype($options));
549
    }
550
551
    /**
552
     * Set context params.
553
     *
554
     * Set the context params for this stream resource.
555
     *
556
     * @param array $params An array of stream resource params
557
     *
558
     * @throws \InvalidArgumentException if passed invalid params
559
     *
560
     * @return $this
561
     *
562
     * @see http://php.net/manual/en/stream.contexts.php
563
     */
564 2
    public function setContextParams($params)
565
    {
566 2
        if (is_array($params)) {
567 2
            $this->context['params'] = $params;
568 2
            $this->updateContext();
569
570 2
            return $this;
571
        }
572
        throw new InvalidArgumentException('Context parameters must be an array, got: ' . gettype($params));
573
    }
574
575
    /**
576
     * Get context options for this stream resource.
577
     *
578
     * Returns the stream context options for this stream resource. Either all
579
     * options for all wrappers, or just the options for the specified wrapper.
580
     *
581
     * @param string $wrapper If present, return options only for this wrapper
582
     *
583
     * @throws \InvalidArgumentException if the wrapper doesn't exist
584
     *
585
     * @return array Context options (either all or for specified wrapper)
586
     */
587 47
    public function getContextOptions($wrapper = null)
588
    {
589 47
        if (is_null($wrapper)) {
590 47
            return $this->context['options'];
591
        }
592
        $this->assertValidWrapper($wrapper);
593
        if (isset($this->context['options'][$wrapper])) {
594
            return $this->context['options'][$wrapper];
595
        }
596
    }
597
598
    /**
599
     * Get context params for this stream resource.
600
     *
601
     * Returns the stream context params for this stream resource.
602
     *
603
     * @return array Context params for this stream resource
604
     */
605 47
    public function getContextParams()
606
    {
607 47
        return $this->context['params'];
608
    }
609
610
    /**
611
     * Get stream context resource.
612
     *
613
     * @return resource|null The stream context resource
614
     */
615 49
    public function getContext()
616
    {
617
        // if context resource hasn't been created, create one
618 49
        if (is_null($this->crh)) {
619 47
            $this->crh = stream_context_create(
620 47
                $this->getContextOptions(),
621 47
                $this->getContextParams()
622 47
            );
623 47
        }
624
        // return context resource handle
625 49
        return $this->crh;
626
    }
627
628
    /**
629
     * Retrieve underlying stream resource handle.
630
     *
631
     * An accessor method for the underlying stream resource object. Also triggers
632
     * stream connection if in lazy open mode. Because this method may potentially
633
     * call the connect() method, it is possible that it may throw an exception
634
     * if there is some issue with opening the stream.
635
     *
636
     * @throws \CSVelte\Exception\IOException
637
     *
638
     * @return resource The underlying stream resource handle
639
     */
640 49
    public function getHandle()
641
    {
642 49
        if (!$this->isConnected() && $this->isLazy()) {
643 5
            $this->connect();
644 4
        }
645
646 48
        return $this->conn;
647
    }
648
649
    /**
650
     * Is the stream connection open?
651
     *
652
     * Tells you whether this stream resource is open or not.
653
     *
654
     * @return bool Whether the stream is open
655
     */
656 79
    public function isConnected()
657
    {
658 79
        return is_resource($this->conn);
659
    }
660
661
    /**
662
     * Get the stream URI.
663
     *
664
     * Accessor method for stream URI.
665
     *
666
     * @return string The stream URI
667
     */
668 47
    public function getUri()
669
    {
670 47
        return $this->uri;
671
    }
672
673
    /**
674
     * Get the access mode.
675
     *
676
     * Tells you what the access mode is. This is the short string of characters
677
     * that you would pass to the fopen function to tell it how to open a file/stream
678
     *
679
     * @return string The file/stream access mode
680
     *
681
     * @see http://php.net/manual/en/function.fopen.php
682
     */
683 76
    public function getMode()
684
    {
685 76
        return sprintf(
686 76
            '%s%s%s',
687 76
            $this->base,
688 76
            $this->plus,
689 76
            $this->flag
690 76
        );
691
    }
692
693
    /**
694
     * Is access mode binary-safe?
695
     *
696
     * @return bool Whether binary-safe flag is set
697
     */
698 4
    public function isBinary()
699
    {
700 4
        return $this->flag == 'b';
701
    }
702
703
    /**
704
     * Is stream connected in text mode?
705
     *
706
     * @return bool Whether text mode flag is set
707
     */
708 3
    public function isText()
709
    {
710 3
        return $this->flag == 't';
711
    }
712
713
    /**
714
     * Is this a lazy open resource?
715
     *
716
     * @return bool Whether this is a lazily-opened resource
717
     */
718 55
    public function isLazy()
719
    {
720 55
        return $this->lazy;
721
    }
722
723
    /**
724
     * Should fopen search include path?
725
     *
726
     * @return bool Whether fopen should search include path for files
727
     */
728 47
    public function getUseIncludePath()
729
    {
730 47
        return $this->useIncludePath;
731
    }
732
733
    /**
734
     * Does the access mode string indicate readability?
735
     *
736
     * Readable, in this context, only refers to the manner in which this stream
737
     * resource was opened (if it even is opened yet). It is no indicator about
738
     * whether or not the underlying stream actually supports read operations.
739
     * It simply refers to the access mode string passed to it by the user.
740
     *
741
     * @return bool Whether access mode indicates readability
742
     */
743 41
    public function isReadable()
744
    {
745 41
        return $this->readable;
746
    }
747
748
    /**
749
     * Does the access mode string indicate writability?
750
     *
751
     * Writable, in this context, only refers to the manner in which this stream
752
     * resource was opened (if it even is opened yet). It is no indicator about
753
     * whether or not the underlying stream actually supports write operations.
754
     * It simply refers to the access mode string passed to it by the user.
755
     *
756
     * @return bool Whether access mode indicates writability
757
     */
758 10
    public function isWritable()
759
    {
760 10
        return $this->writable;
761
    }
762
763
    /**
764
     * Is cursor positioned at the beginning of stream?
765
     *
766
     * Returns true if this stream resource's access mode positions the internal
767
     * cursor at the beginning of the stream.
768
     *
769
     * @return bool Whether cursor positioned at beginning of stream
770
     */
771 2
    public function isCursorPositionedAtBeginning()
772
    {
773 2
        return $this->base != 'a';
774
    }
775
776
    /**
777
     * Is cursor positioned at the end of stream?
778
     *
779
     * Returns true if this stream resource's access mode positions the internal
780
     * cursor at the end of the stream.
781
     *
782
     * @return bool Whether cursor positioned at end of stream
783
     */
784 2
    public function isCursorPositionedAtEnd()
785
    {
786 2
        return $this->base == 'a';
787
    }
788
789
    /**
790
     * Is content truncated to zero-length on opening?
791
     *
792
     * Returns true if this stream resource's access mode indicates truncation of
793
     * stream content to zero-length upon opening.
794
     *
795
     * @return bool Whether stream content is truncated on opening
796
     */
797 3
    public function isTruncated()
798
    {
799 3
        return $this->base == 'w';
800
    }
801
802
    /**
803
     * Does stream access mode indicate file creation?
804
     *
805
     * Returns true if this stream's access mode implies that PHP will attempt to
806
     * create a file if none exists.
807
     *
808
     * @return bool Whether PHP should attempt to create file at $uri
809
     */
810 1
    public function attemptsFileCreation()
811
    {
812 1
        return $this->base != 'r';
813
    }
814
815
    /**
816
     * Does stream access mode indicate the rejection of existing files?
817
     *
818
     * Returns true if this stream's access mode implies that PHP will fail to
819
     * open a file if it already exists.
820
     *
821
     * @return bool Whether PHP should attempt to create file at $uri
822
     */
823 1
    public function rejectsExistingFiles()
824
    {
825 1
        return $this->base == 'x';
826
    }
827
828
    /**
829
     * Are write operations appended to the end of the stream?
830
     *
831
     * Returns true if write operations are appended to the end of the stream
832
     * regardless of the position of the read cursor.
833
     *
834
     * @return bool Whether write operations ore always appended
835
     */
836 1
    public function appendsWriteOps()
837
    {
838 1
        return $this->base == 'w';
839
    }
840
841
    /**
842
     * Initialize resource with PHP resource variable.
843
     *
844
     * Uses a PHP resource variable to initialize this class.
845
     *
846
     * @param resource $handle The stream resource to initialize
847
     *
848
     * @return bool
849
     */
850 22
    protected function initWithResource($handle)
851
    {
852 22
        if (($resource_type = get_resource_type($handle)) != ($exp_resource_type = 'stream')) {
853 1
            throw new InvalidArgumentException(sprintf(
854 1
                'Invalid stream resource type for %s, expected "%s", got: "%s"',
855 1
                __METHOD__,
856 1
                $exp_resource_type,
857
                $resource_type
858 1
            ));
859
        }
860
        // set all this manually
861 21
        $meta = stream_get_meta_data($handle);
862 21
        $this->setUri($meta['uri'])
863 21
            ->setMode($meta['mode']);
864 21
        $this->conn = $handle;
865
866 21
        return true;
867
    }
868
869
    /**
870
     * Update access parameters.
871
     *
872
     * After changing any of the access mode parameters, this method must be
873
     * called in order for readable and writable to stay accurate.
874
     *
875
     * @return $this
876
     */
877 76
    protected function updateAccess()
878
    {
879 76
        $this->readable = isset(self::$readWriteHash['read'][$this->getMode()]);
880 76
        $this->writable = isset(self::$readWriteHash['write'][$this->getMode()]);
881
882 76
        return $this;
883
    }
884
885
    /**
886
     * Set lazy flag.
887
     *
888
     * Set the lazy flag, which tells the class whether to defer the connection
889
     * until the user specifically requests it.
890
     *
891
     * @param bool|null Whether or not to "lazily" open the stream
892
     * @param mixed $lazy
893
     *
894
     * @return $this
895
     */
896 55
    protected function setLazy($lazy)
897
    {
898 55
        if (is_null($lazy)) {
899 49
            $lazy = true;
900 49
        }
901 55
        $this->lazy = (bool) $lazy;
902
903 55
        return $this;
904
    }
905
906
    /**
907
     * Update the stream context.
908
     *
909
     * After setting/updating stream context options and/or params, this method
910
     * must be called in order to update the stream context resource.
911
     *
912
     * @return $this
913
     */
914 2
    protected function updateContext()
915
    {
916
        // if already connected, set the options on the context resource
917
        // otherwise, it will be set at connection time
918 2
        if ($this->isConnected()) {
919
            // set options and params on existing stream resource
920 1
            stream_context_set_params(
921 1
                $this->getContext(),
922 1
                $this->getContextParams() + [
923 1
                    'options' => $this->getContextOptions(),
924
                ]
925 1
            );
926 1
        }
927
928 2
        return $this;
929
    }
930
931
    /**
932
     * Assert that stream resource is not open.
933
     *
934
     * Used internally to ensure that stream is not open, since some methods should
935
     * only be called on unopened stream resources.
936
     *
937
     * @param string $method The method that is asserting
938
     *
939
     * @throws IOException if stream is open
940
     */
941 79
    protected function assertNotConnected($method)
942
    {
943 79
        if ($this->isConnected()) {
944
            throw new IOException("Cannot perform this operation on a stream once it has already been opened: {$method}", IOException::ERR_STREAM_ALREADY_OPEN);
945
        }
946 79
    }
947
948
    /**
949
     * Assert that given wrapper is a valid, registered stream wrapper.
950
     *
951
     * Used internally to ensure that a given stream wrapper is valid and available
952
     *
953
     * @param string $name The name of the stream wrapper
954
     *
955
     * @throws \InvalidArgumentException if wrapper doesn't exist
956
     */
957 2
    protected function assertValidWrapper($name)
958
    {
959 2
        if (!in_array($name, stream_get_wrappers())) {
960
            throw new InvalidArgumentException("{$name} is not a known stream wrapper.");
961
        }
962
    }
963
}