annotate vendor/typo3/phar-stream-wrapper/src/PharStreamWrapper.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents af1871eacc83
children
rev   line source
Chris@17 1 <?php
Chris@17 2 namespace TYPO3\PharStreamWrapper;
Chris@17 3
Chris@17 4 /*
Chris@17 5 * This file is part of the TYPO3 project.
Chris@17 6 *
Chris@17 7 * It is free software; you can redistribute it and/or modify it under the terms
Chris@17 8 * of the MIT License (MIT). For the full copyright and license information,
Chris@17 9 * please read the LICENSE file that was distributed with this source code.
Chris@17 10 *
Chris@17 11 * The TYPO3 project - inspiring people to share!
Chris@17 12 */
Chris@17 13
Chris@18 14 use TYPO3\PharStreamWrapper\Resolver\PharInvocation;
Chris@18 15
Chris@17 16 class PharStreamWrapper
Chris@17 17 {
Chris@17 18 /**
Chris@17 19 * Internal stream constants that are not exposed to PHP, but used...
Chris@17 20 * @see https://github.com/php/php-src/blob/e17fc0d73c611ad0207cac8a4a01ded38251a7dc/main/php_streams.h
Chris@17 21 */
Chris@17 22 const STREAM_OPEN_FOR_INCLUDE = 128;
Chris@17 23
Chris@17 24 /**
Chris@17 25 * @var resource
Chris@17 26 */
Chris@17 27 public $context;
Chris@17 28
Chris@17 29 /**
Chris@17 30 * @var resource
Chris@17 31 */
Chris@17 32 protected $internalResource;
Chris@17 33
Chris@17 34 /**
Chris@18 35 * @var PharInvocation
Chris@18 36 */
Chris@18 37 protected $invocation;
Chris@18 38
Chris@18 39 /**
Chris@17 40 * @return bool
Chris@17 41 */
Chris@17 42 public function dir_closedir()
Chris@17 43 {
Chris@17 44 if (!is_resource($this->internalResource)) {
Chris@17 45 return false;
Chris@17 46 }
Chris@17 47
Chris@17 48 $this->invokeInternalStreamWrapper(
Chris@17 49 'closedir',
Chris@17 50 $this->internalResource
Chris@17 51 );
Chris@17 52 return !is_resource($this->internalResource);
Chris@17 53 }
Chris@17 54
Chris@17 55 /**
Chris@17 56 * @param string $path
Chris@17 57 * @param int $options
Chris@17 58 * @return bool
Chris@17 59 */
Chris@17 60 public function dir_opendir($path, $options)
Chris@17 61 {
Chris@17 62 $this->assert($path, Behavior::COMMAND_DIR_OPENDIR);
Chris@17 63 $this->internalResource = $this->invokeInternalStreamWrapper(
Chris@17 64 'opendir',
Chris@17 65 $path,
Chris@17 66 $this->context
Chris@17 67 );
Chris@17 68 return is_resource($this->internalResource);
Chris@17 69 }
Chris@17 70
Chris@17 71 /**
Chris@17 72 * @return string|false
Chris@17 73 */
Chris@17 74 public function dir_readdir()
Chris@17 75 {
Chris@17 76 return $this->invokeInternalStreamWrapper(
Chris@17 77 'readdir',
Chris@17 78 $this->internalResource
Chris@17 79 );
Chris@17 80 }
Chris@17 81
Chris@17 82 /**
Chris@17 83 * @return bool
Chris@17 84 */
Chris@17 85 public function dir_rewinddir()
Chris@17 86 {
Chris@17 87 if (!is_resource($this->internalResource)) {
Chris@17 88 return false;
Chris@17 89 }
Chris@17 90
Chris@17 91 $this->invokeInternalStreamWrapper(
Chris@17 92 'rewinddir',
Chris@17 93 $this->internalResource
Chris@17 94 );
Chris@17 95 return is_resource($this->internalResource);
Chris@17 96 }
Chris@17 97
Chris@17 98 /**
Chris@17 99 * @param string $path
Chris@17 100 * @param int $mode
Chris@17 101 * @param int $options
Chris@17 102 * @return bool
Chris@17 103 */
Chris@17 104 public function mkdir($path, $mode, $options)
Chris@17 105 {
Chris@17 106 $this->assert($path, Behavior::COMMAND_MKDIR);
Chris@17 107 return $this->invokeInternalStreamWrapper(
Chris@17 108 'mkdir',
Chris@17 109 $path,
Chris@17 110 $mode,
Chris@17 111 (bool) ($options & STREAM_MKDIR_RECURSIVE),
Chris@17 112 $this->context
Chris@17 113 );
Chris@17 114 }
Chris@17 115
Chris@17 116 /**
Chris@17 117 * @param string $path_from
Chris@17 118 * @param string $path_to
Chris@17 119 * @return bool
Chris@17 120 */
Chris@17 121 public function rename($path_from, $path_to)
Chris@17 122 {
Chris@17 123 $this->assert($path_from, Behavior::COMMAND_RENAME);
Chris@17 124 $this->assert($path_to, Behavior::COMMAND_RENAME);
Chris@17 125 return $this->invokeInternalStreamWrapper(
Chris@17 126 'rename',
Chris@17 127 $path_from,
Chris@17 128 $path_to,
Chris@17 129 $this->context
Chris@17 130 );
Chris@17 131 }
Chris@17 132
Chris@17 133 /**
Chris@17 134 * @param string $path
Chris@17 135 * @param int $options
Chris@17 136 * @return bool
Chris@17 137 */
Chris@17 138 public function rmdir($path, $options)
Chris@17 139 {
Chris@17 140 $this->assert($path, Behavior::COMMAND_RMDIR);
Chris@17 141 return $this->invokeInternalStreamWrapper(
Chris@17 142 'rmdir',
Chris@17 143 $path,
Chris@17 144 $this->context
Chris@17 145 );
Chris@17 146 }
Chris@17 147
Chris@17 148 /**
Chris@17 149 * @param int $cast_as
Chris@17 150 */
Chris@17 151 public function stream_cast($cast_as)
Chris@17 152 {
Chris@17 153 throw new Exception(
Chris@17 154 'Method stream_select() cannot be used',
Chris@17 155 1530103999
Chris@17 156 );
Chris@17 157 }
Chris@17 158
Chris@17 159 public function stream_close()
Chris@17 160 {
Chris@17 161 $this->invokeInternalStreamWrapper(
Chris@17 162 'fclose',
Chris@17 163 $this->internalResource
Chris@17 164 );
Chris@17 165 }
Chris@17 166
Chris@17 167 /**
Chris@17 168 * @return bool
Chris@17 169 */
Chris@17 170 public function stream_eof()
Chris@17 171 {
Chris@17 172 return $this->invokeInternalStreamWrapper(
Chris@17 173 'feof',
Chris@17 174 $this->internalResource
Chris@17 175 );
Chris@17 176 }
Chris@17 177
Chris@17 178 /**
Chris@17 179 * @return bool
Chris@17 180 */
Chris@17 181 public function stream_flush()
Chris@17 182 {
Chris@17 183 return $this->invokeInternalStreamWrapper(
Chris@17 184 'fflush',
Chris@17 185 $this->internalResource
Chris@17 186 );
Chris@17 187 }
Chris@17 188
Chris@17 189 /**
Chris@17 190 * @param int $operation
Chris@17 191 * @return bool
Chris@17 192 */
Chris@17 193 public function stream_lock($operation)
Chris@17 194 {
Chris@17 195 return $this->invokeInternalStreamWrapper(
Chris@17 196 'flock',
Chris@17 197 $this->internalResource,
Chris@17 198 $operation
Chris@17 199 );
Chris@17 200 }
Chris@17 201
Chris@17 202 /**
Chris@17 203 * @param string $path
Chris@17 204 * @param int $option
Chris@17 205 * @param string|int $value
Chris@17 206 * @return bool
Chris@17 207 */
Chris@17 208 public function stream_metadata($path, $option, $value)
Chris@17 209 {
Chris@17 210 $this->assert($path, Behavior::COMMAND_STEAM_METADATA);
Chris@17 211 if ($option === STREAM_META_TOUCH) {
Chris@17 212 return call_user_func_array(
Chris@17 213 array($this, 'invokeInternalStreamWrapper'),
Chris@17 214 array_merge(array('touch', $path), (array) $value)
Chris@17 215 );
Chris@17 216 }
Chris@17 217 if ($option === STREAM_META_OWNER_NAME || $option === STREAM_META_OWNER) {
Chris@17 218 return $this->invokeInternalStreamWrapper(
Chris@17 219 'chown',
Chris@17 220 $path,
Chris@17 221 $value
Chris@17 222 );
Chris@17 223 }
Chris@17 224 if ($option === STREAM_META_GROUP_NAME || $option === STREAM_META_GROUP) {
Chris@17 225 return $this->invokeInternalStreamWrapper(
Chris@17 226 'chgrp',
Chris@17 227 $path,
Chris@17 228 $value
Chris@17 229 );
Chris@17 230 }
Chris@17 231 if ($option === STREAM_META_ACCESS) {
Chris@17 232 return $this->invokeInternalStreamWrapper(
Chris@17 233 'chmod',
Chris@17 234 $path,
Chris@17 235 $value
Chris@17 236 );
Chris@17 237 }
Chris@17 238 return false;
Chris@17 239 }
Chris@17 240
Chris@17 241 /**
Chris@17 242 * @param string $path
Chris@17 243 * @param string $mode
Chris@17 244 * @param int $options
Chris@17 245 * @param string|null $opened_path
Chris@17 246 * @return bool
Chris@17 247 */
Chris@17 248 public function stream_open(
Chris@17 249 $path,
Chris@17 250 $mode,
Chris@17 251 $options,
Chris@17 252 &$opened_path = null
Chris@17 253 ) {
Chris@17 254 $this->assert($path, Behavior::COMMAND_STREAM_OPEN);
Chris@17 255 $arguments = array($path, $mode, (bool) ($options & STREAM_USE_PATH));
Chris@17 256 // only add stream context for non include/require calls
Chris@17 257 if (!($options & static::STREAM_OPEN_FOR_INCLUDE)) {
Chris@17 258 $arguments[] = $this->context;
Chris@17 259 // work around https://bugs.php.net/bug.php?id=66569
Chris@17 260 // for including files from Phar stream with OPcache enabled
Chris@17 261 } else {
Chris@17 262 Helper::resetOpCache();
Chris@17 263 }
Chris@17 264 $this->internalResource = call_user_func_array(
Chris@17 265 array($this, 'invokeInternalStreamWrapper'),
Chris@17 266 array_merge(array('fopen'), $arguments)
Chris@17 267 );
Chris@17 268 if (!is_resource($this->internalResource)) {
Chris@17 269 return false;
Chris@17 270 }
Chris@17 271 if ($opened_path !== null) {
Chris@17 272 $metaData = stream_get_meta_data($this->internalResource);
Chris@17 273 $opened_path = $metaData['uri'];
Chris@17 274 }
Chris@17 275 return true;
Chris@17 276 }
Chris@17 277
Chris@17 278 /**
Chris@17 279 * @param int $count
Chris@17 280 * @return string
Chris@17 281 */
Chris@17 282 public function stream_read($count)
Chris@17 283 {
Chris@17 284 return $this->invokeInternalStreamWrapper(
Chris@17 285 'fread',
Chris@17 286 $this->internalResource,
Chris@17 287 $count
Chris@17 288 );
Chris@17 289 }
Chris@17 290
Chris@17 291 /**
Chris@17 292 * @param int $offset
Chris@17 293 * @param int $whence
Chris@17 294 * @return bool
Chris@17 295 */
Chris@17 296 public function stream_seek($offset, $whence = SEEK_SET)
Chris@17 297 {
Chris@17 298 return $this->invokeInternalStreamWrapper(
Chris@17 299 'fseek',
Chris@17 300 $this->internalResource,
Chris@17 301 $offset,
Chris@17 302 $whence
Chris@17 303 ) !== -1;
Chris@17 304 }
Chris@17 305
Chris@17 306 /**
Chris@17 307 * @param int $option
Chris@17 308 * @param int $arg1
Chris@17 309 * @param int $arg2
Chris@17 310 * @return bool
Chris@17 311 */
Chris@17 312 public function stream_set_option($option, $arg1, $arg2)
Chris@17 313 {
Chris@17 314 if ($option === STREAM_OPTION_BLOCKING) {
Chris@17 315 return $this->invokeInternalStreamWrapper(
Chris@17 316 'stream_set_blocking',
Chris@17 317 $this->internalResource,
Chris@17 318 $arg1
Chris@17 319 );
Chris@17 320 }
Chris@17 321 if ($option === STREAM_OPTION_READ_TIMEOUT) {
Chris@17 322 return $this->invokeInternalStreamWrapper(
Chris@17 323 'stream_set_timeout',
Chris@17 324 $this->internalResource,
Chris@17 325 $arg1,
Chris@17 326 $arg2
Chris@17 327 );
Chris@17 328 }
Chris@17 329 if ($option === STREAM_OPTION_WRITE_BUFFER) {
Chris@17 330 return $this->invokeInternalStreamWrapper(
Chris@17 331 'stream_set_write_buffer',
Chris@17 332 $this->internalResource,
Chris@17 333 $arg2
Chris@17 334 ) === 0;
Chris@17 335 }
Chris@17 336 return false;
Chris@17 337 }
Chris@17 338
Chris@17 339 /**
Chris@17 340 * @return array
Chris@17 341 */
Chris@17 342 public function stream_stat()
Chris@17 343 {
Chris@17 344 return $this->invokeInternalStreamWrapper(
Chris@17 345 'fstat',
Chris@17 346 $this->internalResource
Chris@17 347 );
Chris@17 348 }
Chris@17 349
Chris@17 350 /**
Chris@17 351 * @return int
Chris@17 352 */
Chris@17 353 public function stream_tell()
Chris@17 354 {
Chris@17 355 return $this->invokeInternalStreamWrapper(
Chris@17 356 'ftell',
Chris@17 357 $this->internalResource
Chris@17 358 );
Chris@17 359 }
Chris@17 360
Chris@17 361 /**
Chris@17 362 * @param int $new_size
Chris@17 363 * @return bool
Chris@17 364 */
Chris@17 365 public function stream_truncate($new_size)
Chris@17 366 {
Chris@17 367 return $this->invokeInternalStreamWrapper(
Chris@17 368 'ftruncate',
Chris@17 369 $this->internalResource,
Chris@17 370 $new_size
Chris@17 371 );
Chris@17 372 }
Chris@17 373
Chris@17 374 /**
Chris@17 375 * @param string $data
Chris@17 376 * @return int
Chris@17 377 */
Chris@17 378 public function stream_write($data)
Chris@17 379 {
Chris@17 380 return $this->invokeInternalStreamWrapper(
Chris@17 381 'fwrite',
Chris@17 382 $this->internalResource,
Chris@17 383 $data
Chris@17 384 );
Chris@17 385 }
Chris@17 386
Chris@17 387 /**
Chris@17 388 * @param string $path
Chris@17 389 * @return bool
Chris@17 390 */
Chris@17 391 public function unlink($path)
Chris@17 392 {
Chris@17 393 $this->assert($path, Behavior::COMMAND_UNLINK);
Chris@17 394 return $this->invokeInternalStreamWrapper(
Chris@17 395 'unlink',
Chris@17 396 $path,
Chris@17 397 $this->context
Chris@17 398 );
Chris@17 399 }
Chris@17 400
Chris@17 401 /**
Chris@17 402 * @param string $path
Chris@17 403 * @param int $flags
Chris@17 404 * @return array|false
Chris@17 405 */
Chris@17 406 public function url_stat($path, $flags)
Chris@17 407 {
Chris@17 408 $this->assert($path, Behavior::COMMAND_URL_STAT);
Chris@17 409 $functionName = $flags & STREAM_URL_STAT_QUIET ? '@stat' : 'stat';
Chris@17 410 return $this->invokeInternalStreamWrapper($functionName, $path);
Chris@17 411 }
Chris@17 412
Chris@17 413 /**
Chris@17 414 * @param string $path
Chris@17 415 * @param string $command
Chris@17 416 */
Chris@17 417 protected function assert($path, $command)
Chris@17 418 {
Chris@18 419 if (Manager::instance()->assert($path, $command) === true) {
Chris@18 420 $this->collectInvocation($path);
Chris@17 421 return;
Chris@17 422 }
Chris@17 423
Chris@17 424 throw new Exception(
Chris@17 425 sprintf(
Chris@17 426 'Denied invocation of "%s" for command "%s"',
Chris@17 427 $path,
Chris@17 428 $command
Chris@17 429 ),
Chris@17 430 1535189880
Chris@17 431 );
Chris@17 432 }
Chris@17 433
Chris@17 434 /**
Chris@18 435 * @param string $path
Chris@18 436 */
Chris@18 437 protected function collectInvocation($path)
Chris@18 438 {
Chris@18 439 if (isset($this->invocation)) {
Chris@18 440 return;
Chris@18 441 }
Chris@18 442
Chris@18 443 $manager = Manager::instance();
Chris@18 444 $this->invocation = $manager->resolve($path);
Chris@18 445 if ($this->invocation === null) {
Chris@18 446 throw new Exception(
Chris@18 447 'Expected invocation could not be resolved',
Chris@18 448 1556389591
Chris@18 449 );
Chris@18 450 }
Chris@18 451 // confirm, previous interceptor(s) validated invocation
Chris@18 452 $this->invocation->confirm();
Chris@18 453 $collection = $manager->getCollection();
Chris@18 454 if (!$collection->has($this->invocation)) {
Chris@18 455 $collection->collect($this->invocation);
Chris@18 456 }
Chris@18 457 }
Chris@18 458
Chris@18 459 /**
Chris@18 460 * @return Manager|Assertable
Chris@18 461 * @deprecated Use Manager::instance() directly
Chris@17 462 */
Chris@17 463 protected function resolveAssertable()
Chris@17 464 {
Chris@17 465 return Manager::instance();
Chris@17 466 }
Chris@17 467
Chris@17 468 /**
Chris@17 469 * Invokes commands on the native PHP Phar stream wrapper.
Chris@17 470 *
Chris@17 471 * @param string $functionName
Chris@17 472 * @param mixed ...$arguments
Chris@17 473 * @return mixed
Chris@17 474 */
Chris@17 475 private function invokeInternalStreamWrapper($functionName)
Chris@17 476 {
Chris@17 477 $arguments = func_get_args();
Chris@17 478 array_shift($arguments);
Chris@17 479 $silentExecution = $functionName{0} === '@';
Chris@17 480 $functionName = ltrim($functionName, '@');
Chris@17 481 $this->restoreInternalSteamWrapper();
Chris@17 482
Chris@17 483 try {
Chris@17 484 if ($silentExecution) {
Chris@17 485 $result = @call_user_func_array($functionName, $arguments);
Chris@17 486 } else {
Chris@17 487 $result = call_user_func_array($functionName, $arguments);
Chris@17 488 }
Chris@17 489 } catch (\Exception $exception) {
Chris@17 490 $this->registerStreamWrapper();
Chris@17 491 throw $exception;
Chris@17 492 } catch (\Throwable $throwable) {
Chris@17 493 $this->registerStreamWrapper();
Chris@17 494 throw $throwable;
Chris@17 495 }
Chris@17 496
Chris@17 497 $this->registerStreamWrapper();
Chris@17 498 return $result;
Chris@17 499 }
Chris@17 500
Chris@17 501 private function restoreInternalSteamWrapper()
Chris@17 502 {
Chris@17 503 stream_wrapper_restore('phar');
Chris@17 504 }
Chris@17 505
Chris@17 506 private function registerStreamWrapper()
Chris@17 507 {
Chris@17 508 stream_wrapper_unregister('phar');
Chris@17 509 stream_wrapper_register('phar', get_class($this));
Chris@17 510 }
Chris@17 511 }