Projet

Général

Profil

Paste
Télécharger (11,5 ko) Statistiques
| Branche: | Révision:

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

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

    
22
    /**
23
     * @var resource
24
     */
25
    public $context;
26

    
27
    /**
28
     * @var resource
29
     */
30
    protected $internalResource;
31

    
32
    /**
33
     * @return bool
34
     */
35
    public function dir_closedir()
36
    {
37
        if (!is_resource($this->internalResource)) {
38
            return false;
39
        }
40

    
41
        $this->invokeInternalStreamWrapper(
42
            'closedir',
43
            $this->internalResource
44
        );
45
        return !is_resource($this->internalResource);
46
    }
47

    
48
    /**
49
     * @param string $path
50
     * @param int $options
51
     * @return bool
52
     */
53
    public function dir_opendir($path, $options)
54
    {
55
        $this->assert($path, Behavior::COMMAND_DIR_OPENDIR);
56
        $this->internalResource = $this->invokeInternalStreamWrapper(
57
            'opendir',
58
            $path,
59
            $this->context
60
        );
61
        return is_resource($this->internalResource);
62
    }
63

    
64
    /**
65
     * @return string|false
66
     */
67
    public function dir_readdir()
68
    {
69
        return $this->invokeInternalStreamWrapper(
70
            'readdir',
71
            $this->internalResource
72
        );
73
    }
74

    
75
    /**
76
     * @return bool
77
     */
78
    public function dir_rewinddir()
79
    {
80
        if (!is_resource($this->internalResource)) {
81
            return false;
82
        }
83

    
84
        $this->invokeInternalStreamWrapper(
85
            'rewinddir',
86
            $this->internalResource
87
        );
88
        return is_resource($this->internalResource);
89
    }
90

    
91
    /**
92
     * @param string $path
93
     * @param int $mode
94
     * @param int $options
95
     * @return bool
96
     */
97
    public function mkdir($path, $mode, $options)
98
    {
99
        $this->assert($path, Behavior::COMMAND_MKDIR);
100
        return $this->invokeInternalStreamWrapper(
101
            'mkdir',
102
            $path,
103
            $mode,
104
            (bool) ($options & STREAM_MKDIR_RECURSIVE),
105
            $this->context
106
        );
107
    }
108

    
109
    /**
110
     * @param string $path_from
111
     * @param string $path_to
112
     * @return bool
113
     */
114
    public function rename($path_from, $path_to)
115
    {
116
        $this->assert($path_from, Behavior::COMMAND_RENAME);
117
        $this->assert($path_to, Behavior::COMMAND_RENAME);
118
        return $this->invokeInternalStreamWrapper(
119
            'rename',
120
            $path_from,
121
            $path_to,
122
            $this->context
123
        );
124
    }
125

    
126
    /**
127
     * @param string $path
128
     * @param int $options
129
     * @return bool
130
     */
131
    public function rmdir($path, $options)
132
    {
133
        $this->assert($path, Behavior::COMMAND_RMDIR);
134
        return $this->invokeInternalStreamWrapper(
135
            'rmdir',
136
            $path,
137
            $this->context
138
        );
139
    }
140

    
141
    /**
142
     * @param int $cast_as
143
     */
144
    public function stream_cast($cast_as)
145
    {
146
        throw new Exception(
147
            'Method stream_select() cannot be used',
148
            1530103999
149
        );
150
    }
151

    
152
    public function stream_close()
153
    {
154
        $this->invokeInternalStreamWrapper(
155
            'fclose',
156
            $this->internalResource
157
        );
158
    }
159

    
160
    /**
161
     * @return bool
162
     */
163
    public function stream_eof()
164
    {
165
        return $this->invokeInternalStreamWrapper(
166
            'feof',
167
            $this->internalResource
168
        );
169
    }
170

    
171
    /**
172
     * @return bool
173
     */
174
    public function stream_flush()
175
    {
176
        return $this->invokeInternalStreamWrapper(
177
            'fflush',
178
            $this->internalResource
179
        );
180
    }
181

    
182
    /**
183
     * @param int $operation
184
     * @return bool
185
     */
186
    public function stream_lock($operation)
187
    {
188
        return $this->invokeInternalStreamWrapper(
189
            'flock',
190
            $this->internalResource,
191
            $operation
192
        );
193
    }
194

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

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

    
271
    /**
272
     * @param int $count
273
     * @return string
274
     */
