mirror of
https://codeberg.org/icewind/streams.git
synced 2026-06-03 16:44:07 +02:00
initial version
This commit is contained in:
commit
d54615fd3a
11 changed files with 494 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
.idea
|
||||||
|
vendor
|
||||||
|
composer.lock
|
||||||
21
.travis.yml
Normal file
21
.travis.yml
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
language: php
|
||||||
|
php:
|
||||||
|
- 5.5
|
||||||
|
- 5.4
|
||||||
|
- hhvm
|
||||||
|
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
- CURRENT_DIR=`pwd`
|
||||||
|
|
||||||
|
install:
|
||||||
|
- composer install --dev --no-interaction
|
||||||
|
|
||||||
|
script:
|
||||||
|
- mkdir -p build/logs
|
||||||
|
- cd tests
|
||||||
|
- phpunit --coverage-clover ../build/logs/clover.xml --configuration phpunit.xml
|
||||||
|
|
||||||
|
after_script:
|
||||||
|
- cd $CURRENT_DIR
|
||||||
|
- php vendor/bin/coveralls -v
|
||||||
24
composer.json
Normal file
24
composer.json
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"name" : "Icewind/Streams",
|
||||||
|
"description" : "A set of generic stream wrappers",
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"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/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
76
src/CallbackWrapper.php
Normal file
76
src/CallbackWrapper.php
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
<?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)
|
||||||
|
* ]
|
||||||
|
* ]
|
||||||
|
*
|
||||||
|
* All callbacks are called before the operation is executed on the source stream
|
||||||
|
*/
|
||||||
|
class CallBackWrapper extends Wrapper {
|
||||||
|
/**
|
||||||
|
* @var callable
|
||||||
|
*/
|
||||||
|
protected $readCallback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var callable
|
||||||
|
*/
|
||||||
|
protected $writeCallback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var callable
|
||||||
|
*/
|
||||||
|
protected $closeCallback;
|
||||||
|
|
||||||
|
public function stream_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'];
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_read($count) {
|
||||||
|
if ($this->readCallback) {
|
||||||
|
call_user_func($this->readCallback, $count);
|
||||||
|
}
|
||||||
|
return parent::stream_read($count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_write($data) {
|
||||||
|
if ($this->writeCallback) {
|
||||||
|
call_user_func($this->writeCallback, $data);
|
||||||
|
}
|
||||||
|
return parent::stream_write($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_close() {
|
||||||
|
if ($this->closeCallback) {
|
||||||
|
call_user_func($this->closeCallback);
|
||||||
|
}
|
||||||
|
return parent::stream_close();
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/NullWrapper.php
Normal file
15
src/NullWrapper.php
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?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;
|
||||||
|
|
||||||
|
class NullWrapper extends Wrapper {
|
||||||
|
public function stream_open() {
|
||||||
|
$this->loadContext('null');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
105
src/Wrapper.php
Normal file
105
src/Wrapper.php
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
<?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 {
|
||||||
|
/**
|
||||||
|
* The wrapped stream
|
||||||
|
*
|
||||||
|
* @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, "callable" options not set');
|
||||||
|
}
|
||||||
|
if (isset($context['source']) and is_resource($context['source'])) {
|
||||||
|
$this->setSourceStream($context['source']);
|
||||||
|
} else {
|
||||||
|
throw new \BadMethodCallException('Invalid context, source not set');
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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:
|
||||||
|
stream_set_blocking($this->source, $arg1);
|
||||||
|
break;
|
||||||
|
case STREAM_OPTION_READ_TIMEOUT:
|
||||||
|
stream_set_timeout($this->source, $arg1, $arg2);
|
||||||
|
break;
|
||||||
|
case STREAM_OPTION_WRITE_BUFFER:
|
||||||
|
stream_set_write_buffer($this->source, $arg1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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() {
|
||||||
|
return fclose($this->source);
|
||||||
|
}
|
||||||
|
}
|
||||||
81
tests/CallbackWrapper.php
Normal file
81
tests/CallbackWrapper.php
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
<?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\Tests;
|
||||||
|
|
||||||
|
class CallableWrapper extends Wrapper {
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
stream_wrapper_register('callback', '\Icewind\Streams\CallbackWrapper');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tearDown() {
|
||||||
|
stream_wrapper_unregister('callback');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param resource $source
|
||||||
|
* @param callable $read
|
||||||
|
* @param callable $write
|
||||||
|
* @param callable $close
|
||||||
|
* @return resource
|
||||||
|
*/
|
||||||
|
protected function wrapSource($source, $read = null, $write = null, $close = null) {
|
||||||
|
$context = stream_context_create(array(
|
||||||
|
'callback' => array(
|
||||||
|
'source' => $source,
|
||||||
|
'read' => $read,
|
||||||
|
'write' => $write,
|
||||||
|
'close' => $close
|
||||||
|
)
|
||||||
|
));
|
||||||
|
return fopen('callback://', 'r+', false, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReadCallback() {
|
||||||
|
$called = false;
|
||||||
|
$callBack = function () use (&$called) {
|
||||||
|
$called = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
$source = fopen('php://temp', 'r+');
|
||||||
|
fwrite($source, 'foobar');
|
||||||
|
rewind($source);
|
||||||
|
|
||||||
|
$wrapped = $this->wrapSource($source, $callBack);
|
||||||
|
$this->assertEquals('foo', fread($wrapped, 3));
|
||||||
|
$this->assertTrue($called);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWriteCallback() {
|
||||||
|
$lastData = '';
|
||||||
|
$callBack = function ($data) use (&$lastData) {
|
||||||
|
$lastData = $data;
|
||||||
|
};
|
||||||
|
|
||||||
|
$source = fopen('php://temp', 'r+');
|
||||||
|
|
||||||
|
$wrapped = $this->wrapSource($source, null, $callBack);
|
||||||
|
fwrite($wrapped, 'foobar');
|
||||||
|
$this->assertEquals('foobar', $lastData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCloseCallback() {
|
||||||
|
$called = false;
|
||||||
|
$callBack = function () use (&$called) {
|
||||||
|
$called = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
$source = fopen('php://temp', 'r+');
|
||||||
|
fwrite($source, 'foobar');
|
||||||
|
rewind($source);
|
||||||
|
|
||||||
|
$wrapped = $this->wrapSource($source, null, null, $callBack);
|
||||||
|
fclose($wrapped);
|
||||||
|
$this->assertTrue($called);
|
||||||
|
}
|
||||||
|
}
|
||||||
52
tests/NullWrapper.php
Normal file
52
tests/NullWrapper.php
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
<?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\Tests;
|
||||||
|
|
||||||
|
class NullWrapperTest extends Wrapper {
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
stream_wrapper_register('null', '\Icewind\Streams\NullWrapper');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tearDown() {
|
||||||
|
stream_wrapper_unregister('null');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param resource $source
|
||||||
|
* @return resource
|
||||||
|
*/
|
||||||
|
protected function wrapSource($source) {
|
||||||
|
$context = stream_context_create(array(
|
||||||
|
'null' => array(
|
||||||
|
'source' => $source
|
||||||
|
)
|
||||||
|
));
|
||||||
|
return fopen('null://', 'r+', false, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \BadMethodCallException
|
||||||
|
*/
|
||||||
|
public function testNoContext() {
|
||||||
|
$context = stream_context_create(array());
|
||||||
|
fopen('null://', 'r+', false, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \BadMethodCallException
|
||||||
|
*/
|
||||||
|
public function testNoSource() {
|
||||||
|
$context = stream_context_create(array(
|
||||||
|
'null' => array(
|
||||||
|
'source' => 'bar'
|
||||||
|
)
|
||||||
|
));
|
||||||
|
fopen('null://', 'r+', false, $context);
|
||||||
|
}
|
||||||
|
}
|
||||||
102
tests/Wrapper.php
Normal file
102
tests/Wrapper.php
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
<?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\Tests;
|
||||||
|
|
||||||
|
abstract class Wrapper extends \PHPUnit_Framework_TestCase {
|
||||||
|
/**
|
||||||
|
* @param resource $source
|
||||||
|
* @return resource
|
||||||
|
*/
|
||||||
|
abstract protected function wrapSource($source);
|
||||||
|
|
||||||
|
public function testRead() {
|
||||||
|
$source = fopen('php://temp', 'r+');
|
||||||
|
fwrite($source, 'foobar');
|
||||||
|
rewind($source);
|
||||||
|
|
||||||
|
$wrapped = $this->wrapSource($source);
|
||||||
|
$this->assertEquals('foo', fread($wrapped, 3));
|
||||||
|
$this->assertEquals('bar', fread($wrapped, 3));
|
||||||
|
$this->assertEquals('', fread($wrapped, 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWrite() {
|
||||||
|
$source = fopen('php://temp', 'r+');
|
||||||
|
rewind($source);
|
||||||
|
|
||||||
|
$wrapped = $this->wrapSource($source);
|
||||||
|
|
||||||
|
$this->assertEquals(6, fwrite($wrapped, 'foobar'));
|
||||||
|
rewind($source);
|
||||||
|
$this->assertEquals('foobar', stream_get_contents($source));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClose() {
|
||||||
|
$source = fopen('php://temp', 'r+');
|
||||||
|
rewind($source);
|
||||||
|
|
||||||
|
$wrapped = $this->wrapSource($source);
|
||||||
|
|
||||||
|
fclose($wrapped);
|
||||||
|
$this->assertFalse(is_resource($source));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSeekTell() {
|
||||||
|
$source = fopen('php://temp', 'r+');
|
||||||
|
fwrite($source, 'foobar');
|
||||||
|
rewind($source);
|
||||||
|
|
||||||
|
$wrapped = $this->wrapSource($source);
|
||||||
|
|
||||||
|
$this->assertEquals(0, ftell($wrapped));
|
||||||
|
|
||||||
|
fseek($wrapped, 2);
|
||||||
|
$this->assertEquals(2, ftell($source));
|
||||||
|
$this->assertEquals(2, ftell($wrapped));
|
||||||
|
|
||||||
|
fseek($wrapped, 2, SEEK_CUR);
|
||||||
|
$this->assertEquals(4, ftell($source));
|
||||||
|
$this->assertEquals(4, ftell($wrapped));
|
||||||
|
|
||||||
|
fseek($wrapped, -1, SEEK_END);
|
||||||
|
$this->assertEquals(5, ftell($source));
|
||||||
|
$this->assertEquals(5, ftell($wrapped));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testStat() {
|
||||||
|
$source = fopen(__FILE__, 'r+');
|
||||||
|
$wrapped = $this->wrapSource($source);
|
||||||
|
$this->assertEquals(stat(__FILE__), fstat($wrapped));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTruncate() {
|
||||||
|
$source = fopen('php://temp', 'r+');
|
||||||
|
fwrite($source, 'foobar');
|
||||||
|
rewind($source);
|
||||||
|
$wrapped = $this->wrapSource($source);
|
||||||
|
|
||||||
|
ftruncate($wrapped, 2);
|
||||||
|
$this->assertEquals('fo', fread($wrapped, 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLock() {
|
||||||
|
$source = tmpfile();
|
||||||
|
$wrapped = $this->wrapSource($source);
|
||||||
|
if (!flock($wrapped, LOCK_EX)) {
|
||||||
|
$this->fail('Unable to acquire lock');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testStreamOptions() {
|
||||||
|
$source = fopen('php://temp', 'r+');
|
||||||
|
$wrapped = $this->wrapSource($source);
|
||||||
|
stream_set_blocking($wrapped, 0);
|
||||||
|
stream_set_timeout($wrapped, 1, 0);
|
||||||
|
stream_set_write_buffer($wrapped, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
9
tests/bootstrap.php
Normal file
9
tests/bootstrap.php
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?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
|
||||||
|
*/
|
||||||
|
|
||||||
|
date_default_timezone_set('UTC');
|
||||||
|
require_once __DIR__ . '/../vendor/autoload.php';
|
||||||
6
tests/phpunit.xml
Normal file
6
tests/phpunit.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<phpunit bootstrap="bootstrap.php">
|
||||||
|
<testsuite name='Stream'>
|
||||||
|
<directory suffix='.php'>./</directory>
|
||||||
|
</testsuite>
|
||||||
|
</phpunit>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue