Completed
Push — releases/v0.2.1 ( bb9532...8d5d04 )
by Luke
02:55
created

Resource::isWritable()   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 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 ($errno, $errstr, $errfile, $errline) use (&$e) {
243 4
                $e = new IOException(sprintf(
244 4
                    "Could not open connection for %s using mode %s:\n\nError: #%d (%s)\n Found at: %s:%d",
245 4
                    $this->getUri(),
246 4
                    $this->getMode(),
247 4
                    $errno,
248 4
                    $errstr,
249 4
                    $errfile,
250
                    $errline
251 4
                ), IOException::ERR_STREAM_CONNECTION_FAILED);
252 79
            };
253 79
            set_error_handler($errhandler->bindTo($this));
254 79
            $this->conn = fopen(
255 79
                $this->getUri(),
256 79
                $this->getMode(),
257 79
                $this->getUseIncludePath(),
258 79
                $this->getContext()
259 79
            );
260 79
            restore_error_handler();
261 79
            if ($e) throw $e;
262 75
        }
263 75
        return $this->isConnected();
264
    }
265
266
    /**
267
     * Close connection.
268
     *
269
     * Close the connection to this stream (if open).
270
     *
271
     * @return boolean|null Whether close was successful, or null if already closed
272
     */
273 89
    public function disconnect()
274
    {
275 89
        if ($this->isConnected()) {
276 80
            return fclose($this->conn);
277
        }
278
        // return null if nothing to close
279 78
        return;
280
    }
281
282
    /**
283
     * Set stream URI.
284
     *
285
     * Set the stream URI. Can only be set if the connection isn't open yet.
286
     * If you try to set the URI on an open resource, an IOException will be thrown
287
     *
288
     * @param string $uri The URI for this stream resource to open
289
     * @return $this
290
     * @throws \InvalidArgumentException if not a valid stream uri
291
     * @throws \CSVelte\Exception\IOException if stream has already been opened
292
     * @todo I'm pretty sure that the parse_url function is too restrictive. It
293
     *     will reject URIs that are perfectly valid.
294
     */
295 94
    public function setUri($uri)
296
    {
297 94
        $this->assertNotConnected(__METHOD__);
298
299 94
        if (is_object($uri) && method_exists($uri, '__toString')) {
300
            $uri = (string) $uri;
301
        }
302
303 94
        if (is_string($uri) && parse_url($uri)) {
304 93
            $this->uri = $uri;
305 93
            return $this;
306
        }
307
308 1
        throw new InvalidArgumentException(sprintf(
309 1
            'Not a valid stream uri, expected "%s", got: "%s"',
310 1
            'string',
311 1
            gettype($uri)
312 1
        ));
313
    }
314
315
    /**
316
     * Set the fopen mode.
317
     *
318
     * Thank you to GitHub user "binsoul" whose AccessMode class inspired this
319
     * Also thanks to the author(s) of Guzzle streams implementation, where the
320
     * readwritehash idea came from. Both libraries are MIT licensed, so my
321
     * merciless theft of their code is alright.
322
     *
323
     * @param string $mode A 1-3 character string determining open mode
324
     * @return $this
325
     * @throws \InvalidArgumentException if not a valid stream access mode
326
     * @throws \CSVelte\Exception\IOException if stream has already been opened
327
     * @see http://php.net/manual/en/function.fopen.php
328
     * @see https://github.com/binsoul/io-stream/blob/master/src/AccessMode.php
329
     * @see https://raw.githubusercontent.com/guzzle/streams/master/src/Stream.php
330
     * @todo convert $mode to lower case and test it
331
     */
332 93
    public function setMode($mode = null)
333
    {
334 93
        $this->assertNotConnected(__METHOD__);
335 93
        if (is_null($mode)) $mode = "r+b";
336
337 93
        $mode = substr($mode, 0, 3);
338 93
        $rest = substr($mode, 1);
339
340 93
        $base = substr($mode, 0, 1);
341 93
        $plus = (strpos($rest, '+') !== false) ? '+' : '';
342 93
        $flag = trim($rest, '+');
343
344 93
        $this->flag = '';
345 93
        $this->setBaseMode($base)
346 93
             ->setIsPlus($plus == '+')
347 93
             ->setIsText($flag == 't')
348 93
             ->setIsBinary($flag == 'b');
349
350 93
        return $this;
351
    }
352
353
    /**
354
     * Update access parameters.
355
     *
356
     * After changing any of the access mode parameters, this method must be
357
     * called in order for readable and writable to stay accurate.
358
     *
359
     * @return $this
360
     */