275
    public function stream_read($count)
276
    {
277
        return $this->invokeInternalStreamWrapper(
278
            'fread',
279
            $this->internalResource,
280
            $count
281
        );
282
    }
283

    
284
    /**
285
     * @param int $offset
286
     * @param int $whence
287
     * @return bool
288
     */
289
    public function stream_seek($offset, $whence = SEEK_SET)
290
    {
291
        return $this->invokeInternalStreamWrapper(
292
            'fseek',
293
            $this->internalResource,
294
            $offset,
295
            $whence
296
        ) !== -1;
297
    }
298

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

    
332
    /**
333
     * @return array
334
     */
335
    public function stream_stat()
336
    {
337
        return $this->invokeInternalStreamWrapper(
338
            'fstat',
339
            $this->internalResource
340
        );
341
    }
342

    
343
    /**
344
     * @return int
345
     */
346
    public function stream_tell()
347
    {
348
        return $this->invokeInternalStreamWrapper(
349
            'ftell',
350
            $this->internalResource
351
        );
352
    }
353

    
354
    /**
355
     * @param int $new_size
356
     * @return bool
357
     */
358
    public function stream_truncate($new_size)
359
    {
360
        return $this->invokeInternalStreamWrapper(
361
            'ftruncate',
362
            $this->internalResource,
363
            $new_size
364
        );
365
    }
366

    
367
    /**
368
     * @param string $data
369
     * @return int
370
     */
371
    public function stream_write($data)
372
    {
373
        return $this->invokeInternalStreamWrapper(
374
            'fwrite',
375
            $this->internalResource,
376
            $data
377
        );
378
    }
379

    
380
    /**
381
     * @param string $path
382
     * @return bool
383
     */
384
    public function unlink($path)
385
    {
386
        $this->assert($path, Behavior::COMMAND_UNLINK);
387
        return $this->invokeInternalStreamWrapper(
388
            'unlink',
389
            $path,
390
            $this->context
391
        );
392
    }
393

    
394
    /**
395
     * @param string $path
396
     * @param int $flags
397
     * @return array|false
398
     */
399
    public function url_stat($path, $flags)
400
    {
401
        $this->assert($path, Behavior::COMMAND_URL_STAT);
402
        $functionName = $flags & STREAM_URL_STAT_QUIET ? '@stat' : 'stat';
403
        return $this->invokeInternalStreamWrapper($functionName, $path);
404
    }
405

    
406
    /**
407
     * @param string $path
408
     * @param string $command
409
     */
410
    protected function assert($path, $command)
411
    {
412
        if ($this->resolveAssertable()->assert($path, $command) === true) {
413
            return;
414
        }
415

    
416
        throw new Exception(
417
            sprintf(
418
                'Denied invocation of "%s" for command "%s"',
419
                $path,
420
                $command
421
            ),
422
            1535189880
423
        );
424
    }
425

    
426
    /**
427
     * @return Assertable
428
     */
429
    protected function resolveAssertable()
430
    {
431
        return Manager::instance();
432
    }
433

    
434
    /**
435
     * Invokes commands on the native PHP Phar stream wrapper.
436
     *
437
     * @param string $functionName
438
     * @param mixed ...$arguments
439
     * @return mixed
440
     */
441
    private function invokeInternalStreamWrapper($functionName)
442
    {
443
        $arguments = func_get_args();
444
        array_shift($arguments);
445
        $silentExecution = $functionName{0} === '@';
446
        $functionName = ltrim($functionName, '@');
447
        $this->restoreInternalSteamWrapper();
448

    
449
        try {
450
            if ($silentExecution) {
451
                $result = @call_user_func_array($functionName, $arguments);
452
            } else {
453
                $result = call_user_func_array($functionName, $arguments);
454
            }
455
        } catch (\Exception $exception) {
456
            $this->registerStreamWrapper();
457
            throw $exception;
458
        } catch (\Throwable $throwable) {
459
            $this->registerStreamWrapper();
460
            throw $throwable;
461
        }
462

    
463
        $this->registerStreamWrapper();
464
        return $result;
465
    }
466

    
467
    private function restoreInternalSteamWrapper()
468
    {
469
        stream_wrapper_restore('phar');
470
    }
471

    
472
    private function registerStreamWrapper()
473
    {
474
        stream_wrapper_unregister('phar');
475
        stream_wrapper_register('phar', get_class($this));
476
    }
477
}