Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/3rdparty.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Appelman <robin@icewind.nl>2021-10-14 15:44:24 +0300
committerRobin Appelman <robin@icewind.nl>2021-10-14 15:44:24 +0300
commit2e6fdcd066bccc74969dfe788d6151ab529d53f0 (patch)
tree91004e53f260cd81a1d69a37ceaa4f9b8eec2e60
parentb9519d3f7d57b17401ccf4eee98455570c494e6a (diff)
[20] Bump icewind/streams to 0.7.5composer/stable20/icewind/streams-0.7.5
Signed-off-by: Robin Appelman <robin@icewind.nl>
-rw-r--r--composer.json2
-rw-r--r--composer.lock24
-rw-r--r--composer/autoload_classmap.php5
-rw-r--r--composer/autoload_psr4.php1
-rw-r--r--composer/autoload_static.php10
-rw-r--r--composer/installed.json24
-rw-r--r--composer/installed.php10
-rw-r--r--icewind/streams/.github/workflows/ci.yaml83
-rw-r--r--icewind/streams/.gitignore3
-rw-r--r--icewind/streams/src/CallbackWrapper.php46
-rw-r--r--icewind/streams/src/CountWrapper.php15
-rw-r--r--icewind/streams/src/Directory.php2
-rw-r--r--icewind/streams/src/DirectoryFilter.php17
-rw-r--r--icewind/streams/src/DirectoryWrapper.php49
-rw-r--r--icewind/streams/src/File.php10
-rw-r--r--icewind/streams/src/HashWrapper.php78
-rw-r--r--icewind/streams/src/IteratorDirectory.php42
-rw-r--r--icewind/streams/src/NullWrapper.php18
-rw-r--r--icewind/streams/src/Path.php4
-rw-r--r--icewind/streams/src/PathWrapper.php6
-rw-r--r--icewind/streams/src/ReadHashWrapper.php40
-rw-r--r--icewind/streams/src/RetryWrapper.php22
-rw-r--r--icewind/streams/src/SeekableWrapper.php25
-rw-r--r--icewind/streams/src/Url.php6
-rw-r--r--icewind/streams/src/UrlCallback.php (renamed from icewind/streams/src/UrlCallBack.php)60
-rw-r--r--icewind/streams/src/Wrapper.php65
-rw-r--r--icewind/streams/src/WrapperHandler.php114
-rw-r--r--icewind/streams/src/WriteHashWrapper.php37
28 files changed, 533 insertions, 285 deletions
diff --git a/composer.json b/composer.json
index f3a00412..880a47a6 100644
--- a/composer.json
+++ b/composer.json
@@ -21,7 +21,7 @@
"doctrine/dbal": "2.10.2",
"guzzlehttp/guzzle": "6.5.2",
"icewind/searchdav": "^2.0.0",
- "icewind/streams": "v0.7.1",
+ "icewind/streams": "v0.7.5",
"jeremeamia/superclosure": "^2.4",
"league/flysystem": "^1.0",
"microsoft/azure-storage-blob": "1.5.0",
diff --git a/composer.lock b/composer.lock
index 40d65ee5..c19bf5f4 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "9c81b3768ac0e6bf0c3f4d3bc9b3aa96",
+ "content-hash": "12672044acefa5f05fcd287fa61748d7",
"packages": [
{
"name": "aws/aws-sdk-php",
@@ -1657,29 +1657,29 @@
},
{
"name": "icewind/streams",
- "version": "v0.7.1",
+ "version": "v0.7.5",
"source": {
"type": "git",
"url": "https://github.com/icewind1991/Streams.git",
- "reference": "4db3ed6c366e90b958d00e1d4c6360a9b39b2121"
+ "reference": "0c6aae16ebdadb257f0bd089c1e1e4cf5e20ddc2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/icewind1991/Streams/zipball/4db3ed6c366e90b958d00e1d4c6360a9b39b2121",
- "reference": "4db3ed6c366e90b958d00e1d4c6360a9b39b2121",
+ "url": "https://api.github.com/repos/icewind1991/Streams/zipball/0c6aae16ebdadb257f0bd089c1e1e4cf5e20ddc2",
+ "reference": "0c6aae16ebdadb257f0bd089c1e1e4cf5e20ddc2",
"shasum": ""
},
"require": {
- "php": ">=5.3"
+ "php": ">=7.1"
},
"require-dev": {
- "phpunit/phpunit": "^4.8",
- "satooshi/php-coveralls": "v1.0.0"
+ "friendsofphp/php-cs-fixer": "^2",
+ "phpstan/phpstan": "^0.12",
+ "phpunit/phpunit": "^9"
},
"type": "library",
"autoload": {
"psr-4": {
- "Icewind\\Streams\\Tests\\": "tests/",
"Icewind\\Streams\\": "src/"
}
},
@@ -1694,7 +1694,11 @@
}
],
"description": "A set of generic stream wrappers",
- "time": "2019-02-15T12:57:29+00:00"
+ "support": {
+ "issues": "https://github.com/icewind1991/Streams/issues",
+ "source": "https://github.com/icewind1991/Streams/tree/v0.7.5"
+ },
+ "time": "2021-06-14T14:02:48+00:00"
},
{
"name": "jeremeamia/superclosure",
diff --git a/composer/autoload_classmap.php b/composer/autoload_classmap.php
index e2bd2b42..9367604d 100644
--- a/composer/autoload_classmap.php
+++ b/composer/autoload_classmap.php
@@ -1395,14 +1395,19 @@ return array(
'Icewind\\Streams\\DirectoryFilter' => $vendorDir . '/icewind/streams/src/DirectoryFilter.php',
'Icewind\\Streams\\DirectoryWrapper' => $vendorDir . '/icewind/streams/src/DirectoryWrapper.php',
'Icewind\\Streams\\File' => $vendorDir . '/icewind/streams/src/File.php',
+ 'Icewind\\Streams\\HashWrapper' => $vendorDir . '/icewind/streams/src/HashWrapper.php',
'Icewind\\Streams\\IteratorDirectory' => $vendorDir . '/icewind/streams/src/IteratorDirectory.php',
'Icewind\\Streams\\NullWrapper' => $vendorDir . '/icewind/streams/src/NullWrapper.php',
'Icewind\\Streams\\Path' => $vendorDir . '/icewind/streams/src/Path.php',
'Icewind\\Streams\\PathWrapper' => $vendorDir . '/icewind/streams/src/PathWrapper.php',
+ 'Icewind\\Streams\\ReadHashWrapper' => $vendorDir . '/icewind/streams/src/ReadHashWrapper.php',
'Icewind\\Streams\\RetryWrapper' => $vendorDir . '/icewind/streams/src/RetryWrapper.php',
'Icewind\\Streams\\SeekableWrapper' => $vendorDir . '/icewind/streams/src/SeekableWrapper.php',
'Icewind\\Streams\\Url' => $vendorDir . '/icewind/streams/src/Url.php',
+ 'Icewind\\Streams\\UrlCallback' => $vendorDir . '/icewind/streams/src/UrlCallback.php',
'Icewind\\Streams\\Wrapper' => $vendorDir . '/icewind/streams/src/Wrapper.php',
+ 'Icewind\\Streams\\WrapperHandler' => $vendorDir . '/icewind/streams/src/WrapperHandler.php',
+ 'Icewind\\Streams\\WriteHashWrapper' => $vendorDir . '/icewind/streams/src/WriteHashWrapper.php',
'JmesPath\\AstRuntime' => $vendorDir . '/mtdowling/jmespath.php/src/AstRuntime.php',
'JmesPath\\CompilerRuntime' => $vendorDir . '/mtdowling/jmespath.php/src/CompilerRuntime.php',
'JmesPath\\DebugRuntime' => $vendorDir . '/mtdowling/jmespath.php/src/DebugRuntime.php',
diff --git a/composer/autoload_psr4.php b/composer/autoload_psr4.php
index 22302727..b795e70d 100644
--- a/composer/autoload_psr4.php
+++ b/composer/autoload_psr4.php
@@ -60,7 +60,6 @@ return array(
'League\\Flysystem\\' => array($vendorDir . '/league/flysystem/src'),
'JsonSchema\\' => array($vendorDir . '/justinrainbow/json-schema/src/JsonSchema'),
'JmesPath\\' => array($vendorDir . '/mtdowling/jmespath.php/src'),
- 'Icewind\\Streams\\Tests\\' => array($vendorDir . '/icewind/streams/tests'),
'Icewind\\Streams\\' => array($vendorDir . '/icewind/streams/src'),
'ID3Parser\\' => array($vendorDir . '/christophwurst/id3parser/src'),
'Http\\Promise\\' => array($vendorDir . '/php-http/promise/src'),
diff --git a/composer/autoload_static.php b/composer/autoload_static.php
index 63db5a2e..58985155 100644
--- a/composer/autoload_static.php
+++ b/composer/autoload_static.php
@@ -220,7 +220,6 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652
),
'I' =>
array (
- 'Icewind\\Streams\\Tests\\' => 22,
'Icewind\\Streams\\' => 16,
'ID3Parser\\' => 10,
),
@@ -494,10 +493,6 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652
array (
0 => __DIR__ . '/..' . '/mtdowling/jmespath.php/src',
),
- 'Icewind\\Streams\\Tests\\' =>
- array (
- 0 => __DIR__ . '/..' . '/icewind/streams/tests',
- ),
'Icewind\\Streams\\' =>
array (
0 => __DIR__ . '/..' . '/icewind/streams/src',
@@ -2024,14 +2019,19 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652
'Icewind\\Streams\\DirectoryFilter' => __DIR__ . '/..' . '/icewind/streams/src/DirectoryFilter.php',
'Icewind\\Streams\\DirectoryWrapper' => __DIR__ . '/..' . '/icewind/streams/src/DirectoryWrapper.php',
'Icewind\\Streams\\File' => __DIR__ . '/..' . '/icewind/streams/src/File.php',
+ 'Icewind\\Streams\\HashWrapper' => __DIR__ . '/..' . '/icewind/streams/src/HashWrapper.php',
'Icewind\\Streams\\IteratorDirectory' => __DIR__ . '/..' . '/icewind/streams/src/IteratorDirectory.php',
'Icewind\\Streams\\NullWrapper' => __DIR__ . '/..' . '/icewind/streams/src/NullWrapper.php',
'Icewind\\Streams\\Path' => __DIR__ . '/..' . '/icewind/streams/src/Path.php',
'Icewind\\Streams\\PathWrapper' => __DIR__ . '/..' . '/icewind/streams/src/PathWrapper.php',
+ 'Icewind\\Streams\\ReadHashWrapper' => __DIR__ . '/..' . '/icewind/streams/src/ReadHashWrapper.php',
'Icewind\\Streams\\RetryWrapper' => __DIR__ . '/..' . '/icewind/streams/src/RetryWrapper.php',
'Icewind\\Streams\\SeekableWrapper' => __DIR__ . '/..' . '/icewind/streams/src/SeekableWrapper.php',
'Icewind\\Streams\\Url' => __DIR__ . '/..' . '/icewind/streams/src/Url.php',
+ 'Icewind\\Streams\\UrlCallback' => __DIR__ . '/..' . '/icewind/streams/src/UrlCallback.php',
'Icewind\\Streams\\Wrapper' => __DIR__ . '/..' . '/icewind/streams/src/Wrapper.php',
+ 'Icewind\\Streams\\WrapperHandler' => __DIR__ . '/..' . '/icewind/streams/src/WrapperHandler.php',
+ 'Icewind\\Streams\\WriteHashWrapper' => __DIR__ . '/..' . '/icewind/streams/src/WriteHashWrapper.php',
'JmesPath\\AstRuntime' => __DIR__ . '/..' . '/mtdowling/jmespath.php/src/AstRuntime.php',
'JmesPath\\CompilerRuntime' => __DIR__ . '/..' . '/mtdowling/jmespath.php/src/CompilerRuntime.php',
'JmesPath\\DebugRuntime' => __DIR__ . '/..' . '/mtdowling/jmespath.php/src/DebugRuntime.php',
diff --git a/composer/installed.json b/composer/installed.json
index c8c53a23..16a7901b 100644
--- a/composer/installed.json
+++ b/composer/installed.json
@@ -1726,32 +1726,32 @@
},
{
"name": "icewind/streams",
- "version": "v0.7.1",
- "version_normalized": "0.7.1.0",
+ "version": "v0.7.5",
+ "version_normalized": "0.7.5.0",
"source": {
"type": "git",
"url": "https://github.com/icewind1991/Streams.git",
- "reference": "4db3ed6c366e90b958d00e1d4c6360a9b39b2121"
+ "reference": "0c6aae16ebdadb257f0bd089c1e1e4cf5e20ddc2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/icewind1991/Streams/zipball/4db3ed6c366e90b958d00e1d4c6360a9b39b2121",
- "reference": "4db3ed6c366e90b958d00e1d4c6360a9b39b2121",
+ "url": "https://api.github.com/repos/icewind1991/Streams/zipball/0c6aae16ebdadb257f0bd089c1e1e4cf5e20ddc2",
+ "reference": "0c6aae16ebdadb257f0bd089c1e1e4cf5e20ddc2",
"shasum": ""
},
"require": {
- "php": ">=5.3"
+ "php": ">=7.1"
},
"require-dev": {
- "phpunit/phpunit": "^4.8",
- "satooshi/php-coveralls": "v1.0.0"
+ "friendsofphp/php-cs-fixer": "^2",
+ "phpstan/phpstan": "^0.12",
+ "phpunit/phpunit": "^9"
},
- "time": "2019-02-15T12:57:29+00:00",
+ "time": "2021-06-14T14:02:48+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
- "Icewind\\Streams\\Tests\\": "tests/",
"Icewind\\Streams\\": "src/"
}
},
@@ -1766,6 +1766,10 @@
}
],
"description": "A set of generic stream wrappers",
+ "support": {
+ "issues": "https://github.com/icewind1991/Streams/issues",
+ "source": "https://github.com/icewind1991/Streams/tree/v0.7.5"
+ },
"install-path": "../icewind/streams"
},
{
diff --git a/composer/installed.php b/composer/installed.php
index a3d76517..eaa76376 100644
--- a/composer/installed.php
+++ b/composer/installed.php
@@ -5,7 +5,7 @@
'type' => 'library',
'install_path' => __DIR__ . '/../',
'aliases' => array(),
- 'reference' => 'd529936eea0b8174fb9acba170f4b68ba276fb00',
+ 'reference' => 'b9519d3f7d57b17401ccf4eee98455570c494e6a',
'name' => 'nextcloud/3rdparty',
'dev' => false,
),
@@ -236,12 +236,12 @@
'dev_requirement' => false,
),
'icewind/streams' => array(
- 'pretty_version' => 'v0.7.1',
- 'version' => '0.7.1.0',
+ 'pretty_version' => 'v0.7.5',
+ 'version' => '0.7.5.0',
'type' => 'library',
'install_path' => __DIR__ . '/../icewind/streams',
'aliases' => array(),
- 'reference' => '4db3ed6c366e90b958d00e1d4c6360a9b39b2121',
+ 'reference' => '0c6aae16ebdadb257f0bd089c1e1e4cf5e20ddc2',
'dev_requirement' => false,
),
'jeremeamia/superclosure' => array(
@@ -322,7 +322,7 @@
'type' => 'library',
'install_path' => __DIR__ . '/../',
'aliases' => array(),
- 'reference' => 'd529936eea0b8174fb9acba170f4b68ba276fb00',
+ 'reference' => 'b9519d3f7d57b17401ccf4eee98455570c494e6a',
'dev_requirement' => false,
),
'nextcloud/lognormalizer' => array(
diff --git a/icewind/streams/.github/workflows/ci.yaml b/icewind/streams/.github/workflows/ci.yaml
new file mode 100644
index 00000000..f7422333
--- /dev/null
+++ b/icewind/streams/.github/workflows/ci.yaml
@@ -0,0 +1,83 @@
+on: [push, pull_request]
+
+name: CI
+
+jobs:
+ php-cs-fixer:
+ name: PHP-CS-Fixer
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@master
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: '8.0'
+ - name: PHP-CS-Fixer
+ uses: OskarStark/php-cs-fixer-ga@2.16.7
+ with:
+ args: --diff --dry-run --allow-risky yes --stop-on-violation --using-cache=no --path-mode=intersection
+
+ phpstan:
+ name: PHPStan Static Analysis
+ runs-on: ubuntu-20.04
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: '8.0'
+ - name: Composer
+ run: composer install
+ - env:
+ BACKEND: smbclient
+ run: php ./vendor/bin/phpstan analyse --level 5 src
+
+ phpunit:
+ runs-on: ubuntu-20.04
+ name: Unit tests
+
+ strategy:
+ matrix:
+ php-version:
+ - "7.3"
+ - "7.4"
+ - "8.0"
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: "${{ matrix.php-version }}"
+ coverage: pcov
+ - name: Composer
+ run: composer install
+ - name: PHPUnit Tests
+ run: php ./vendor/bin/phpunit tests -c tests/phpunit.xml --coverage-clover=coverage.xml
+ - uses: codecov/codecov-action@v1
+ with:
+ files: ./coverage.xml
+
+ phpunit-8:
+ runs-on: ubuntu-20.04
+ name: Unit tests
+
+ strategy:
+ matrix:
+ php-version:
+ - "7.1"
+ - "7.2"
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: "${{ matrix.php-version }}"
+ - name: Composer
+ run: |
+ echo '{"autoload": {"psr-4": {"Icewind\\Streams\\": "src/"}},"autoload-dev": {"psr-4": {"Icewind\\Streams\\Tests\\": "tests/"}}}' > composer.json
+ composer require --dev phpunit/phpunit ^6
+ - name: PHPUnit Tests
+ run: php ./vendor/bin/phpunit tests -c tests/phpunit.xml
diff --git a/icewind/streams/.gitignore b/icewind/streams/.gitignore
index 4f389129..a8fa5d4a 100644
--- a/icewind/streams/.gitignore
+++ b/icewind/streams/.gitignore
@@ -1,3 +1,6 @@
.idea
vendor
composer.lock
+build
+example.php
+*.cache
diff --git a/icewind/streams/src/CallbackWrapper.php b/icewind/streams/src/CallbackWrapper.php
index 67f9110d..5d78b5a3 100644
--- a/icewind/streams/src/CallbackWrapper.php
+++ b/icewind/streams/src/CallbackWrapper.php
@@ -25,27 +25,27 @@ namespace Icewind\Streams;
*/
class CallbackWrapper extends Wrapper {
/**
- * @var callable
+ * @var callable|null
*/
protected $readCallback;
/**
- * @var callable
+ * @var callable|null
*/
protected $writeCallback;
/**
- * @var callable
+ * @var callable|null
*/
protected $closeCallback;
/**
- * @var callable
+ * @var callable|null
*/
protected $readDirCallBack;
/**
- * @var callable
+ * @var callable|null
*/
protected $preCloseCallback;
@@ -53,30 +53,28 @@ class CallbackWrapper extends Wrapper {
* 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
+ * @param callable|null $read (optional)
+ * @param callable|null $write (optional)
+ * @param callable|null $close (optional)
+ * @param callable|null $readDir (optional)
+ * @param callable|null $preClose (optional)
+ * @return resource|bool
*
- * @throws \BadMethodCallException
*/
public static function wrap($source, $read = null, $write = null, $close = null, $readDir = null, $preClose = null) {
- $context = stream_context_create(array(
- 'callback' => array(
- 'source' => $source,
- 'read' => $read,
- 'write' => $write,
- 'close' => $close,
- 'readDir' => $readDir,
- 'preClose' => $preClose,
- )
- ));
- return Wrapper::wrapSource($source, $context, 'callback', '\Icewind\Streams\CallbackWrapper');
+ $context = [
+ 'source' => $source,
+ 'read' => $read,
+ 'write' => $write,
+ 'close' => $close,
+ 'readDir' => $readDir,
+ 'preClose' => $preClose,
+ ];
+ return self::wrapSource($source, $context);
}
protected function open() {
- $context = $this->loadContext('callback');
+ $context = $this->loadContext();
$this->readCallback = $context['read'];
$this->writeCallback = $context['write'];
@@ -112,7 +110,7 @@ class CallbackWrapper extends Wrapper {
public function stream_close() {
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
$this->preCloseCallback = null;
}
diff --git a/icewind/streams/src/CountWrapper.php b/icewind/streams/src/CountWrapper.php
index 8b86ab91..b3346209 100644
--- a/icewind/streams/src/CountWrapper.php
+++ b/icewind/streams/src/CountWrapper.php
@@ -55,7 +55,7 @@ class CountWrapper extends Wrapper {
*
* @param resource $source
* @param callable $callback
- * @return resource
+ * @return resource|bool
*
* @throws \BadMethodCallException
*/
@@ -63,17 +63,14 @@ class CountWrapper extends Wrapper {
if (!is_callable($callback)) {
throw new \InvalidArgumentException('Invalid or missing callback');
}
- $context = stream_context_create(array(
- 'count' => array(
- 'source' => $source,
- 'callback' => $callback
- )
- ));
- return Wrapper::wrapSource($source, $context, 'callback', '\Icewind\Streams\CountWrapper');
+ return self::wrapSource($source, [
+ 'source' => $source,
+ 'callback' => $callback
+ ]);
}
protected function open() {
- $context = $this->loadContext('count');
+ $context = $this->loadContext();
$this->callback = $context['callback'];
return true;
}
diff --git a/icewind/streams/src/Directory.php b/icewind/streams/src/Directory.php
index c80a8783..912be76a 100644
--- a/icewind/streams/src/Directory.php
+++ b/icewind/streams/src/Directory.php
@@ -19,7 +19,7 @@ interface Directory {
public function dir_opendir($path, $options);
/**
- * @return string
+ * @return string|bool
*/
public function dir_readdir();
diff --git a/icewind/streams/src/DirectoryFilter.php b/icewind/streams/src/DirectoryFilter.php
index 4b869699..80b27e8b 100644
--- a/icewind/streams/src/DirectoryFilter.php
+++ b/icewind/streams/src/DirectoryFilter.php
@@ -25,7 +25,7 @@ class DirectoryFilter extends DirectoryWrapper {
* @return bool
*/
public function dir_opendir($path, $options) {
- $context = $this->loadContext('filter');
+ $context = $this->loadContext();
$this->filter = $context['filter'];
return true;
}
@@ -36,7 +36,7 @@ class DirectoryFilter extends DirectoryWrapper {
public function dir_readdir() {
$file = readdir($this->source);
$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) {
$file = readdir($this->source);
}
@@ -46,15 +46,12 @@ class DirectoryFilter extends DirectoryWrapper {
/**
* @param resource $source
* @param callable $filter
- * @return resource
+ * @return resource|bool
*/
public static function wrap($source, callable $filter) {
- $options = array(
- 'filter' => array(
- 'source' => $source,
- 'filter' => $filter
- )
- );
- return self::wrapWithOptions($options, '\Icewind\Streams\DirectoryFilter');
+ return self::wrapSource($source, [
+ 'source' => $source,
+ 'filter' => $filter
+ ]);
}
}
diff --git a/icewind/streams/src/DirectoryWrapper.php b/icewind/streams/src/DirectoryWrapper.php
index 63e4805a..7f2f5c29 100644
--- a/icewind/streams/src/DirectoryWrapper.php
+++ b/icewind/streams/src/DirectoryWrapper.php
@@ -7,37 +7,9 @@
namespace Icewind\Streams;
-class DirectoryWrapper implements Directory {
- /**
- * @var resource
- */
- 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;
+class DirectoryWrapper extends Wrapper implements Directory {
+ public function stream_open($path, $mode, $options, &$opened_path) {
+ return false;
}
/**
@@ -46,7 +18,7 @@ class DirectoryWrapper implements Directory {
* @return bool
*/
public function dir_opendir($path, $options) {
- $this->loadContext('dir');
+ $this->loadContext();
return true;
}
@@ -72,17 +44,4 @@ class DirectoryWrapper implements Directory {
rewinddir($this->source);
return true;
}
-
- /**
- * @param array $options the options for the context to wrap the stream with
- * @param string $class
- * @return resource
- */
- protected static function wrapWithOptions($options, $class) {
- $context = stream_context_create($options);
- stream_wrapper_register('dirwrapper', $class);
- $wrapped = opendir('dirwrapper://', $context);
- stream_wrapper_unregister('dirwrapper');
- return $wrapped;
- }
}
diff --git a/icewind/streams/src/File.php b/icewind/streams/src/File.php
index 252b7b89..9662414a 100644
--- a/icewind/streams/src/File.php
+++ b/icewind/streams/src/File.php
@@ -15,7 +15,7 @@ interface File {
* @param string $path
* @param string $mode
* @param int $options
- * @param string &$opened_path
+ * @param string $opened_path
* @return bool
*/
public function stream_open($path, $mode, $options, &$opened_path);
@@ -28,19 +28,19 @@ interface File {
public function stream_seek($offset, $whence = SEEK_SET);
/**
- * @return int
+ * @return int|false
*/
public function stream_tell();
/**
* @param int $count
- * @return string
+ * @return string|false
*/
public function stream_read($count);
/**
* @param string $data
- * @return int
+ * @return int|false
*/
public function stream_write($data);
@@ -59,7 +59,7 @@ interface File {
public function stream_truncate($size);
/**
- * @return array
+ * @return array|false
*/
public function stream_stat();
diff --git a/icewind/streams/src/HashWrapper.php b/icewind/streams/src/HashWrapper.php
new file mode 100644
index 00000000..616c2fe5
--- /dev/null
+++ b/icewind/streams/src/HashWrapper.php
@@ -0,0 +1,78 @@
+<?php
+/**
+ * @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl>
+ *
+ * @author Roeland Jago Douma <roeland@famdouma.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;
+
+abstract class HashWrapper extends Wrapper {
+
+ /**
+ * @var callable|null
+ */
+ private $callback;
+
+ /**
+ * @var resource|\HashContext
+ */
+ private $hashContext;
+
+ /**
+ * Wraps a stream to make it seekable
+ *
+ * @param resource $source
+ * @param string $hash
+ * @param callable $callback
+ * @return resource|bool
+ *
+ * @throws \BadMethodCallException
+ */
+ public static function wrap($source, $hash, $callback) {
+ $context = [
+ 'hash' => $hash,
+ 'callback' => $callback,
+ ];
+ return self::wrapSource($source, $context);
+ }
+
+ public function dir_opendir($path, $options) {
+ return false;
+ }
+
+ public function stream_open($path, $mode, $options, &$opened_path) {
+ $context = $this->loadContext();
+ $this->callback = $context['callback'];
+ $this->hashContext = hash_init($context['hash']);
+ return true;
+ }
+
+ protected function updateHash($data) {
+ hash_update($this->hashContext, $data);
+ }
+
+ public function stream_close() {
+ $hash = hash_final($this->hashContext);
+ if ($this->hashContext !== false && is_callable($this->callback)) {
+ call_user_func($this->callback, $hash);
+ }
+ return parent::stream_close();
+ }
+}
diff --git a/icewind/streams/src/IteratorDirectory.php b/icewind/streams/src/IteratorDirectory.php
index 6dfa42a8..a3872ddf 100644
--- a/icewind/streams/src/IteratorDirectory.php
+++ b/icewind/streams/src/IteratorDirectory.php
@@ -20,7 +20,7 @@ namespace Icewind\Streams;
*
* 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
*/
@@ -36,18 +36,13 @@ class IteratorDirectory implements Directory {
*
* @param string $name
* @return array
- * @throws \Exception
+ * @throws \BadMethodCallException
*/
- 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');
- }
+ protected function loadContext($name = null) {
+ $context = parent::loadContext($name);
if (isset($context['iterator'])) {
$this->iterator = $context['iterator'];
- } else if (isset($context['array'])) {
+ } elseif (isset($context['array'])) {
$this->iterator = new \ArrayIterator($context['array']);
} else {
throw new \BadMethodCallException('Invalid context, iterator or array not set');
@@ -61,12 +56,12 @@ class IteratorDirectory implements Directory {
* @return bool
*/
public function dir_opendir($path, $options) {
- $this->loadContext('dir');
+ $this->loadContext();
return true;
}
/**
- * @return string
+ * @return string|bool
*/
public function dir_readdir() {
if ($this->iterator->valid()) {
@@ -97,27 +92,22 @@ class IteratorDirectory implements Directory {
* Creates a directory handle from the provided array or iterator
*
* @param \Iterator | array $source
- * @return resource
+ * @return resource|bool
*
* @throws \BadMethodCallException
*/
public static function wrap($source) {
if ($source instanceof \Iterator) {
- $context = stream_context_create(array(
- 'dir' => array(
- 'iterator' => $source)
- ));
- } else if (is_array($source)) {
- $context = stream_context_create(array(
- 'dir' => array(
- 'array' => $source)
- ));
+ $options = [
+ 'iterator' => $source
+ ];
+ } elseif (is_array($source)) {
+ $options = [
+ 'array' => $source
+ ];
} else {
throw new \BadMethodCallException('$source should be an Iterator or array');
}
- stream_wrapper_register('iterator', '\Icewind\Streams\IteratorDirectory');
- $wrapped = opendir('iterator://', $context);
- stream_wrapper_unregister('iterator');
- return $wrapped;
+ return self::wrapSource(self::NO_SOURCE_DIR, $options);
}
}
diff --git a/icewind/streams/src/NullWrapper.php b/icewind/streams/src/NullWrapper.php
index b6c71d98..92aef2c7 100644
--- a/icewind/streams/src/NullWrapper.php
+++ b/icewind/streams/src/NullWrapper.php
@@ -11,29 +11,17 @@ namespace Icewind\Streams;
* Stream wrapper that does nothing, used for tests
*/
class NullWrapper extends Wrapper {
- /**
- * Wraps a stream with the provided callbacks
- *
- * @param resource $source
- * @return resource
- *
- * @throws \BadMethodCallException
- */
public static function wrap($source) {
- $context = stream_context_create(array(
- 'null' => array(
- 'source' => $source)
- ));
- return Wrapper::wrapSource($source, $context, 'null', '\Icewind\Streams\NullWrapper');
+ return self::wrapSource($source);
}
public function stream_open($path, $mode, $options, &$opened_path) {
- $this->loadContext('null');
+ $this->loadContext();
return true;
}
public function dir_opendir($path, $options) {
- $this->loadContext('null');
+ $this->loadContext();
return true;
}
}
diff --git a/icewind/streams/src/Path.php b/icewind/streams/src/Path.php
index bef9fd5f..42d74a2a 100644
--- a/icewind/streams/src/Path.php
+++ b/icewind/streams/src/Path.php
@@ -38,7 +38,7 @@ class Path {
* @param string $class
* @param array $contextOptions
*/
- public function __construct($class, $contextOptions = array()) {
+ public function __construct($class, $contextOptions = []) {
$this->class = $class;
$this->contextOptions = $contextOptions;
}
@@ -75,7 +75,7 @@ class Path {
*/
protected function appendDefaultContent($values) {
if (!is_array(current($values))) {
- $values = array($this->getProtocol() => $values);
+ $values = [$this->getProtocol() => $values];
}
$context = stream_context_get_default();
$defaults = stream_context_get_options($context);
diff --git a/icewind/streams/src/PathWrapper.php b/icewind/streams/src/PathWrapper.php
index 88af7e17..d9f3014c 100644
--- a/icewind/streams/src/PathWrapper.php
+++ b/icewind/streams/src/PathWrapper.php
@@ -16,10 +16,8 @@ class PathWrapper extends NullWrapper {
* @return Path|string
*/
public static function getPath($source) {
- return new Path(__CLASS__, [
- 'null' => [
- 'source' => $source
- ]
+ return new Path(NullWrapper::class, [
+ NullWrapper::getProtocol() => ['source' => $source]
]);
}
}
diff --git a/icewind/streams/src/ReadHashWrapper.php b/icewind/streams/src/ReadHashWrapper.php
new file mode 100644
index 00000000..16cf006c
--- /dev/null
+++ b/icewind/streams/src/ReadHashWrapper.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl>
+ *
+ * @author Roeland Jago Douma <roeland@famdouma.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;
+
+/**
+ * Wrapper that calculates the hash on the stream on read
+ *
+ * The stream and hash should be passed in when wrapping the stream.
+ * On close the callback will be called with the calculated checksum.
+ *
+ * For supported hashes see: http://php.net/manual/en/function.hash-algos.php
+ */
+class ReadHashWrapper extends HashWrapper {
+ public function stream_read($count) {
+ $data = parent::stream_read($count);
+ $this->updateHash($data);
+ return $data;
+ }
+}
diff --git a/icewind/streams/src/RetryWrapper.php b/icewind/streams/src/RetryWrapper.php
index 8238f19f..d4727aa9 100644
--- a/icewind/streams/src/RetryWrapper.php
+++ b/icewind/streams/src/RetryWrapper.php
@@ -11,25 +11,8 @@ namespace Icewind\Streams;
* Wrapper that retries reads/writes to remote streams that dont deliver/recieve all requested data at once
*/
class RetryWrapper extends Wrapper {
-
- /**
- * Wraps a stream with the provided callbacks
- *
- * @param resource $source
- * @return resource
- */
public static function wrap($source) {
- $context = stream_context_create(array(
- 'retry' => array(
- 'source' => $source
- )
- ));
- return Wrapper::wrapSource($source, $context, 'retry', '\Icewind\Streams\RetryWrapper');
- }
-
- protected function open() {
- $this->loadContext('retry');
- return true;
+ return self::wrapSource($source);
}
public function dir_opendir($path, $options) {
@@ -37,7 +20,8 @@ class RetryWrapper extends Wrapper {
}
public function stream_open($path, $mode, $options, &$opened_path) {
- return $this->open();
+ $this->loadContext();
+ return true;
}
public function stream_read($count) {
diff --git a/icewind/streams/src/SeekableWrapper.php b/icewind/streams/src/SeekableWrapper.php
index d41fd73e..f131e753 100644
--- a/icewind/streams/src/SeekableWrapper.php
+++ b/icewind/streams/src/SeekableWrapper.php
@@ -25,21 +25,8 @@ class SeekableWrapper extends Wrapper {
*/
protected $cache;
- /**
- * Wraps a stream to make it seekable
- *
- * @param resource $source
- * @return resource
- *
- * @throws \BadMethodCallException
- */
public static function wrap($source) {
- $context = stream_context_create(array(
- 'callback' => array(
- 'source' => $source
- )
- ));
- return Wrapper::wrapSource($source, $context, 'callback', '\Icewind\Streams\SeekableWrapper');
+ return self::wrapSource($source);
}
public function dir_opendir($path, $options) {
@@ -47,8 +34,12 @@ class SeekableWrapper extends Wrapper {
}
public function stream_open($path, $mode, $options, &$opened_path) {
- $this->loadContext('callback');
- $this->cache = fopen('php://temp', 'w+');
+ $this->loadContext();
+ $cache = fopen('php://temp', 'w+');
+ if ($cache === false) {
+ return false;
+ }
+ $this->cache = $cache;
return true;
}
@@ -72,7 +63,7 @@ class SeekableWrapper extends Wrapper {
public function stream_seek($offset, $whence = SEEK_SET) {
if ($whence === SEEK_SET) {
$target = $offset;
- } else if ($whence === SEEK_CUR) {
+ } elseif ($whence === SEEK_CUR) {
$current = ftell($this->cache);
$target = $current + $offset;
} else {
diff --git a/icewind/streams/src/Url.php b/icewind/streams/src/Url.php
index d6822608..38cbbdd8 100644
--- a/icewind/streams/src/Url.php
+++ b/icewind/streams/src/Url.php
@@ -22,7 +22,7 @@ interface Url {
* @param string $path
* @param string $mode
* @param int $options
- * @param string &$opened_path
+ * @param string $opened_path
* @return bool
*/
public function stream_open($path, $mode, $options, &$opened_path);
@@ -50,7 +50,7 @@ interface Url {
public function rmdir($path, $options);
/**
- * @param string
+ * @param string $path
* @return bool
*/
public function unlink($path);
@@ -58,7 +58,7 @@ interface Url {
/**
* @param string $path
* @param int $flags
- * @return array
+ * @return array|false
*/
public function url_stat($path, $flags);
}
diff --git a/icewind/streams/src/UrlCallBack.php b/icewind/streams/src/UrlCallback.php
index 580bfc6b..09ba2aef 100644
--- a/icewind/streams/src/UrlCallBack.php
+++ b/icewind/streams/src/UrlCallback.php
@@ -47,24 +47,30 @@ class UrlCallback extends Wrapper implements Url {
* @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,
+ public static function wrap(
+ $source,
+ $fopen = null,
+ $opendir = null,
+ $mkdir = null,
+ $rename = null,
+ $rmdir = null,
+ $unlink = null,
+ $stat = null
+ ) {
+ return new Path(static::class, [
+ 'source' => $source,
+ 'fopen' => $fopen,
'opendir' => $opendir,
- 'mkdir' => $mkdir,
- 'rename' => $rename,
- 'rmdir' => $rmdir,
- 'unlink' => $unlink,
- 'stat' => $stat
- );
- return new Path('\Icewind\Streams\UrlCallBack', $options);
+ 'mkdir' => $mkdir,
+ 'rename' => $rename,
+ 'rmdir' => $rmdir,
+ 'unlink' => $unlink,
+ 'stat' => $stat
+ ]);
}
- protected function loadContext($url) {
+ protected function loadUrlContext($url) {
list($protocol) = explode('://', $url);
$options = stream_context_get_options($this->context);
return $options[$protocol];
@@ -77,40 +83,48 @@ class UrlCallback extends Wrapper implements Url {
}
public function stream_open($path, $mode, $options, &$opened_path) {
- $context = $this->loadContext($path);
+ $context = $this->loadUrlContext($path);
$this->callCallBack($context, 'fopen');
- $this->setSourceStream(fopen($context['source'], $mode));
+ $source = fopen($context['source'], $mode);
+ if ($source === false) {
+ return false;
+ }
+ $this->setSourceStream($source);
return true;
}
public function dir_opendir($path, $options) {
- $context = $this->loadContext($path);
+ $context = $this->loadUrlContext($path);
$this->callCallBack($context, 'opendir');
- $this->setSourceStream(opendir($context['source']));
+ $source = opendir($context['source']);
+ if ($source === false) {
+ return false;
+ }
+ $this->setSourceStream($source);
return true;
}
public function mkdir($path, $mode, $options) {
- $context = $this->loadContext($path);
+ $context = $this->loadUrlContext($path);
$this->callCallBack($context, 'mkdir');
- return mkdir($context['source'], $mode, $options & STREAM_MKDIR_RECURSIVE);
+ return mkdir($context['source'], $mode, ($options & STREAM_MKDIR_RECURSIVE) > 0);
}
public function rmdir($path, $options) {
- $context = $this->loadContext($path);
+ $context = $this->loadUrlContext($path);
$this->callCallBack($context, 'rmdir');
return rmdir($context['source']);
}
public function rename($source, $target) {
- $context = $this->loadContext($source);
+ $context = $this->loadUrlContext($source);
$this->callCallBack($context, 'rename');
list(, $target) = explode('://', $target);
return rename($context['source'], $target);
}
public function unlink($path) {
- $context = $this->loadContext($path);
+ $context = $this->loadUrlContext($path);
$this->callCallBack($context, 'unlink');
return unlink($context['source']);
}
diff --git a/icewind/streams/src/Wrapper.php b/icewind/streams/src/Wrapper.php
index babd2c1a..03d0b202 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, Directory {
+abstract class Wrapper extends WrapperHandler implements File, Directory {
/**
* @var resource
*/
@@ -25,44 +25,15 @@ abstract class Wrapper implements File, Directory {
*/
protected $source;
- protected static function wrapSource($source, $context, $protocol, $class) {
- 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) {
- $meta = stream_get_meta_data($resource);
- return $meta['stream_type'] == 'dir';
- }
-
/**
- * Load the source from the stream context and return the context options
- *
- * @param string $name
- * @return array
- * @throws \Exception
+ * @param resource $source
*/
- 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');
- }
+ protected function setSourceStream($source) {
+ $this->source = $source;
+ }
+
+ protected function loadContext($name = null) {
+ $context = parent::loadContext($name);
if (isset($context['source']) and is_resource($context['source'])) {
$this->setSourceStream($context['source']);
} else {
@@ -71,13 +42,6 @@ abstract class Wrapper implements File, Directory {
return $context;
}
- /**
- * @param resource $source
- */
- protected function setSourceStream($source) {
- $this->source = $source;
- }
-
public function stream_seek($offset, $whence = SEEK_SET) {
$result = fseek($this->source, $offset, $whence);
return $result == 0 ? true : false;
@@ -98,14 +62,13 @@ abstract class Wrapper implements File, Directory {
public function stream_set_option($option, $arg1, $arg2) {
switch ($option) {
case STREAM_OPTION_BLOCKING:
- stream_set_blocking($this->source, $arg1);
- break;
+ return stream_set_blocking($this->source, (bool)$arg1);
case STREAM_OPTION_READ_TIMEOUT:
- stream_set_timeout($this->source, $arg1, $arg2);
- break;
+ return stream_set_timeout($this->source, $arg1, $arg2);
case STREAM_OPTION_WRITE_BUFFER:
- stream_set_write_buffer($this->source, $arg1);
+ return stream_set_write_buffer($this->source, $arg1) === 0;
}
+ return false;
}
public function stream_truncate($size) {
@@ -129,7 +92,9 @@ abstract class Wrapper implements File, Directory {
}
public function stream_close() {
- return fclose($this->source);
+ if (is_resource($this->source)) {
+ return fclose($this->source);
+ }
}
public function dir_readdir() {
diff --git a/icewind/streams/src/WrapperHandler.php b/icewind/streams/src/WrapperHandler.php
new file mode 100644
index 00000000..258e3ec8
--- /dev/null
+++ b/icewind/streams/src/WrapperHandler.php
@@ -0,0 +1,114 @@
+<?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 {
+ /** @var resource $context */
+ protected $context;
+
+ 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));
+ }
+
+ private static function buildContext($protocol, $context, $source) {
+ if (is_array($context)) {
+ $context['source'] = $source;
+ return stream_context_create([$protocol => $context]);
+ } else {
+ return $context;
+ }
+ }
+
+ /**
+ * @param resource|int $source
+ * @param resource|array $context
+ * @param string|null $protocol deprecated, protocol is now automatically generated
+ * @param string|null $class deprecated, class is now automatically generated
+ * @return bool|resource
+ */
+ protected static function wrapSource($source, $context = [], $protocol = null, $class = null, $mode = 'r+') {
+ if ($class === null) {
+ $class = static::class;
+ }
+
+ if ($protocol === null) {
+ $protocol = self::getProtocol($class);
+ }
+
+ $context = self::buildContext($protocol, $context, $source);
+ try {
+ stream_wrapper_register($protocol, $class);
+ if (self::isDirectoryHandle($source)) {
+ return opendir($protocol . '://', $context);
+ } else {
+ return fopen($protocol . '://', $mode, false, $context);
+ }
+ } finally {
+ stream_wrapper_unregister($protocol);
+ }
+ }
+
+ protected static function isDirectoryHandle($resource) {
+ if ($resource === self::NO_SOURCE_DIR) {
+ return true;
+ }
+ if (!is_resource($resource)) {
+ throw new \BadMethodCallException('Invalid stream source');
+ }
+ $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;
+ }
+}
diff --git a/icewind/streams/src/WriteHashWrapper.php b/icewind/streams/src/WriteHashWrapper.php
new file mode 100644
index 00000000..279d9fd1
--- /dev/null
+++ b/icewind/streams/src/WriteHashWrapper.php
@@ -0,0 +1,37 @@
+<?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;
+
+/**
+ * Wrapper that calculates the hash on the stream on write
+ *
+ * The stream and hash should be passed in when wrapping the stream.
+ * On close the callback will be called with the calculated checksum.
+ *
+ * For supported hashes see: http://php.net/manual/en/function.hash-algos.php
+ */
+class WriteHashWrapper extends HashWrapper {
+ public function stream_write($data) {
+ $this->updateHash($data);
+ return parent::stream_write($data);
+ }
+}