Completed
Push — releases/v0.2.1 ( a7ee8f...03c290 )
by Luke
03:04
created

Resource::isCursorPositionedAtBeginning()   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 0
Metric Value
cc 1
eloc 2
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
 * @version   v0.2.1
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 \InvalidArgumentException;
17
use CSVelte\Exception\IOException;
18
19
/**
20
 * Stream Resource.
21
 *
22
 * Represents a stream resource connection. May be open or closed. This allows
23
 * me to provide a nice, clean, easy-to-use interface for opening stream
24
 * resources in a particular mode as well as to lazy-open a stream.
25
 *
26
 * @package    CSVelte
27
 * @subpackage CSVelte\IO
28
 * @copyright  (c) 2016, Luke Visinoni <[email protected]>
29
 * @author     Luke Visinoni <[email protected]>
30
 * @since      v0.2.1
31
 */
32
class Resource
33
{
34
    /**
35
     * Available base access modes
36
     * @var string base access mode must be one of these letters
37
     */
38
    protected static $bases = "rwaxc";
39
40
    /**
41
     * Hash of readable/writable stream open mode types.
42
     *
43
     * Mercilessly stolen from:
44
     * https://github.com/guzzle/streams/blob/master/src/Stream.php
45
     *
46
     * My kudos and sincere thanks go out to Michael Dowling and Graham Campbell
47
     * of the guzzle/streams PHP package. Thanks for the inspiration (in some cases)
48
     * and the not suing me for outright theft (in this case).
49
     *
50
     * @var array Hash of readable and writable stream types
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 boolean 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
     * @see http://php.net/manual/en/stream.contexts.php
108
     */
109
    protected $context = [
110
        'options' => [],
111
        'params' => []
112
    ];
113
114
    /**
115
     * Context resource handle.
116
     *
117
     * Holds a context resource handle object for $this->context
118
     *
119
     * @var resource The context resource handle
120
     */
121
    protected $crh;
122
123
    /**
124
     * Should fopen use include path?
125
     *
126
     * @var boolean True if fopen should use the include path to find potential files
127
     */
128
    protected $useIncludePath;
129
130
    /**
131
     * Base open mode.
132
     *
133
     * @var string A single character for base open mode (r, w, a, x or c)
134
     */
135
    protected $base = '';
136
137
    /**
138
     * Plus reading or plus writing.
139
     *
140
     * @var string Either a plus or an empty string
141
     */
142
    protected $plus = '';
143
144
    /**
145
     * Binary or text flag.
146
     *
147
     * @var string Either "b" or "t" for binary or text
148
     */
149
    protected $flag = '';
150
151
    /**
152
     * Does access mode string indicate readability?
153
     *
154
     * @var bool Whether access mode indicates readability
155
     */
156
    protected $readable = false;
157
158
    /**
159
     * Does access mode string indicate writability
160
     *
161
     * @var bool Whether access mode indicates writability
162
     */
163
    protected $writable = false;
164
165
    /**
166
     * Resource constructor.
167
     *
168
     * Instantiates a stream resource. If lazy is set to true, the connection
169
     * is delayed until the first call to getResource().
170
     *
171
     * @param string|resource $uri  The URI to connect to OR a stream resource handle
172
     * @param string $mode The connection mode
173
     * @param boolean $lazy Whether connection should be deferred until an I/O
174
     *     operation is requested (such as read or write) on the attached stream
175
     * @throws \CSVelte\Exception\IOException if connection fails
176
     * @todo Does stream_get_meta_data belong in Stream or Resource?
177
     */
178 96
    public function __construct($uri, $mode = null, $lazy = null, $use_include_path = null, $context_options = null, $context_params = null)
179
    {
180
        // first, check if we're wrapping an existing stream resource
181 96
        if (is_resource($uri)) {
182 6
            $this->initWithResource($uri);
183 5
            return;
184
        }
185
186
        // ok we're opening a new stream resource handle
187 90
        $this->setUri($uri)
188 89
             ->setMode($mode)
189 89
             ->setLazy($lazy)
190 89
             ->setUseIncludePath($use_include_path)
191 89
             ->setContext($context_options, $context_params);
192 89
        if (!$this->isLazy()) {
193 4
            $this->connect();
194 3
        }
195 88
    }
196
197
    /**
198
     * Initialize resource with PHP resource variable.
199
     *
200
     * Uses a PHP resource variable to initialize this class.
201
     *
202
     * @param resource $handle The stream resource to initialize
203
     * @return bool
204
     */
205 6
    protected function initWithResource($handle)
206
    {
207 6
        if (($resource_type = get_resource_type($handle)) != ($exp_resource_type = "stream")) {
208 1
            throw new InvalidArgumentException(sprintf(
209 1
                'Invalid stream resource type for %s, expected "%s", got: "%s"',
210 1
                __METHOD__,
211 1
                $exp_resource_type,
212
                $resource_type
213 1
            ));
214
        }
215
        // set all this manually
216 5
        $meta = stream_get_meta_data($handle);
217 5
        $this->setUri($meta['uri'])
218 5
            ->setMode($meta['mode']);
219 5
        $this->conn = $handle;
220 5
        return true;
221
    }
222
223
    /**
224
     * Class destructor
225
     */
226 90
    public function __destruct()
227
    {
228 90
        $this->disconnect();
229 90
    }
230
231
    /**
232
     * Invoke magic method.
233
     *
234
     * Creates and returns a Stream object for this resource
235
     *
236
     * @return resource The underlying stream resource
237
     */
238 4
    public function __invoke()
239
    {
240 4
        return new Stream($this);
241
    }
242
243
    /**
244
     * Connect (open connection) to file/stream.
245
     *
246
     * File open is (by default) delayed until the user explicitly calls connect()
247
     * or they request the resource handle with getHandle().
248
     *
249
     * @return boolean True if connection was successful
250
     * @throws \CSVelte\Exception\IOException if connection fails
251
     */
252 80
    public function connect()
253
    {
254 80
        if (!$this->isConnected()) {
255 80
            $e = null;
256 80
            $errhandler = function () use (&$e) {
257 4
                $e = new IOException(sprintf(
258 4
                    "Could not open connection for %s using mode %s",
259 4
                    $this->getUri(),
260 4
                    $this->getMode()
261 4
                ), IOException::ERR_STREAM_CONNECTION_FAILED);
262 80
            };
263 80
            set_error_handler($errhandler->bindTo($this));
264 80
            $this->conn = fopen(
265 80
                $this->getUri(),
266 80
                $this->getMode(),
267 80
                $this->getUseIncludePath(),
268 80
                $this->getContext()
269 80
            );
270 80
            restore_error_handler();
271 80
            if ($e) throw $e;
272 76
        }
273 76
        return $this->isConnected();
274
    }
275
276
    /**
277
     * Close connection.
278
     *
279
     * Close the connection to this stream (if open).
280
     *
281
     * @return boolean|null Whether close was successful, or null if already closed
282
     */
283 90
    public function disconnect()
284
    {
285 90
        if ($this->isConnected()) {
286 81
            return fclose($this->conn);
287
        }
288
        // return null if nothing to close
289 79
        return;
290
    }
291
292
    /**
293
     * Set stream URI.
294
     *
295
     * Set the stream URI. Can only be set if the connection isn't open yet.
296
     * If you try to set the URI on an open resource, an IOException will be thrown
297
     *
298
     * @param string $uri The URI for this stream resource to open
299
     * @return $this
300
     * @throws \InvalidArgumentException if not a valid stream uri
301
     * @throws \CSVelte\Exception\IOException if stream has already been opened
302
     * @todo I'm pretty sure that the parse_url function is too restrictive. It
303
     *     will reject URIs that are perfectly valid.
304
     */
305 95
    public function setUri($uri)
306
    {
307 95
        $this->assertNotConnected(__METHOD__);
308
309 95
        if (is_object($uri) && !method_exists($uri, '__toString')) {
310 1
            throw new InvalidArgumentException(sprintf(
311 1
                'Not a valid stream uri, expected "string", got: "%s"',
312 1
                gettype($uri)
313 1
            ));
314
        }
315
316 94
        $uri = (string) $uri;
317 94
        if (/*is_string($uri) && */parse_url($uri)) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
318 94
            $this->uri = $uri;
319 94
            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
     * @return $this
333
     * @throws \InvalidArgumentException if not a valid stream access mode
334
     * @throws \CSVelte\Exception\IOException if stream has already been opened
335
     * @see http://php.net/manual/en/function.fopen.php
336
     * @see https://github.com/binsoul/io-stream/blob/master/src/AccessMode.php
337
     * @see https://raw.githubusercontent.com/guzzle/streams/master/src/Stream.php
338
     * @todo convert $mode to lower case and test it
339
     */
340 94
    public function setMode($mode = null)
341
    {
342 94
        $this->assertNotConnected(__METHOD__);
343 94
        if (is_null($mode)) $mode = "r+b";
344
345 94
        $mode = substr($mode, 0, 3);
346 94
        $rest = substr($mode, 1);
347
348 94
        $base = substr($mode, 0, 1);
349 94
        $plus = (strpos($rest, '+') !== false) ? '+' : '';
350 94
        $flag = trim($rest, '+');
351
352 94
        $this->flag = '';
353 94
        $this->setBaseMode($base)
354 94
             ->setIsPlus($plus == '+')
355 94
             ->setIsText($flag == 't')
356 94
             ->setIsBinary($flag == 'b');
357
358 94
        return $this;
359
    }
360
361
    /**
362
     * Update access parameters.
363
     *
364
     * After changing any of the access mode parameters, this method must be
365
     * called in order for readable and writable to stay accurate.
366
     *
367
     * @return $this
368
     */
369 94
    protected function updateAccess()
370
    {
371 94
        $this->readable = isset(self::$readWriteHash['read'][$this->getMode()]);
372 94
        $this->writable = isset(self::$readWriteHash['write'][$this->getMode()]);
373 94
        return $this;
374
    }
375
376
    /**
377
     * Set base access mode character.
378
     *
379
     * @param string $base The base mode character (must be one of "rwaxc")
380
     * @return $this
381
     * @throws \InvalidArgumentException If passed invalid base char
382
     * @throws \CSVelte\Exception\IOException if stream has already been opened
383
     */
384 94
    public function setBaseMode($base)
385
    {
386 94
        $this->assertNotConnected(__METHOD__);
387 94
        if (strpos(self::$bases, $base) === false) {
388
            throw new InvalidArgumentException("\"{$base}\" is not a valid base stream access mode.");
389
        }
390 94
        $this->base = $base;
391 94
        return $this->updateAccess();
392
    }
393
394
    /**
395
     * Set plus mode.
396
     *
397
     * @param boolean $isPlus Whether base access mode should include the + sign
398
     * @return $this
399
     * @throws \CSVelte\Exception\IOException if stream has already been opened
400
     */
401 94
    public function setIsPlus($isPlus)
402
    {
403 94
        $this->assertNotConnected(__METHOD__);
404 94
        $this->plus = $isPlus ? '+' : '';
405 94
        return $this->updateAccess();
406
    }
407
408
    /**
409
     * Set binary-safe mode.
410
     *
411
     * @param boolean $isBinary Whether binary safe mode or not
412
     * @return $this
413
     * @throws \CSVelte\Exception\IOException if stream has already been opened
414
     */
415 94
    public function setIsBinary($isBinary)
416
    {
417 94
        $this->assertNotConnected(__METHOD__);
418 94
        if ($isBinary) {
419 67
            $this->flag = 'b';
420 67
        }
421 94
        return $this;
422
    }
423
424
    /**
425
     * Set text mode.
426
     *
427
     * @param boolean $isText Whether text mode or not
428
     * @return $this
429
     * @throws \CSVelte\Exception\IOException if stream has already been opened
430
     */
431 94
    public function setIsText($isText)
432
    {
433 94
        $this->assertNotConnected(__METHOD__);
434 94
        if ($isText) {
435 1
            $this->flag = 't';
436 1
        }
437 94
        return $this;
438
    }
439
440
    /**
441
     * Set lazy flag.
442
     *
443
     * Set the lazy flag, which tells the class whether to defer the connection
444
     * until the user specifically requests it.
445
     *
446
     * @param boolean|null Whether or not to "lazily" open the stream
447
     * @return $this
448
     */
449 89
    protected function setLazy($lazy)
450
    {
451 89
        if (is_null($lazy)) $lazy = true;
452 89
        $this->lazy = (boolean) $lazy;
453 89
        return $this;
454
    }
455
456
    /**
457
     * Set use include path flag.
458
     *
459
     * Sets whether or not fopen should search the include path for files. Can
460
     * only be set if resource isn't open already. If called when resource is
461
     * already open an exception will be thrown.
462
     *
463
     * @param boolean $use_include_path Whether to search include path for files
464
     * @throws \CSVelte\Exception\IOException
465
     * @return $this
466
     */
467 89
    public function setUseIncludePath($use_include_path)
468
    {
469 89
        $this->assertNotConnected(__METHOD__);
470 89
        $this->useIncludePath = (boolean) $use_include_path;
471 89
        return $this;
472
    }
473
474
    /**
475
     * Set stream context options and params.
476
     *
477
     * Sets arrays of stream context options and params. Check out the URI below
478
     * for more on stream contexts.
479
     *
480
     * @param array|null $options Stream context options
481
     * @param array|null $params  Stream Context params
482
     * @return $this
483
     * @see http://php.net/manual/en/stream.contexts.php
484
     */
485 89
    public function setContext($options = null, $params = null)
486
    {
487 89
        if (is_array($options)) {
488 2
            foreach ($options as $wrap => $opts) {
489 2
                $this->setContextOptions($opts, $wrap);
490 2
            }
491 2
        }
492 89
        if (!is_null($params)) {
493 2
            $this->setContextParams($params);
494 2
        }
495 89
        return $this;
496
    }
497
498
    /**
499
     * Set context resource directly
500
     * @param resource|null $context Stream context resource to set directly
501
     * @return $this
502
     * @see http://php.net/manual/en/function.stream-context-create.php
503
     * @todo Need to write a unit test for passing this method a null value
504
     */
505 75
    public function setContextResource($context)
506
    {
507 75
        if (!is_null($context)) {
508 8
            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 4
            $this->crh = $context;
517 4
        }
518 71
        return $this;
519
    }
520
521
    /**
522
     * Update the stream context.
523
     *
524
     * After setting/updating stream context options and/or params, this method
525
     * must be called in order to update the stream context resource.
526
     *
527
     * @return $this
528
     */
529 2
    protected function updateContext()
530
    {
531
        // if already connected, set the options on the context resource
532
        // otherwise, it will be set at connection time
533 2
        if ($this->isConnected()) {
534
            // set options and params on existing stream resource
535 1
            stream_context_set_params(
536 1
                $this->getContext(),
537 1
                $this->getContextParams() + [
538 1
                    'options' => $this->getContextOptions()
539 1
                ]
540 1
            );
541 1
        }
542 2
        return $this;
543
    }
544
545
    /**
546
     * Set context options.
547
     *
548
     * Sets stream context options for this stream resource.
549
     *
550
     * @param array $options An array of stream context options
551
     * @param string $wrapper The wrapper these options belong to (if no wrapper
552
     *     argument, then $options should be an associative array with key being
553
     *     a wrapper name and value being its options)
554
     * @return $this
555
     * @throws \InvalidArgumentException if passed invalid options or wrapper
556
     * @see http://php.net/manual/en/stream.contexts.php
557
     */
558 2
    public function setContextOptions($options, $wrapper = null)
559
    {
560 2
        if (is_array($options)) {
561 2
            if (is_null($wrapper)) {
562
                $this->context['options'] = $options;
563
            } else {
564 2
                $this->assertValidWrapper($wrapper);
565 2
                $this->context['options'][$wrapper] = $options;
566
            }
567 2
            $this->updateContext();
568 2
            return $this;
569
        }
570
        throw new InvalidArgumentException("Context options must be an array, got: " . gettype($options));
571
    }
572
573
    /**
574
     * Set context params.
575
     *
576
     * Set the context params for this stream resource.
577
     *
578
     * @param array $params An array of stream resource params
579
     * @return $this
580
     * @throws \InvalidArgumentException if passed invalid params
581
     * @see http://php.net/manual/en/stream.contexts.php
582
     */
583 2
    public function setContextParams($params)
584
    {
585 2
        if (is_array($params)) {
586 2
            $this->context['params'] = $params;
587 2
            $this->updateContext();
588 2
            return $this;
589
        }
590
        throw new InvalidArgumentException("Context parameters must be an array, got: " . gettype($params));
591
    }
592
593
    /**
594
     * Get context options for this stream resource.
595
     *
596
     * Returns the stream context options for this stream resource. Either all
597
     * options for all wrappers, or just the options for the specified wrapper.
598
     *
599
     * @param  string $wrapper If present, return options only for this wrapper
600
     * @return array Context options (either all or for specified wrapper)
601
     * @throws \InvalidArgumentException if the wrapper doesn't exist
602
     */
603 80
    public function getContextOptions($wrapper = null)
604
    {
605 80
        if (is_null($wrapper)) {
606 80
            return $this->context['options'];
607
        }
608
        $this->assertValidWrapper($wrapper);
609
        if (isset($this->context['options'][$wrapper])) {
610
            return $this->context['options'][$wrapper];
611
        }
612
    }
613
614
    /**
615
     * Get context params for this stream resource.
616
     *
617
     * Returns the stream context params for this stream resource.
618
     *
619
     * @return array Context params for this stream resource
620
     */
621 80
    public function getContextParams()
622
    {
623 80
        return $this->context['params'];
624
    }
625
626
    /**
627
     * Get stream context resource.
628
     * @return resource|null The stream context resource
629
     */
630 83
    public function getContext()
631
    {
632
        // if context resource hasn't been created, create one
633 83
        if (is_null($this->crh)) {
634 80
            $this->crh = stream_context_create(
635 80
                $this->getContextOptions(),
636 80
                $this->getContextParams()
637 80
            );
638 80
        }
639
        // return context resource handle
640 83
        return $this->crh;
641
    }
642
643
    /**
644
     * Retrieve underlying stream resource handle.
645
     *
646
     * An accessor method for the underlying stream resource object. Also triggers
647
     * stream connection if in lazy open mode. Because this method may potentially
648
     * call the connect() method, it is possible that it may throw an exception
649
     * if there is some issue with opening the stream.
650
     *
651
     * @return resource The underlying stream resource handle
652
     * @throws \CSVelte\Exception\IOException
653
     */
654 67
    public function getHandle()
655
    {
656 67
        if (!$this->isConnected() && $this->isLazy()) {
657 6
            $this->connect();
658 5
        }
659 66
        return $this->conn;
660
    }
661
662
    /**
663
     * Is the stream connection open?
664
     *
665
     * Tells you whether this stream resource is open or not.
666
     *
667
     * @return boolean Whether the stream is open
668
     */
669 95
    public function isConnected()
670
    {
671 95
        return is_resource($this->conn);
672
    }
673
674
    /**
675
     * Get the stream URI.
676
     *
677
     * Accessor method for stream URI.
678
     *
679
     * @return string The stream URI
680
     */
681 80
    public function getUri()
682
    {
683 80
        return $this->uri;
684
    }
685
686
    /**
687
     * Get the access mode.
688
     *
689
     * Tells you what the access mode is. This is the short string of characters
690
     * that you would pass to the fopen function to tell it how to open a file/stream
691
     *
692
     * @return string The file/stream access mode
693
     * @see http://php.net/manual/en/function.fopen.php
694
     */
695 94
    public function getMode()
696
    {
697 94
        return sprintf(
698 94
            "%s%s%s",
699 94
            $this->base,
700 94
            $this->plus,
701 94
            $this->flag
702 94
        );
703
    }
704
705
    /**
706
     * Is access mode binary-safe?
707
     * @return boolean Whether binary-safe flag is set
708
     */
709 4
    public function isBinary()
710
    {
711 4
        return $this->flag == "b";
712
    }
713
714
    /**
715
     * Is stream connected in text mode?
716
     * @return boolean Whether text mode flag is set
717
     */
718 3
    public function isText()
719
    {
720 3
        return $this->flag == "t";
721
    }
722
723
    /**
724
     * Is this a lazy open resource?
725
     * @return boolean Whether this is a lazily-opened resource
726
     */
727 89
    public function isLazy()
728
    {
729 89
        return $this->lazy;
730
    }
731
732
    /**
733
     * Should fopen search include path?
734
     * @return boolean Whether fopen should search include path for files
735
     */
736 80
    public function getUseIncludePath()
737
    {
738 80
        return $this->useIncludePath;
739
    }
740
741
    /**
742
     * Does the access mode string indicate readability?
743
     *
744
     * Readable, in this context, only refers to the manner in which this stream
745
     * resource was opened (if it even is opened yet). It is no indicator about
746
     * whether or not the underlying stream actually supports read operations.
747
     * It simply refers to the access mode string passed to it by the user.
748
     *
749
     * @return boolean Whether access mode indicates readability
750
     */
751 52
    public function isReadable()
752
    {
753 52
        return $this->readable;
754
    }
755
756
    /**
757
     * Does the access mode string indicate writability?
758
     *
759
     * Writable, in this context, only refers to the manner in which this stream
760
     * resource was opened (if it even is opened yet). It is no indicator about
761
     * whether or not the underlying stream actually supports write operations.
762
     * It simply refers to the access mode string passed to it by the user.
763
     *
764
     * @return boolean Whether access mode indicates writability
765
     */
766 21
    public function isWritable()
767
    {
768 21
        return $this->writable;
769
    }
770
771
    /**
772
     * Is cursor positioned at the beginning of stream?
773
     *
774
     * Returns true if this stream resource's access mode positions the internal
775
     * cursor at the beginning of the stream.
776
     *
777
     * @return boolean Whether cursor positioned at beginning of stream
778
     */
779 2
    public function isCursorPositionedAtBeginning()
780
    {
781 2
        return $this->base != 'a';
782
    }
783
784
    /**
785
     * Is cursor positioned at the end of stream?
786
     *
787
     * Returns true if this stream resource's access mode positions the internal
788
     * cursor at the end of the stream.
789
     *
790
     * @return boolean Whether cursor positioned at end of stream
791
     */
792 2
    public function isCursorPositionedAtEnd()
793
    {
794 2
        return $this->base == 'a';
795
    }
796
797
    /**
798
     * Is content truncated to zero-length on opening?
799
     *
800
     * Returns true if this stream resource's access mode indicates truncation of
801
     * stream content to zero-length upon opening.
802
     *
803
     * @return boolean Whether stream content is truncated on opening
804
     */
805 3
    public function isTruncated()
806
    {
807 3
        return $this->base == 'w';
808
    }
809
810
    /**
811
     * Does stream access mode indicate file creation?
812
     *
813
     * Returns true if this stream's access mode implies that PHP will attempt to
814
     * create a file if none exists.
815
     *
816
     * @return boolean Whether PHP should attempt to create file at $uri
817
     */
818 1
    public function attemptsFileCreation()
819
    {
820 1
        return $this->base != 'r';
821
    }
822
823
    /**
824
     * Does stream access mode indicate the rejection of existing files?
825
     *
826
     * Returns true if this stream's access mode implies that PHP will fail to
827
     * open a file if it already exists.
828
     *
829
     * @return boolean Whether PHP should attempt to create file at $uri
830
     */
831 1
    public function rejectsExistingFiles()
832
    {
833 1
        return $this->base == 'x';
834
    }
835
836
    /**
837
     * Are write operations appended to the end of the stream?
838
     *
839
     * Returns true if write operations are appended to the end of the stream
840
     * regardless of the position of the read cursor.
841
     *
842
     * @return boolean Whether write operations ore always appended
843
     */
844 1
    public function appendsWriteOps()
845
    {
846 1
        return $this->base == 'w';
847
    }
848
849
    /**
850
     * Assert that stream resource is not open.
851
     *
852
     * Used internally to ensure that stream is not open, since some methods should
853
     * only be called on unopened stream resources.
854
     *
855
     * @param  string The method that is asserting
856
     * @return void
857
     * @throws \CSVelte\Exception\IOException if stream is open
858
     */
859 95
    protected function assertNotConnected($method)
860
    {
861 95
        if ($this->isConnected()) {
862
            throw new IOException("Cannot perform this operation on a stream once it has already been opened: {$method}", IOException::ERR_STREAM_ALREADY_OPEN);
863
        }
864 95
    }
865
866
    /**
867
     * Assert that given wrapper is a valid, registered stream wrapper
868
     *
869
     * Used internally to ensure that a given stream wrapper is valid and available
870
     *
871
     * @param  string The name of the stream wrapper
872
     * @return void
873
     * @throws \InvalidArgumentException if wrapper doesn't exist
874
     */
875 2
    protected function assertValidWrapper($name)
876
    {
877 2
        if (!in_array($name, stream_get_wrappers())) {
878
            throw new InvalidArgumentException("{$name} is not a known stream wrapper.");
879
        }
880 2
    }
881
882
}
883