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

@ -17,6 +17,7 @@ namespace Icewind\Streams;
* '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);
}
}

View file

@ -14,10 +14,11 @@ class CallbackWrapper extends Wrapper {
* @param callable $read
* @param callable $write
* @param callable $close
* @param callable $readDir
* @return resource
*/
protected function wrapSource($source, $read = null, $write = null, $close = null) {
return \Icewind\Streams\CallbackWrapper::wrap($source, $read, $write, $close);
protected function wrapSource($source, $read = null, $write = null, $close = null, $readDir = null) {
return \Icewind\Streams\CallbackWrapper::wrap($source, $read, $write, $close, $readDir);
}
/**
@ -69,4 +70,17 @@ class CallbackWrapper extends Wrapper {
fclose($wrapped);
$this->assertTrue($called);
}
public function testReadDirCallback() {
$called = false;
$callBack = function () use (&$called) {
$called = true;
};
$source = opendir(sys_get_temp_dir());
$wrapped = $this->wrapSource($source, null, null, null, $callBack);
readdir($wrapped);
$this->assertTrue($called);
}
}

View file

@ -102,4 +102,38 @@ abstract class Wrapper extends \PHPUnit_Framework_TestCase {
stream_set_timeout($wrapped, 1, 0);
stream_set_write_buffer($wrapped, 0);
}
public function testReadDir() {
$source = opendir(__DIR__);
$content = array();
while (($name = readdir($source)) !== false) {
$content[] = $name;
}
closedir($source);
$source = opendir(__DIR__);
$wrapped = $this->wrapSource($source);
$wrappedContent = array();
while (($name = readdir($wrapped)) !== false) {
$wrappedContent[] = $name;
}
$this->assertEquals($content, $wrappedContent);
}
public function testRewindDir() {
$source = opendir(__DIR__);
$content = array();
while (($name = readdir($source)) !== false) {
$content[] = $name;
}
closedir($source);
$source = opendir(__DIR__);
$wrapped = $this->wrapSource($source);
$this->assertEquals($content[0], readdir($wrapped));
$this->assertEquals($content[1], readdir($wrapped));
$this->assertEquals($content[2], readdir($wrapped));
rewinddir($wrapped);
$this->assertEquals($content[0], readdir($wrapped));
}
}