Is it possible to use this with virtual file system stream wrapper?
mmenozzi opened this issue · comments
Hi,
probably I'm missing something but I would like to use this library with https://github.com/mikey179/vfsStream and I get an error. I made a super simple test script:
<?php
require __DIR__ . '/vendor/autoload.php';
\org\bovigo\vfs\vfsStream::setup();
$path = \org\bovigo\vfs\vfsStream::url('root/file.txt');
$result = @\file_put_contents($path, 'hello world');
var_dump($result);
var_dump(file_get_contents($path));
\Amp\Loop::run(function () use ($path) {
$result = yield \Amp\File\put($path, 'hello world');
var_dump($result);
var_dump(yield \Amp\File\get($path));
});
And the output is:
int(11)
string(11) "hello world"
PHP Fatal error: Uncaught Amp\Parallel\Worker\TaskException: Uncaught Amp\File\FilesystemException in worker with message "file_put_contents(vfs://root/file.txt): failed to open stream: No such file or directory" and code "0" in /private/tmp/test/vendor/amphp/parallel/lib/Worker/Internal/TaskFailure.php:55
Stack trace:
#0 /private/tmp/test/vendor/amphp/parallel/lib/Worker/AbstractWorker.php(89): Amp\Parallel\Worker\Internal\TaskFailure->promise()
#1 [internal function]: Amp\Parallel\Worker\AbstractWorker->Amp\Parallel\Worker\{closure}()
#2 /private/tmp/test/vendor/amphp/amp/lib/Coroutine.php(74): Generator->send(Object(Amp\Parallel\Worker\Internal\TaskFailure))
#3 /private/tmp/test/vendor/amphp/amp/lib/Internal/Placeholder.php(127): Amp\Coroutine->Amp\{closure}(NULL, Object(Amp\Parallel\Worker\Internal\TaskFailure))
#4 /private/tmp/test/vendor/amphp/amp/lib/Coroutine.php(79): Amp\Coroutine->resolve(Object(Amp\Parallel\Worker\Internal\TaskFailure))
#5 /private/tmp/test/vendor/amphp/amp/lib/Internal/Pla
Fatal error: Uncaught Amp\Parallel\Worker\TaskException: Uncaught Amp\File\FilesystemException in worker with message "file_put_contents(vfs://root/file.txt): failed to open stream: No such file or directory" and code "0" in /private/tmp/test/vendor/amphp/parallel/lib/Worker/Internal/TaskFailure.php:55
Stack trace:
#0 /private/tmp/test/vendor/amphp/parallel/lib/Worker/AbstractWorker.php(89): Amp\Parallel\Worker\Internal\TaskFailure->promise()
#1 [internal function]: Amp\Parallel\Worker\AbstractWorker->Amp\Parallel\Worker\{closure}()
#2 /private/tmp/test/vendor/amphp/amp/lib/Coroutine.php(74): Generator->send(Object(Amp\Parallel\Worker\Internal\TaskFailure))
#3 /private/tmp/test/vendor/amphp/amp/lib/Internal/Placeholder.php(127): Amp\Coroutine->Amp\{closure}(NULL, Object(Amp\Parallel\Worker\Internal\TaskFailure))
#4 /private/tmp/test/vendor/amphp/amp/lib/Coroutine.php(79): Amp\Coroutine->resolve(Object(Amp\Parallel\Worker\Internal\TaskFailure))
#5 /private/tmp/test/vendor/amphp/amp/lib/Internal/Placeholder.php(127): Amp\Co in /private/tmp/test/vendor/amphp/file/lib/ParallelDriver.php on line 48
As you can see the plain PHP file_put_contents
function works (it returns a non false result and then the content is red from the same file).
But with Amp\File\put
it doesn't work (I get a fatal error). Why this happens?
Ok sorry found it what I was missing. I had to force the BlockingDriver with \Amp\File\filesystem(new \Amp\File\BlockingDriver());
, so:
<?php
require __DIR__ . '/vendor/autoload.php';
\org\bovigo\vfs\vfsStream::setup();
$path = \org\bovigo\vfs\vfsStream::url('root/file.txt');
$result = @\file_put_contents($path, 'hello world');
var_dump($result);
var_dump(file_get_contents($path));
\Amp\Loop::run(function () use ($path) {
\Amp\File\filesystem(new \Amp\File\BlockingDriver());
$result = yield \Amp\File\put($path, 'hello world');
var_dump($result);
var_dump(yield \Amp\File\get($path));
});
If you call \org\bovigo\vfs\vfsStream::setup();
in a file automatically autoloaded by Composer it should also work with the ParallelDriver
, but probably won't work with Eio
or Uv
.
Thank you @kelunik I'll try your suggestion asap.
Hi @kelunik,
could you please explain why calling \org\bovigo\vfs\vfsStream::setup();
in a file autoloaded by Composer should make work even the ParallelDriver
? Is it because the vfs
stream wrapper must be registered before... before what?
Anyway, I tried to put this in my composer.json
file:
"autoload-dev": {
// ...
"files": ["tests/vfs_setup.php"]
}
And the following in the tests/vfs_setup.php
file:
<?php
\org\bovigo\vfs\vfsStream::setup();
But my test are failing with the ParallelDriver
. Instead if I call filesystem(new BlockingDriver());
in the PHPUnit's setUp
method all works fine.
I also verified that the tests/vfs_setup.php
script is included by putting a var_dump
in it and I can confirm it's included.
@mmenozzi I assume org\bovigo\vfs\vfsStream::setup()
registers the custom stream wrapper. The parallel driver automatically includes the autoloader and thus registers the stream wrapper also in the child process used to access the files then.
But I guess it doesn't work, because org\bovigo\vfs\vfsStream::url()
would also need to be called in the child process, right?
I assume org\bovigo\vfs\vfsStream::setup() registers the custom stream wrapper.
Yes it does: https://github.com/mikey179/vfsStream/blob/master/src/main/php/org/bovigo/vfs/vfsStream.php#L167
But I guess it doesn't work, because org\bovigo\vfs\vfsStream::url() would also need to be called in the child process, right?
What do you mean with "child process"? The org\bovigo\vfs\vfsStream::url()
needs to be called in the test code to create a "vfs" path. For example \org\bovigo\vfs\vfsStream::url('root/file.txt');
returns vfs://root/file.txt
. The code under test doesn't need to call org\bovigo\vfs\vfsStream::url()
it just uses the vfs path.
ParallelDriver is using a process pool to delegate the blocking tasks to another process and keeping the main process free of blocking operations.
These child processes need to be appropriately initialized, so that the file operations executed in that separate process are aware of your vfs setup.
Thank you @bwoebi.
So maybe it's a matter of shared memory? I mean if there are multiple children processes which perform file operations and the memory is not shared by default maybe I get wrong results. The vfs stream simulates the file system in RAM.
@mmenozzi The complexity isn't worth it. Just use the BlockingDriver
for tests.
Sure @kelunik, thank you!