migrate therinaldos.com data
Build & Deploy to DigitalOcean Space / build (push) Failing after 2m38s

This commit is contained in:
2024-05-05 15:50:45 -04:00
commit ef1ff240d4
23182 changed files with 3801898 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Robin Appelman
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,133 @@
<?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 write, read and close
*
* 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)
* 'readdir' => function(){} (optional)
* ]
* ]
*
* All callbacks are called after the operation is executed on the source stream
*/
class CallbackWrapper extends Wrapper {
/**
* @var callable|null
*/
protected $readCallback;
/**
* @var callable|null
*/
protected $writeCallback;
/**
* @var callable|null
*/
protected $closeCallback;
/**
* @var callable|null
*/
protected $readDirCallBack;
/**
* @var callable|null
*/
protected $preCloseCallback;
/**
* Wraps a stream with the provided callbacks
*
* @param resource $source
* @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|false
*
*/
public static function wrap($source, $read = null, $write = null, $close = null, $readDir = null, $preClose = null) {
$context = [
'source' => $source,
'read' => $read,
'write' => $write,
'close' => $close,
'readDir' => $readDir,
'preClose' => $preClose,
];
return self::wrapSource($source, $context);
}
protected function open() {
$context = $this->loadContext();
$this->readCallback = $context['read'];
$this->writeCallback = $context['write'];
$this->closeCallback = $context['close'];
$this->readDirCallBack = $context['readDir'];
$this->preCloseCallback = $context['preClose'];
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 (is_callable($this->readCallback)) {
call_user_func($this->readCallback, strlen($result));
}
return $result;
}
public function stream_write($data) {
$result = parent::stream_write($data);
if (is_callable($this->writeCallback)) {
call_user_func($this->writeCallback, $data);
}
return $result;
}
public function stream_close() {
if (is_callable($this->preCloseCallback)) {
call_user_func($this->preCloseCallback, $this->source);
// prevent further calls by potential PHP 7 GC ghosts
$this->preCloseCallback = null;
}
$result = parent::stream_close();
if (is_callable($this->closeCallback)) {
call_user_func($this->closeCallback);
// prevent further calls by potential PHP 7 GC ghosts
$this->closeCallback = null;
}
return $result;
}
public function dir_readdir() {
$result = parent::dir_readdir();
if (is_callable($this->readDirCallBack)) {
call_user_func($this->readDirCallBack);
}
return $result;
}
}
@@ -0,0 +1,103 @@
<?php
/**
* @copyright Copyright (c) 2018 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 counts the amount of data read and written
*
* The following options should be passed in the context when opening the stream
* [
* 'callback' => [
* 'source' => resource
* 'callback' => function($readCount, $writeCount){}
* ]
* ]
*
* The callback will be called when the stream is closed
*/
class CountWrapper extends Wrapper {
/**
* @var int
*/
protected $readCount = 0;
/**
* @var int
*/
protected $writeCount = 0;
/**
* @var callable
*/
protected $callback;
/**
* Wraps a stream with the provided callbacks
*
* @param resource $source
* @param callable $callback
* @return resource|false
*
* @throws \BadMethodCallException
*/
public static function wrap($source, $callback) {
if (!is_callable($callback)) {
throw new \InvalidArgumentException('Invalid or missing callback');
}
return self::wrapSource($source, [
'source' => $source,
'callback' => $callback
]);
}
protected function open() {
$context = $this->loadContext();
$this->callback = $context['callback'];
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);
$this->readCount += strlen($result);
return $result;
}
public function stream_write($data) {
$result = parent::stream_write($data);
$this->writeCount += strlen($data);
return $result;
}
public function stream_close() {
$result = parent::stream_close();
call_user_func($this->callback, $this->readCount, $this->writeCount);
return $result;
}
}
+35
View File
@@ -0,0 +1,35 @@
<?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 implements a directory
*/
interface Directory {
/**
* @param string $path
* @param array $options
* @return bool
*/
public function dir_opendir($path, $options);
/**
* @return string|bool
*/
public function dir_readdir();
/**
* @return bool
*/
public function dir_closedir();
/**
* @return bool
*/
public function dir_rewinddir();
}
@@ -0,0 +1,57 @@
<?php
/**
* Copyright (c) 2015 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 allows filtering of directories
*
* The filter callback will be called for each entry in the folder
* when the callback return false the entry will be filtered out
*/
class DirectoryFilter extends DirectoryWrapper {
/**
* @var callable
*/
private $filter;
/**
* @param string $path
* @param array $options
* @return bool
*/
public function dir_opendir($path, $options) {
$context = $this->loadContext();
$this->filter = $context['filter'];
return true;
}
/**
* @return string
*/
public function dir_readdir() {
$file = readdir($this->source);
$filter = $this->filter;
// 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);
}
return $file;
}
/**
* @param resource $source
* @param callable $filter
* @return resource|false
*/
public static function wrap($source, callable $filter) {
return self::wrapSource($source, [
'source' => $source,
'filter' => $filter
]);
}
}
@@ -0,0 +1,47 @@
<?php
/**
* Copyright (c) 2015 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Licensed under the MIT license:
* http://opensource.org/licenses/MIT
*/
namespace Icewind\Streams;
class DirectoryWrapper extends Wrapper implements Directory {
public function stream_open($path, $mode, $options, &$opened_path) {
return false;
}
/**
* @param string $path
* @param array $options
* @return bool
*/
public function dir_opendir($path, $options) {
$this->loadContext();
return true;
}
/**
* @return string|false
*/
public function dir_readdir() {
return readdir($this->source);
}
/**
* @return bool
*/
public function dir_closedir() {
closedir($this->source);
return true;
}
/**
* @return bool
*/
public function dir_rewinddir() {
rewinddir($this->source);
return true;
}
}
+86
View File
@@ -0,0 +1,86 @@
<?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 implements a file
*/
interface File {
/**
* @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 int $offset
* @param int $whence
* @return bool
*/
public function stream_seek($offset, $whence = SEEK_SET);
/**
* @return int|false
*/
public function stream_tell();
/**
* @param int $count
* @return string|false
*/
public function stream_read($count);
/**
* @param string $data
* @return int|false
*/
public function stream_write($data);
/**
* @param int $option
* @param int $arg1
* @param int $arg2
* @return bool
*/
public function stream_set_option($option, $arg1, $arg2);
/**
* @param int $size
* @return bool
*/
public function stream_truncate($size);
/**
* @return array|false
*/
public function stream_stat();
/**
* @param int $operation
* @return bool
*/
public function stream_lock($operation);
/**
* @return bool
*/
public function stream_flush();
/**
* @return bool
*/
public function stream_eof();
/**
* @return bool
*/
public function stream_close();
}
@@ -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|false
*
* @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();
}
}
@@ -0,0 +1,113 @@
<?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;
/**
* Create a directory handle from an iterator or array
*
* The following options should be passed in the context when opening the stream
* [
* 'dir' => [
* 'array' => string[]
* 'iterator' => \Iterator
* ]
* ]
*
* Either 'array' or 'iterator' need to be set, if both are set, 'iterator' takes preference
*/
class IteratorDirectory extends WrapperHandler implements Directory {
/**
* @var resource
*/
public $context;
/**
* @var \Iterator
*/
protected $iterator;
/**
* Load the source from the stream context and return the context options
*
* @param string $name
* @return array
* @throws \BadMethodCallException
*/
protected function loadContext($name = null) {
$context = parent::loadContext($name);
if (isset($context['iterator'])) {
$this->iterator = $context['iterator'];
} elseif (isset($context['array'])) {
$this->iterator = new \ArrayIterator($context['array']);
} else {
throw new \BadMethodCallException('Invalid context, iterator or array not set');
}
return $context;
}
/**
* @param string $path
* @param array $options
* @return bool
*/
public function dir_opendir($path, $options) {
$this->loadContext();
return true;
}
/**
* @return string|bool
*/
public function dir_readdir() {
if ($this->iterator->valid()) {
$result = $this->iterator->current();
$this->iterator->next();
return $result;
} else {
return false;
}
}
/**
* @return bool
*/
public function dir_closedir() {
return true;
}
/**
* @return bool
*/
public function dir_rewinddir() {
$this->iterator->rewind();
return true;
}
/**
* Creates a directory handle from the provided array or iterator
*
* @param \Iterator | array $source
* @return resource|false
*
* @throws \BadMethodCallException
*/
public static function wrap($source) {
if ($source instanceof \Iterator) {
$options = [
'iterator' => $source
];
} elseif (is_array($source)) {
$options = [
'array' => $source
];
} else {
throw new \BadMethodCallException('$source should be an Iterator or array');
}
return self::wrapSource(self::NO_SOURCE_DIR, $options);
}
}
@@ -0,0 +1,27 @@
<?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;
/**
* Stream wrapper that does nothing, used for tests
*/
class NullWrapper extends Wrapper {
public static function wrap($source) {
return self::wrapSource($source);
}
public function stream_open($path, $mode, $options, &$opened_path) {
$this->loadContext();
return true;
}
public function dir_opendir($path, $options) {
$this->loadContext();
return true;
}
}
+108
View File
@@ -0,0 +1,108 @@
<?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 = []) {
$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->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 array $values
*/
protected function appendDefaultContent($values) {
if (!is_array(current($values))) {
$values = [$this->getProtocol() => $values];
}
$context = stream_context_get_default();
$defaults = stream_context_get_options($context);
foreach ($values as $key => $value) {
$defaults[$key] = $value;
}
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();
}
}
@@ -0,0 +1,23 @@
<?php
/**
* Copyright (c) 2016 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 maps to an existing stream when opened
*/
class PathWrapper extends NullWrapper {
/**
* @param resource $source
* @return Path|string
*/
public static function getPath($source) {
return new Path(NullWrapper::class, [
NullWrapper::getProtocol() => ['source' => $source]
]);
}
}
@@ -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;
}
}
@@ -0,0 +1,52 @@
<?php
/**
* Copyright (c) 2016 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 retries reads/writes to remote streams that dont deliver/recieve all requested data at once
*/
class RetryWrapper extends Wrapper {
public static function wrap($source) {
return self::wrapSource($source);
}
public function dir_opendir($path, $options) {
return false;
}
public function stream_open($path, $mode, $options, &$opened_path) {
$this->loadContext();
return true;
}
public function stream_read($count) {
$result = parent::stream_read($count);
$bytesReceived = strlen($result);
while (strlen($result) > 0 && $bytesReceived < $count && !$this->stream_eof()) {
$result .= parent::stream_read($count - $bytesReceived);
$bytesReceived = strlen($result);
}
return $result;
}
public function stream_write($data) {
$bytesToSend = strlen($data);
$bytesWritten = parent::stream_write($data);
$result = $bytesWritten;
while ($bytesWritten > 0 && $result < $bytesToSend && !$this->stream_eof()) {
$dataLeft = substr($data, $result);
$bytesWritten = parent::stream_write($dataLeft);
$result += $bytesWritten;
}
return $result;
}
}
@@ -0,0 +1,83 @@
<?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 write, read and close
*
* The following options should be passed in the context when opening the stream
* [
* 'callback' => [
* 'source' => resource
* ]
* ]
*
* All callbacks are called after the operation is executed on the source stream
*/
class SeekableWrapper extends Wrapper {
/**
* @var resource
*/
protected $cache;
public static function wrap($source) {
return self::wrapSource($source);
}
public function dir_opendir($path, $options) {
return false;
}
public function stream_open($path, $mode, $options, &$opened_path) {
$this->loadContext();
$cache = fopen('php://temp', 'w+');
if ($cache === false) {
return false;
}
$this->cache = $cache;
return true;
}
protected function readTill($position) {
$current = ftell($this->source);
if ($position > $current) {
$data = parent::stream_read($position - $current);
$cachePosition = ftell($this->cache);
fseek($this->cache, $current);
fwrite($this->cache, $data);
fseek($this->cache, $cachePosition);
}
}
public function stream_read($count) {
$current = ftell($this->cache);
$this->readTill($current + $count);
return fread($this->cache, $count);
}
public function stream_seek($offset, $whence = SEEK_SET) {
if ($whence === SEEK_SET) {
$target = $offset;
} elseif ($whence === SEEK_CUR) {
$current = ftell($this->cache);
$target = $current + $offset;
} else {
return false;
}
$this->readTill($target);
return fseek($this->cache, $target) === 0;
}
public function stream_tell() {
return ftell($this->cache);
}
public function stream_eof() {
return parent::stream_eof() and (ftell($this->source) === ftell($this->cache));
}
}
+64
View File
@@ -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 $path
* @return bool
*/
public function unlink($path);
/**
* @param string $path
* @param int $flags
* @return array|false
*/
public function url_stat($path, $flags);
}
@@ -0,0 +1,135 @@
<?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
*/
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
]);
}
protected function loadUrlContext($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->loadUrlContext($path);
$this->callCallBack($context, 'fopen');
$source = fopen($context['source'], $mode);
if ($source === false) {
return false;
}
$this->setSourceStream($source);
return true;
}
public function dir_opendir($path, $options) {
$context = $this->loadUrlContext($path);
$this->callCallBack($context, 'opendir');
$source = opendir($context['source']);
if ($source === false) {
return false;
}
$this->setSourceStream($source);
return true;
}
public function mkdir($path, $mode, $options) {
$context = $this->loadUrlContext($path);
$this->callCallBack($context, 'mkdir');
return mkdir($context['source'], $mode, ($options & STREAM_MKDIR_RECURSIVE) > 0);
}
public function rmdir($path, $options) {
$context = $this->loadUrlContext($path);
$this->callCallBack($context, 'rmdir');
return rmdir($context['source']);
}
public function rename($source, $target) {
$context = $this->loadUrlContext($source);
$this->callCallBack($context, 'rename');
list(, $target) = explode('://', $target);
return rename($context['source'], $target);
}
public function unlink($path) {
$context = $this->loadUrlContext($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');
}
}
+131
View File
@@ -0,0 +1,131 @@
<?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;
/**
* Base class for stream wrappers, wraps an existing stream
*
* This wrapper itself doesn't implement any functionality but is just a base class for other wrappers to extend
*/
abstract class Wrapper extends WrapperHandler implements File, Directory {
/**
* @var resource
*/
public $context;
/**
* The wrapped stream
*
* @var resource
*/
protected $source;
/**
* @param resource $source
*/
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 {
throw new \BadMethodCallException('Invalid context, source not set');
}
return $context;
}
public function stream_seek($offset, $whence = SEEK_SET) {
$result = fseek($this->source, $offset, $whence);
return $result == 0;
}
public function stream_tell() {
return ftell($this->source);
}
public function stream_read($count) {
return fread($this->source, $count);
}
public function stream_write($data) {
return fwrite($this->source, $data);
}
public function stream_set_option($option, $arg1, $arg2) {
switch ($option) {
case STREAM_OPTION_BLOCKING:
return stream_set_blocking($this->source, (bool)$arg1);
case STREAM_OPTION_READ_TIMEOUT:
return stream_set_timeout($this->source, $arg1, $arg2);
case STREAM_OPTION_WRITE_BUFFER:
return stream_set_write_buffer($this->source, $arg1) === 0;
}
return false;
}
public function stream_truncate($size) {
return ftruncate($this->source, $size);
}
public function stream_stat() {
return fstat($this->source);
}
public function stream_lock($mode) {
return flock($this->source, $mode);
}
public function stream_flush() {
return fflush($this->source);
}
public function stream_eof() {
return feof($this->source);
}
public function stream_close() {
if (is_resource($this->source)) {
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);
}
public function getSource() {
return $this->source;
}
/**
* Retrieves header/metadata from the source stream.
*
* This is equivalent to calling `stream_get_meta_data` on the source stream except nested stream wrappers are handled transparently
*
* @return array
*/
public function getMetaData(): array {
$meta = stream_get_meta_data($this->source);
while (isset($meta['wrapper_data']) && $meta['wrapper_data'] instanceof Wrapper) {
$meta = $meta['wrapper_data']->getMetaData();
}
return $meta;
}
}
@@ -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 resource|false
*/
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;
}
}
@@ -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);
}
}