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