Add directory support to wrappers

This commit is contained in:
Robin Appelman 2014-08-27 15:36:58 +02:00
commit 53cfe7f4f1
5 changed files with 121 additions and 28 deletions

View file

@ -13,10 +13,11 @@ namespace Icewind\Streams;
* The following options should be passed in the context when opening the stream
* [
* 'callback' => [
* 'source' => resource
* 'read' => function($count){} (optional)
* 'write' => function($data){} (optional)
* 'close' => function(){} (optional)
* 'source' => resource
* 'read' => function($count){} (optional)
* 'write' => function($data){} (optional)
* 'close' => function(){} (optional)
* 'readdir' => function(){} (optional)
* ]
* ]
*
@ -38,6 +39,11 @@ class CallbackWrapper extends Wrapper {
*/
protected $closeCallback;
/**
* @var callable
*/
protected $readDirCallBack;
/**
* Wraps a stream with the provided callbacks
*
@ -45,31 +51,25 @@ class CallbackWrapper extends Wrapper {
* @param callable $read (optional)
* @param callable $write (optional)
* @param callable $close (optional)
* @param callable $readDir (optional)
* @return resource
*
* @throws \BadMethodCallException
*/
public static function wrap($source, $read = null, $write = null, $close = null) {
public static function wrap($source, $read = null, $write = null, $close = null, $readDir = null) {
$context = stream_context_create(array(
'callback' => array(
'source' => $source,
'read' => $read,
'write' => $write,
'close' => $close
'close' => $close,
'readDir' => $readDir
)
));
stream_wrapper_register('callback', '\Icewind\Streams\CallbackWrapper');
try {
$wrapped = fopen('callback://', 'r+', false, $context);
} catch (\BadMethodCallException $e) {
stream_wrapper_unregister('callback');
throw $e;
}
stream_wrapper_unregister('callback');
return $wrapped;
return Wrapper::wrapSource($source, $context, 'callback', '\Icewind\Streams\CallbackWrapper');
}
public function stream_open($path, $mode, $options, &$opened_path) {
protected function open() {
$context = $this->loadContext('callback');
if (is_callable($context['read'])) {
@ -81,9 +81,20 @@ class CallbackWrapper extends Wrapper {
if (is_callable($context['close'])) {
$this->closeCallback = $context['close'];
}
if (is_callable($context['readDir'])) {
$this->readDirCallBack = $context['readDir'];
}
return true;
}
public function dir_opendir($path, $options) {
return $this->open();
}
public function stream_open($path, $mode, $options, &$opened_path) {
return $this->open();
}
public function stream_read($count) {
$result = parent::stream_read($count);
if ($this->readCallback) {
@ -107,4 +118,12 @@ class CallbackWrapper extends Wrapper {
}
return $result;
}
public function dir_readdir() {
$result = parent::dir_readdir();
if ($this->readDirCallBack) {
call_user_func($this->readDirCallBack);
}
return $result;
}
}

View file

@ -24,19 +24,16 @@ class NullWrapper extends Wrapper {
'null' => array(
'source' => $source)
));
stream_wrapper_register('null', '\Icewind\Streams\NullWrapper');
try {
$wrapped = fopen('null://', 'r+', false, $context);
} catch (\BadMethodCallException $e) {
stream_wrapper_unregister('null');
throw $e;
}
stream_wrapper_unregister('null');
return $wrapped;
return Wrapper::wrapSource($source, $context, 'null', '\Icewind\Streams\NullWrapper');
}
public function stream_open($path, $mode, $options, &$opened_path) {
$this->loadContext('null');
return true;
}
public function dir_opendir($path, $options) {
$this->loadContext('null');
return true;
}
}

View file

@ -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
*/
abstract class Wrapper implements File {
abstract class Wrapper implements File, Directory {
/**
* @var resource
*/
@ -25,6 +25,22 @@ abstract class Wrapper implements File {
*/
protected $source;
protected static function wrapSource($source, $context, $protocol, $class) {
try {
stream_wrapper_register($protocol, $class);
if (@rewinddir($source) === false) {
$wrapped = fopen($protocol . '://', 'r+', false, $context);
} else {
$wrapped = opendir($protocol . '://', $context);
}
} catch (\BadMethodCallException $e) {
stream_wrapper_unregister($protocol);
throw $e;
}
stream_wrapper_unregister($protocol);
return $wrapped;
}
/**
* Load the source from the stream context and return the context options
*
@ -107,4 +123,17 @@ abstract class Wrapper implements File {
public function stream_close() {
return fclose($this->source);
}
public function dir_readdir() {
return readdir($this->source);
}
public function dir_closedir() {
closedir($this->source);
return true;
}
public function dir_rewinddir() {
return rewind($this->source);
}
}