Projet

Général

Profil

Paste
Télécharger (12,4 ko) Statistiques
| Branche: | Révision:

root / drupal7 / misc / typo3 / phar-stream-wrapper / src / PharStreamWrapper.php @ 6b24a280

1
<?php
2
namespace TYPO3\PharStreamWrapper;
3

    
4
/*
5
 * This file is part of the TYPO3 project.
6
 *
7
 * It is free software; you can redistribute it and/or modify it under the terms
8
 * of the MIT License (MIT). For the full copyright and license information,
9
 * please read the LICENSE file that was distributed with this source code.
10
 *
11
 * The TYPO3 project - inspiring people to share!
12
 */
13

    
14
use TYPO3\PharStreamWrapper\Resolver\PharInvocation;
15

    
16
class PharStreamWrapper
17
{
18
    /**
19
     * Internal stream constants that are not exposed to PHP, but used...
20
     * @see https://github.com/php/php-src/blob/e17fc0d73c611ad0207cac8a4a01ded38251a7dc/main/php_streams.h
21
     */
22
    const STREAM_OPEN_FOR_INCLUDE = 128;
23

    
24
    /**
25
     * @var resource
26
     */
27
    public $context;
28

    
29
    /**
30
     * @var resource
31
     */
32
    protected $internalResource;
33

    
34
    /**
35
     * @var PharInvocation
36
     */
37
    protected $invocation;
38

    
39
    /**
40
     * @return bool
41
     */
42
    public function dir_closedir()
43
    {
44
        if (!is_resource($this->internalResource)) {
45
            return false;
46
        }
47

    
48
        $this->invokeInternalStreamWrapper(
49
            'closedir',
50
            $this->internalResource
51
        );
52
        return !is_resource($this->internalResource);
53
    }
54

    
55
    /**
56
     * @param string $path
57
     * @param int $options
58
     * @return bool
59
     */
60
    public function dir_opendir($path, $options)
61
    {
62
        $this->assert($path, Behavior::COMMAND_DIR_OPENDIR);
63
        $this->internalResource = $this->invokeInternalStreamWrapper(
64
            'opendir',
65
            $path,
66
            $this->context
67
        );
68
        return is_resource($this->internalResource);
69
    }
70

    
71
    /**
72
     * @return string|false
73
     */
74
    public function dir_readdir()
75
    {
76
        return $this->invokeInternalStreamWrapper(
77
            'readdir',
78
            $this->internalResource
79
        );
80
    }
81

    
82
    /**
83
     * @return bool
84
     */
85
    public function dir_rewinddir()
86
    {
87
        if (!is_resource($this->internalResource)) {
88
            return false;
89
        }
90

    
91
        $this->invokeInternalStreamWrapper(
92
            'rewinddir',
93
            $this->internalResource
94
        );
95
        return is_resource($this->internalResource);
96
    }
97

    
98
    /**
99
     * @param string $path
100
     * @param int $mode
101
     * @param int $options
102
     * @return bool
103
     */
104
    public function mkdir($path, $mode, $options)
105
    {
106
        $this->assert($path, Behavior::COMMAND_MKDIR);
107
        return $this->invokeInternalStreamWrapper(
108
            'mkdir',
109
            $path,
110
            $mode,
111
            (bool) ($options & STREAM_MKDIR_RECURSIVE),
112
            $this->context
113
        );
114
    }
115

    
116
    /**
117
     * @param string $path_from
118
     * @param string $path_to
119
     * @return bool
120
     */
121
    public function rename($path_from, $path_to)
122
    {
123
        $this->assert($path_from, Behavior::COMMAND_RENAME);
124
        $this->assert($path_to, Behavior::COMMAND_RENAME);
125
        return $this->invokeInternalStreamWrapper(
126
            'rename',
127
            $path_from,
128
            $path_to,
129
            $this->context
130
        );
131
    }
132

    
133
    /**
134
     * @param string $path
135
     * @param int $options
136
     * @return bool
137
     */
138
    public function rmdir($path, $options)
139
    {
140
        $this->assert($path, Behavior::COMMAND_RMDIR);
141
        return $this->invokeInternalStreamWrapper(
142
            'rmdir',
143
            $path,
144
            $this->context
145
        );
146
    }
147

    
148
    /**
149
     * @param int $cast_as
150
     */
151
    public function stream_cast($cast_as)
152
    {
153
        throw new Exception(
154
            'Method stream_select() cannot be used',
155
            1530103999
156
        );
157
    }
158

    
159
    public function stream_close()
160
    {
161
        $this->invokeInternalStreamWrapper(
162
            'fclose',
163
            $this->internalResource
164
        );
165
    }
166

    
167
    /**
168
     * @return bool
169
     */
170
    public function stream_eof()
171
    {
172
        return $this->invokeInternalStreamWrapper(
173
            'feof',
174
            $this->internalResource
175
        );
176
    }
177

    
178
    /**
179
     * @return bool
180
     */
181
    public function stream_flush()
182
    {
183
        return $this->invokeInternalStreamWrapper(
184
            'fflush',
185
            $this->internalResource
186
        );
187
    }
188

    
189
    /**
190
     * @param int $operation
191
     * @return bool
192
     */
193
    public function stream_lock($operation)
194
    {
195
        return $this->invokeInternalStreamWrapper(
196
            'flock',
197
            $this->internalResource,
198
            $operation
199
        );
200
    }
201

    
202
    /**
203
     * @param string $path
204
     * @param int $option
205
     * @param string|int $value
206
     * @return bool
207
     */
208
    public function stream_metadata($path, $option, $value)
209
    {
210
        $this->assert($path, Behavior::COMMAND_STEAM_METADATA);
211
        if ($option === STREAM_META_TOUCH) {
212
            return call_user_func_array(
213
                array($this, 'invokeInternalStreamWrapper'),
214
                array_merge(array('touch', $path), (array) $value)
215
            );
216
        }
217
        if ($option === STREAM_META_OWNER_NAME || $option === STREAM_META_OWNER) {
218
            return $this->invokeInternalStreamWrapper(
219
                'chown',
220
                $path,
221
                $value
222
            );
223
        }
224
        if ($option === STREAM_META_GROUP_NAME || $option === STREAM_META_GROUP) {
225
            return $this->invokeInternalStreamWrapper(
226
                'chgrp',
227
                $path,
228
                $value
229
            );
230
        }
231
        if ($option === STREAM_META_ACCESS) {
232
            return $this->invokeInternalStreamWrapper(
233
                'chmod',
234
                $path,
235
                $value
236
            );
237
        }
238
        return false;
239
    }
240

    
241
    /**
242
     * @param string $path
243
     * @param string $mode
244
     * @param int $options
245
     * @param string|null $opened_path
246
     * @return bool
247
     */
248
    public function stream_open(
249
        $path,
250
        $mode,
251
        $options,
252
        &$opened_path = null
253
    ) {
254
        $this->assert($path, Behavior::COMMAND_STREAM_OPEN);
255
        $arguments = array($path, $mode, (bool) ($options & STREAM_USE_PATH));
256
        // only add stream context for non include/require calls
257
        if (!($options & static::STREAM_OPEN_FOR_INCLUDE)) {
258
            $arguments[] = $this->context;
259
        // work around https://bugs.php.net/bug.php?id=66569
260
        // for including files from Phar stream with OPcache enabled
261
        } else {
262
            Helper::resetOpCache();
263
        }
264
        $this->internalResource = call_user_func_array(
265
            array($this, 'invokeInternalStreamWrapper'),
266
            array_merge(array('fopen'), $arguments)
267
        );
268
        if (!is_resource($this->internalResource)) {
269
            return false;
270
        }
271
        if ($opened_path !== null) {
272
            $metaData = stream_get_meta_data($this->internalResource);
273
            $opened_path = $metaData['uri'];
274
        }
275
        return true;
276
    }
277

    
278
    /**
279
     * @param int $count
280
     * @return string
281
     */
282
    public function stream_read($count)
283
    {
284
        return $this->invokeInternalStreamWrapper(
285
            'fread',
286
            $this->internalResource,
287
            $count
288
        );
289
    }
290

    
291
    /**
292
     * @param int $offset
293
     * @param int $whence
294
     * @return bool
295
     */
296
    public function stream_seek($offset, $whence = SEEK_SET)
297
    {
298
        return $this->invokeInternalStreamWrapper(
299
            'fseek',
300
            $this->internalResource,
301
            $offset,
302
            $whence
303
        ) !== -1;
304
    }
305

    
306
    /**
307
     * @param int $option
308
     * @param int $arg1
309
     * @param int $arg2
310
     * @return bool
311
     */
312
    public function stream_set_option($option, $arg1, $arg2)
313
    {
314
        if ($option === STREAM_OPTION_BLOCKING) {
315
            return $this->invokeInternalStreamWrapper(
316
                'stream_set_blocking',
317
                $this->internalResource,
318
                $arg1
319
            );
320
        }
321
        if ($option === STREAM_OPTION_READ_TIMEOUT) {
322
            return $this->invokeInternalStreamWrapper(
323
                'stream_set_timeout',
324
                $this->internalResource,
325
                $arg1,
326
                $arg2
327
            );
328
        }
329
        if ($option === STREAM_OPTION_WRITE_BUFFER) {
330
            return $this->invokeInternalStreamWrapper(
331
                'stream_set_write_buffer',
332
                $this->internalResource,
333
                $arg2
334
            ) === 0;
335
        }
336
        return false;
337
    }
338

    
339
    /**
340
     * @return array
341
     */
342
    public function stream_stat()
343
    {
344
        return $this->invokeInternalStreamWrapper(
345
            'fstat',
346
            $this->internalResource
347
        );
348
    }
349

    
350
    /**
351
     * @return int
352
     */
353
    public function stream_tell()
354
    {
355
        return $this->invokeInternalStreamWrapper(
356
            'ftell',
357
            $this->internalResource
358
        );
359
    }
360

    
361
    /**
362
     * @param int $new_size
363
     * @return bool
364
     */
365
    public function stream_truncate($new_size)
366
    {
367
        return $this->invokeInternalStreamWrapper(
368
            'ftruncate',
369
            $this->internalResource,
370
            $new_size
371
        );
372
    }
373

    
374
    /**
375
     * @param string $data
376
     * @return int
377
     */
378
    public function stream_write($data)
379
    {
380
        return $this->invokeInternalStreamWrapper(
381
            'fwrite',
382
            $this->internalResource,
383
            $data
384
        );
385
    }
386

    
387
    /**
388
     * @param string $path
389
     * @return bool
390
     */
391
    public function unlink($path)
392
    {
393
        $this->assert($path, Behavior::COMMAND_UNLINK);
394
        return $this->invokeInternalStreamWrapper(
395
            'unlink',
396
            $path,
397
            $this->context
398
        );
399
    }
400

    
401
    /**
402
     * @param string $path
403
     * @param int $flags
404
     * @return array|false
405
     */
406
    public function url_stat($path, $flags)
407
    {
408
        $this->assert($path, Behavior::COMMAND_URL_STAT);
409
        $functionName = $flags & STREAM_URL_STAT_QUIET ? '@stat' : 'stat';
410
        return $this->invokeInternalStreamWrapper($functionName, $path);
411
    }
412

    
413
    /**
414
     * @param string $path
415
     * @param string $command
416
     */
417
    protected function assert($path, $command)
418
    {
419
        if (Manager::instance()->assert($path, $command) === true) {
420
            $this->collectInvocation($path);
421
            return;
422
        }
423

    
424
        throw new Exception(
425
            sprintf(
426
                'Denied invocation of "%s" for command "%s"',
427
                $path,
428
                $command
429
            ),
430
            1535189880
431
        );
432
    }
433

    
434
    /**
435
     * @param string $path
436
     */
437
    protected function collectInvocation($path)
438
    {
439
        if (isset($this->invocation)) {
440
            return;
441
        }
442

    
443
        $manager = Manager::instance();
444
        $this->invocation = $manager->resolve($path);
445
        if ($this->invocation === null) {
446
            throw new Exception(
447
                'Expected invocation could not be resolved',
448
                1556389591
449
            );
450
        }
451
        // confirm, previous interceptor(s) validated invocation
452
        $this->invocation->confirm();
453
        $collection = $manager->getCollection();
454
        if (!$collection->has($this->invocation)) {
455
            $collection->collect($this->invocation);
456
        }
457
    }
458

    
459
    /**
460
     * @return Manager|Assertable
461
     * @deprecated Use Manager::instance() directly
462
     */
463
    protected function resolveAssertable()
464
    {
465
        return Manager::instance();
466
    }
467

    
468
    /**
469
     * Invokes commands on the native PHP Phar stream wrapper.
470
     *
471
     * @param string $functionName
472
     * @param mixed ...$arguments
473
     * @return mixed
474
     */
475
    private function invokeInternalStreamWrapper($functionName)
476
    {
477
        $arguments = func_get_args();
478
        array_shift($arguments);
479
        $silentExecution = $functionName[0] === '@';
480
        $functionName = ltrim($functionName, '@');
481
        $this->restoreInternalSteamWrapper();
482

    
483
        try {
484
            if ($silentExecution) {
485
                $result = @call_user_func_array($functionName, $arguments);
486
            } else {
487
                $result = call_user_func_array($functionName, $arguments);
488
            }
489
        } catch (\Exception $exception) {
490
            $this->registerStreamWrapper();
491
            throw $exception;
492
        } catch (\Throwable $throwable) {
493
            $this->registerStreamWrapper();
494
            throw $throwable;
495
        }
496

    
497
        $this->registerStreamWrapper();
498
        return $result;
499
    }
500

    
501
    private function restoreInternalSteamWrapper()
502
    {
503
        stream_wrapper_restore('phar');
504
    }
505

    
506
    private function registerStreamWrapper()
507
    {
508
        stream_wrapper_unregister('phar');
509
        stream_wrapper_register('phar', get_class($this));
510
    }
511
}