Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Adapter::getContents & Adapter::putContents #62

Merged
merged 1 commit into from Apr 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions src/AdapterInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,48 @@ public function write($fileDescriptor, $data, $length, $offset);
*/
public function close($fd);

/**
* Reads the entire file.
*
* This is an optimization for adapters which can optimize
* the open -> (seek ->) read -> close sequence into one call.
*
* @param string $path
* @param int $offset
* @param int|null $length
* @return PromiseInterface
*/
public function getContents($path, $offset = 0, $length = null);

/**
* Writes the given content to the specified file.
* If the file exists, the file is truncated.
* If the file does not exist, the file will be created.
*
* This is an optimization for adapters which can optimize
* the open -> write -> close sequence into one call.
*
* @param string $path
* @param string $content
* @return PromiseInterface
* @see AdapterInterface::appendContents()
*/
public function putContents($path, $content);

/**
* Appends the given content to the specified file.
* If the file does not exist, the file will be created.
*
* This is an optimization for adapters which can optimize
* the open -> write -> close sequence into one call.
*
* @param string $path
* @param string $content
* @return PromiseInterface
* @see AdapterInterface::putContents()
*/
public function appendContents($path, $content);

/**
* Rename a node.
*
Expand Down
76 changes: 75 additions & 1 deletion src/ChildProcess/Adapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use DateTime;
use Exception;
use Throwable;
use React\EventLoop\LoopInterface;
use React\Filesystem\ObjectStream;
use React\Filesystem\ObjectStreamSink;
Expand Down Expand Up @@ -181,6 +182,10 @@ public function callFilesystem($function, $args, $errorResultCode = -1)
return $this->pool->rpc(Factory::rpc($function, $args))->then(function (Payload $payload) {
return \React\Promise\resolve($payload->getPayload());
}, function ($payload) {
if ($payload instanceof Throwable) {
return \React\Promise\reject($payload);
}

return \React\Promise\reject(new Exception($payload['error']['message']));
});
}
Expand Down Expand Up @@ -280,7 +285,76 @@ public function close($fd)
return $fileDescriptor->softTerminate();
});
}


/**
* Reads the entire file.
*
* This is an optimization for adapters which can optimize
* the open -> (seek ->) read -> close sequence into one call.
*
* @param string $path
* @param int $offset
* @param int|null $length
* @return PromiseInterface
*/
public function getContents($path, $offset = 0, $length = null)
{
return $this->invoker->invokeCall('getContents', [
'path' => $path,
'offset' => $offset,
'maxlen' => $length,
])->then(function ($payload) {
return \React\Promise\resolve(base64_decode($payload['chunk']));
});
}

/**
* Writes the given content to the specified file.
* If the file exists, the file is truncated.
* If the file does not exist, the file will be created.
*
* This is an optimization for adapters which can optimize
* the open -> write -> close sequence into one call.
*
* @param string $path
* @param string $content
* @return PromiseInterface
* @see AdapterInterface::appendContents()
*/
public function putContents($path, $content)
{
return $this->invoker->invokeCall('putContents', [
'path' => $path,
'chunk' => base64_encode($content),
'flags' => 0,
])->then(function ($payload) {
return \React\Promise\resolve($payload['written']);
});
}

/**
* Appends the given content to the specified file.
* If the file does not exist, the file will be created.
*
* This is an optimization for adapters which can optimize
* the open -> write -> close sequence into one call.
*
* @param string $path
* @param string $content
* @return PromiseInterface
* @see AdapterInterface::putContents()
*/
public function appendContents($path, $content)
{
return $this->invoker->invokeCall('putContents', [
'path' => $path,
'chunk' => base64_encode($content),
'flags' => FILE_APPEND,
])->then(function ($payload) {
return \React\Promise\resolve($payload['written']);
});
}

/**
* @param string $path
* @return PromiseInterface
Expand Down
30 changes: 30 additions & 0 deletions src/ChildProcess/Process.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ public function __construct(Messenger $messenger)
'read',
'write',
'close',
'getContents',
'putContents',
'rename',
'readlink',
'symlink',
Expand Down Expand Up @@ -247,6 +249,34 @@ public function close(array $payload)
]);
}

/**
* @param array $payload
* @return PromiseInterface
*/
public function getContents(array $payload)
{
if ($payload['maxlen'] > 0) {
$chunk = file_get_contents($payload['path'], false, null, $payload['offset'], $payload['maxlen']);
} else {
$chunk = file_get_contents($payload['path'], false, null, $payload['offset']);
}

return \React\Promise\resolve([
'chunk' => base64_encode($chunk),
]);
}

/**
* @param array $payload
* @return PromiseInterface
*/
public function putContents(array $payload)
{
return \React\Promise\resolve([
'written' => file_put_contents($payload['path'], base64_decode($payload['chunk']), $payload['flags']),
]);
}