361 93
    protected function updateAccess()
362
    {
363 93
        $this->readable = isset(self::$readWriteHash['read'][$this->getMode()]);
364 93
        $this->writable = isset(self::$readWriteHash['write'][$this->getMode()]);
365 93
        return $this;
366
    }
367
368
    /**
369
     * Set base access mode character.
370
     *
371
     * @param string $base The base mode character (must be one of "rwaxc")
372
     * @return $this
373
     * @throws \InvalidArgumentException If passed invalid base char
374
     * @throws \CSVelte\Exception\IOException if stream has already been opened
375
     */
376 93
    public function setBaseMode($base)
377
    {
378 93
        $this->assertNotConnected(__METHOD__);
379 93
        if (strpos(self::$bases, $base) === false) {
380
            throw new InvalidArgumentException("\"{$base}\" is not a valid base stream access mode.");
381
        }
382 93
        $this->base = $base;
383 93
        return $this->updateAccess();
384
    }
385
386
    /**
387
     * Set plus mode.
388
     *
389
     * @param boolean $isPlus Whether base access mode should include the + sign
390
     * @return $this
391
     * @throws \CSVelte\Exception\IOException if stream has already been opened
392
     */
393 93
    public function setIsPlus($isPlus)
394
    {
395 93
        $this->assertNotConnected(__METHOD__);
396 93
        $this->plus = $isPlus ? '+' : '';
397 93
        return $this->updateAccess();
398
    }
399
400
    /**
401
     * Set binary-safe mode.
402
     *
403
     * @param boolean $isBinary Whether binary safe mode or not
404
     * @return $this
405
     * @throws \CSVelte\Exception\IOException if stream has already been opened
406
     */
407 93
    public function setIsBinary($isBinary)
408
    {
409 93
        $this->assertNotConnected(__METHOD__);
410 93
        if ($isBinary) {
411 66
            $this->flag = 'b';
412 66
        }
413 93
        return $this;
414
    }
415
416
    /**
417
     * Set text mode.
418
     *
419
     * @param boolean $isText Whether text mode or not
420
     * @return $this
421
     * @throws \CSVelte\Exception\IOException if stream has already been opened
422
     */
423 93
    public function setIsText($isText)
424
    {
425 93
        $this->assertNotConnected(__METHOD__);
426 93
        if ($isText) {
427 1
            $this->flag = 't';
428 1
        }
429 93
        return $this;
430
    }
431
432
    /**
433
     * Set lazy flag.
434
     *
435
     * Set the lazy flag, which tells the class whether to defer the connection
436
     * until the user specifically requests it.
437
     *
438
     * @param boolean|null Whether or not to "lazily" open the stream
439
     * @return $this
440
     */
441 88
    protected function setLazy($lazy)
442
    {
443 88
        if (is_null($lazy)) $lazy = true;
444 88
        $this->lazy = (boolean) $lazy;
445 88
        return $this;
446
    }
447
448
    /**
449
     * Set use include path flag.
450
     *
451
     * Sets whether or not fopen should search the include path for files. Can
452
     * only be set if resource isn't open already. If called when resource is
453
     * already open an exception will be thrown.
454
     *
455
     * @param boolean $use_include_path Whether to search include path for files
456
     * @throws \CSVelte\Exception\IOException
457
     * @return $this
458
     */
459 88
    public function setUseIncludePath($use_include_path)
460
    {
461 88
        $this->assertNotConnected(__METHOD__);
462 88
        $this->useIncludePath = (boolean) $use_include_path;
463 88
        return $this;
464
    }
465
466
    /**
467
     * Set stream context options and params.
468
     *
469
     * Sets arrays of stream context options and params. Check out the URI below
470
     * for more on stream contexts.
471
     *
472
     * @param array|null $options Stream context options
473
     * @param array|null $params  Stream Context params
474
     * @return $this
475
     * @see http://php.net/manual/en/stream.contexts.php
476
     */
477 88
    public function setContext($options = null, $params = null)
478
    {
479 88
        if (is_array($options)) {
480 2
            foreach ($options as $wrap => $opts) {
481 2
                $this->setContextOptions($opts, $wrap);
482 2
            }
483 2
        }
484 88
        if (!is_null($params)) {
485 2
            $this->setContextParams($params);
486 2
        }
487 88
        return $this;
488
    }
489
490
    /**
491
     * Set context resource directly
492
     * @param resource|null $context Stream context resource to set directly
493
     * @return $this
494
     * @see http://php.net/manual/en/function.stream-context-create.php
495
     * @todo Need to write a unit test for passing this method a null value
496
     */
497 75
    public function setContextResource($context)
498
    {
499 75
        if (!is_null($context)) {
500 8
            if (!is_resource($context) || get_resource_type($context) != "stream-context") {
501 4
                throw new InvalidArgumentException(sprintf(
502 4
                    "Invalid argument for %s. Expecting resource of type \"stream-context\" but got: \"%s\"",
503 4
                    __METHOD__,
504 4
                    gettype($context)
505 4
                ));
506
            }
507
            // don't need to call updateContext() because its already a context resource
508 4
            $this->crh = $context;
509 4
        }
510 71
        return $this;
511
    }
512
513
    /**
514
     * Update the stream context.
515
     *
516
     * After setting/updating stream context options and/or params, this method
517
     * must be called in order to update the stream context resource.
518
     *
519
     * @return $this
520
     */
521 2
    protected function updateContext()
522
    {
523
        // if already connected, set the options on the context resource
524
        // otherwise, it will be set at connection time
525 2
        if ($this->isConnected()) {
526
            // set options and params on existing stream resource
527 1
            stream_context_set_params(
528 1
                $this->getContext(),
529 1
                $this->getContextParams() + [
530 1
                    'options' => $this->getContextOptions()
531 1
                ]
532 1
            );
533 1
        }
534 2
        return $this;
535
    }
536
537
    /**
538
     * Set context options.
539
     *
540
     * Sets stream context options for this stream resource.
541
     *
542
     * @param array $options An array of stream context options
543
     * @param string $wrapper The wrapper these options belong to (if no wrapper
544
     *     argument, then $options should be an associative array with key being
545
     *     a wrapper name and value being its options)
546
     * @return $this
547
     * @throws \InvalidArgumentException if passed invalid options or wrapper
548
     * @see http://php.net/manual/en/stream.contexts.php
549
     */
550 2
    public function setContextOptions($options, $wrapper = null)
551
    {
552 2
        if (is_array($options)) {
553 2
            if (is_null($wrapper)) {
554
                $this->context['options'] = $options;
555
            } else {
556 2
                $this->assertValidWrapper($wrapper);
557 2
                $this->context['options'][$wrapper] = $options;
558
            }
559 2
            $this->updateContext();
560 2
            return $this;
561
        }
562
        throw new InvalidArgumentException("Context options must be an array, got: " . gettype($options));
563
    }
564
565
    /**
566
     * Set context params.
567
     *
568
     * Set the context params for this stream resource.
569
     *
570
     * @param array $params An array of stream resource params
571
     * @return $this
572
     * @throws \InvalidArgumentException if passed invalid params
573
     * @see http://php.net/manual/en/stream.contexts.php
574
     */
575 2
    public function setContextParams($params)
576
    {
577 2
        if (is_array($params)) {
578 2
            $this->context['params'] = $params;
579 2
            $this->updateContext();
580 2
            return $this;
581
        }
582
        throw new InvalidArgumentException("Context parameters must be an array, got: " . gettype($params));
583
    }
584
585
    /**
586
     * Get context options for this stream resource.
587
     *
588
     * Returns the stream context options for this stream resource. Either all
589
     * options for all wrappers, or just the options for the specified wrapper.
590
     *
591
     * @param  string $wrapper If present, return options only for this wrapper
592
     * @return array Context options (either all or for specified wrapper)
593
     * @throws \InvalidArgumentException if the wrapper doesn't exist
594
     */
595 79
    public function getContextOptions($wrapper = null)
596
    {
597 79
        if (is_null($wrapper)) {
598 79
            return $this->context['options'];
599
        }
600
        $this->assertValidWrapper($wrapper);
601
        if (isset($this->context['options'][$wrapper])) {
602
            return $this->context['options'][$wrapper];
603
        }
604
    }
605
606
    /**
607
     * Get context params for this stream resource.
608
     *
609
     * Returns the stream context params for this stream resource.
610
     *
611
     * @return array Context params for this stream resource
612
     */
613 79
    public function getContextParams()
614
    {
615 79
        return $this->context['params'];
616
    }
617
618
    /**
619
     * Get stream context resource.
620
     * @return resource|null The stream context resource
621
     */
622 82
    public function getContext()
