diff options
author | Roeland Jago Douma <rullzer@owncloud.com> | 2015-11-18 11:38:19 +0300 |
---|---|---|
committer | Roeland Jago Douma <rullzer@owncloud.com> | 2015-11-18 11:38:19 +0300 |
commit | da0cd71e5b38087101baf116ed2e3373d67664cb (patch) | |
tree | aa1acf5e16202a3f0ba58a75fcd945d58e68cdde /icewind | |
parent | be700d4918627e06eb3e8c5f3b025911061badff (diff) |
Bump 3rdparty for icewind/streams-0.3.0
Diffstat (limited to 'icewind')
-rw-r--r-- | icewind/streams/README.md | 52 | ||||
-rw-r--r-- | icewind/streams/composer.json | 23 | ||||
-rw-r--r-- | icewind/streams/src/CallbackWrapper.php | 67 | ||||
-rw-r--r-- | icewind/streams/src/File.php | 2 | ||||
-rw-r--r-- | icewind/streams/src/IteratorDirectory.php | 4 | ||||
-rw-r--r-- | icewind/streams/src/NullWrapper.php | 15 | ||||
-rw-r--r-- | icewind/streams/src/Path.php | 104 | ||||
-rw-r--r-- | icewind/streams/src/Url.php | 64 | ||||
-rw-r--r-- | icewind/streams/src/UrlCallBack.php | 121 | ||||
-rw-r--r-- | icewind/streams/src/Wrapper.php | 31 |
10 files changed, 367 insertions, 116 deletions
diff --git a/icewind/streams/README.md b/icewind/streams/README.md deleted file mode 100644 index 54f6d19a..00000000 --- a/icewind/streams/README.md +++ /dev/null @@ -1,52 +0,0 @@ -#Streams# - -[![Build Status](https://travis-ci.org/icewind1991/Streams.svg?branch=master)](https://travis-ci.org/icewind1991/Streams) -[![Coverage Status](https://img.shields.io/coveralls/icewind1991/Streams.svg)](https://coveralls.io/r/icewind1991/Streams?branch=master) - -Generic stream wrappers for php. - -##CallBackWrapper## - -A `CallBackWrapper` can be used to register callbacks on read, write and closing of the stream, -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 -and can be any valid [php callable](http://php.net/manual/en/language.types.callable.php) - -###Example### -```php -<?php - -use \Icewind\Streams\CallBackWrapper; - -require('vendor/autoload.php'); - -// get an existing stream to wrap -$source = fopen('php://temp', 'r+'); - -// register the callbacks -$stream = CallbackWrapper::wrap($source, - // read callback - function ($count) { - echo "read " . $count . "bytes\n"; - }, - // write callback - function ($data) { - echo "wrote '" . $data . "'\n"; - }, - // close callback - function () { - echo "stream closed\n"; - }); - -fwrite($stream, 'some dummy data'); - -rewind($stream); -fread($stream, 5); - -fclose($stream); -``` - -Note: due to php's internal stream buffering the `$count` passed to the read callback -will be equal to php's internal buffer size (8192 on default) an not the number of bytes -requested by `fopen()` diff --git a/icewind/streams/composer.json b/icewind/streams/composer.json deleted file mode 100644 index 86d3c834..00000000 --- a/icewind/streams/composer.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name" : "icewind/streams", - "description" : "A set of generic stream wrappers", - "license" : "MIT", - "authors" : [ - { - "name" : "Robin Appelman", - "email": "icewind@owncloud.com" - } - ], - "require" : { - "php": ">=5.3" - }, - "require-dev" : { - "satooshi/php-coveralls": "dev-master" - }, - "autoload" : { - "psr-4": { - "Icewind\\Streams\\Tests\\": "tests/", - "Icewind\\Streams\\": "src/" - } - } -} diff --git a/icewind/streams/src/CallbackWrapper.php b/icewind/streams/src/CallbackWrapper.php index fd99aa6e..c5847b95 100644 --- a/icewind/streams/src/CallbackWrapper.php +++ b/icewind/streams/src/CallbackWrapper.php @@ -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) * ] * ] * @@ -39,54 +40,56 @@ class CallbackWrapper extends Wrapper { protected $closeCallback; /** + * @var callable + */ + protected $readDirCallBack; + + /** * Wraps a stream with the provided callbacks * * @param resource $source * @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 (isset($context['read']) and is_callable($context['read'])) { - $this->readCallback = $context['read']; - } - if (isset($context['write']) and is_callable($context['write'])) { - $this->writeCallback = $context['write']; - } - if (isset($context['close']) and is_callable($context['close'])) { - $this->closeCallback = $context['close']; - } + $this->readCallback = $context['read']; + $this->writeCallback = $context['write']; + $this->closeCallback = $context['close']; + $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) { + if (is_callable($this->readCallback)) { call_user_func($this->readCallback, $count); } return $result; @@ -94,7 +97,7 @@ class CallbackWrapper extends Wrapper { public function stream_write($data) { $result = parent::stream_write($data); - if ($this->writeCallback) { + if (is_callable($this->writeCallback)) { call_user_func($this->writeCallback, $data); } return $result; @@ -102,9 +105,17 @@ class CallbackWrapper extends Wrapper { public function stream_close() { $result = parent::stream_close(); - if ($this->closeCallback) { + if (is_callable($this->closeCallback)) { call_user_func($this->closeCallback); } return $result; } + + public function dir_readdir() { + $result = parent::dir_readdir(); + if (is_callable($this->readDirCallBack)) { + call_user_func($this->readDirCallBack); + } + return $result; + } } diff --git a/icewind/streams/src/File.php b/icewind/streams/src/File.php index 6202ef4a..252b7b89 100644 --- a/icewind/streams/src/File.php +++ b/icewind/streams/src/File.php @@ -21,7 +21,7 @@ interface File { public function stream_open($path, $mode, $options, &$opened_path); /** - * @param string $offset + * @param int $offset * @param int $whence * @return bool */ diff --git a/icewind/streams/src/IteratorDirectory.php b/icewind/streams/src/IteratorDirectory.php index c4eac5d4..6dfa42a8 100644 --- a/icewind/streams/src/IteratorDirectory.php +++ b/icewind/streams/src/IteratorDirectory.php @@ -45,9 +45,9 @@ class IteratorDirectory implements Directory { } else { throw new \BadMethodCallException('Invalid context, "' . $name . '" options not set'); } - if (isset($context['iterator']) and $context['iterator'] instanceof \Iterator) { + if (isset($context['iterator'])) { $this->iterator = $context['iterator']; - } else if (isset($context['array']) and is_array($context['array'])) { + } else if (isset($context['array'])) { $this->iterator = new \ArrayIterator($context['array']); } else { throw new \BadMethodCallException('Invalid context, iterator or array not set'); diff --git a/icewind/streams/src/NullWrapper.php b/icewind/streams/src/NullWrapper.php index 8cbaaa75..b6c71d98 100644 --- a/icewind/streams/src/NullWrapper.php +++ b/icewind/streams/src/NullWrapper.php @@ -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; + } } diff --git a/icewind/streams/src/Path.php b/icewind/streams/src/Path.php new file mode 100644 index 00000000..46d2156b --- /dev/null +++ b/icewind/streams/src/Path.php @@ -0,0 +1,104 @@ +<?php +/** + * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\Streams; + +/** + * A string-like object that automatically registers a stream wrapper when used and removes the stream wrapper when no longer used + * + * Can optionally pass context options to the stream wrapper + */ +class Path { + + /** + * @var bool + */ + protected $registered = false; + + /** + * @var string + */ + protected $protocol; + + /** + * @var string + */ + protected $class; + + /** + * @var array + */ + protected $contextOptions; + + /** + * @param string $class + * @param array $contextOptions + */ + public function __construct($class, $contextOptions = array()) { + $this->class = $class; + $this->contextOptions = $contextOptions; + } + + public function getProtocol() { + if (!$this->protocol) { + $this->protocol = 'auto' . uniqid(); + } + return $this->protocol; + } + + public function wrapPath($path) { + return $this->getProtocol() . '://' . $path; + } + + protected function register() { + if (!$this->registered) { + $this->appendDefaultContent($this->getProtocol(), $this->contextOptions); + stream_wrapper_register($this->getProtocol(), $this->class); + $this->registered = true; + } + } + + protected function unregister() { + stream_wrapper_unregister($this->getProtocol()); + $this->unsetDefaultContent($this->getProtocol()); + $this->registered = false; + } + + /** + * Add values to the default stream context + * + * @param string $key + * @param array $values + */ + protected function appendDefaultContent($key, $values) { + $context = stream_context_get_default(); + $defaults = stream_context_get_options($context); + $defaults[$key] = $values; + stream_context_set_default($defaults); + } + + /** + * Remove values from the default stream context + * + * @param string $key + */ + protected function unsetDefaultContent($key) { + $context = stream_context_get_default(); + $defaults = stream_context_get_options($context); + unset($defaults[$key]); + stream_context_set_default($defaults); + } + + public function __toString() { + $this->register(); + return $this->protocol . '://'; + } + + public function __destruct() { + $this->unregister(); + } +} diff --git a/icewind/streams/src/Url.php b/icewind/streams/src/Url.php new file mode 100644 index 00000000..d6822608 --- /dev/null +++ b/icewind/streams/src/Url.php @@ -0,0 +1,64 @@ +<?php +/** + * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\Streams; + +/** + * Interface for stream wrappers that implement url functions such as unlink, stat + */ +interface Url { + /** + * @param string $path + * @param array $options + * @return bool + */ + public function dir_opendir($path, $options); + + /** + * @param string $path + * @param string $mode + * @param int $options + * @param string &$opened_path + * @return bool + */ + public function stream_open($path, $mode, $options, &$opened_path); + + /** + * @param string $path + * @param int $mode + * @param int $options + * @return bool + */ + public function mkdir($path, $mode, $options); + + /** + * @param string $source + * @param string $target + * @return bool + */ + public function rename($source, $target); + + /** + * @param string $path + * @param int $options + * @return bool + */ + public function rmdir($path, $options); + + /** + * @param string + * @return bool + */ + public function unlink($path); + + /** + * @param string $path + * @param int $flags + * @return array + */ + public function url_stat($path, $flags); +} diff --git a/icewind/streams/src/UrlCallBack.php b/icewind/streams/src/UrlCallBack.php new file mode 100644 index 00000000..580bfc6b --- /dev/null +++ b/icewind/streams/src/UrlCallBack.php @@ -0,0 +1,121 @@ +<?php +/** + * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\Streams; + +/** + * Wrapper that provides callbacks for url actions such as fopen, unlink, rename + * + * Usage: + * + * $path = UrlCallBack('/path/so/source', function(){ + * echo 'fopen'; + * }, function(){ + * echo 'opendir'; + * }, function(){ + * echo 'mkdir'; + * }, function(){ + * echo 'rename'; + * }, function(){ + * echo 'rmdir'; + * }, function(){ + * echo 'unlink'; + * }, function(){ + * echo 'stat'; + * }); + * + * mkdir($path); + * ... + * + * All callbacks are called after the operation is executed on the source stream + */ +class UrlCallback extends Wrapper implements Url { + + /** + * @param string $source + * @param callable $fopen + * @param callable $opendir + * @param callable $mkdir + * @param callable $rename + * @param callable $rmdir + * @param callable $unlink + * @param callable $stat + * @return \Icewind\Streams\Path + * + * @throws \BadMethodCallException + * @throws \Exception + */ + public static function wrap($source, $fopen = null, $opendir = null, $mkdir = null, $rename = null, $rmdir = null, + $unlink = null, $stat = null) { + $options = array( + 'source' => $source, + 'fopen' => $fopen, + 'opendir' => $opendir, + 'mkdir' => $mkdir, + 'rename' => $rename, + 'rmdir' => $rmdir, + 'unlink' => $unlink, + 'stat' => $stat + ); + return new Path('\Icewind\Streams\UrlCallBack', $options); + } + + protected function loadContext($url) { + list($protocol) = explode('://', $url); + $options = stream_context_get_options($this->context); + return $options[$protocol]; + } + + protected function callCallBack($context, $callback) { + if (is_callable($context[$callback])) { + call_user_func($context[$callback]); + } + } + + public function stream_open($path, $mode, $options, &$opened_path) { + $context = $this->loadContext($path); + $this->callCallBack($context, 'fopen'); + $this->setSourceStream(fopen($context['source'], $mode)); + return true; + } + + public function dir_opendir($path, $options) { + $context = $this->loadContext($path); + $this->callCallBack($context, 'opendir'); + $this->setSourceStream(opendir($context['source'])); + return true; + } + + public function mkdir($path, $mode, $options) { + $context = $this->loadContext($path); + $this->callCallBack($context, 'mkdir'); + return mkdir($context['source'], $mode, $options & STREAM_MKDIR_RECURSIVE); + } + + public function rmdir($path, $options) { + $context = $this->loadContext($path); + $this->callCallBack($context, 'rmdir'); + return rmdir($context['source']); + } + + public function rename($source, $target) { + $context = $this->loadContext($source); + $this->callCallBack($context, 'rename'); + list(, $target) = explode('://', $target); + return rename($context['source'], $target); + } + + public function unlink($path) { + $context = $this->loadContext($path); + $this->callCallBack($context, 'unlink'); + return unlink($context['source']); + } + + public function url_stat($path, $flags) { + throw new \Exception('stat is not supported due to php bug 50526'); + } +} diff --git a/icewind/streams/src/Wrapper.php b/icewind/streams/src/Wrapper.php index 2e3a6e6c..53de2942 100644 --- a/icewind/streams/src/Wrapper.php +++ b/icewind/streams/src/Wrapper.php @@ -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); + } } |