/**
* @param array $payload
* @return PromiseInterface
Expand Down
69 changes: 69 additions & 0 deletions src/Eio/Adapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,75 @@ public function close($fd)
});
}

/**
* Reads the entire file.
*
* This is an optimization for adapters which can optimize
* the open -> (seek ->) read -> close sequence into one call.
*
* @param string $path
* @param int $offset
* @param int|null $length
* @return PromiseInterface
*/
public function getContents($path, $offset = 0, $length = null)
{
if ($length === null) {
return $this->stat($path)->then(function ($stat) use ($path, $offset) {
return $this->getContents($path, $offset, $stat['size']);
});
}

return $this->open($path, 'r')->then(function ($fd) use ($offset, $length) {
return $this->read($fd, $length, $offset)->always(function () use ($fd) {
return $this->close($fd);
});
});
}

/**
* Writes the given content to the specified file.
* If the file exists, the file is truncated.
* If the file does not exist, the file will be created.
*
* This is an optimization for adapters which can optimize
* the open -> write -> close sequence into one call.
*
* @param string $path
* @param string $content
* @return PromiseInterface
* @see AdapterInterface::appendContents()
*/
public function putContents($path, $content)
{
return $this->open($path, 'cw')->then(function ($fd) use ($content) {
return $this->write($fd, $content, strlen($content), 0)->always(function () use ($fd) {
return $this->close($fd);
});
});
}

/**
* Appends the given content to the specified file.
* If the file does not exist, the file will be created.
*
* This is an optimization for adapters which can optimize
* the open -> write -> close sequence into one call.
*
* @param string $path
* @param string $content
* @return PromiseInterface
* @see AdapterInterface::putContents()
*/
public function appendContents($path, $content)
{
return $this->open($path, 'cwa')->then(function ($fd) use ($content) {
return $this->write($fd, $content, strlen($content), 0)->always(function () use ($fd) {
return $this->close($fd);
});
});
}

/**
* {@inheritDoc}
*/
Expand Down
9 changes: 0 additions & 9 deletions tests/Adapters/AbstractAdaptersTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,6 @@ protected function getEioProvider(callable $loopFactory)
];
}

protected function getPthreadsProvider(callable $loopFactory)
{
$loop = $loopFactory();
return [
$loop,
new Pthreads\Adapter($loop),
];
}

protected function getFacoryProvider(callable $loopFactory)
{
$loop = $loopFactory();
Expand Down
3 changes: 0 additions & 3 deletions tests/Adapters/DirectoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@
namespace React\Tests\Filesystem\Adapters;

use React\EventLoop\LoopInterface;
use React\Filesystem\ChildProcess;
use React\Filesystem\Eio;
use React\Filesystem\FilesystemInterface;
use React\Filesystem\Pthreads;

/**
* @group adapters
Expand Down
3 changes: 0 additions & 3 deletions tests/Adapters/FileTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@
namespace React\Tests\Filesystem\Adapters;

use React\EventLoop\LoopInterface;
use React\Filesystem\ChildProcess;
use React\Filesystem\Eio;
use React\Filesystem\FilesystemInterface;
use React\Filesystem\Pthreads;

/**
* @group adapters
Expand Down
3 changes: 0 additions & 3 deletions tests/Adapters/InterfaceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@

use React\EventLoop\LoopInterface;
use React\Filesystem\AdapterInterface;
use React\Filesystem\ChildProcess;
use React\Filesystem\Eio;
use React\Filesystem\Pthreads;

class InterfaceTest extends AbstractAdaptersTest
{
Expand Down
46 changes: 46 additions & 0 deletions tests/ChildProcess/AdapterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -416,4 +416,50 @@ public function testErrorFromPool()
]);
$this->await($adapter->touch('foo.bar'), $loop, 1);
}

public function testGetContents()
{
$loop = \React\EventLoop\Factory::create();
$adapter = new Adapter($loop);

$contents = $this->await($adapter->getContents(__FILE__), $loop);
$this->assertSame(file_get_contents(__FILE__), $contents);
}

public function testGetContentsMinMax()
{
$loop = \React\EventLoop\Factory::create();
$adapter = new Adapter($loop);

$contents = $this->await($adapter->getContents(__FILE__, 5, 10), $loop);
$this->assertSame(file_get_contents(__FILE__, false, null, 5, 10), $contents);
}

public function testPutContents()
{
$loop = \React\EventLoop\Factory::create();
$adapter = new Adapter($loop);

$tempFile = $this->tmpDir . uniqid('', true);
$contents = sha1_file(__FILE__);

$this->await($adapter->putContents($tempFile, $contents), $loop);
$this->assertSame($contents, file_get_contents($tempFile));
}

public function testAppendContents()
{
$loop = \React\EventLoop\Factory::create();
$adapter = new Adapter($loop);

$tempFile = $this->tmpDir . uniqid('', true);
$contents = sha1_file(__FILE__);

file_put_contents($tempFile, $contents);
$time = sha1(time());
$contents .= $time;

$this->await($adapter->appendContents($tempFile, $time, FILE_APPEND), $loop);
$this->assertSame($contents, file_get_contents($tempFile));
}
}
Loading