amphp / file

An abstraction layer and non-blocking file access solution that keeps your application responsive.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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!