<?php /** * Streamwrapper Implementation (PoC covering include) */ declare(strict_types=1); class FileStreamWrapper { /** * @var resource */ public $context; /** * @var resource */ private $handle; /** * @var string */ const protocol = 'file'; public static function init() { $result = stream_wrapper_unregister(self::protocol); if (false === $result) { throw new UnexpectedValueException('Failed to unregister'); } stream_wrapper_register(self::protocol, FileStreamWrapper::class, 0); } private static function restore() { $result = stream_wrapper_restore(self::protocol); if (false === $result) { throw new UnexpectedValueException('Failed to restore'); } } public function stream_open($path, $mode, $options, &$opened_path): bool { if (isset($this->handle)) { throw new UnexpectedValueException('Handle congruency'); } $use_include_path = null; // TODO(hakre): implenent based on options $use_include_path = true; $context = $this->context; if (null === $context) { $context = stream_context_get_default(); } self::restore(); $handle = fopen($path, $mode, $use_include_path, $context); self::init(); if (false === $handle) { return false; } $meta = stream_get_meta_data($handle); if (!isset($meta['uri'])) { throw new UnexpectedValueException('Uri not in meta data'); } $opened_path = $meta['uri']; $this->handle = $handle; return true; } /** * @return array */ public function stream_stat(): array { self::restore(); $array = fstat($this->handle); self::init(); return $array; } private $declaredTicks = false; /** * @param $count * * @return string */ public function stream_read(int $count): string { self::restore(); $result = fread($this->handle, $count); self::init(); if (!$this->declaredTicks) { $result = preg_replace( '~^(<\?php\s*)$~m', "\\0\ndeclare(ticks=1);\n", $result, 1 ); $this->declaredTicks = true; } return $result; } public function stream_eof(): bool { self::restore(); $result = feof($this->handle); self::init(); return $result; } }