623
    {
624
        // if context resource hasn't been created, create one
625 82
        if (is_null($this->crh)) {
626 79
            $this->crh = stream_context_create(
627 79
                $this->getContextOptions(),
628 79
                $this->getContextParams()
629 79
            );
630 79
        }
631
        // return context resource handle
632 82
        return $this->crh;
633
    }
634
635
    /**
636
     * Retrieve underlying stream resource handle.
637
     *
638
     * An accessor method for the underlying stream resource object. Also triggers
639
     * stream connection if in lazy open mode. Because this method may potentially
640
     * call the connect() method, it is possible that it may throw an exception
641
     * if there is some issue with opening the stream.
642
     *
643
     * @return resource The underlying stream resource handle
644
     * @throws \CSVelte\Exception\IOException
645
     */
646 66
    public function getHandle()
647
    {
648 66
        if (!$this->isConnected() && $this->isLazy()) {
649 5
            $this->connect();
650 4
        }
651 65
        return $this->conn;
652
    }
653
654
    /**
655
     * Is the stream connection open?
656
     *
657
     * Tells you whether this stream resource is open or not.
658
     *
659
     * @return boolean Whether the stream is open
660
     */
661 94
    public function isConnected()
662
    {
663 94
        return is_resource($this->conn);
664
    }
665
666
    /**
667
     * Get the stream URI.
668
     *
669
     * Accessor method for stream URI.
670
     *
671
     * @return string The stream URI
672
     */
673 79
    public function getUri()
674
    {
675 79
        return $this->uri;
676
    }
677
678
    /**
679
     * Get the access mode.
680
     *
681
     * Tells you what the access mode is. This is the short string of characters
682
     * that you would pass to the fopen function to tell it how to open a file/stream
683
     *
684
     * @return string The file/stream access mode
685
     * @see http://php.net/manual/en/function.fopen.php
686
     */
687 93
    public function getMode()
688
    {
689 93
        return sprintf(
690 93
            "%s%s%s",
691 93
            $this->base,
692 93
            $this->plus,
693 93
            $this->flag
694 93
        );
695
    }
696
697
    /**
698
     * Is access mode binary-safe?
699
     * @return boolean Whether binary-safe flag is set
700
     */
701 4
    public function isBinary()
702
    {
703 4
        return $this->flag == "b";
704
    }
705
706
    /**
707
     * Is stream connected in text mode?
708
     * @return boolean Whether text mode flag is set
709
     */
710 3
    public function isText()
711
    {
712 3
        return $this->flag == "t";
713
    }
714
715
    /**
716
     * Is this a lazy open resource?
717
     * @return boolean Whether this is a lazily-opened resource
718
     */
719 88
    public function isLazy()
720
    {
721 88
        return $this->lazy;
722
    }
723
724
    /**
725
     * Should fopen search include path?
726
     * @return boolean Whether fopen should search include path for files
727
     */
728 79
    public function getUseIncludePath()
729
    {
730 79
        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 boolean Whether access mode indicates readability
742
     */
743 51
    public function isReadable()
744
    {
745 51
        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 boolean Whether access mode indicates writability
757
     */
758 21
    public function isWritable()
759
    {
760 21
        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 boolean 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 boolean 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 boolean 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 boolean 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 boolean 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 boolean Whether write operations ore always appended
835
     */
836 1
    public function appendsWriteOps()
837
    {
838 1
        return $this->base == 'w';
839
    }
840
841
    /**
842
     * Assert that stream resource is not open.
843
     *
844
     * Used internally to ensure that stream is not open, since some methods should
845
     * only be called on unopened stream resources.
846
     *
847
     * @param  string The method that is asserting
848
     * @return void
849
     * @throws \CSVelte\Exception\IOException if stream is open
850
     */
851 94
    protected function assertNotConnected($method)
852
    {
853 94
        if ($this->isConnected()) {
854
            throw new IOException("Cannot perform this operation on a stream once it has already been opened: {$method}", IOException::ERR_STREAM_ALREADY_OPEN);
855
        }
856 94
    }
857
858
    /**
859
     * Assert that given wrapper is a valid, registered stream wrapper
860
     *
861
     * Used internally to ensure that a given stream wrapper is valid and available
862
     *
863
     * @param  string The name of the stream wrapper
864
     * @return void
865
     * @throws \InvalidArgumentException if wrapper doesn't exist
866
     */
867 2
    protected function assertValidWrapper($name)
868
    {
869 2
        if (!in_array($name, stream_get_wrappers())) {
870
            throw new InvalidArgumentException("{$name} is not a known stream wrapper.");
871
        }
872 2
    }
873
874
}
875