mirror of
https://codeberg.org/icewind/streams.git
synced 2026-06-03 16:44:07 +02:00
cleanup context handling for wrappers
This commit is contained in:
parent
3171a535de
commit
2c8ae56d02
16 changed files with 182 additions and 258 deletions
|
|
@ -14,7 +14,7 @@ it wraps an existing stream and can thus be used for any stream in php
|
||||||
The callbacks are passed in the stream context along with the source stream
|
The callbacks are passed in the stream context along with the source stream
|
||||||
and can be any valid [php callable](http://php.net/manual/en/language.types.callable.php)
|
and can be any valid [php callable](http://php.net/manual/en/language.types.callable.php)
|
||||||
|
|
||||||
###Example###
|
### Example ###
|
||||||
```php
|
```php
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,30 +53,28 @@ class CallbackWrapper extends Wrapper {
|
||||||
* Wraps a stream with the provided callbacks
|
* Wraps a stream with the provided callbacks
|
||||||
*
|
*
|
||||||
* @param resource $source
|
* @param resource $source
|
||||||
* @param callable $read (optional)
|
* @param callable|null $read (optional)
|
||||||
* @param callable $write (optional)
|
* @param callable|null $write (optional)
|
||||||
* @param callable $close (optional)
|
* @param callable|null $close (optional)
|
||||||
* @param callable $readDir (optional)
|
* @param callable|null $readDir (optional)
|
||||||
|
* @param callable|null $preClose (optional)
|
||||||
* @return resource
|
* @return resource
|
||||||
*
|
*
|
||||||
* @throws \BadMethodCallException
|
|
||||||
*/
|
*/
|
||||||
public static function wrap($source, $read = null, $write = null, $close = null, $readDir = null, $preClose = null) {
|
public static function wrap($source, $read = null, $write = null, $close = null, $readDir = null, $preClose = null) {
|
||||||
$context = stream_context_create(array(
|
$context = [
|
||||||
'callback' => array(
|
'source' => $source,
|
||||||
'source' => $source,
|
'read' => $read,
|
||||||
'read' => $read,
|
'write' => $write,
|
||||||
'write' => $write,
|
'close' => $close,
|
||||||
'close' => $close,
|
'readDir' => $readDir,
|
||||||
'readDir' => $readDir,
|
'preClose' => $preClose,
|
||||||
'preClose' => $preClose,
|
];
|
||||||
)
|
|
||||||
));
|
|
||||||
return self::wrapSource($source, $context);
|
return self::wrapSource($source, $context);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function open() {
|
protected function open() {
|
||||||
$context = $this->loadContext('callback');
|
$context = $this->loadContext();
|
||||||
|
|
||||||
$this->readCallback = $context['read'];
|
$this->readCallback = $context['read'];
|
||||||
$this->writeCallback = $context['write'];
|
$this->writeCallback = $context['write'];
|
||||||
|
|
@ -112,7 +110,7 @@ class CallbackWrapper extends Wrapper {
|
||||||
|
|
||||||
public function stream_close() {
|
public function stream_close() {
|
||||||
if (is_callable($this->preCloseCallback)) {
|
if (is_callable($this->preCloseCallback)) {
|
||||||
call_user_func($this->preCloseCallback, $this->loadContext('callback')['source']);
|
call_user_func($this->preCloseCallback, $this->source);
|
||||||
// prevent further calls by potential PHP 7 GC ghosts
|
// prevent further calls by potential PHP 7 GC ghosts
|
||||||
$this->preCloseCallback = null;
|
$this->preCloseCallback = null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,17 +63,14 @@ class CountWrapper extends Wrapper {
|
||||||
if (!is_callable($callback)) {
|
if (!is_callable($callback)) {
|
||||||
throw new \InvalidArgumentException('Invalid or missing callback');
|
throw new \InvalidArgumentException('Invalid or missing callback');
|
||||||
}
|
}
|
||||||
$context = stream_context_create(array(
|
return self::wrapSource($source, [
|
||||||
'count' => array(
|
'source' => $source,
|
||||||
'source' => $source,
|
'callback' => $callback
|
||||||
'callback' => $callback
|
]);
|
||||||
)
|
|
||||||
));
|
|
||||||
return self::wrapSource($source, $context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function open() {
|
protected function open() {
|
||||||
$context = $this->loadContext('count');
|
$context = $this->loadContext();
|
||||||
$this->callback = $context['callback'];
|
$this->callback = $context['callback'];
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ class DirectoryFilter extends DirectoryWrapper {
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function dir_opendir($path, $options) {
|
public function dir_opendir($path, $options) {
|
||||||
$context = $this->loadContext('filter');
|
$context = $this->loadContext();
|
||||||
$this->filter = $context['filter'];
|
$this->filter = $context['filter'];
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -36,7 +36,7 @@ class DirectoryFilter extends DirectoryWrapper {
|
||||||
public function dir_readdir() {
|
public function dir_readdir() {
|
||||||
$file = readdir($this->source);
|
$file = readdir($this->source);
|
||||||
$filter = $this->filter;
|
$filter = $this->filter;
|
||||||
// keep reading untill we have an accepted entry or we're at the end of the folder
|
// keep reading until we have an accepted entry or we're at the end of the folder
|
||||||
while ($file !== false && $filter($file) === false) {
|
while ($file !== false && $filter($file) === false) {
|
||||||
$file = readdir($this->source);
|
$file = readdir($this->source);
|
||||||
}
|
}
|
||||||
|
|
@ -49,12 +49,9 @@ class DirectoryFilter extends DirectoryWrapper {
|
||||||
* @return resource
|
* @return resource
|
||||||
*/
|
*/
|
||||||
public static function wrap($source, callable $filter) {
|
public static function wrap($source, callable $filter) {
|
||||||
$options = array(
|
return self::wrapSource($source, [
|
||||||
'filter' => array(
|
'source' => $source,
|
||||||
'source' => $source,
|
'filter' => $filter
|
||||||
'filter' => $filter
|
]);
|
||||||
)
|
|
||||||
);
|
|
||||||
return self::wrapWithOptions($options);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,37 +7,9 @@
|
||||||
|
|
||||||
namespace Icewind\Streams;
|
namespace Icewind\Streams;
|
||||||
|
|
||||||
class DirectoryWrapper implements Directory {
|
class DirectoryWrapper extends Wrapper implements Directory {
|
||||||
/**
|
public function stream_open($path, $mode, $options, &$opened_path) {
|
||||||
* @var resource
|
return false;
|
||||||
*/
|
|
||||||
public $context;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var resource
|
|
||||||
*/
|
|
||||||
protected $source;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load the source from the stream context and return the context options
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @return array
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
protected function loadContext($name) {
|
|
||||||
$context = stream_context_get_options($this->context);
|
|
||||||
if (isset($context[$name])) {
|
|
||||||
$context = $context[$name];
|
|
||||||
} else {
|
|
||||||
throw new \BadMethodCallException('Invalid context, "' . $name . '" options not set');
|
|
||||||
}
|
|
||||||
if (isset($context['source']) and is_resource($context['source'])) {
|
|
||||||
$this->source = $context['source'];
|
|
||||||
} else {
|
|
||||||
throw new \BadMethodCallException('Invalid context, source not set');
|
|
||||||
}
|
|
||||||
return $context;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -46,7 +18,7 @@ class DirectoryWrapper implements Directory {
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function dir_opendir($path, $options) {
|
public function dir_opendir($path, $options) {
|
||||||
$this->loadContext('dir');
|
$this->loadContext();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,18 +44,4 @@ class DirectoryWrapper implements Directory {
|
||||||
rewinddir($this->source);
|
rewinddir($this->source);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array $options the options for the context to wrap the stream with
|
|
||||||
* @param null $class deprecated, class is now automatically generated
|
|
||||||
* @return resource
|
|
||||||
*/
|
|
||||||
protected static function wrapWithOptions($options, $class = null) {
|
|
||||||
$class = static::class;
|
|
||||||
$context = stream_context_create($options);
|
|
||||||
stream_wrapper_register('dirwrapper', $class);
|
|
||||||
$wrapped = opendir('dirwrapper://', $context);
|
|
||||||
stream_wrapper_unregister('dirwrapper');
|
|
||||||
return $wrapped;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ namespace Icewind\Streams;
|
||||||
*
|
*
|
||||||
* Either 'array' or 'iterator' need to be set, if both are set, 'iterator' takes preference
|
* Either 'array' or 'iterator' need to be set, if both are set, 'iterator' takes preference
|
||||||
*/
|
*/
|
||||||
class IteratorDirectory implements Directory {
|
class IteratorDirectory extends WrapperHandler implements Directory {
|
||||||
/**
|
/**
|
||||||
* @var resource
|
* @var resource
|
||||||
*/
|
*/
|
||||||
|
|
@ -36,15 +36,10 @@ class IteratorDirectory implements Directory {
|
||||||
*
|
*
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @return array
|
* @return array
|
||||||
* @throws \Exception
|
* @throws \BadMethodCallException
|
||||||
*/
|
*/
|
||||||
protected function loadContext($name) {
|
protected function loadContext($name = null) {
|
||||||
$context = stream_context_get_options($this->context);
|
$context = parent::loadContext($name);
|
||||||
if (isset($context[$name])) {
|
|
||||||
$context = $context[$name];
|
|
||||||
} else {
|
|
||||||
throw new \BadMethodCallException('Invalid context, "' . $name . '" options not set');
|
|
||||||
}
|
|
||||||
if (isset($context['iterator'])) {
|
if (isset($context['iterator'])) {
|
||||||
$this->iterator = $context['iterator'];
|
$this->iterator = $context['iterator'];
|
||||||
} else if (isset($context['array'])) {
|
} else if (isset($context['array'])) {
|
||||||
|
|
@ -61,7 +56,7 @@ class IteratorDirectory implements Directory {
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function dir_opendir($path, $options) {
|
public function dir_opendir($path, $options) {
|
||||||
$this->loadContext('dir');
|
$this->loadContext();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -103,21 +98,16 @@ class IteratorDirectory implements Directory {
|
||||||
*/
|
*/
|
||||||
public static function wrap($source) {
|
public static function wrap($source) {
|
||||||
if ($source instanceof \Iterator) {
|
if ($source instanceof \Iterator) {
|
||||||
$context = stream_context_create(array(
|
$options = [
|
||||||
'dir' => array(
|
'iterator' => $source
|
||||||
'iterator' => $source)
|
];
|
||||||
));
|
|
||||||
} else if (is_array($source)) {
|
} else if (is_array($source)) {
|
||||||
$context = stream_context_create(array(
|
$options = [
|
||||||
'dir' => array(
|
'array' => $source
|
||||||
'array' => $source)
|
];
|
||||||
));
|
|
||||||
} else {
|
} else {
|
||||||
throw new \BadMethodCallException('$source should be an Iterator or array');
|
throw new \BadMethodCallException('$source should be an Iterator or array');
|
||||||
}
|
}
|
||||||
stream_wrapper_register('iterator', static::class);
|
return self::wrapSource(self::NO_SOURCE_DIR, $options);
|
||||||
$wrapped = opendir('iterator://', $context);
|
|
||||||
stream_wrapper_unregister('iterator');
|
|
||||||
return $wrapped;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,29 +11,17 @@ namespace Icewind\Streams;
|
||||||
* Stream wrapper that does nothing, used for tests
|
* Stream wrapper that does nothing, used for tests
|
||||||
*/
|
*/
|
||||||
class NullWrapper extends Wrapper {
|
class NullWrapper extends Wrapper {
|
||||||
/**
|
|
||||||
* Wraps a stream with the provided callbacks
|
|
||||||
*
|
|
||||||
* @param resource $source
|
|
||||||
* @return resource
|
|
||||||
*
|
|
||||||
* @throws \BadMethodCallException
|
|
||||||
*/
|
|
||||||
public static function wrap($source) {
|
public static function wrap($source) {
|
||||||
$context = stream_context_create(array(
|
return self::wrapSource($source);
|
||||||
'null' => array(
|
|
||||||
'source' => $source)
|
|
||||||
));
|
|
||||||
return self::wrapSource($source, $context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function stream_open($path, $mode, $options, &$opened_path) {
|
public function stream_open($path, $mode, $options, &$opened_path) {
|
||||||
$this->loadContext('null');
|
$this->loadContext();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function dir_opendir($path, $options) {
|
public function dir_opendir($path, $options) {
|
||||||
$this->loadContext('null');
|
$this->loadContext();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ class Path {
|
||||||
* @param string $class
|
* @param string $class
|
||||||
* @param array $contextOptions
|
* @param array $contextOptions
|
||||||
*/
|
*/
|
||||||
public function __construct($class, $contextOptions = array()) {
|
public function __construct($class, $contextOptions = []) {
|
||||||
$this->class = $class;
|
$this->class = $class;
|
||||||
$this->contextOptions = $contextOptions;
|
$this->contextOptions = $contextOptions;
|
||||||
}
|
}
|
||||||
|
|
@ -75,7 +75,7 @@ class Path {
|
||||||
*/
|
*/
|
||||||
protected function appendDefaultContent($values) {
|
protected function appendDefaultContent($values) {
|
||||||
if (!is_array(current($values))) {
|
if (!is_array(current($values))) {
|
||||||
$values = array($this->getProtocol() => $values);
|
$values = [$this->getProtocol() => $values];
|
||||||
}
|
}
|
||||||
$context = stream_context_get_default();
|
$context = stream_context_get_default();
|
||||||
$defaults = stream_context_get_options($context);
|
$defaults = stream_context_get_options($context);
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,8 @@ class PathWrapper extends NullWrapper {
|
||||||
* @return Path|string
|
* @return Path|string
|
||||||
*/
|
*/
|
||||||
public static function getPath($source) {
|
public static function getPath($source) {
|
||||||
return new Path(__CLASS__, [
|
return new Path(NullWrapper::class, [
|
||||||
'null' => [
|
NullWrapper::getProtocol() => ['source' => $source]
|
||||||
'source' => $source
|
|
||||||
]
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,25 +11,8 @@ namespace Icewind\Streams;
|
||||||
* Wrapper that retries reads/writes to remote streams that dont deliver/recieve all requested data at once
|
* Wrapper that retries reads/writes to remote streams that dont deliver/recieve all requested data at once
|
||||||
*/
|
*/
|
||||||
class RetryWrapper extends Wrapper {
|
class RetryWrapper extends Wrapper {
|
||||||
|
|
||||||
/**
|
|
||||||
* Wraps a stream with the provided callbacks
|
|
||||||
*
|
|
||||||
* @param resource $source
|
|
||||||
* @return resource
|
|
||||||
*/
|
|
||||||
public static function wrap($source) {
|
public static function wrap($source) {
|
||||||
$context = stream_context_create(array(
|
return self::wrapSource($source);
|
||||||
'retry' => array(
|
|
||||||
'source' => $source
|
|
||||||
)
|
|
||||||
));
|
|
||||||
return self::wrapSource($source, $context);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function open() {
|
|
||||||
$this->loadContext('retry');
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function dir_opendir($path, $options) {
|
public function dir_opendir($path, $options) {
|
||||||
|
|
@ -37,7 +20,8 @@ class RetryWrapper extends Wrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function stream_open($path, $mode, $options, &$opened_path) {
|
public function stream_open($path, $mode, $options, &$opened_path) {
|
||||||
return $this->open();
|
$this->loadContext();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function stream_read($count) {
|
public function stream_read($count) {
|
||||||
|
|
|
||||||
|
|
@ -25,21 +25,8 @@ class SeekableWrapper extends Wrapper {
|
||||||
*/
|
*/
|
||||||
protected $cache;
|
protected $cache;
|
||||||
|
|
||||||
/**
|
|
||||||
* Wraps a stream to make it seekable
|
|
||||||
*
|
|
||||||
* @param resource $source
|
|
||||||
* @return resource
|
|
||||||
*
|
|
||||||
* @throws \BadMethodCallException
|
|
||||||
*/
|
|
||||||
public static function wrap($source) {
|
public static function wrap($source) {
|
||||||
$context = stream_context_create(array(
|
return self::wrapSource($source);
|
||||||
'callback' => array(
|
|
||||||
'source' => $source
|
|
||||||
)
|
|
||||||
));
|
|
||||||
return self::wrapSource($source, $context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function dir_opendir($path, $options) {
|
public function dir_opendir($path, $options) {
|
||||||
|
|
@ -47,7 +34,7 @@ class SeekableWrapper extends Wrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function stream_open($path, $mode, $options, &$opened_path) {
|
public function stream_open($path, $mode, $options, &$opened_path) {
|
||||||
$this->loadContext('callback');
|
$this->loadContext();
|
||||||
$this->cache = fopen('php://temp', 'w+');
|
$this->cache = fopen('php://temp', 'w+');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ class UrlCallback extends Wrapper implements Url {
|
||||||
*/
|
*/
|
||||||
public static function wrap($source, $fopen = null, $opendir = null, $mkdir = null, $rename = null, $rmdir = null,
|
public static function wrap($source, $fopen = null, $opendir = null, $mkdir = null, $rename = null, $rmdir = null,
|
||||||
$unlink = null, $stat = null) {
|
$unlink = null, $stat = null) {
|
||||||
$options = array(
|
return new Path(static::class, [
|
||||||
'source' => $source,
|
'source' => $source,
|
||||||
'fopen' => $fopen,
|
'fopen' => $fopen,
|
||||||
'opendir' => $opendir,
|
'opendir' => $opendir,
|
||||||
|
|
@ -60,11 +60,10 @@ class UrlCallback extends Wrapper implements Url {
|
||||||
'rmdir' => $rmdir,
|
'rmdir' => $rmdir,
|
||||||
'unlink' => $unlink,
|
'unlink' => $unlink,
|
||||||
'stat' => $stat
|
'stat' => $stat
|
||||||
);
|
]);
|
||||||
return new Path(static::class, $options);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function loadContext($url) {
|
protected function loadUrlContext($url) {
|
||||||
list($protocol) = explode('://', $url);
|
list($protocol) = explode('://', $url);
|
||||||
$options = stream_context_get_options($this->context);
|
$options = stream_context_get_options($this->context);
|
||||||
return $options[$protocol];
|
return $options[$protocol];
|
||||||
|
|
@ -77,40 +76,40 @@ class UrlCallback extends Wrapper implements Url {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function stream_open($path, $mode, $options, &$opened_path) {
|
public function stream_open($path, $mode, $options, &$opened_path) {
|
||||||
$context = $this->loadContext($path);
|
$context = $this->loadUrlContext($path);
|
||||||
$this->callCallBack($context, 'fopen');
|
$this->callCallBack($context, 'fopen');
|
||||||
$this->setSourceStream(fopen($context['source'], $mode));
|
$this->setSourceStream(fopen($context['source'], $mode));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function dir_opendir($path, $options) {
|
public function dir_opendir($path, $options) {
|
||||||
$context = $this->loadContext($path);
|
$context = $this->loadUrlContext($path);
|
||||||
$this->callCallBack($context, 'opendir');
|
$this->callCallBack($context, 'opendir');
|
||||||
$this->setSourceStream(opendir($context['source']));
|
$this->setSourceStream(opendir($context['source']));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function mkdir($path, $mode, $options) {
|
public function mkdir($path, $mode, $options) {
|
||||||
$context = $this->loadContext($path);
|
$context = $this->loadUrlContext($path);
|
||||||
$this->callCallBack($context, 'mkdir');
|
$this->callCallBack($context, 'mkdir');
|
||||||
return mkdir($context['source'], $mode, $options & STREAM_MKDIR_RECURSIVE);
|
return mkdir($context['source'], $mode, $options & STREAM_MKDIR_RECURSIVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function rmdir($path, $options) {
|
public function rmdir($path, $options) {
|
||||||
$context = $this->loadContext($path);
|
$context = $this->loadUrlContext($path);
|
||||||
$this->callCallBack($context, 'rmdir');
|
$this->callCallBack($context, 'rmdir');
|
||||||
return rmdir($context['source']);
|
return rmdir($context['source']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function rename($source, $target) {
|
public function rename($source, $target) {
|
||||||
$context = $this->loadContext($source);
|
$context = $this->loadUrlContext($source);
|
||||||
$this->callCallBack($context, 'rename');
|
$this->callCallBack($context, 'rename');
|
||||||
list(, $target) = explode('://', $target);
|
list(, $target) = explode('://', $target);
|
||||||
return rename($context['source'], $target);
|
return rename($context['source'], $target);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function unlink($path) {
|
public function unlink($path) {
|
||||||
$context = $this->loadContext($path);
|
$context = $this->loadUrlContext($path);
|
||||||
$this->callCallBack($context, 'unlink');
|
$this->callCallBack($context, 'unlink');
|
||||||
return unlink($context['source']);
|
return unlink($context['source']);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ namespace Icewind\Streams;
|
||||||
*
|
*
|
||||||
* This wrapper itself doesn't implement any functionality but is just a base class for other wrappers to extend
|
* This wrapper itself doesn't implement any functionality but is just a base class for other wrappers to extend
|
||||||
*/
|
*/
|
||||||
abstract class Wrapper implements File, Directory {
|
abstract class Wrapper extends WrapperHandler implements File, Directory {
|
||||||
/**
|
/**
|
||||||
* @var resource
|
* @var resource
|
||||||
*/
|
*/
|
||||||
|
|
@ -26,55 +26,14 @@ abstract class Wrapper implements File, Directory {
|
||||||
protected $source;
|
protected $source;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $source
|
* @param resource $source
|
||||||
* @param $context
|
|
||||||
* @param null $protocol deprecated, protocol is now automatically generated
|
|
||||||
* @param null $class deprecated, class is now automatically generated
|
|
||||||
* @return bool|resource
|
|
||||||
*/
|
*/
|
||||||
protected static function wrapSource($source, $context, $protocol = null, $class = null) {
|
protected function setSourceStream($source) {
|
||||||
if ($class === null) {
|
$this->source = $source;
|
||||||
$class = static::class;
|
|
||||||
}
|
|
||||||
$parts = explode('\\', $class);
|
|
||||||
$protocol = strtolower(array_pop($parts));
|
|
||||||
if (!is_resource($source)) {
|
|
||||||
throw new \BadMethodCallException();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
stream_wrapper_register($protocol, $class);
|
|
||||||
if (self::isDirectoryHandle($source)) {
|
|
||||||
$wrapped = opendir($protocol . '://', $context);
|
|
||||||
} else {
|
|
||||||
$wrapped = fopen($protocol . '://', 'r+', false, $context);
|
|
||||||
}
|
|
||||||
} catch (\BadMethodCallException $e) {
|
|
||||||
stream_wrapper_unregister($protocol);
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
stream_wrapper_unregister($protocol);
|
|
||||||
return $wrapped;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static function isDirectoryHandle($resource) {
|
protected function loadContext($name = null) {
|
||||||
$meta = stream_get_meta_data($resource);
|
$context = parent::loadContext($name);
|
||||||
return $meta['stream_type'] == 'dir';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load the source from the stream context and return the context options
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @return array
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
protected function loadContext($name) {
|
|
||||||
$context = stream_context_get_options($this->context);
|
|
||||||
if (isset($context[$name])) {
|
|
||||||
$context = $context[$name];
|
|
||||||
} else {
|
|
||||||
throw new \BadMethodCallException('Invalid context, "' . $name . '" options not set');
|
|
||||||
}
|
|
||||||
if (isset($context['source']) and is_resource($context['source'])) {
|
if (isset($context['source']) and is_resource($context['source'])) {
|
||||||
$this->setSourceStream($context['source']);
|
$this->setSourceStream($context['source']);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -83,13 +42,6 @@ abstract class Wrapper implements File, Directory {
|
||||||
return $context;
|
return $context;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param resource $source
|
|
||||||
*/
|
|
||||||
protected function setSourceStream($source) {
|
|
||||||
$this->source = $source;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function stream_seek($offset, $whence = SEEK_SET) {
|
public function stream_seek($offset, $whence = SEEK_SET) {
|
||||||
$result = fseek($this->source, $offset, $whence);
|
$result = fseek($this->source, $offset, $whence);
|
||||||
return $result == 0 ? true : false;
|
return $result == 0 ? true : false;
|
||||||
|
|
|
||||||
108
src/WrapperHandler.php
Normal file
108
src/WrapperHandler.php
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (c) 2019 Robin Appelman <robin@icewind.nl>
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Icewind\Streams;
|
||||||
|
|
||||||
|
|
||||||
|
class WrapperHandler {
|
||||||
|
const NO_SOURCE_DIR = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the protocol name that is generated for the class
|
||||||
|
* @param string|null $class
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function getProtocol($class = null) {
|
||||||
|
if ($class === null) {
|
||||||
|
$class = static::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parts = explode('\\', $class);
|
||||||
|
return strtolower(array_pop($parts));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param resource|int $source
|
||||||
|
* @param resource|array $context
|
||||||
|
* @param null $protocol deprecated, protocol is now automatically generated
|
||||||
|
* @param null $class deprecated, class is now automatically generated
|
||||||
|
* @return bool|resource
|
||||||
|
*/
|
||||||
|
protected static function wrapSource($source, $context = [], $protocol = null, $class = null) {
|
||||||
|
if ($class === null) {
|
||||||
|
$class = static::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
$protocol = self::getProtocol($class);
|
||||||
|
|
||||||
|
if (is_array($context)) {
|
||||||
|
$context['source'] = $source;
|
||||||
|
$context = stream_context_create([$protocol => $context]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($source !== self::NO_SOURCE_DIR && !is_resource($source)) {
|
||||||
|
throw new \BadMethodCallException();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
stream_wrapper_register($protocol, $class);
|
||||||
|
if (self::isDirectoryHandle($source)) {
|
||||||
|
$wrapped = opendir($protocol . '://', $context);
|
||||||
|
} else {
|
||||||
|
$wrapped = fopen($protocol . '://', 'r+', false, $context);
|
||||||
|
}
|
||||||
|
} catch (\BadMethodCallException $e) {
|
||||||
|
stream_wrapper_unregister($protocol);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
stream_wrapper_unregister($protocol);
|
||||||
|
return $wrapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function isDirectoryHandle($resource) {
|
||||||
|
if ($resource === self::NO_SOURCE_DIR) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
$meta = stream_get_meta_data($resource);
|
||||||
|
return $meta['stream_type'] === 'dir' || $meta['stream_type'] === 'user-space-dir';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the source from the stream context and return the context options
|
||||||
|
*
|
||||||
|
* @param string|null $name if not set, the generated protocol name is used
|
||||||
|
* @return array
|
||||||
|
* @throws \BadMethodCallException
|
||||||
|
*/
|
||||||
|
protected function loadContext($name = null) {
|
||||||
|
if ($name === null) {
|
||||||
|
$parts = explode('\\', static::class);
|
||||||
|
$name = strtolower(array_pop($parts));
|
||||||
|
}
|
||||||
|
|
||||||
|
$context = stream_context_get_options($this->context);
|
||||||
|
if (isset($context[$name])) {
|
||||||
|
$context = $context[$name];
|
||||||
|
} else {
|
||||||
|
throw new \BadMethodCallException('Invalid context, "' . $name . '" options not set');
|
||||||
|
}
|
||||||
|
return $context;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,21 +9,13 @@ namespace Icewind\Streams\Tests;
|
||||||
|
|
||||||
class DirectoryWrapperNull extends \Icewind\Streams\DirectoryWrapper {
|
class DirectoryWrapperNull extends \Icewind\Streams\DirectoryWrapper {
|
||||||
public static function wrap($source) {
|
public static function wrap($source) {
|
||||||
$options = array(
|
return self::wrapSource($source);
|
||||||
'dir' => array(
|
|
||||||
'source' => $source)
|
|
||||||
);
|
|
||||||
return self::wrapWithOptions($options);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DirectoryWrapperDummy extends \Icewind\Streams\DirectoryWrapper {
|
class DirectoryWrapperDummy extends \Icewind\Streams\DirectoryWrapper {
|
||||||
public static function wrap($source) {
|
public static function wrap($source) {
|
||||||
$options = array(
|
return self::wrapSource($source);
|
||||||
'dir' => array(
|
|
||||||
'source' => $source)
|
|
||||||
);
|
|
||||||
return self::wrapWithOptions($options);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function dir_readdir() {
|
public function dir_readdir() {
|
||||||
|
|
|
||||||
|
|
@ -8,20 +8,8 @@
|
||||||
namespace Icewind\Streams\Tests;
|
namespace Icewind\Streams\Tests;
|
||||||
|
|
||||||
class PartialWrapper extends \Icewind\Streams\NullWrapper {
|
class PartialWrapper extends \Icewind\Streams\NullWrapper {
|
||||||
/**
|
|
||||||
* Wraps a stream with the provided callbacks
|
|
||||||
*
|
|
||||||
* @param resource $source
|
|
||||||
* @return resource
|
|
||||||
*
|
|
||||||
* @throws \BadMethodCallException
|
|
||||||
*/
|
|
||||||
public static function wrap($source) {
|
public static function wrap($source) {
|
||||||
$context = stream_context_create(array(
|
return self::wrapSource($source);
|
||||||
'null' => array(
|
|
||||||
'source' => $source)
|
|
||||||
));
|
|
||||||
return self::wrapSource($source, $context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function stream_read($count) {
|
public function stream_read($count) {
|
||||||
|
|
@ -36,20 +24,8 @@ class PartialWrapper extends \Icewind\Streams\NullWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
class FailWrapper extends \Icewind\Streams\NullWrapper {
|
class FailWrapper extends \Icewind\Streams\NullWrapper {
|
||||||
/**
|
|
||||||
* Wraps a stream with the provided callbacks
|
|
||||||
*
|
|
||||||
* @param resource $source
|
|
||||||
* @return resource
|
|
||||||
*
|
|
||||||
* @throws \BadMethodCallException
|
|
||||||
*/
|
|
||||||
public static function wrap($source) {
|
public static function wrap($source) {
|
||||||
$context = stream_context_create(array(
|
return self::wrapSource($source);
|
||||||
'null' => array(
|
|
||||||
'source' => $source)
|
|
||||||
));
|
|
||||||
return self::wrapSource($source, $context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function stream_read($count) {
|
public function stream_read($count) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue