Completed
Push — releases/v0.2.1 ( 8d5d04...e66e88 )
by Luke
03:06
created

Resource::updateContext()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 2

Importance

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