From 016ceaa08931af3000fc2bd5f51ca0a2a09bb19f Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Tue, 5 Oct 2021 23:32:05 +0200 Subject: [PATCH] Rewrite This is a full rewrite of this package including the following features/changes: * ext-uv adapter * child process adapter * fallback (blocking) adapter (for native Windows) * simplified the internals to remove unnecessary complexity * file_get_contents/file_put_contents like API to read/write from/to files * start has become an object instead of an array This PR doesn't include the following features/changes as those will be added in with followup PR's: * ext-eio adapter * macos support * mode support to define permissions when creating files/directories * symlinks read/creating/deleting * deleting of directories --- .github/workflows/ci.yml | 51 + .gitignore | 1 + .travis.yml | 29 - README.md | 321 +- composer.json | 83 +- composer.lock | 3227 +++++++++++++---- examples/directory_create_recursive.php | 26 - examples/directory_listing.php | 19 + examples/directory_ls.php | 17 - examples/directory_ls_recursive.php | 17 - ...rectory_ls_recursive_regexp_interfaces.php | 20 - examples/directory_ls_recursive_streaming.php | 22 - ...ive_streaming_strlen_file_get_contents.php | 48 - examples/directory_ls_streaming.php | 22 - examples/directory_nested_listing.php | 32 + examples/directory_rename.php | 15 - examples/directory_size.php | 15 - examples/directory_size_recursive.php | 24 - examples/directory_stat.php | 17 - examples/directory_to_directory_copy.php | 27 - examples/file_chown.php | 16 - examples/file_create.php | 30 - examples/file_create_new.php | 24 + examples/file_duplex_stream.php | 51 - examples/file_exists.php | 24 - examples/file_get_contents.php | 15 - examples/file_parent.php | 14 - examples/file_put_contents.php | 19 - examples/file_read.php | 12 + examples/file_size.php | 15 - examples/file_stat.php | 29 +- examples/file_tail.php | 24 + examples/file_time.php | 23 - examples/file_to_directory_copy.php | 26 - examples/file_to_file_copy.php | 23 - examples/file_touch.php | 31 - examples/file_unlink.php | 12 + examples/file_write.php | 12 + examples/link_get_contents.php | 1 - examples/link_readlink.php | 15 - examples/link_symlink.php | 15 - examples/node_doesnt_exist.php | 10 + examples/tail.php | 36 - examples/test.txt | 0 src/AdapterInterface.php | 239 +- src/ChildProcess/Adapter.php | 505 +-- src/ChildProcess/Directory.php | 75 + src/ChildProcess/File.php | 71 + src/ChildProcess/NotExist.php | 75 + src/ChildProcess/Process.php | 316 -- src/ChildProcess/StatTrait.php | 31 + src/Factory.php | 26 + src/Fallback/Adapter.php | 47 + src/Fallback/Directory.php | 67 + src/Fallback/File.php | 63 + src/Fallback/NotExist.php | 76 + src/Fallback/StatTrait.php | 27 + src/Filesystem.php | 136 - src/FilesystemInterface.php | 65 - src/MappedTypeDetector.php | 63 - src/ModeTypeDetector.php | 67 +- src/Node/Directory.php | 362 -- src/Node/DirectoryInterface.php | 71 +- src/Node/File.php | 228 -- src/Node/FileInterface.php | 73 +- src/Node/GenericOperationInterface.php | 40 - src/Node/GenericOperationTrait.php | 103 - src/Node/Link.php | 63 - src/Node/LinkInterface.php | 13 - src/Node/NodeInterface.php | 39 +- src/Node/NotExistInterface.php | 19 + src/Node/RecursiveInvoker.php | 54 - src/NodeNotFound.php | 18 + src/ObjectStream.php | 63 - src/ObjectStreamSink.php | 27 - src/OpenFileLimiter.php | 85 - src/PermissionFlagResolver.php | 66 - src/PollInterface.php | 12 + src/QueuedCall.php | 74 - src/Stat.php | 59 + src/Stream/DuplexStream.php | 48 - src/Stream/GenericStreamInterface.php | 11 - src/Stream/GenericStreamTrait.php | 98 - src/Stream/ReadableStream.php | 27 - src/Stream/ReadableStreamTrait.php | 108 - src/Stream/StreamFactory.php | 28 - src/Stream/WritableStream.php | 25 - src/Stream/WritableStreamTrait.php | 42 - src/TypeDetectorInterface.php | 4 +- src/Uv/Adapter.php | 79 + src/Uv/Directory.php | 113 + src/Uv/File.php | 109 + src/Uv/NotExist.php | 108 + src/Uv/Poll.php | 43 + src/Uv/StatTrait.php | 36 + src/functions.php | 37 - src/functions_include.php | 7 - tests/AbstractFilesystemTestCase.php | 43 + tests/AbstractFlagResolverTest.php | 17 - tests/Adapters/AbstractAdaptersTest.php | 96 - tests/Adapters/DirectoryTest.php | 218 -- tests/Adapters/FileTest.php | 280 -- tests/Adapters/InterfaceTest.php | 25 - tests/Adapters/reactphp-logo.png | Bin 58832 -> 0 bytes tests/ChildProcess/AdapterTest.php | 374 -- tests/ChildProcess/PoolRpcErrorMock.php | 44 - .../ChildProcess/PoolRpcErrorMockFactory.php | 32 - tests/ChildProcess/ProcessTest.php | 59 - tests/ChildProcess/SingletonPoolStub.php | 80 - tests/DirectoryTest.php | 63 + tests/FactoryTest.php | 24 + tests/FileTest.php | 276 ++ tests/FilesystemTest.php | 100 +- tests/FlagResolverTest.php | 35 - tests/FunctionsTest.php | 20 - tests/ModeTypeDetectorTest.php | 79 - tests/Node/DirectoryTest.php | 405 --- tests/Node/FileTest.php | 424 --- tests/Node/GenericOperationTraitTest.php | 152 - tests/Node/NodeTestTrait.php | 21 - tests/Node/RecursiveInvokerTest.php | 89 - tests/NotExistTest.php | 25 + tests/ObjectStreamSinkTest.php | 27 - tests/ObjectStreamTest.php | 31 - tests/OpenFileLimiterTest.php | 44 - tests/PermissionFlagResolverTest.php | 115 - tests/Stream/DuplexStreamTest.php | 24 - tests/Stream/GenericStreamTraitTest.php | 19 - tests/Stream/ReadableStreamTest.php | 275 -- tests/Stream/StreamFactoryTest.php | 46 - tests/Stream/WritableStreamTest.php | 231 -- tests/TestCase.php | 119 - tests/UnknownNodeType.php | 94 - 133 files changed, 4759 insertions(+), 8098 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .travis.yml delete mode 100644 examples/directory_create_recursive.php create mode 100644 examples/directory_listing.php delete mode 100644 examples/directory_ls.php delete mode 100644 examples/directory_ls_recursive.php delete mode 100644 examples/directory_ls_recursive_regexp_interfaces.php delete mode 100644 examples/directory_ls_recursive_streaming.php delete mode 100644 examples/directory_ls_recursive_streaming_strlen_file_get_contents.php delete mode 100644 examples/directory_ls_streaming.php create mode 100644 examples/directory_nested_listing.php delete mode 100644 examples/directory_rename.php delete mode 100644 examples/directory_size.php delete mode 100644 examples/directory_size_recursive.php delete mode 100644 examples/directory_stat.php delete mode 100644 examples/directory_to_directory_copy.php delete mode 100644 examples/file_chown.php delete mode 100644 examples/file_create.php create mode 100644 examples/file_create_new.php delete mode 100644 examples/file_duplex_stream.php delete mode 100644 examples/file_exists.php delete mode 100644 examples/file_get_contents.php delete mode 100644 examples/file_parent.php delete mode 100644 examples/file_put_contents.php create mode 100644 examples/file_read.php delete mode 100644 examples/file_size.php create mode 100644 examples/file_tail.php delete mode 100644 examples/file_time.php delete mode 100644 examples/file_to_directory_copy.php delete mode 100644 examples/file_to_file_copy.php delete mode 100644 examples/file_touch.php create mode 100644 examples/file_unlink.php create mode 100644 examples/file_write.php delete mode 120000 examples/link_get_contents.php delete mode 100644 examples/link_readlink.php delete mode 100644 examples/link_symlink.php create mode 100644 examples/node_doesnt_exist.php delete mode 100644 examples/tail.php delete mode 100644 examples/test.txt create mode 100644 src/ChildProcess/Directory.php create mode 100644 src/ChildProcess/File.php create mode 100644 src/ChildProcess/NotExist.php delete mode 100644 src/ChildProcess/Process.php create mode 100644 src/ChildProcess/StatTrait.php create mode 100644 src/Factory.php create mode 100644 src/Fallback/Adapter.php create mode 100644 src/Fallback/Directory.php create mode 100644 src/Fallback/File.php create mode 100644 src/Fallback/NotExist.php create mode 100644 src/Fallback/StatTrait.php delete mode 100644 src/Filesystem.php delete mode 100644 src/FilesystemInterface.php delete mode 100644 src/MappedTypeDetector.php delete mode 100644 src/Node/Directory.php delete mode 100644 src/Node/File.php delete mode 100644 src/Node/GenericOperationInterface.php delete mode 100644 src/Node/GenericOperationTrait.php delete mode 100644 src/Node/Link.php delete mode 100644 src/Node/LinkInterface.php create mode 100644 src/Node/NotExistInterface.php delete mode 100644 src/Node/RecursiveInvoker.php create mode 100644 src/NodeNotFound.php delete mode 100644 src/ObjectStream.php delete mode 100644 src/ObjectStreamSink.php delete mode 100644 src/OpenFileLimiter.php delete mode 100644 src/PermissionFlagResolver.php create mode 100644 src/PollInterface.php delete mode 100644 src/QueuedCall.php create mode 100644 src/Stat.php delete mode 100644 src/Stream/DuplexStream.php delete mode 100644 src/Stream/GenericStreamInterface.php delete mode 100644 src/Stream/GenericStreamTrait.php delete mode 100644 src/Stream/ReadableStream.php delete mode 100644 src/Stream/ReadableStreamTrait.php delete mode 100644 src/Stream/StreamFactory.php delete mode 100644 src/Stream/WritableStream.php delete mode 100644 src/Stream/WritableStreamTrait.php create mode 100644 src/Uv/Adapter.php create mode 100644 src/Uv/Directory.php create mode 100644 src/Uv/File.php create mode 100644 src/Uv/NotExist.php create mode 100644 src/Uv/Poll.php create mode 100644 src/Uv/StatTrait.php delete mode 100644 src/functions.php delete mode 100644 src/functions_include.php create mode 100644 tests/AbstractFilesystemTestCase.php delete mode 100644 tests/AbstractFlagResolverTest.php delete mode 100644 tests/Adapters/AbstractAdaptersTest.php delete mode 100644 tests/Adapters/DirectoryTest.php delete mode 100644 tests/Adapters/FileTest.php delete mode 100644 tests/Adapters/InterfaceTest.php delete mode 100644 tests/Adapters/reactphp-logo.png delete mode 100644 tests/ChildProcess/AdapterTest.php delete mode 100644 tests/ChildProcess/PoolRpcErrorMock.php delete mode 100644 tests/ChildProcess/PoolRpcErrorMockFactory.php delete mode 100644 tests/ChildProcess/ProcessTest.php delete mode 100644 tests/ChildProcess/SingletonPoolStub.php create mode 100644 tests/DirectoryTest.php create mode 100644 tests/FactoryTest.php create mode 100644 tests/FileTest.php delete mode 100644 tests/FlagResolverTest.php delete mode 100644 tests/FunctionsTest.php delete mode 100644 tests/ModeTypeDetectorTest.php delete mode 100644 tests/Node/DirectoryTest.php delete mode 100644 tests/Node/FileTest.php delete mode 100644 tests/Node/GenericOperationTraitTest.php delete mode 100644 tests/Node/NodeTestTrait.php delete mode 100644 tests/Node/RecursiveInvokerTest.php create mode 100644 tests/NotExistTest.php delete mode 100644 tests/ObjectStreamSinkTest.php delete mode 100644 tests/ObjectStreamTest.php delete mode 100644 tests/OpenFileLimiterTest.php delete mode 100644 tests/PermissionFlagResolverTest.php delete mode 100644 tests/Stream/DuplexStreamTest.php delete mode 100644 tests/Stream/GenericStreamTraitTest.php delete mode 100644 tests/Stream/ReadableStreamTest.php delete mode 100644 tests/Stream/StreamFactoryTest.php delete mode 100644 tests/Stream/WritableStreamTest.php delete mode 100644 tests/TestCase.php delete mode 100644 tests/UnknownNodeType.php diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..58f82e00 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,51 @@ +name: CI + +on: + push: + pull_request: + +jobs: + PHPUnit: + name: PHPUnit (PHP ${{ matrix.php }} with ${{ matrix.extensions }} on ${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - ubuntu-latest + - windows-latest + php: + - "8.1" + - "8.0" + - "7.4" + extensions: + - "" + - "uv-amphp/ext-uv@master" + steps: + - uses: actions/checkout@v2 + - name: Install libuv + if: matrix.os == 'ubuntu-latest' + run: sudo apt-get install libuv1-dev + - uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + coverage: pcov + extensions: ${{ matrix.extensions }} + - run: composer install + - run: vendor/bin/phpunit --coverage-text --debug + PHPUnit-Docker: + name: PHPUnit (PHP ${{ matrix.php }} on Docker) + runs-on: ubuntu-latest + container: + image: wyrihaximusnet/php:${{ matrix.php }}-nts-alpine-slim-dev-root + strategy: + fail-fast: false + matrix: + php: + - "8.1" + - "8.0" + - "7.4" + steps: + - uses: actions/checkout@v2 + - run: composer install + - run: vendor/bin/phpunit --coverage-text diff --git a/.gitignore b/.gitignore index 22d0d82f..58be7c72 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ vendor +.phpunit.result.cache diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index d1b6f65f..00000000 --- a/.travis.yml +++ /dev/null @@ -1,29 +0,0 @@ -language: php - -cache: - directories: - - $HOME/.composer/cache/files - -php: - - 5.4 - - 5.5 - - 5.6 - - 7.0 - - 7.1 - - 7.2 - - nightly - -matrix: - allow_failures: - - php: 5.4 - - php: 7.1 - - php: nightly - -install: - - ./travis-init.sh - - composer install -n - -script: - - ./vendor/bin/phpunit --exclude-group permissions - - export phploc=~/.phpenv/versions/$(phpenv version-name)/bin/php - - sudo $phploc ./vendor/bin/phpunit --group permissions diff --git a/README.md b/README.md index f6782dad..6f80e0a0 100644 --- a/README.md +++ b/README.md @@ -1,147 +1,288 @@ -Filesystem -========== +# Filesystem Component + +[![CI status](https://github.com/reactphp/filesystem/workflows/CI/badge.svg)](https://github.com/reactphp/filesystem/actions) + +[ReactPHP](https://reactphp.org/)'s filesystem component that enables non-blocking filesystem operations. + +**Table of Contents** + +* [Quickstart example](#quickstart-example) +* [Usage](#usage) + * [Factory](#factory) + * [create()](#create) + * [Filesystem implementations](#filesystem-implementations) + * [ChildProcess](#childprocess) + * [Uv](#uv) + * [AdapterInterface](#adapterinterface) + * [detect()](#detect) + * [directory()](#directory) + * [file()](#file) + * [NodeInterface](#nodeinterface) + * [path()](#path) + * [name()](#name) + * [stat()](#stat) + * [DirectoryInterface](#directoryinterface) + * [ls](#ls) + * [FileInterface](#fileinterface) + * [getContents()](#getcontents) + * [putContents()](#putcontents) + * [NotExistInterface](#notexistinterface) + * [createDirectory()](#createdirectory) + * [createFile()](#createfile) +* [Install](#install) +* [Tests](#tests) +* [License](#license) + +## Quickstart example + +Here is a program that lists everything in the current directory. -[![Build Status](https://secure.travis-ci.org/reactphp/filesystem.png?branch=master)](http://travis-ci.org/reactphp/filesystem) [![Code Climate](https://codeclimate.com/github/reactphp/filesystem/badges/gpa.svg)](https://codeclimate.com/github/reactphp/filesystem) +```php +use React\Filesystem\Factory; +use React\Filesystem\Node\DirectoryInterface; +use React\Filesystem\Node\NodeInterface; + +Factory::create()->detect(__DIR__)->then(function (DirectoryInterface $directory) { + return $directory->ls(); +})->then(static function ($nodes) { + foreach ($nodes as $node) { + assert($node instanceof NodeInterface); + echo $node->name(), ': ', get_class($node), PHP_EOL; + } + echo '----------------------------', PHP_EOL, 'Done listing directory', PHP_EOL; +}, function (Throwable $throwable) { + echo $throwable; +}); +``` -[ReactPHP](https://reactphp.org/)'s evented asynchronous, non-blocking filesystem access library. +See also the [examples](examples). -Table of Contents ------------------ +## Usage -1. [Introduction](#introduction) -2. [Adapters](#adapters) -3. [Examples](#examples) - * [Creating filesystem object](#creating-filesystem-object) - * [File object](#file-object) - * [Reading files](#reading-files) - * [Writing files](#writing-files) - * [Directory object](#directory-object) - * [List contents](#list-contents) -4. [Install](#install) -5. [License](#license) +See [`Factory::create()`](#create). -Introduction ------------- +### Factory -`react/filesystem` is a package to power your application with asynchronous, non-blocking filesystem access. Asynchronous access is enabled by various adapters described below. +The `Factory` class exists as a convenient way to pick the best available +[filesystem implementation](#filesystem-implementations). -Adapters ------------- +#### create() -* ChildProcessAdapter - Adapter using child processes to perform IO actions (default adapter if no extensions are installed) +The `create(): AdapterInterface` method can be used to create a new filesystem instance: -Examples --------- +```php +$filesystem = \React\Filesystem\Factory::create(); +``` -`Adding examples here over time.` +This method always returns an instance implementing [`adapterinterface`](#adapterinterface), +the actual [Filesystem implementations](#filesystem-implementations) is an implementation detail. -Creating filesystem object --------------------------- +This method can be called at any time. However, certain scheduling mechanisms are used that will make the event loop +busier with every new instance of a filesystem adapter. To prevent that it is preferred you create it once and inject +it where required. -```php -file(__FILE__); // Returns a \React\Filesystem\Node\FileInterface compatible object -``` +The factory will determine the most performant filesystem for your environment. Any extension based filesystem are +preferred before falling back to less performant filesystems. When no extensions are detected it will fall back to +the [`ChildProcess`](#childprocess) on Linux/Mac machines, and to an internal fallback filesystem for windows that +uses blocking system calls. This blocking filesystem isn't documented and will be removed once +the [`ChildProcess`](#childprocess) filesystem works on Windows. It's merely mentioned here for reference until then. + +Advanced! If you explicitly need a certain filesystem implementation, you can +manually instantiate one of the following classes. +Note that you may have to install the required PHP extensions for the respective +event loop implementation first or they will throw a `BadMethodCallException` on creation. + +#### ChildProcess + +A [`child process`](https://reactphp.org/child-process/) based filesystem. + +This uses the blocking calls like the [`file_get_contents()`](https://www.php.net/manual/en/function.file-get-contents.php) +function to do filesystem calls and is the only implementation which works out of the box with PHP. + +Due to using child processes to handle filesystem calls, this filesystem the least performant is only used when no +extensions are found to create a more performant filesystem. + +#### Uv + +An `ext-uv` based filesystem. + +This filesystem uses the [`uv` PECL extension](https://pecl.php.net/package/uv), that +provides an interface to `libuv` library. + +This filesystem is known to work with PHP 7+. + +### AdapterInterface + +#### detect() + +The `detect(string $path): PromiseInterface` is the preferred way to get an object representing a node on the filesystem. + +When calling this method it will attempt to detect what kind of node the path is you've given it, and return an object +implementing [`NodeInterface`](#nodeinterface). If nothing exists at the given path, a [`NotExistInterface`](#notexistinterface) object will be +returned which you can use to create a file or directory. + +#### directory() + +The `directory(string $path): DirectoryInterface` creates an object representing a directory at the specified path. + +Keep in mind that unlike the `detect` method the `directory` method cannot guarantee the path you pass is actually a +directory on the filesystem and may result in unexpected behavior. + +#### file() + +The `file(string $path): DirectoryInterface` creates an object representing a file at the specified path. + +Keep in mind that unlike the `detect` method the `file` method cannot guarantee the path you pass is actually a +file on the filesystem and may result in unexpected behavior. + +### NodeInterface + +The `NodeInterface` is at the core of all other node interfaces such as `FileInterface` or `DirectoryInterface`. It +provides basic methods that are useful for all types of nodes. + +#### path() + +The `path(): string` method returns the path part of the node's location. So if the full path is `/path/to/file.ext` this method returns `/path/to/`. -Reading files -------------- +#### name() + +The `name(): string` method returns the name part of the node's location. So if the full path is `/path/to/file.ext` this method returns `file.ext`. + +#### stat() + +The `stat(): PromiseInterface` method stats the node and provides you with information such as its size, full path, create/update time. + +### DirectoryInterface + +#### ls + +The `ls(): PromiseInterface>` method list all contents of the given directory and will return an +array with nodes in it. It will do it's best to detect which type a node is itself, and otherwise fallback +to `FilesystemInterface::detect(string $path): PromiseInterface`. + +### FileInterface + +The `*Contents` methods on this interface are designed to behave the same as PHP's `file_(get|put)_contents` functions +as possible. Resulting in a very familiar API to read/stream from files, or write/append to a file. + +#### getContents + +For reading from files `getContents(int $offset = 0 , ?int $maxlen = null): PromiseInterface` provides two +arguments that control how much data it reads from the file. Without arguments, it will read everything: ```php -$filesystem->getContents('test.txt')->then(function($contents) { -}); +$file->getContents(); ``` -Which is a convenience method for: +The offset and maximum length let you 'select' a chunk of the file to be read. The following will skip the first `2048` +bytes and then read up to `1024` bytes from the file. However, if the file only contains `512` bytes after the `2048` +offset it will only return those `512` bytes. ```php -$filesystem->file('test.txt')->open('r')->then(function($stream) { - return \React\Stream\BufferedSink::createPromise($stream); -})->then(function($contents) { - // ... -}); +$file->getContents(2048, 1024); ``` -Which in it's turn is a convenience method for: +It is possible to tail files with, the following example uses a timer as trigger to check for updates: ```php -$filesystem->file('test.txt')->open('r')->then(function ($stream) use ($node) { - $buffer = ''; - $deferred = new \React\Promise\Deferred(); - $stream->on('data', function ($data) use (&$buffer) { - $buffer += $data; +$offset = 0; +Loop::addPeriodicTimer(1, function (TimerInterface $timer) use ($file, &$offset, $loop): void { + $file->getContents($offset)->then(function (string $contents) use (&$offset, $timer, $loop): void { + echo $contents; // Echo's the content for example purposes + $offset += strlen($contents); }); - $stream->on('end', function ($data) use ($stream, $deferred, &$buffer) { - $stream->close(); - $deferred->resolve(&$buffer); - }); - return $deferred->promise(); }); ``` -Writing files -------------- +#### putContents + +Writing to file's is `putContents(string $contents, int $flags = 0): PromiseInterface` specialty. By default, when +passing it contents, it will truncate the file when it exists or create a new one and then fill it with the contents +given. -Open a file for writing (`w` flag) and write `abcde` to `test.txt` and close it. Create it (`c` flag) when it doesn't exists and truncate it (`t` flag) when it does. ```php -$filesystem->file('test.txt')->open('cwt')->then(function ($stream) { - $stream->write('a'); - $stream->write('b'); - $stream->write('c'); - $stream->write('d'); - $stream->end('e'); -}); +$file->putContents('ReactPHP'); ``` -Directory object --------------------------- +Appending files is also supported, by using the `\FILE_APPEND` constant the file is appended when it exists. ```php -putContents(' is awesome!', \FILE_APPEND); +``` -$loop = \React\EventLoop\Factory::create(); -$filesystem = \React\Filesystem\Filesystem::create($loop); +### NotExistInterface -$dir = $filesystem->dir(__DIR__); // Returns a \React\Filesystem\Node\DirectoryInterface compatible object +Both creation methods will check if the parent directory exists and create it if it doesn't. Effectively making this +creation process recursively. + +#### createDirectory + +The following will create `lets/make/a/nested/directory` as a recursive directory structure. + +```php +$filesystem->directory( + __DIR__ . 'lets' . DIRECTORY_SEPARATOR . 'make' . DIRECTORY_SEPARATOR . 'a' . DIRECTORY_SEPARATOR . 'nested' . DIRECTORY_SEPARATOR . 'directory' +)->createDirectory(); ``` -List contents -------------- +#### createFile + +The following will create `with-a-file.txt` in `lets/make/a/nested/directory` and write `This is amazing!` into that file. ```php -$filesystem->dir(__DIR__)->ls()->then(function (array $list) { - foreach ($list as $node) { - echo $node->getPath(), PHP_EOL; - } +use React\Filesystem\Node\FileInterface;$filesystem->file( + __DIR__ . 'lets' . DIRECTORY_SEPARATOR . 'make' . DIRECTORY_SEPARATOR . 'a' . DIRECTORY_SEPARATOR . 'nested' . DIRECTORY_SEPARATOR . 'directory' . DIRECTORY_SEPARATOR . 'with-a-file.txt' +)->createFile()->then(function (FileInterface $file) { + return $file->putContents('This is amazing!') }); ``` -Install -------- +## Install + The recommended way to install this library is [through Composer](https://getcomposer.org). [New to Composer?](https://getcomposer.org/doc/00-intro.md) +This project follows [SemVer](https://semver.org/). This will install the latest supported version: ```bash -$ composer require react/filesystem:^0.1.1 +$ composer require react/filesystem:^0.2 +``` + +Installing any of the event loop extensions is suggested, but entirely optional. +See also [event loop implementations](#loop-implementations) for more details. + +## Tests + +To run the test suite, you first need to clone this repo and then install all +dependencies [through Composer](https://getcomposer.org): + +```bash +$ composer install +``` + +To run the test suite, go to the project root and run: + +```bash +$ php vendor/bin/phpunit ``` -License -------- +## License -React/Filesystem is released under the [MIT](https://github.com/reactphp/filesystem/blob/master/LICENSE) license. +MIT, see [LICENSE file](LICENSE). diff --git a/composer.json b/composer.json index f2c1004a..52f0de90 100644 --- a/composer.json +++ b/composer.json @@ -1,42 +1,49 @@ { - "name": "react/filesystem", - "description": "Asynchronous filesystem abstraction.", - "keywords": ["filesystem", "asynchronous"], - "license": "MIT", - "authors": [ - { - "name": "Cees-Jan Kiewiet", - "email": "ceesjank@gmail.com" - } - ], - "require": { - "php": ">=5.4.0", - "evenement/evenement": "^3.0 || ^2.0", - "react/event-loop": "^1.0 || ^0.5 || ^0.4", - "react/promise": "~2.2", - "react/promise-stream": "^1.1", - "react/stream": "^1.0 || ^0.7 || ^0.6 || ^0.5 || ^0.4", - "wyrihaximus/react-child-process-pool": "^1.3" - }, - "require-dev": { - "clue/block-react": "^1.1", - "phpunit/phpunit": "^6.0 || ^5.0 || ^4.8" - }, - "autoload": { - "psr-4": { - "React\\Filesystem\\": "src/" + "name": "react/filesystem", + "description": "Asynchronous filesystem abstraction.", + "keywords": [ + "filesystem", + "asynchronous" + ], + "license": "MIT", + "authors": [ + { + "name": "Cees-Jan Kiewiet", + "email": "ceesjank@gmail.com" + } + ], + "require": { + "php": ">=7.4.0", + "react/event-loop": "^1.2", + "react/promise": "^2.8", + "react/promise-stream": "^1.2", + "react/stream": "^1.2", + "wyrihaximus/react-child-process-promise-closure": "^1.0" }, - "files": ["src/functions_include.php"] - }, - "autoload-dev": { - "psr-4": { - "React\\Tests\\Filesystem\\": "tests/" - } - }, - "config": { - "sort-packages": true, - "platform": { - "php": "5.4" + "require-dev": { + "clue/block-react": "^1.4", + "phpunit/phpunit": "^9.5" + }, + "autoload": { + "psr-4": { + "React\\Filesystem\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "React\\Tests\\Filesystem\\": "tests/" + } + }, + "suggest": { + "ext-uv": "* for better I/O performance" + }, + "config": { + "sort-packages": true, + "platform": { + "php": "7.4.7" + }, + "allow-plugins": { + "wyrihaximus/composer-update-bin-autoload-path": true + } } - } } diff --git a/composer.lock b/composer.lock index 04110066..b912387b 100644 --- a/composer.lock +++ b/composer.lock @@ -1,37 +1,98 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "0301b26ae1ada823f6fbeb0b71fc57bb", + "content-hash": "f5ce03ce451f08ea50b1130047d65bfa", "packages": [ + { + "name": "cakephp/core", + "version": "4.3.5", + "source": { + "type": "git", + "url": "https://github.com/cakephp/core.git", + "reference": "58f3af964f3e33769153d743a204f6e97f23c157" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/core/zipball/58f3af964f3e33769153d743a204f6e97f23c157", + "reference": "58f3af964f3e33769153d743a204f6e97f23c157", + "shasum": "" + }, + "require": { + "cakephp/utility": "^4.0", + "php": ">=7.2.0" + }, + "suggest": { + "cakephp/cache": "To use Configure::store() and restore().", + "cakephp/event": "To use PluginApplicationInterface or plugin applications.", + "league/container": "To use Container and ServiceProvider classes" + }, + "type": "library", + "autoload": { + "files": [ + "functions.php" + ], + "psr-4": { + "Cake\\Core\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/core/graphs/contributors" + } + ], + "description": "CakePHP Framework Core classes", + "homepage": "https://cakephp.org", + "keywords": [ + "cakephp", + "core", + "framework" + ], + "support": { + "forum": "https://stackoverflow.com/tags/cakephp", + "irc": "irc://irc.freenode.org/cakephp", + "issues": "https://github.com/cakephp/cakephp/issues", + "source": "https://github.com/cakephp/core" + }, + "time": "2022-01-27T02:05:53+00:00" + }, { "name": "cakephp/utility", - "version": "3.4.7", + "version": "4.3.5", "source": { "type": "git", "url": "https://github.com/cakephp/utility.git", - "reference": "f194ce6c83f54d96ce8ae0020b5f58fa850a8ef4" + "reference": "3d352060ca3e49c81c3fd2bdb092ee345d8f4e38" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/utility/zipball/f194ce6c83f54d96ce8ae0020b5f58fa850a8ef4", - "reference": "f194ce6c83f54d96ce8ae0020b5f58fa850a8ef4", + "url": "https://api.github.com/repos/cakephp/utility/zipball/3d352060ca3e49c81c3fd2bdb092ee345d8f4e38", + "reference": "3d352060ca3e49c81c3fd2bdb092ee345d8f4e38", "shasum": "" }, + "require": { + "cakephp/core": "^4.0", + "php": ">=7.2.0" + }, "suggest": { "ext-intl": "To use Text::transliterate() or Text::slug()", "lib-ICU": "To use Text::transliterate() or Text::slug()" }, "type": "library", "autoload": { - "psr-4": { - "Cake\\Utility\\": "." - }, "files": [ "bootstrap.php" - ] + ], + "psr-4": { + "Cake\\Utility\\": "." + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -40,41 +101,56 @@ "authors": [ { "name": "CakePHP Community", - "homepage": "https://cakephp.org" + "homepage": "https://github.com/cakephp/utility/graphs/contributors" } ], "description": "CakePHP Utility classes such as Inflector, String, Hash, and Security", - "time": "2017-04-18T04:05:20+00:00" + "homepage": "https://cakephp.org", + "keywords": [ + "cakephp", + "hash", + "inflector", + "security", + "string", + "utility" + ], + "support": { + "forum": "https://stackoverflow.com/tags/cakephp", + "irc": "irc://irc.freenode.org/cakephp", + "issues": "https://github.com/cakephp/cakephp/issues", + "source": "https://github.com/cakephp/utility" + }, + "time": "2022-01-28T18:02:00+00:00" }, { "name": "doctrine/inflector", - "version": "v1.1.0", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/doctrine/inflector.git", - "reference": "90b2128806bfde671b6952ab8bea493942c1fdae" + "reference": "8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/90b2128806bfde671b6952ab8bea493942c1fdae", - "reference": "90b2128806bfde671b6952ab8bea493942c1fdae", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89", + "reference": "8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89", "shasum": "" }, "require": { - "php": ">=5.3.2" + "php": "^7.2 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "4.*" + "doctrine/coding-standard": "^8.2", + "phpstan/phpstan": "^0.12", + "phpstan/phpstan-phpunit": "^0.12", + "phpstan/phpstan-strict-rules": "^0.12", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "vimeo/psalm": "^4.10" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, "autoload": { - "psr-0": { - "Doctrine\\Common\\Inflector\\": "lib/" + "psr-4": { + "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" } }, "notification-url": "https://packagist.org/downloads/", @@ -82,6 +158,10 @@ "MIT" ], "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, { "name": "Roman Borschel", "email": "roman@code-factory.org" @@ -90,10 +170,6 @@ "name": "Benjamin Eberlei", "email": "kontakt@beberlei.de" }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, { "name": "Jonathan Wage", "email": "jonwage@gmail.com" @@ -103,46 +179,67 @@ "email": "schmittjoh@gmail.com" } ], - "description": "Common String Manipulations with regard to casing and singular/plural rules.", - "homepage": "http://www.doctrine-project.org", + "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", + "homepage": "https://www.doctrine-project.org/projects/inflector.html", "keywords": [ "inflection", - "pluralize", - "singularize", - "string" + "inflector", + "lowercase", + "manipulation", + "php", + "plural", + "singular", + "strings", + "uppercase", + "words" + ], + "support": { + "issues": "https://github.com/doctrine/inflector/issues", + "source": "https://github.com/doctrine/inflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", + "type": "tidelift" + } ], - "time": "2015-11-06T14:35:42+00:00" + "time": "2021-10-22T20:16:43+00:00" }, { "name": "doctrine/instantiator", - "version": "1.0.5", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b", + "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b", "shasum": "" }, "require": { - "php": ">=5.3,<8.0-DEV" + "php": "^7.1 || ^8.0" }, "require-dev": { - "athletic/athletic": "~0.1.8", + "doctrine/coding-standard": "^8.0", "ext-pdo": "*", "ext-phar": "*", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~2.0" + "phpbench/phpbench": "^0.13 || 1.0.0-alpha2", + "phpstan/phpstan": "^0.12", + "phpstan/phpstan-phpunit": "^0.12", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, "autoload": { "psr-4": { "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" @@ -156,43 +253,56 @@ { "name": "Marco Pivetta", "email": "ocramius@gmail.com", - "homepage": "http://ocramius.github.com/" + "homepage": "https://ocramius.github.io/" } ], "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://github.com/doctrine/instantiator", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", "keywords": [ "constructor", "instantiate" ], - "time": "2015-06-14T21:17:01+00:00" + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.4.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2020-11-10T18:47:58+00:00" }, { "name": "evenement/evenement", - "version": "v2.1.0", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/igorw/evenement.git", - "reference": "6ba9a777870ab49f417e703229d53931ed40fd7a" + "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/igorw/evenement/zipball/6ba9a777870ab49f417e703229d53931ed40fd7a", - "reference": "6ba9a777870ab49f417e703229d53931ed40fd7a", + "url": "https://api.github.com/repos/igorw/evenement/zipball/531bfb9d15f8aa57454f5f0285b18bec903b8fb7", + "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7", "shasum": "" }, "require": { - "php": ">=5.4.0" + "php": ">=7.0" }, "require-dev": { - "phpunit/phpunit": "^6.0||^5.7||^4.8.35" + "phpunit/phpunit": "^6.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, "autoload": { "psr-0": { "Evenement": "src" @@ -213,7 +323,11 @@ "event-dispatcher", "event-emitter" ], - "time": "2017-07-17T17:39:19+00:00" + "support": { + "issues": "https://github.com/igorw/evenement/issues", + "source": "https://github.com/igorw/evenement/tree/master" + }, + "time": "2017-07-23T21:35:13+00:00" }, { "name": "indigophp/hash-compat", @@ -264,37 +378,103 @@ "hash_equals", "hash_pbkdf2" ], + "support": { + "issues": "https://github.com/indigophp/hash-compat/issues", + "source": "https://github.com/indigophp/hash-compat/tree/master" + }, + "abandoned": true, "time": "2015-08-22T07:03:35+00:00" }, + { + "name": "opis/closure", + "version": "3.6.3", + "source": { + "type": "git", + "url": "https://github.com/opis/closure.git", + "reference": "3d81e4309d2a927abbe66df935f4bb60082805ad" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/opis/closure/zipball/3d81e4309d2a927abbe66df935f4bb60082805ad", + "reference": "3d81e4309d2a927abbe66df935f4bb60082805ad", + "shasum": "" + }, + "require": { + "php": "^5.4 || ^7.0 || ^8.0" + }, + "require-dev": { + "jeremeamia/superclosure": "^2.0", + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.6.x-dev" + } + }, + "autoload": { + "psr-4": { + "Opis\\Closure\\": "src/" + }, + "files": [ + "functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marius Sarca", + "email": "marius.sarca@gmail.com" + }, + { + "name": "Sorin Sarca", + "email": "sarca_sorin@hotmail.com" + } + ], + "description": "A library that can be used to serialize closures (anonymous functions) and arbitrary objects.", + "homepage": "https://opis.io/closure", + "keywords": [ + "anonymous functions", + "closure", + "function", + "serializable", + "serialization", + "serialize" + ], + "support": { + "issues": "https://github.com/opis/closure/issues", + "source": "https://github.com/opis/closure/tree/3.6.3" + }, + "time": "2022-01-27T09:35:39+00:00" + }, { "name": "paragonie/random_compat", - "version": "v2.0.11", + "version": "v9.99.100", "source": { "type": "git", "url": "https://github.com/paragonie/random_compat.git", - "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8" + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/5da4d3c796c275c55f057af5a643ae297d96b4d8", - "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", "shasum": "" }, "require": { - "php": ">=5.2.0" + "php": ">= 7" }, "require-dev": { - "phpunit/phpunit": "4.*|5.*" + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" }, "suggest": { "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." }, "type": "library", - "autoload": { - "files": [ - "lib/random.php" - ] - }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" @@ -309,31 +489,37 @@ "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", "keywords": [ "csprng", + "polyfill", "pseudorandom", "random" ], - "time": "2017-09-27T21:40:39+00:00" + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/random_compat/issues", + "source": "https://github.com/paragonie/random_compat" + }, + "time": "2020-10-15T08:29:30+00:00" }, { "name": "react/cache", - "version": "v0.4.2", + "version": "v1.1.1", "source": { "type": "git", "url": "https://github.com/reactphp/cache.git", - "reference": "75494f26b4ef089db9bf8c90b63c296246e099e8" + "reference": "4bf736a2cccec7298bdf745db77585966fc2ca7e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/cache/zipball/75494f26b4ef089db9bf8c90b63c296246e099e8", - "reference": "75494f26b4ef089db9bf8c90b63c296246e099e8", + "url": "https://api.github.com/repos/reactphp/cache/zipball/4bf736a2cccec7298bdf745db77585966fc2ca7e", + "reference": "4bf736a2cccec7298bdf745db77585966fc2ca7e", "shasum": "" }, "require": { "php": ">=5.3.0", - "react/promise": "~2.0|~1.1" + "react/promise": "^3.0 || ^2.0 || ^1.1" }, "require-dev": { - "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" }, "type": "library", "autoload": { @@ -345,6 +531,28 @@ "license": [ "MIT" ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], "description": "Async, Promise-based cache interface for ReactPHP", "keywords": [ "cache", @@ -352,31 +560,46 @@ "promise", "reactphp" ], - "time": "2017-12-20T16:47:13+00:00" + "support": { + "issues": "https://github.com/reactphp/cache/issues", + "source": "https://github.com/reactphp/cache/tree/v1.1.1" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2021-02-02T06:47:52+00:00" }, { "name": "react/child-process", - "version": "v0.5.2", + "version": "v0.6.4", "source": { "type": "git", "url": "https://github.com/reactphp/child-process.git", - "reference": "aae49d7f1340bafb695b9af3ce4421ea41a39620" + "reference": "a778f3fb828d68caf8a9ab6567fd8342a86f12fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/child-process/zipball/aae49d7f1340bafb695b9af3ce4421ea41a39620", - "reference": "aae49d7f1340bafb695b9af3ce4421ea41a39620", + "url": "https://api.github.com/repos/reactphp/child-process/zipball/a778f3fb828d68caf8a9ab6567fd8342a86f12fe", + "reference": "a778f3fb828d68caf8a9ab6567fd8342a86f12fe", "shasum": "" }, "require": { "evenement/evenement": "^3.0 || ^2.0 || ^1.0", "php": ">=5.3.0", - "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", - "react/stream": "^1.0 || ^0.7.6" + "react/event-loop": "^1.2", + "react/stream": "^1.2" }, "require-dev": { - "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35", - "sebastian/environment": "^3.0 || ^2.0 || ^1.0" + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35", + "react/socket": "^1.8", + "sebastian/environment": "^5.0 || ^3.0 || ^2.0 || ^1.0" }, "type": "library", "autoload": { @@ -388,39 +611,74 @@ "license": [ "MIT" ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], "description": "Event-driven library for executing child processes with ReactPHP.", "keywords": [ "event-driven", "process", "reactphp" ], - "time": "2018-01-18T14:53:06+00:00" + "support": { + "issues": "https://github.com/reactphp/child-process/issues", + "source": "https://github.com/reactphp/child-process/tree/v0.6.4" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2021-10-12T10:37:07+00:00" }, { "name": "react/dns", - "version": "v0.4.13", + "version": "v1.9.0", "source": { "type": "git", "url": "https://github.com/reactphp/dns.git", - "reference": "7d1e08c300fd7de600810883386ee5e2a64898f4" + "reference": "6d38296756fa644e6cb1bfe95eff0f9a4ed6edcb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/dns/zipball/7d1e08c300fd7de600810883386ee5e2a64898f4", - "reference": "7d1e08c300fd7de600810883386ee5e2a64898f4", + "url": "https://api.github.com/repos/reactphp/dns/zipball/6d38296756fa644e6cb1bfe95eff0f9a4ed6edcb", + "reference": "6d38296756fa644e6cb1bfe95eff0f9a4ed6edcb", "shasum": "" }, "require": { "php": ">=5.3.0", - "react/cache": "~0.4.0|~0.3.0", - "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", - "react/promise": "^2.1 || ^1.2.1", - "react/promise-timer": "^1.2", - "react/stream": "^1.0 || ^0.7 || ^0.6 || ^0.5 || ^0.4.5" + "react/cache": "^1.0 || ^0.6 || ^0.5", + "react/event-loop": "^1.2", + "react/promise": "^3.0 || ^2.7 || ^1.2.1", + "react/promise-timer": "^1.8" }, "require-dev": { "clue/block-react": "^1.2", - "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" + "phpunit/phpunit": "^9.3 || ^4.8.35" }, "type": "library", "autoload": { @@ -432,6 +690,28 @@ "license": [ "MIT" ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], "description": "Async DNS resolver for ReactPHP", "keywords": [ "async", @@ -439,31 +719,46 @@ "dns-resolver", "reactphp" ], - "time": "2018-02-27T12:51:22+00:00" + "support": { + "issues": "https://github.com/reactphp/dns/issues", + "source": "https://github.com/reactphp/dns/tree/v1.9.0" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2021-12-20T08:46:54+00:00" }, { "name": "react/event-loop", - "version": "v0.5.2", + "version": "v1.2.0", "source": { "type": "git", "url": "https://github.com/reactphp/event-loop.git", - "reference": "e94985d93c689c554265b01014f8c3064921ca27" + "reference": "be6dee480fc4692cec0504e65eb486e3be1aa6f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/event-loop/zipball/e94985d93c689c554265b01014f8c3064921ca27", - "reference": "e94985d93c689c554265b01014f8c3064921ca27", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/be6dee480fc4692cec0504e65eb486e3be1aa6f2", + "reference": "be6dee480fc4692cec0504e65eb486e3be1aa6f2", "shasum": "" }, "require": { "php": ">=5.3.0" }, "require-dev": { - "phpunit/phpunit": "~4.8.35 || ^5.7 || ^6.4" + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" }, "suggest": { "ext-event": "~1.0 for ExtEventLoop", - "ext-pcntl": "For signal handling support when using the StreamSelectLoop" + "ext-pcntl": "For signal handling support when using the StreamSelectLoop", + "ext-uv": "* for ExtUvLoop" }, "type": "library", "autoload": { @@ -475,41 +770,77 @@ "license": [ "MIT" ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", "keywords": [ "asynchronous", "event-loop" ], - "time": "2018-04-24T11:23:06+00:00" + "support": { + "issues": "https://github.com/reactphp/event-loop/issues", + "source": "https://github.com/reactphp/event-loop/tree/v1.2.0" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2021-07-11T12:31:24+00:00" }, { "name": "react/promise", - "version": "v2.5.1", + "version": "v2.8.0", "source": { "type": "git", "url": "https://github.com/reactphp/promise.git", - "reference": "62785ae604c8d69725d693eb370e1d67e94c4053" + "reference": "f3cff96a19736714524ca0dd1d4130de73dbbbc4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/62785ae604c8d69725d693eb370e1d67e94c4053", - "reference": "62785ae604c8d69725d693eb370e1d67e94c4053", + "url": "https://api.github.com/repos/reactphp/promise/zipball/f3cff96a19736714524ca0dd1d4130de73dbbbc4", + "reference": "f3cff96a19736714524ca0dd1d4130de73dbbbc4", "shasum": "" }, "require": { "php": ">=5.4.0" }, "require-dev": { - "phpunit/phpunit": "~4.8" + "phpunit/phpunit": "^7.0 || ^6.5 || ^5.7 || ^4.8.36" }, "type": "library", "autoload": { - "psr-4": { - "React\\Promise\\": "src/" - }, "files": [ "src/functions_include.php" - ] + ], + "psr-4": { + "React\\Promise\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -526,30 +857,34 @@ "promise", "promises" ], - "time": "2017-03-25T12:08:31+00:00" + "support": { + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/v2.8.0" + }, + "time": "2020-05-12T15:16:56+00:00" }, { "name": "react/promise-stream", - "version": "v1.1.1", + "version": "v1.3.0", "source": { "type": "git", "url": "https://github.com/reactphp/promise-stream.git", - "reference": "00e269d611e9c9a29356aef64c07f7e513e73dc9" + "reference": "3ebd94fe0d8edbf44937948af28d02d5437e9949" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise-stream/zipball/00e269d611e9c9a29356aef64c07f7e513e73dc9", - "reference": "00e269d611e9c9a29356aef64c07f7e513e73dc9", + "url": "https://api.github.com/repos/reactphp/promise-stream/zipball/3ebd94fe0d8edbf44937948af28d02d5437e9949", + "reference": "3ebd94fe0d8edbf44937948af28d02d5437e9949", "shasum": "" }, "require": { "php": ">=5.3", "react/promise": "^2.1 || ^1.2", - "react/stream": "^1.0 || ^0.7 || ^0.6 || ^0.5 || ^0.4 || ^0.3" + "react/stream": "^1.0 || ^0.7 || ^0.6 || ^0.5 || ^0.4.6" }, "require-dev": { "clue/block-react": "^1.0", - "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35", + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35", "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3", "react/promise-timer": "^1.0" }, @@ -569,7 +904,23 @@ "authors": [ { "name": "Christian Lück", - "email": "christian@lueck.tv" + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], "description": "The missing link between Promise-land and Stream-land for ReactPHP", @@ -582,38 +933,52 @@ "stream", "unwrap" ], - "time": "2017-12-22T12:02:05+00:00" + "support": { + "issues": "https://github.com/reactphp/promise-stream/issues", + "source": "https://github.com/reactphp/promise-stream/tree/v1.3.0" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2021-10-18T10:47:09+00:00" }, { "name": "react/promise-timer", - "version": "v1.2.1", + "version": "v1.8.0", "source": { "type": "git", "url": "https://github.com/reactphp/promise-timer.git", - "reference": "9b4cd9cbe7457e0d87fe8aa7ccceab8a2c830fbd" + "reference": "0bbbcc79589e5bfdddba68a287f1cb805581a479" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise-timer/zipball/9b4cd9cbe7457e0d87fe8aa7ccceab8a2c830fbd", - "reference": "9b4cd9cbe7457e0d87fe8aa7ccceab8a2c830fbd", + "url": "https://api.github.com/repos/reactphp/promise-timer/zipball/0bbbcc79589e5bfdddba68a287f1cb805581a479", + "reference": "0bbbcc79589e5bfdddba68a287f1cb805581a479", "shasum": "" }, "require": { "php": ">=5.3", - "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", - "react/promise": "~2.1|~1.2" + "react/event-loop": "^1.2", + "react/promise": "^3.0 || ^2.7.0 || ^1.2.1" }, "require-dev": { - "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" }, "type": "library", "autoload": { + "files": [ + "src/functions_include.php" + ], "psr-4": { "React\\Promise\\Timer\\": "src/" - }, - "files": [ - "src/functions.php" - ] + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -622,11 +987,27 @@ "authors": [ { "name": "Christian Lück", - "email": "christian@lueck.tv" + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], "description": "A trivial implementation of timeouts for Promises, built on top of ReactPHP.", - "homepage": "https://github.com/react/promise-timer", + "homepage": "https://github.com/reactphp/promise-timer", "keywords": [ "async", "event-loop", @@ -635,34 +1016,49 @@ "timeout", "timer" ], - "time": "2017-12-22T15:41:41+00:00" + "support": { + "issues": "https://github.com/reactphp/promise-timer/issues", + "source": "https://github.com/reactphp/promise-timer/tree/v1.8.0" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2021-12-06T11:08:48+00:00" }, { "name": "react/socket", - "version": "v0.8.10", + "version": "v1.11.0", "source": { "type": "git", "url": "https://github.com/reactphp/socket.git", - "reference": "d3957313c92b539537fccc80170c05a27ec25796" + "reference": "f474156aaab4f09041144fa8b57c7d70aed32a1c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/socket/zipball/d3957313c92b539537fccc80170c05a27ec25796", - "reference": "d3957313c92b539537fccc80170c05a27ec25796", + "url": "https://api.github.com/repos/reactphp/socket/zipball/f474156aaab4f09041144fa8b57c7d70aed32a1c", + "reference": "f474156aaab4f09041144fa8b57c7d70aed32a1c", "shasum": "" }, "require": { "evenement/evenement": "^3.0 || ^2.0 || ^1.0", "php": ">=5.3.0", - "react/dns": "^0.4.13", - "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", - "react/promise": "^2.1 || ^1.2", - "react/promise-timer": "~1.0", - "react/stream": "^1.0 || ^0.7.1" + "react/dns": "^1.8", + "react/event-loop": "^1.2", + "react/promise": "^2.6.0 || ^1.2.1", + "react/promise-timer": "^1.8", + "react/stream": "^1.2" }, "require-dev": { - "clue/block-react": "^1.2", - "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" + "clue/block-react": "^1.5", + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35", + "react/promise-stream": "^1.2" }, "type": "library", "autoload": { @@ -674,38 +1070,74 @@ "license": [ "MIT" ], - "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", - "keywords": [ - "Connection", - "Socket", - "async", - "reactphp", - "stream" - ], - "time": "2018-02-28T09:32:38+00:00" - }, - { + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", + "keywords": [ + "Connection", + "Socket", + "async", + "reactphp", + "stream" + ], + "support": { + "issues": "https://github.com/reactphp/socket/issues", + "source": "https://github.com/reactphp/socket/tree/v1.11.0" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2022-01-14T10:14:32+00:00" + }, + { "name": "react/stream", - "version": "v0.7.7", + "version": "v1.2.0", "source": { "type": "git", "url": "https://github.com/reactphp/stream.git", - "reference": "10100896018fd847a257cd81143b8e1b7be08e40" + "reference": "7a423506ee1903e89f1e08ec5f0ed430ff784ae9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/stream/zipball/10100896018fd847a257cd81143b8e1b7be08e40", - "reference": "10100896018fd847a257cd81143b8e1b7be08e40", + "url": "https://api.github.com/repos/reactphp/stream/zipball/7a423506ee1903e89f1e08ec5f0ed430ff784ae9", + "reference": "7a423506ee1903e89f1e08ec5f0ed430ff784ae9", "shasum": "" }, "require": { "evenement/evenement": "^3.0 || ^2.0 || ^1.0", "php": ">=5.3.8", - "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5" + "react/event-loop": "^1.2" }, "require-dev": { "clue/stream-filter": "~1.2", - "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" }, "type": "library", "autoload": { @@ -717,6 +1149,28 @@ "license": [ "MIT" ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", "keywords": [ "event-driven", @@ -728,7 +1182,160 @@ "stream", "writable" ], - "time": "2018-01-19T15:04:38+00:00" + "support": { + "issues": "https://github.com/reactphp/stream/issues", + "source": "https://github.com/reactphp/stream/tree/v1.2.0" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2021-07-11T12:37:55+00:00" + }, + { + "name": "thecodingmachine/safe", + "version": "v1.3.3", + "source": { + "type": "git", + "url": "https://github.com/thecodingmachine/safe.git", + "reference": "a8ab0876305a4cdaef31b2350fcb9811b5608dbc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thecodingmachine/safe/zipball/a8ab0876305a4cdaef31b2350fcb9811b5608dbc", + "reference": "a8ab0876305a4cdaef31b2350fcb9811b5608dbc", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "require-dev": { + "phpstan/phpstan": "^0.12", + "squizlabs/php_codesniffer": "^3.2", + "thecodingmachine/phpstan-strict-rules": "^0.12" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.1-dev" + } + }, + "autoload": { + "files": [ + "deprecated/apc.php", + "deprecated/libevent.php", + "deprecated/mssql.php", + "deprecated/stats.php", + "lib/special_cases.php", + "generated/apache.php", + "generated/apcu.php", + "generated/array.php", + "generated/bzip2.php", + "generated/calendar.php", + "generated/classobj.php", + "generated/com.php", + "generated/cubrid.php", + "generated/curl.php", + "generated/datetime.php", + "generated/dir.php", + "generated/eio.php", + "generated/errorfunc.php", + "generated/exec.php", + "generated/fileinfo.php", + "generated/filesystem.php", + "generated/filter.php", + "generated/fpm.php", + "generated/ftp.php", + "generated/funchand.php", + "generated/gmp.php", + "generated/gnupg.php", + "generated/hash.php", + "generated/ibase.php", + "generated/ibmDb2.php", + "generated/iconv.php", + "generated/image.php", + "generated/imap.php", + "generated/info.php", + "generated/ingres-ii.php", + "generated/inotify.php", + "generated/json.php", + "generated/ldap.php", + "generated/libxml.php", + "generated/lzf.php", + "generated/mailparse.php", + "generated/mbstring.php", + "generated/misc.php", + "generated/msql.php", + "generated/mysql.php", + "generated/mysqli.php", + "generated/mysqlndMs.php", + "generated/mysqlndQc.php", + "generated/network.php", + "generated/oci8.php", + "generated/opcache.php", + "generated/openssl.php", + "generated/outcontrol.php", + "generated/password.php", + "generated/pcntl.php", + "generated/pcre.php", + "generated/pdf.php", + "generated/pgsql.php", + "generated/posix.php", + "generated/ps.php", + "generated/pspell.php", + "generated/readline.php", + "generated/rpminfo.php", + "generated/rrd.php", + "generated/sem.php", + "generated/session.php", + "generated/shmop.php", + "generated/simplexml.php", + "generated/sockets.php", + "generated/sodium.php", + "generated/solr.php", + "generated/spl.php", + "generated/sqlsrv.php", + "generated/ssdeep.php", + "generated/ssh2.php", + "generated/stream.php", + "generated/strings.php", + "generated/swoole.php", + "generated/uodbc.php", + "generated/uopz.php", + "generated/url.php", + "generated/var.php", + "generated/xdiff.php", + "generated/xml.php", + "generated/xmlrpc.php", + "generated/yaml.php", + "generated/yaz.php", + "generated/zip.php", + "generated/zlib.php" + ], + "psr-4": { + "Safe\\": [ + "lib/", + "deprecated/", + "generated/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHP core functions that throw exceptions instead of returning FALSE on error", + "support": { + "issues": "https://github.com/thecodingmachine/safe/issues", + "source": "https://github.com/thecodingmachine/safe/tree/v1.3.3" + }, + "time": "2020-10-28T17:51:34+00:00" }, { "name": "tivie/php-os-detector", @@ -776,79 +1383,185 @@ "os", "os detection" ], + "support": { + "issues": "https://github.com/tivie/php-os-detector/issues", + "source": "https://github.com/tivie/php-os-detector/tree/master" + }, "time": "2017-10-21T03:33:59+00:00" }, { - "name": "wyrihaximus/cpu-core-detector", - "version": "1.0.1", + "name": "wyrihaximus/composer-update-bin-autoload-path", + "version": "1.1.1", "source": { "type": "git", - "url": "https://github.com/WyriHaximus/php-cpu-core-detector.git", - "reference": "459dbf380172f06de6bf1aaecb9b1a003f7937b1" + "url": "https://github.com/WyriHaximus/php-composer-update-bin-autoload-path.git", + "reference": "33413e3af4f4d7ab4de3653a706aed57f51e84af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/WyriHaximus/php-cpu-core-detector/zipball/459dbf380172f06de6bf1aaecb9b1a003f7937b1", - "reference": "459dbf380172f06de6bf1aaecb9b1a003f7937b1", + "url": "https://api.github.com/repos/WyriHaximus/php-composer-update-bin-autoload-path/zipball/33413e3af4f4d7ab4de3653a706aed57f51e84af", + "reference": "33413e3af4f4d7ab4de3653a706aed57f51e84af", "shasum": "" }, "require": { - "php": "^5.4||^7.0", - "react/child-process": "^0.5 || ^0.4", - "tivie/php-os-detector": "^1.0", - "wyrihaximus/react-child-process-promise": "^2.0", - "wyrihaximus/ticking-promise": "^1.5" + "composer-plugin-api": "^2", + "php": "^8 || ^7.4" }, "require-dev": { - "phake/phake": "~1.0.6", - "phpunit/phpunit": "^4.0||^5.0", - "squizlabs/php_codesniffer": "^1.5.6", - "vectorface/dunit": "~2.0" + "wyrihaximus/test-utilities": "^3" + }, + "type": "composer-plugin", + "extra": { + "class": "WyriHaximus\\Composer\\BinAutoloadPathUpdater", + "unused": [ + "php" + ] }, - "type": "library", "autoload": { "psr-4": { - "WyriHaximus\\CpuCoreDetector\\": "src/" - }, + "WyriHaximus\\Composer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "🏰 Composer plugin that fills a bin file with the absolute composer autoload path", + "support": { + "issues": "https://github.com/WyriHaximus/php-composer-update-bin-autoload-path/issues", + "source": "https://github.com/WyriHaximus/php-composer-update-bin-autoload-path/tree/1.1.1" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + } + ], + "time": "2021-03-14T20:55:38+00:00" + }, + { + "name": "wyrihaximus/constants", + "version": "1.6.0", + "source": { + "type": "git", + "url": "https://github.com/WyriHaximus/php-constants.git", + "reference": "32ceffdd881593c7fa24d8fcbf9deb58687484cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/WyriHaximus/php-constants/zipball/32ceffdd881593c7fa24d8fcbf9deb58687484cb", + "reference": "32ceffdd881593c7fa24d8fcbf9deb58687484cb", + "shasum": "" + }, + "require": { + "php": "^8 || ^7 || ^5.3" + }, + "type": "library", + "autoload": { "files": [ - "src/functions_include.php" + "src/Boolean/constants_include.php", + "src/ComposerAutoloader/constants_include.php", + "src/HTTPStatusCodes/constants_include.php", + "src/Numeric/constants_include.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], + "authors": [ + { + "name": "Cees-Jan Kiewiet", + "email": "ceesjank@gmail.com", + "homepage": "https://www.wyrihaximus.net/" + } + ], + "description": "Collection of constants for PHP", + "support": { + "issues": "https://github.com/WyriHaximus/php-constants/issues", + "source": "https://github.com/WyriHaximus/php-constants/tree/1.6.0" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + } + ], + "time": "2020-11-28T12:04:43+00:00" + }, + { + "name": "wyrihaximus/file-descriptors", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/WyriHaximus/php-file-descriptors.git", + "reference": "7e2a8330c6dfe535a597e8a317b31dddf3b49398" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/WyriHaximus/php-file-descriptors/zipball/7e2a8330c6dfe535a597e8a317b31dddf3b49398", + "reference": "7e2a8330c6dfe535a597e8a317b31dddf3b49398", + "shasum": "" + }, + "require": { + "php": "^8 || ^7.4", + "tivie/php-os-detector": "^1.1" + }, + "require-dev": { + "wyrihaximus/iterator-or-array-to-array": "^1.1", + "wyrihaximus/test-utilities": "^3.3.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "WyriHaximus\\FileDescriptors\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], "authors": [ { "name": "Cees-Jan Kiewiet", "email": "ceesjank@gmail.com" } ], - "time": "2018-02-24T17:44:46+00:00" + "description": "List open file descriptors for the current process cross platform", + "support": { + "issues": "https://github.com/WyriHaximus/php-file-descriptors/issues", + "source": "https://github.com/WyriHaximus/php-file-descriptors/tree/1.1.0" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + } + ], + "time": "2020-11-25T23:03:56+00:00" }, { "name": "wyrihaximus/json-throwable", - "version": "1.1.1", + "version": "4.1.0", "source": { "type": "git", "url": "https://github.com/WyriHaximus/php-json-throwable.git", - "reference": "fc3588681dcccc3f1f2f94c8eca71687f0899340" + "reference": "d01d59a101d4d2639ec3be770646a7e6b168fafb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/WyriHaximus/php-json-throwable/zipball/fc3588681dcccc3f1f2f94c8eca71687f0899340", - "reference": "fc3588681dcccc3f1f2f94c8eca71687f0899340", + "url": "https://api.github.com/repos/WyriHaximus/php-json-throwable/zipball/d01d59a101d4d2639ec3be770646a7e6b168fafb", + "reference": "d01d59a101d4d2639ec3be770646a7e6b168fafb", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.5", - "php": "^7.0 || ^5.4" + "php": "^8 || ^7.4", + "thecodingmachine/safe": "^1.1", + "wyrihaximus/json-utilities": "^1.2" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^2.2", - "jakub-onderka/php-console-highlighter": "^0.3.2", - "jakub-onderka/php-parallel-lint": "^0.9.2", - "phpunit/phpunit": "^4.8.35 || ^5.0 || ^6.0" + "wyrihaximus/test-utilities": "^3.3.1" }, "type": "library", "autoload": { @@ -869,49 +1582,48 @@ "email": "ceesjank@gmail.com" } ], - "description": "JSON encode and decode throwables and exceptions", - "time": "2018-03-12T21:39:58+00:00" + "description": "📠 JSON encode and decode throwables and exceptions", + "support": { + "issues": "https://github.com/WyriHaximus/php-json-throwable/issues", + "source": "https://github.com/WyriHaximus/php-json-throwable/tree/4.1.0" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + } + ], + "time": "2020-11-25T13:14:10+00:00" }, { - "name": "wyrihaximus/react-child-process-messenger", - "version": "2.8.1", + "name": "wyrihaximus/json-utilities", + "version": "1.3.0", "source": { "type": "git", - "url": "https://github.com/WyriHaximus/reactphp-child-process-messenger.git", - "reference": "afde926a729868f5d6f64f378836123c3b247dea" + "url": "https://github.com/WyriHaximus/php-json-utilities.git", + "reference": "dce46df18c70ab7e206fe7bda4f4c59506f17be6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/WyriHaximus/reactphp-child-process-messenger/zipball/afde926a729868f5d6f64f378836123c3b247dea", - "reference": "afde926a729868f5d6f64f378836123c3b247dea", + "url": "https://api.github.com/repos/WyriHaximus/php-json-utilities/zipball/dce46df18c70ab7e206fe7bda4f4c59506f17be6", + "reference": "dce46df18c70ab7e206fe7bda4f4c59506f17be6", "shasum": "" }, "require": { - "cakephp/utility": "^3.4", - "doctrine/inflector": "^1.0", - "evenement/evenement": "^3.0 || ^2.0 || ^1.0", - "indigophp/hash-compat": "^1.0", - "paragonie/random_compat": "^2.0", - "php": "^5.4||^7.0", - "react/child-process": "^0.5 || ^0.4", - "react/promise": "^2.2", - "react/promise-stream": "^1.1", - "react/socket": "^0.8.1", - "wyrihaximus/json-throwable": "^2.0 || ^1.1.1", - "wyrihaximus/ticking-promise": "^1.4" + "php": "^8 || ^7.4", + "thecodingmachine/safe": "^1.1" }, "require-dev": { - "clue/block-react": "^1.2", - "friendsofphp/php-cs-fixer": "^2.2", - "jakub-onderka/php-console-highlighter": "^0.3.2", - "jakub-onderka/php-parallel-lint": "^0.9.2", - "phpunit/phpunit": "^4.8.35||^5.0||^6.0" + "wyrihaximus/test-utilities": "^3.3.1" }, "type": "library", "autoload": { "psr-4": { - "WyriHaximus\\React\\ChildProcess\\Messenger\\": "src/" - } + "WyriHaximus\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -920,52 +1632,51 @@ "authors": [ { "name": "Cees-Jan Kiewiet", - "email": "ceesjank@gmail.com", - "homepage": "http://wyrihaximus.net/" + "email": "ceesjank@gmail.com" } ], - "description": "Messenger decorator for react/child-process", - "time": "2018-03-23T21:26:12+00:00" + "description": "Utilities for php-json-* packages", + "support": { + "issues": "https://github.com/WyriHaximus/php-json-utilities/issues", + "source": "https://github.com/WyriHaximus/php-json-utilities/tree/1.3.0" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + } + ], + "time": "2020-11-25T11:27:40+00:00" }, { - "name": "wyrihaximus/react-child-process-pool", - "version": "1.4.2", + "name": "wyrihaximus/react-child-process-closure", + "version": "1.2.0", "source": { "type": "git", - "url": "https://github.com/WyriHaximus/reactphp-child-process-pool.git", - "reference": "232f2aef55ce6df816f837442e79ff2976ebc8ed" + "url": "https://github.com/WyriHaximus/reactphp-child-process-closure.git", + "reference": "6dfe8a535f86326315eaa545e20f5139e3b2d428" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/WyriHaximus/reactphp-child-process-pool/zipball/232f2aef55ce6df816f837442e79ff2976ebc8ed", - "reference": "232f2aef55ce6df816f837442e79ff2976ebc8ed", + "url": "https://api.github.com/repos/WyriHaximus/reactphp-child-process-closure/zipball/6dfe8a535f86326315eaa545e20f5139e3b2d428", + "reference": "6dfe8a535f86326315eaa545e20f5139e3b2d428", "shasum": "" }, "require": { - "evenement/evenement": "^3.0 || ^2.0", - "php": "^5.4||^7.0", - "wyrihaximus/cpu-core-detector": "^1.0.0", - "wyrihaximus/react-child-process-messenger": "^2.7.1", - "wyrihaximus/ticking-promise": "^1.5" + "opis/closure": "^3.5", + "php": "^8 || ^7.4", + "react/event-loop": "^1.2", + "react/promise": "^2.8", + "wyrihaximus/react-child-process-messenger": "^4.0" }, "require-dev": { - "clue/block-react": "^1.1", - "phake/phake": "^2.2.1", - "phpunit/phpunit": "^4.8.35||^5.0", - "squizlabs/php_codesniffer": "^1.5.6", - "vectorface/dunit": "~2.0" - }, - "suggest": { - "wyrihaximus/react-child-process-pool-redis-queue": "Redis RPC queue" + "wyrihaximus/async-test-utilities": "^4.0.4" }, "type": "library", "autoload": { "psr-4": { - "WyriHaximus\\React\\ChildProcess\\Pool\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] + "WyriHaximus\\React\\ChildProcess\\Closure\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -977,33 +1688,120 @@ "email": "ceesjank@gmail.com" } ], - "time": "2018-04-23T17:21:00+00:00" + "description": "Run closures in ReactPHP Child Processes", + "support": { + "issues": "https://github.com/WyriHaximus/reactphp-child-process-closure/issues", + "source": "https://github.com/WyriHaximus/reactphp-child-process-closure/tree/1.2.0" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + } + ], + "time": "2021-09-15T06:56:15+00:00" + }, + { + "name": "wyrihaximus/react-child-process-messenger", + "version": "4.0.2", + "source": { + "type": "git", + "url": "https://github.com/WyriHaximus/reactphp-child-process-messenger.git", + "reference": "a484664b3a6b4a8e16551d8a139017d03db2b6f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/WyriHaximus/reactphp-child-process-messenger/zipball/a484664b3a6b4a8e16551d8a139017d03db2b6f9", + "reference": "a484664b3a6b4a8e16551d8a139017d03db2b6f9", + "shasum": "" + }, + "require": { + "cakephp/utility": "^4.2.4", + "doctrine/inflector": "^2.0.3", + "evenement/evenement": "^3.0.1", + "ext-hash": "^8 || ^7.4", + "ext-json": "^8 || ^7.4", + "indigophp/hash-compat": "^1.1", + "paragonie/random_compat": "^9.0 || ^2.0", + "php": "^8 || ^7.4", + "react/child-process": "^0.6.2", + "react/event-loop": "^1.1.1", + "react/promise": "^2.8", + "react/promise-stream": "^1.2", + "react/promise-timer": "^1.6", + "react/socket": "^1.6", + "thecodingmachine/safe": "^1.3.3", + "wyrihaximus/composer-update-bin-autoload-path": "^1.1.1", + "wyrihaximus/constants": "^1.6", + "wyrihaximus/file-descriptors": "^1.1", + "wyrihaximus/json-throwable": "^4.1", + "wyrihaximus/ticking-promise": "^2" + }, + "require-dev": { + "wyrihaximus/async-test-utilities": "^3.4.18" + }, + "type": "library", + "extra": { + "unused": [ + "php", + "react/promise-stream" + ], + "wyrihaximus": { + "bin-autoload-path-update": [ + "bin/child-process" + ] + } + }, + "autoload": { + "psr-4": { + "WyriHaximus\\React\\ChildProcess\\Messenger\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Cees-Jan Kiewiet", + "email": "ceesjank@gmail.com", + "homepage": "http://wyrihaximus.net/" + } + ], + "description": "Messenger decorator for react/child-process", + "support": { + "issues": "https://github.com/WyriHaximus/reactphp-child-process-messenger/issues", + "source": "https://github.com/WyriHaximus/reactphp-child-process-messenger/tree/4.0.2" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + } + ], + "time": "2022-02-08T15:53:16+00:00" }, { - "name": "wyrihaximus/react-child-process-promise", - "version": "2.0.1", + "name": "wyrihaximus/react-child-process-promise-closure", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/WyriHaximus/reactphp-child-process-promise.git", - "reference": "9b6f1bace7af43afc79fa85b91c793cdb3ff199b" + "url": "https://github.com/WyriHaximus/reactphp-child-process-promise-closure.git", + "reference": "9d8f19feccc6b1b2d20334c916339e3816d9174f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/WyriHaximus/reactphp-child-process-promise/zipball/9b6f1bace7af43afc79fa85b91c793cdb3ff199b", - "reference": "9b6f1bace7af43afc79fa85b91c793cdb3ff199b", + "url": "https://api.github.com/repos/WyriHaximus/reactphp-child-process-promise-closure/zipball/9d8f19feccc6b1b2d20334c916339e3816d9174f", + "reference": "9d8f19feccc6b1b2d20334c916339e3816d9174f", "shasum": "" }, "require": { - "php": "^5.4||^7.0", - "react/child-process": "^0.5 || ^0.4", - "react/promise": "^2.2", - "wyrihaximus/ticking-promise": "^1.5.2" + "php": "^7.0", + "react/promise": "^2.5", + "wyrihaximus/react-child-process-closure": "^1.0" }, "require-dev": { - "phake/phake": "^2.1", - "phpunit/phpunit": "^4.4||^5.0", - "squizlabs/php_codesniffer": "^1.5.6", - "vectorface/dunit": "^2.0" + "api-clients/test-utilities": "^4.1" }, "type": "library", "autoload": { @@ -1021,36 +1819,36 @@ "authors": [ { "name": "Cees-Jan Kiewiet", - "email": "ceesjank@gmail.com", - "homepage": "http://wyrihaximus.net/" + "email": "ceesjank@gmail.com" } ], - "description": "Wrapping ticks into a promise", - "time": "2017-10-17T11:37:15+00:00" + "support": { + "issues": "https://github.com/WyriHaximus/reactphp-child-process-promise-closure/issues", + "source": "https://github.com/WyriHaximus/reactphp-child-process-promise-closure/tree/master" + }, + "time": "2017-05-28T19:18:35+00:00" }, { "name": "wyrihaximus/ticking-promise", - "version": "1.6.3", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/WyriHaximus/TickingPromise.git", - "reference": "4bb99024402bb7526de8880f3dab0c1f0858def5" + "reference": "d3903d4bebe8e3c5b11464c0bb81802cdeeb3751" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/WyriHaximus/TickingPromise/zipball/4bb99024402bb7526de8880f3dab0c1f0858def5", - "reference": "4bb99024402bb7526de8880f3dab0c1f0858def5", + "url": "https://api.github.com/repos/WyriHaximus/TickingPromise/zipball/d3903d4bebe8e3c5b11464c0bb81802cdeeb3751", + "reference": "d3903d4bebe8e3c5b11464c0bb81802cdeeb3751", "shasum": "" }, "require": { - "php": "^7.0 || ^5.4", - "react/event-loop": "^1.0 || ^0.5 || ^0.4", - "react/promise": "~2.1" + "php": "^8 || ^7.4", + "react/event-loop": "^1.0", + "react/promise": "^2.8" }, "require-dev": { - "phpunit/phpunit": "~4.4", - "squizlabs/php_codesniffer": "^1.5.6", - "vectorface/dunit": "~1.4" + "wyrihaximus/async-test-utilities": "^3" }, "type": "library", "autoload": { @@ -1073,37 +1871,48 @@ } ], "description": "Wrapping ticks into a promise", - "time": "2018-04-05T12:36:50+00:00" + "support": { + "issues": "https://github.com/WyriHaximus/TickingPromise/issues", + "source": "https://github.com/WyriHaximus/TickingPromise/tree/2.1.0" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + } + ], + "time": "2020-11-25T12:43:21+00:00" } ], "packages-dev": [ { "name": "clue/block-react", - "version": "v1.2.0", + "version": "v1.5.0", "source": { "type": "git", - "url": "https://github.com/clue/php-block-react.git", - "reference": "966c255580ec7a0259338798ddb89f77e121fe9e" + "url": "https://github.com/clue/reactphp-block.git", + "reference": "718b0571a94aa693c6fffc72182e87257ac900f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/clue/php-block-react/zipball/966c255580ec7a0259338798ddb89f77e121fe9e", - "reference": "966c255580ec7a0259338798ddb89f77e121fe9e", + "url": "https://api.github.com/repos/clue/reactphp-block/zipball/718b0571a94aa693c6fffc72182e87257ac900f3", + "reference": "718b0571a94aa693c6fffc72182e87257ac900f3", "shasum": "" }, "require": { "php": ">=5.3", - "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", - "react/promise": "~2.1|~1.2", - "react/promise-timer": "~1.0" + "react/event-loop": "^1.2", + "react/promise": "^3.0 || ^2.7 || ^1.2.1", + "react/promise-timer": "^1.5" }, "require-dev": { - "phpunit/phpunit": "^5.0 || ^4.8" + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35", + "react/http": "^1.4" }, "type": "library", "autoload": { "files": [ - "src/functions.php" + "src/functions_include.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -1113,104 +1922,453 @@ "authors": [ { "name": "Christian Lück", - "email": "christian@lueck.tv" + "email": "christian@clue.engineering" } ], - "description": "Integrate async React PHP components into your blocking environment", - "homepage": "https://github.com/clue/php-block-react", + "description": "Lightweight library that eases integrating async components built for ReactPHP in a traditional, blocking environment.", + "homepage": "https://github.com/clue/reactphp-block", "keywords": [ "async", + "await", "blocking", "event loop", "promise", "reactphp", + "sleep", "synchronous" ], - "time": "2017-08-03T13:14:15+00:00" + "support": { + "issues": "https://github.com/clue/reactphp-block/issues", + "source": "https://github.com/clue/reactphp-block/tree/v1.5.0" + }, + "funding": [ + { + "url": "https://clue.engineering/support", + "type": "custom" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2021-10-20T14:07:33+00:00" }, { - "name": "phpdocumentor/reflection-docblock", - "version": "2.0.5", + "name": "myclabs/deep-copy", + "version": "1.10.2", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "e6a969a640b00d8daa3c66518b0405fb41ae0c4b" + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/e6a969a640b00d8daa3c66518b0405fb41ae0c4b", - "reference": "e6a969a640b00d8daa3c66518b0405fb41ae0c4b", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220", + "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^7.1 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "~4.0" - }, - "suggest": { - "dflydev/markdown": "~1.0", - "erusev/parsedown": "~1.0" + "doctrine/collections": "^1.0", + "doctrine/common": "^2.6", + "phpunit/phpunit": "^7.1" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, "autoload": { - "psr-0": { - "phpDocumentor": [ - "src/" - ] + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "Mike van Riel", - "email": "mike.vanriel@naenius.com" + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2020-11-13T09:40:50+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.13.2", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "210577fe3cf7badcc5814d99455df46564f3c077" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/210577fe3cf7badcc5814d99455df46564f3c077", + "reference": "210577fe3cf7badcc5814d99455df46564f3c077", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.2" + }, + "time": "2021-11-30T19:35:32+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" + }, + "time": "2021-07-20T11:28:43+00:00" + }, + { + "name": "phar-io/version", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "15a90844ad40f127afd244c0cad228de2a80052a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/15a90844ad40f127afd244c0cad228de2a80052a", + "reference": "15a90844ad40f127afd244c0cad228de2a80052a", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.1.1" + }, + "time": "2022-02-07T21:56:48+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + }, + "time": "2021-10-19T17:43:47+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.6.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/93ebd0014cab80c4ea9f5e297ea48672f1b87706", + "reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.0" + }, + "require-dev": { + "ext-tokenizer": "*", + "psalm/phar": "^4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" } ], - "time": "2016-01-25T08:17:30+00:00" + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.0" + }, + "time": "2022-01-04T19:58:01+00:00" }, { "name": "phpspec/prophecy", - "version": "1.7.5", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401" + "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/dfd6be44111a7c41c2e884a336cc4f461b3b2401", - "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/bbcd7380b0ebf3961ee21409db7b38bc31d69a13", + "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", - "sebastian/comparator": "^1.1|^2.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0" + "doctrine/instantiator": "^1.2", + "php": "^7.2 || ~8.0, <8.2", + "phpdocumentor/reflection-docblock": "^5.2", + "sebastian/comparator": "^3.0 || ^4.0", + "sebastian/recursion-context": "^3.0 || ^4.0" }, "require-dev": { - "phpspec/phpspec": "^2.5|^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" + "phpspec/phpspec": "^6.0 || ^7.0", + "phpunit/phpunit": "^8.0 || ^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7.x-dev" + "dev-master": "1.x-dev" } }, "autoload": { - "psr-0": { - "Prophecy\\": "src/" + "psr-4": { + "Prophecy\\": "src/Prophecy" } }, "notification-url": "https://packagist.org/downloads/", @@ -1238,43 +2396,52 @@ "spy", "stub" ], - "time": "2018-02-19T10:16:54+00:00" + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/v1.15.0" + }, + "time": "2021-12-08T12:19:24+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "2.2.4", + "version": "9.2.10", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979" + "reference": "d5850aaf931743067f4bfc1ae4cbd06468400687" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979", - "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d5850aaf931743067f4bfc1ae4cbd06468400687", + "reference": "d5850aaf931743067f4bfc1ae4cbd06468400687", "shasum": "" }, "require": { - "php": ">=5.3.3", - "phpunit/php-file-iterator": "~1.3", - "phpunit/php-text-template": "~1.2", - "phpunit/php-token-stream": "~1.3", - "sebastian/environment": "^1.3.2", - "sebastian/version": "~1.0" + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.13.0", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" }, "require-dev": { - "ext-xdebug": ">=2.1.4", - "phpunit/phpunit": "~4" + "phpunit/phpunit": "^9.3" }, "suggest": { - "ext-dom": "*", - "ext-xdebug": ">=2.2.1", - "ext-xmlwriter": "*" + "ext-pcov": "*", + "ext-xdebug": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.2.x-dev" + "dev-master": "9.2-dev" } }, "autoload": { @@ -1289,7 +2456,7 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], @@ -1300,29 +2467,42 @@ "testing", "xunit" ], - "time": "2015-10-06T15:47:00+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.10" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-05T09:12:13+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "1.4.5", + "version": "3.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1337,7 +2517,7 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], @@ -1347,26 +2527,48 @@ "filesystem", "iterator" ], - "time": "2017-11-27T13:52:08+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" }, { - "name": "phpunit/php-text-template", - "version": "1.2.1", + "name": "phpunit/php-invoker", + "version": "3.1.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, "autoload": { "classmap": [ "src/" @@ -1383,37 +2585,697 @@ "role": "lead" } ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", "keywords": [ - "template" + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.5.13", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "597cb647654ede35e43b137926dfdfef0fb11743" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/597cb647654ede35e43b137926dfdfef0fb11743", + "reference": "597cb647654ede35e43b137926dfdfef0fb11743", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.3.1", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=7.3", + "phpspec/prophecy": "^1.12.1", + "phpunit/php-code-coverage": "^9.2.7", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.5", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.3", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^2.3.4", + "sebastian/version": "^3.0.2" + }, + "require-dev": { + "ext-pdo": "*", + "phpspec/prophecy-phpunit": "^2.0.1" + }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.13" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-01-24T07:33:35+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:08:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "55f4261989e546dc112258c7a75935a81a7ce382" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382", + "reference": "55f4261989e546dc112258c7a75935a81a7ce382", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:49:45+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.7", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:52:27+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:10:38+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "388b6ced16caa751030f6a69e588299fa09200ac" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/388b6ced16caa751030f6a69e588299fa09200ac", + "reference": "388b6ced16caa751030f6a69e588299fa09200ac", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2015-06-21T13:50:34+00:00" + "time": "2020-09-28T05:52:38+00:00" }, { - "name": "phpunit/php-timer", - "version": "1.0.9", + "name": "sebastian/exporter", + "version": "4.0.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/65e8b7db476c5dd267e65eea9cab77584d3cfff9", + "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -1428,42 +3290,73 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", "keywords": [ - "timer" + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2017-02-26T11:10:40+00:00" + "time": "2021-11-11T14:18:36+00:00" }, { - "name": "phpunit/php-token-stream", - "version": "1.4.12", + "name": "sebastian/global-state", + "version": "5.0.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16" + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/1ce90ba27c42e4e44e6d8458241466380b51fa16", - "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/23bd5951f7ff26f12d4e3242864df3e08dec4e49", + "reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49", "shasum": "" }, "require": { - "ext-tokenizer": "*", - "php": ">=5.3.3" + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" }, "require-dev": { - "phpunit/phpunit": "~4.2" + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -1481,58 +3374,48 @@ "email": "sebastian@phpunit.de" } ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", "keywords": [ - "tokenizer" + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2017-12-04T08:55:13+00:00" + "time": "2021-06-11T13:31:12+00:00" }, { - "name": "phpunit/phpunit", - "version": "4.8.36", + "name": "sebastian/lines-of-code", + "version": "1.0.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "46023de9a91eec7dfb06cc56cb4e260017298517" + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/46023de9a91eec7dfb06cc56cb4e260017298517", - "reference": "46023de9a91eec7dfb06cc56cb4e260017298517", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-json": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-spl": "*", - "php": ">=5.3.3", - "phpspec/prophecy": "^1.3.1", - "phpunit/php-code-coverage": "~2.1", - "phpunit/php-file-iterator": "~1.4", - "phpunit/php-text-template": "~1.2", - "phpunit/php-timer": "^1.0.6", - "phpunit/phpunit-mock-objects": "~2.3", - "sebastian/comparator": "~1.2.2", - "sebastian/diff": "~1.2", - "sebastian/environment": "~1.3", - "sebastian/exporter": "~1.2", - "sebastian/global-state": "~1.0", - "sebastian/version": "~1.0", - "symfony/yaml": "~2.1|~3.0" + "nikic/php-parser": "^4.6", + "php": ">=7.3" }, - "suggest": { - "phpunit/php-invoker": "~1.1" + "require-dev": { + "phpunit/phpunit": "^9.3" }, - "bin": [ - "phpunit" - ], "type": "library", "extra": { "branch-alias": { - "dev-master": "4.8.x-dev" + "dev-master": "1.0-dev" } }, "autoload": { @@ -1551,45 +3434,46 @@ "role": "lead" } ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2017-06-21T08:07:12+00:00" + "time": "2020-11-28T06:42:11+00:00" }, { - "name": "phpunit/phpunit-mock-objects", - "version": "2.3.8", + "name": "sebastian/object-enumerator", + "version": "4.0.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983" + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983", - "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.0.2", - "php": ">=5.3.3", - "phpunit/php-text-template": "~1.2", - "sebastian/exporter": "~1.2" + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" }, "require-dev": { - "phpunit/phpunit": "~4.4" - }, - "suggest": { - "ext-soap": "*" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.3.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -1604,44 +3488,47 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" + "email": "sebastian@phpunit.de" } ], - "description": "Mock Object library for PHPUnit", - "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", - "keywords": [ - "mock", - "xunit" + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2015-10-02T06:51:40+00:00" + "time": "2020-10-26T13:12:34+00:00" }, { - "name": "sebastian/comparator", - "version": "1.2.4", + "name": "sebastian/object-reflector", + "version": "2.0.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be" + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", - "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", "shasum": "" }, "require": { - "php": ">=5.3.3", - "sebastian/diff": "~1.2", - "sebastian/exporter": "~1.2 || ~2.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "~4.4" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -1654,56 +3541,49 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "http://www.github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2017-01-29T09:50:25+00:00" + "time": "2020-10-26T13:14:26+00:00" }, { - "name": "sebastian/diff", - "version": "1.4.3", + "name": "sebastian/recursion-context", + "version": "4.0.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4" + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4", - "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -1716,46 +3596,57 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" } ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff" + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2017-05-22T07:24:03+00:00" + "time": "2020-10-26T13:17:30+00:00" }, { - "name": "sebastian/environment", - "version": "1.3.8", + "name": "sebastian/resource-operations", + "version": "3.0.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea" + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/be2c607e43ce4c89ecd60e75c6a85c126e754aea", - "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^4.8 || ^5.0" + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1773,41 +3664,44 @@ "email": "sebastian@phpunit.de" } ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2016-08-18T05:49:44+00:00" + "time": "2020-09-28T06:45:17+00:00" }, { - "name": "sebastian/exporter", - "version": "1.2.2", + "name": "sebastian/type", + "version": "2.3.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4" + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/42c4c2eec485ee3e159ec9884f95b431287edde4", - "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b8cd8a1c753c90bc1a0f5372170e3e489136f914", + "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914", "shasum": "" }, "require": { - "php": ">=5.3.3", - "sebastian/recursion-context": "~1.0" + "php": ">=7.3" }, "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "~4.4" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3.x-dev" + "dev-master": "2.3-dev" } }, "autoload": { @@ -1820,62 +3714,47 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/2.3.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2016-06-17T09:04:28+00:00" + "time": "2021-06-15T12:49:02+00:00" }, { - "name": "sebastian/global-state", - "version": "1.1.1", + "name": "sebastian/version", + "version": "3.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", - "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", "shasum": "" }, "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.2" - }, - "suggest": { - "ext-uopz": "*" + "php": ">=7.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1890,83 +3769,126 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2015-10-12T03:26:01+00:00" + "time": "2020-09-28T06:39:44+00:00" }, { - "name": "sebastian/recursion-context", - "version": "1.0.5", + "name": "symfony/polyfill-ctype", + "version": "v1.24.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7" + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "30885182c981ab175d4d034db0f6f469898070ab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/b19cc3298482a335a95f3016d2f8a6950f0fbcd7", - "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab", + "reference": "30885182c981ab175d4d034db0f6f469898070ab", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.1" }, - "require-dev": { - "phpunit/phpunit": "~4.4" + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { - "classmap": [ - "src/" + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" }, { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.24.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" }, { - "name": "Adam Harvey", - "email": "aharvey@php.net" + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2016-10-03T07:41:43+00:00" + "time": "2021-10-20T20:35:02+00:00" }, { - "name": "sebastian/version", - "version": "1.0.6", + "name": "theseer/tokenizer", + "version": "1.2.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6" + "url": "https://github.com/theseer/tokenizer.git", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", - "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", "shasum": "" }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, "type": "library", "autoload": { "classmap": [ @@ -1979,45 +3901,59 @@ ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" } ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "time": "2015-06-21T13:59:46+00:00" + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2021-07-28T10:34:58+00:00" }, { - "name": "symfony/yaml", - "version": "v2.8.36", + "name": "webmozart/assert", + "version": "1.10.0", "source": { "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "be720fcfae4614df204190d57795351059946a77" + "url": "https://github.com/webmozarts/assert.git", + "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/be720fcfae4614df204190d57795351059946a77", - "reference": "be720fcfae4614df204190d57795351059946a77", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25", + "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": "^7.2 || ^8.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev" + "dev-master": "1.10-dev" } }, "autoload": { "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Webmozart\\Assert\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2025,17 +3961,21 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], - "description": "Symfony Yaml Component", - "homepage": "https://symfony.com", - "time": "2018-01-03T07:36:31+00:00" + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.10.0" + }, + "time": "2021-03-09T10:59:23+00:00" } ], "aliases": [], @@ -2044,10 +3984,11 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=5.4.0" + "php": ">=7.4.0" }, "platform-dev": [], "platform-overrides": { - "php": "5.4" - } + "php": "7.4.7" + }, + "plugin-api-version": "2.2.0" } diff --git a/examples/directory_create_recursive.php b/examples/directory_create_recursive.php deleted file mode 100644 index 7077617c..00000000 --- a/examples/directory_create_recursive.php +++ /dev/null @@ -1,26 +0,0 @@ -getAdapter()), PHP_EOL; -$startDir = $filesystem->dir($start); -$dir = $filesystem->dir($dirName); -echo 'Creating directory: ' . $dirName, PHP_EOL; -$dir->createRecursive('rwxrwx---')->then(function () use ($startDir) { - return $startDir->lsRecursive(); -})->then(function (array $list) { - foreach ($list as $node) { - echo $node->getPath(), PHP_EOL; - } -}, function ($e) { - echo $e->getMessage(), PHP_EOL; -}); - -$loop->run(); - -echo 'Don\'t forget to clean up!', PHP_EOL; diff --git a/examples/directory_listing.php b/examples/directory_listing.php new file mode 100644 index 00000000..b2376517 --- /dev/null +++ b/examples/directory_listing.php @@ -0,0 +1,19 @@ +detect(__DIR__)->then(function (DirectoryInterface $directory) { + return $directory->ls(); +})->then(static function ($nodes) { + foreach ($nodes as $node) { + assert($node instanceof NodeInterface); + echo $node->name(), ': ', get_class($node), PHP_EOL; + } + echo '----------------------------', PHP_EOL, 'Done listing directory', PHP_EOL; +}, function (Throwable $throwable) { + echo $throwable; +}); diff --git a/examples/directory_ls.php b/examples/directory_ls.php deleted file mode 100644 index 593aa2dc..00000000 --- a/examples/directory_ls.php +++ /dev/null @@ -1,17 +0,0 @@ -getAdapter()), PHP_EOL; -$filesystem->dir(__DIR__ . DIRECTORY_SEPARATOR . 'tmp')->ls()->then(function (array $list) { - foreach ($list as $node) { - echo get_class($node), ': ', $node->getPath(), PHP_EOL; - } -}, function ($e) { - echo $e->getMessage(), PHP_EOL; -}); - -$loop->run(); diff --git a/examples/directory_ls_recursive.php b/examples/directory_ls_recursive.php deleted file mode 100644 index c9ba1f36..00000000 --- a/examples/directory_ls_recursive.php +++ /dev/null @@ -1,17 +0,0 @@ -getAdapter()), PHP_EOL; -$filesystem->dir(dirname(__DIR__))->lsRecursive()->then(function (array $list) { - foreach ($list as $node) { - echo $node->getPath(), PHP_EOL; - } -}, function ($e) { - echo $e->getMessage(), PHP_EOL; -}); - -$loop->run(); diff --git a/examples/directory_ls_recursive_regexp_interfaces.php b/examples/directory_ls_recursive_regexp_interfaces.php deleted file mode 100644 index 064ff704..00000000 --- a/examples/directory_ls_recursive_regexp_interfaces.php +++ /dev/null @@ -1,20 +0,0 @@ -getAdapter()), PHP_EOL; -$filesystem->dir(dirname(__DIR__))->lsRecursive()->then(function (array $list) { - $iterator = new ArrayIterator($list); - $interfaces = new RegexIterator($iterator, '/.*?Interface.php$/'); - - foreach ($interfaces as $node) { - echo $node->getPath(), PHP_EOL; - } -}, function ($e) { - echo $e->getMessage(), PHP_EOL; -}); - -$loop->run(); diff --git a/examples/directory_ls_recursive_streaming.php b/examples/directory_ls_recursive_streaming.php deleted file mode 100644 index 64f6b77e..00000000 --- a/examples/directory_ls_recursive_streaming.php +++ /dev/null @@ -1,22 +0,0 @@ -getAdapter()), PHP_EOL; -$dir = $filesystem->dir(dirname(__DIR__)); -$stream = $dir->lsRecursiveStreaming(); -$stream->on('data', function (NodeInterface $node) use (&$i) { - echo $node->getPath(), PHP_EOL; - $i++; -}); -$stream->on('end', function () use (&$i) { - echo 'Found ', $i, ' nodes', PHP_EOL; -}); - -$loop->run(); diff --git a/examples/directory_ls_recursive_streaming_strlen_file_get_contents.php b/examples/directory_ls_recursive_streaming_strlen_file_get_contents.php deleted file mode 100644 index eb3da3d3..00000000 --- a/examples/directory_ls_recursive_streaming_strlen_file_get_contents.php +++ /dev/null @@ -1,48 +0,0 @@ - 8, -]); -echo 'Using ', get_class($filesystem->getAdapter()), PHP_EOL; -$dir = $filesystem->dir(__DIR__); -$stream = $dir->lsRecursiveStreaming(); -$stream->on('data', function (NodeInterface $node) use (&$i) { - if ($node instanceof File) { - $node->getContents()->then(function ($contents) use ($node, &$i) { - echo $node->getPath(), ': ', formatBytes(strlen($contents)), PHP_EOL; - $i++; - }, function ($e) { - var_export($e->getMessage()); - }); - return; - } - - echo $node->getPath(), PHP_EOL; - $i++; -}); -$stream->on('end', function () use (&$i) { - echo 'Found ', $i, ' nodes', PHP_EOL; -}); - -$loop->run(); diff --git a/examples/directory_ls_streaming.php b/examples/directory_ls_streaming.php deleted file mode 100644 index b50eb1d4..00000000 --- a/examples/directory_ls_streaming.php +++ /dev/null @@ -1,22 +0,0 @@ -getAdapter()), PHP_EOL; -$dir = $filesystem->dir(__DIR__); -$stream = $dir->lsStreaming(); -$stream->on('data', function (NodeInterface $node) use (&$i) { - echo $node->getPath(), PHP_EOL; - $i++; -}); -$stream->on('end', function () use (&$i) { - echo 'Found ', $i, ' nodes', PHP_EOL; -}); - -$loop->run(); diff --git a/examples/directory_nested_listing.php b/examples/directory_nested_listing.php new file mode 100644 index 00000000..377de04d --- /dev/null +++ b/examples/directory_nested_listing.php @@ -0,0 +1,32 @@ +detect($dir)->then(function (DirectoryInterface $directory) { + return $directory->ls(); + })->then(static function (array $nodes) use (&$ls) { + foreach ($nodes as $node) { + assert($node instanceof NodeInterface); + echo $node->path() . $node->name(), ': ', get_class($node), PHP_EOL; + if ($node instanceof DirectoryInterface) { + $ls($node->path() . $node->name()); + } + } + })->then(null, function (Throwable $throwable) { + echo $throwable; + }); +}; + +$ls(dirname(__DIR__) . DIRECTORY_SEPARATOR . 'src'); + +Loop::run(); + +echo '----------------------------', PHP_EOL, 'Done listing directory', PHP_EOL; diff --git a/examples/directory_rename.php b/examples/directory_rename.php deleted file mode 100644 index 9fd41948..00000000 --- a/examples/directory_rename.php +++ /dev/null @@ -1,15 +0,0 @@ -dir('new'); - -$dir->rename('new_name')->then(function(\React\Filesystem\Node\DirectoryInterface $newDir){ - echo 'Renamed to ' . $newDir->getPath() . PHP_EOL; -}, function(Exception $e) { - echo 'Error: ' . $e->getMessage() . PHP_EOL; -}); - -$loop->run(); diff --git a/examples/directory_size.php b/examples/directory_size.php deleted file mode 100644 index bc00e244..00000000 --- a/examples/directory_size.php +++ /dev/null @@ -1,15 +0,0 @@ -getAdapter()), PHP_EOL; -$filesystem->dir(dirname(__DIR__))->size()->then(function ($size) { - echo 'Directory "' . dirname(__DIR__) . '" contains ' . $size['directories'] . ' directories, ' . $size['files'] . ' files and is ' . $size['size'] . ' bytes in size', PHP_EOL; -}, function ($e) { - echo $e->getMessage(), PHP_EOL; -}); - -$loop->run(); diff --git a/examples/directory_size_recursive.php b/examples/directory_size_recursive.php deleted file mode 100644 index bc5498a9..00000000 --- a/examples/directory_size_recursive.php +++ /dev/null @@ -1,24 +0,0 @@ -getAdapter()), PHP_EOL; -foreach ([ - 'examples', - 'src', - 'tests', - 'vendor', -] as $directory) { - $path = dirname(__DIR__) . '/' . $directory; - $filesystem->dir($path)->sizeRecursive()->then(function ($size) use ($path) { - echo 'Directory "' . $path . '" contains ' . $size['directories'] . ' directories, ' . $size['files'] . ' files and is ' . $size['size'] . ' bytes in size', PHP_EOL; - }, function ($e) { - echo $e->getMessage(), PHP_EOL, var_export($e->getArgs(), true), PHP_EOL; - }); - -} - -$loop->run(); diff --git a/examples/directory_stat.php b/examples/directory_stat.php deleted file mode 100644 index a9920e8f..00000000 --- a/examples/directory_stat.php +++ /dev/null @@ -1,17 +0,0 @@ -getAdapter()), PHP_EOL; -$filesystem->dir(__DIR__)->stat()->then(function ($data) { - foreach ($data as $key => $value) { - echo $key, ': ', $value, PHP_EOL; - } -}, function ($e) { - echo $e->getMessage(), PHP_EOL; -}); - -$loop->run(); diff --git a/examples/directory_to_directory_copy.php b/examples/directory_to_directory_copy.php deleted file mode 100644 index 2e45af05..00000000 --- a/examples/directory_to_directory_copy.php +++ /dev/null @@ -1,27 +0,0 @@ -getAdapter()), PHP_EOL; -$from = $filesystem->dir(dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'vendor'); -$to = $filesystem->dir(sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'react_filesystem_file_to_file_copy' . DIRECTORY_SEPARATOR . uniqid()); -echo 'From: ', $from->getPath(), PHP_EOL; -echo 'To: ', $to->getPath(), PHP_EOL; -$to->createRecursive()->then(function () use ($from, $to) { - $i = 0; - $stream = $from->copyStreaming($to); - $stream->on('data', function (NodeInterface $node) use (&$i) { - echo $node->getPath(), PHP_EOL; - $i++; - }); - $stream->on('end', function () use (&$i) { - echo 'Copied ', $i, ' nodes', PHP_EOL; - }); -}); - -$loop->run(); diff --git a/examples/file_chown.php b/examples/file_chown.php deleted file mode 100644 index 2ca570d7..00000000 --- a/examples/file_chown.php +++ /dev/null @@ -1,16 +0,0 @@ -getAdapter()), PHP_EOL; -$filesystem->file('test.txt')->chown(1000, 1000)->then(function ($result) { - var_export($result); - echo PHP_EOL; -}, function ($e) { - echo $e->getMessage(), PHP_EOL; -}); - -$loop->run(); diff --git a/examples/file_create.php b/examples/file_create.php deleted file mode 100644 index 30265516..00000000 --- a/examples/file_create.php +++ /dev/null @@ -1,30 +0,0 @@ -getAdapter()), PHP_EOL; -$file = $filesystem->file($fileName); -$file->create() -->then(function () use ($file, $fileName) { - echo 'File "' . $fileName . '" created.', PHP_EOL; - return $file->stat(); -}) -->then(function ($data) use ($file) { - echo 'stat data: ', PHP_EOL; - foreach ($data as $key => $value) { - echo "\t", $key, ': ', $value, PHP_EOL; - } - return $file->remove(); -}) -->then(function () { - echo 'File removed', PHP_EOL; - echo 'Done!', PHP_EOL; -}, function ($e) { - echo $e->getMessage(), PHP_EOL; -}); - -$loop->run(); diff --git a/examples/file_create_new.php b/examples/file_create_new.php new file mode 100644 index 00000000..115f5162 --- /dev/null +++ b/examples/file_create_new.php @@ -0,0 +1,24 @@ +detect(sys_get_temp_dir() . __FILE__ . time() . time() . time() . time() . time() . time())->then(static function (NotExistInterface $node): PromiseInterface { + return $node->createFile(); +})->then(static function (FileInterface $file): PromiseInterface { + return $file->stat(); +})->then(static function (Stat $stat): void { + echo $stat->path(), ': ', get_class($stat), PHP_EOL; + echo 'Mode: ', $stat->mode(), PHP_EOL; + echo 'Uid: ', $stat->uid(), PHP_EOL; + echo 'Gid: ', $stat->gid(), PHP_EOL; + echo 'Size: ', $stat->size(), PHP_EOL; + echo 'Atime: ', $stat->atime()->format(DATE_ISO8601), PHP_EOL; + echo 'Mtime: ', $stat->mtime()->format(DATE_ISO8601), PHP_EOL; + echo 'Ctime: ', $stat->ctime()->format(DATE_ISO8601), PHP_EOL; +})->done(); diff --git a/examples/file_duplex_stream.php b/examples/file_duplex_stream.php deleted file mode 100644 index 89185441..00000000 --- a/examples/file_duplex_stream.php +++ /dev/null @@ -1,51 +0,0 @@ -getAdapter()), PHP_EOL; -$file = $filesystem->file($fileName); -$file->open('ct+') -->then(function (\React\Filesystem\Stream\DuplexStreamInterface $stream) use ($loop, $file, $fileName, $generatedFileContents, &$readedFileContents) { - $stream->on('end', function ($stream) use ($generatedFileContents, &$readedFileContents) { - if (strlen($generatedFileContents) != strlen($readedFileContents)) { - $stream->resume(); - } - }); - $stream->on('data', function ($data) use (&$readedFileContents) { - $readedFileContents .= $data; - }); - $stream->resume(); - $stream->write($generatedFileContents); -}); - -$loop->run(); -$file->remove(); -$loop->run(); - -if ($generatedFileContents == $readedFileContents) { - echo 'Contents read from disk match generated contents', PHP_EOL; -} else { - echo 'Contents read from disk DON\'T match generated contents', PHP_EOL; -} diff --git a/examples/file_exists.php b/examples/file_exists.php deleted file mode 100644 index 036f54cb..00000000 --- a/examples/file_exists.php +++ /dev/null @@ -1,24 +0,0 @@ -getAdapter()), PHP_EOL; -$filesystem->file(__FILE__)->exists()->then(function () use ($filesystem) { - echo 'File "' . __FILE__ . '" exists', PHP_EOL; - return new FulfilledPromise($filesystem); -}, function ($e) { - echo $e->getMessage(), PHP_EOL; -})->then(function ($filesystem) { - $fakeFile = __FILE__ . time(); - $filesystem->file($fakeFile)->exists()->then(null, function () use($fakeFile) { - echo 'File "' . $fakeFile . '" doesn\'t exists', PHP_EOL; - }, function ($e) { - echo $e->getMessage(), PHP_EOL; - }); -}); -$loop->run(); diff --git a/examples/file_get_contents.php b/examples/file_get_contents.php deleted file mode 100644 index 18953121..00000000 --- a/examples/file_get_contents.php +++ /dev/null @@ -1,15 +0,0 @@ -getAdapter()), PHP_EOL; -$filesystem->file(__FILE__)->getContents()->then(function ($contents) { - echo $contents, PHP_EOL; -}, function ($e) { - echo $e->getMessage(), PHP_EOL; -}); - -$loop->run(); diff --git a/examples/file_parent.php b/examples/file_parent.php deleted file mode 100644 index 9be0e4cb..00000000 --- a/examples/file_parent.php +++ /dev/null @@ -1,14 +0,0 @@ -getAdapter()), PHP_EOL; -$filesystem->file(__FILE__); - -do { - echo $node->getName(), PHP_EOL; -} while ($node = $node->getParent()); diff --git a/examples/file_put_contents.php b/examples/file_put_contents.php deleted file mode 100644 index 65c76652..00000000 --- a/examples/file_put_contents.php +++ /dev/null @@ -1,19 +0,0 @@ -getAdapter()), PHP_EOL; -$filesystem->file($filename)->putContents($contents)->then(function ($contents) use ($filename) { - echo file_get_contents($filename), PHP_EOL; -}, function (Exception $e) { - echo $e->getMessage(), PHP_EOL; - echo $e->getTraceAsString(), PHP_EOL; -}); - -$loop->run(); diff --git a/examples/file_read.php b/examples/file_read.php new file mode 100644 index 00000000..1bc58f34 --- /dev/null +++ b/examples/file_read.php @@ -0,0 +1,12 @@ +detect(__FILE__)->then(function (FileInterface $file) { + return $file->getContents(); +})->then(static function (string $contents): void { + echo $contents; +})->done(); diff --git a/examples/file_size.php b/examples/file_size.php deleted file mode 100644 index 6c8e4c94..00000000 --- a/examples/file_size.php +++ /dev/null @@ -1,15 +0,0 @@ -getAdapter()), PHP_EOL; -$filesystem->file(__FILE__)->size()->then(function ($size) { - echo 'File "' . __FILE__ . '" is ' . $size . ' bytes', PHP_EOL; -}, function ($e) { - echo $e->getMessage(), PHP_EOL; -}); - -$loop->run(); diff --git a/examples/file_stat.php b/examples/file_stat.php index fb7b0aae..2b3c19ab 100644 --- a/examples/file_stat.php +++ b/examples/file_stat.php @@ -1,17 +1,20 @@ getAdapter()), PHP_EOL; -$filesystem->file(__FILE__)->stat()->then(function ($data) { - foreach ($data as $key => $value) { - echo $key, ': ', var_export($value, true), PHP_EOL; - } -}, function ($e) { - echo $e->getMessage(), PHP_EOL; -}); - -$loop->run(); +Factory::create()->detect(__FILE__)->then(function (FileInterface $file) { + return $file->stat(); +})->then(static function (Stat $stat): void { + echo $stat->path(), ': ', get_class($stat), PHP_EOL; + echo 'Mode: ', $stat->mode(), PHP_EOL; + echo 'Uid: ', $stat->uid(), PHP_EOL; + echo 'Gid: ', $stat->gid(), PHP_EOL; + echo 'Size: ', $stat->size(), PHP_EOL; + echo 'Atime: ', $stat->atime()->format(DATE_ISO8601), PHP_EOL; + echo 'Mtime: ', $stat->mtime()->format(DATE_ISO8601), PHP_EOL; + echo 'Ctime: ', $stat->ctime()->format(DATE_ISO8601), PHP_EOL; +})->done(); diff --git a/examples/file_tail.php b/examples/file_tail.php new file mode 100644 index 00000000..2d1b57f1 --- /dev/null +++ b/examples/file_tail.php @@ -0,0 +1,24 @@ +detect($filename)->then(function (FileInterface $file) { + Loop::addPeriodicTimer(1, function (TimerInterface $timer) use ($file, &$offset): void { + $file->getContents($offset)->then(function (string $contents) use (&$offset, $timer): void { + echo $contents; + $offset += strlen($contents); + if (trim($contents) === 'done') { + Loop::cancelTimer($timer); + } + }); + }); +})->done(); + +echo 'Append data to "', $filename, '" to see it appear beneath here, put "done" on a new line to stop watching it:', PHP_EOL; diff --git a/examples/file_time.php b/examples/file_time.php deleted file mode 100644 index 6c9e64bc..00000000 --- a/examples/file_time.php +++ /dev/null @@ -1,23 +0,0 @@ -getAdapter()), PHP_EOL; -$filesystem->file(__FILE__)->time()->then(function ($times) { - $nextLine = "\r\n\t"; - echo 'File "' . __FILE__ . '":'; - echo $nextLine; - echo 'Access timestamp: ' . $times['atime']->format('r'); - echo $nextLine; - echo 'Creation timestamp: ' . $times['ctime']->format('r'); - echo $nextLine; - echo 'Modified timestamp: ' . $times['mtime']->format('r'); - echo "\r\n"; -}, function ($e) { - echo $e->getMessage(), PHP_EOL; -}); - -$loop->run(); diff --git a/examples/file_to_directory_copy.php b/examples/file_to_directory_copy.php deleted file mode 100644 index 7e657edb..00000000 --- a/examples/file_to_directory_copy.php +++ /dev/null @@ -1,26 +0,0 @@ -getAdapter()), PHP_EOL; -$from = $filesystem->file(__FILE__); -$to = $filesystem->dir(sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'react_filesystem_file_to_file_copy' . DIRECTORY_SEPARATOR . uniqid()); -echo $to->getPath(), PHP_EOL; -$to->createRecursive()->then(function () use ($from, $to) { - return $from->copy($to); -})->then(function (FileInterface $file) { - echo $file->getPath(), PHP_EOL; - return $file->stat(); -})->then(function ($stats) { - var_export($stats); - echo PHP_EOL; -}); - -$loop->run(); diff --git a/examples/file_to_file_copy.php b/examples/file_to_file_copy.php deleted file mode 100644 index 7edbc140..00000000 --- a/examples/file_to_file_copy.php +++ /dev/null @@ -1,23 +0,0 @@ -getAdapter()), PHP_EOL; -$from = $filesystem->file(__FILE__); -$to = $filesystem->file(sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'react_filesystem_file_to_file_copy_' . uniqid()); - -$from->copy($to)->then(function (FileInterface $file) { - echo $file->getPath(), PHP_EOL; - return $file->stat(); -})->then(function ($stats) { - var_export($stats); - echo PHP_EOL; -}); - -$loop->run(); diff --git a/examples/file_touch.php b/examples/file_touch.php deleted file mode 100644 index 38922063..00000000 --- a/examples/file_touch.php +++ /dev/null @@ -1,31 +0,0 @@ -getAdapter()), PHP_EOL; -$file = $filesystem->file(__FILE__); - -$file->time()->then($timesFunction)->then(function () use ($file) { - return $file->touch(); -})->then(function () use ($file) { - return $file->time(); -})->then($timesFunction)->then(null, function ($e) { - echo $e->getMessage(), PHP_EOL; -}); - -$loop->run(); diff --git a/examples/file_unlink.php b/examples/file_unlink.php new file mode 100644 index 00000000..ad499eea --- /dev/null +++ b/examples/file_unlink.php @@ -0,0 +1,12 @@ +detect($filename)->then(function (FileInterface $file) use ($filename) { + return $file->unlink(); +})->done(); diff --git a/examples/file_write.php b/examples/file_write.php new file mode 100644 index 00000000..0c77a16a --- /dev/null +++ b/examples/file_write.php @@ -0,0 +1,12 @@ +detect(__FILE__ . '.copy')->then(static function (FileInterface $file) { + return $file->putContents(file_get_contents(__FILE__)); +})->then(static function ($result): void { + var_export([$result]); +})->done(); diff --git a/examples/link_get_contents.php b/examples/link_get_contents.php deleted file mode 120000 index 9a4eed64..00000000 --- a/examples/link_get_contents.php +++ /dev/null @@ -1 +0,0 @@ -file_get_contents.php \ No newline at end of file diff --git a/examples/link_readlink.php b/examples/link_readlink.php deleted file mode 100644 index ce45e705..00000000 --- a/examples/link_readlink.php +++ /dev/null @@ -1,15 +0,0 @@ -getAdapter()), PHP_EOL; -$filesystem->getAdapter()->readlink('link_get_contents.php')->then(function ($contents) { - echo $contents, PHP_EOL; -}, function ($e) { - echo $e->getMessage(), PHP_EOL; -}); - -$loop->run(); diff --git a/examples/link_symlink.php b/examples/link_symlink.php deleted file mode 100644 index da7db154..00000000 --- a/examples/link_symlink.php +++ /dev/null @@ -1,15 +0,0 @@ -getAdapter()), PHP_EOL; -$filesystem->getAdapter()->symlink('link_get_contents.php', 'symlink_' . time() . '.php')->then(function ($contents) { - var_export($contents); -}, function ($e) { - echo $e->getMessage(), PHP_EOL; -}); - -$loop->run(); diff --git a/examples/node_doesnt_exist.php b/examples/node_doesnt_exist.php new file mode 100644 index 00000000..6555b9bc --- /dev/null +++ b/examples/node_doesnt_exist.php @@ -0,0 +1,10 @@ +detect(__FILE__ . time() . time() . time() . time() . time() . time())->then(static function (NodeInterface $node): void { + echo get_class($node); +})->done(); diff --git a/examples/tail.php b/examples/tail.php deleted file mode 100644 index 09350111..00000000 --- a/examples/tail.php +++ /dev/null @@ -1,36 +0,0 @@ -getContents($path)->then(function ($content) use ($loop, $filesystem, $path) { - echo $content; - - $lastSize = strlen($content); - $adapter = $filesystem->getAdapter(); - - $adapter->open($path, 'r')->then(function ($fileDescriptor) use ($adapter, $filesystem, $loop, $path, &$lastSize) { - $file = $filesystem->file($path); - - $loop->addPeriodicTimer(1, function () use ($adapter, $fileDescriptor, $file, &$lastSize) { - $file->size()->then(function ($size) use ($adapter, $fileDescriptor, &$lastSize) { - if ($lastSize === $size) { - return; - } - - $adapter->read($fileDescriptor, $size - $lastSize, $lastSize)->then(function ($content) { - echo $content; - }); - - $lastSize = $size; - }); - }); - }); -}); - -$loop->run(); diff --git a/examples/test.txt b/examples/test.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/src/AdapterInterface.php b/src/AdapterInterface.php index e32bf893..0ebe94b8 100644 --- a/src/AdapterInterface.php +++ b/src/AdapterInterface.php @@ -2,246 +2,17 @@ namespace React\Filesystem; -use React\Filesystem\ObjectStream; -use React\EventLoop\LoopInterface; +use React\Filesystem\Node; use React\Promise\PromiseInterface; interface AdapterInterface { - const CREATION_MODE = 'rwxrw----'; - - /** - * Checks whether the current installation supports the adapter. - * - * @return bool - */ - public static function isSupported(); - - /** - * Return the loop associated with this adapter. - * - * @return LoopInterface - */ - public function getLoop(); - - /** - * Get the relevant filesystem for this adapter. - * - * @internal - * @return FilesystemInterface - */ - public function getFilesystem(); - - /** - * Set the relevant filesystem for this adapter. - * - * @internal - * @param FilesystemInterface $filesystem - * @return void - */ - public function setFilesystem(FilesystemInterface $filesystem); - - /** - * Call the underlying filesystem. - * - * @internal - * @param string $function - * @param array $args - * @param int $errorResultCode - * @return PromiseInterface - */ - public function callFilesystem($function, $args, $errorResultCode = -1); - - /** - * Create a directory at the given path with the given mode. - * - * @param string $path - * @param $mode - * @return PromiseInterface - */ - public function mkdir($path, $mode = self::CREATION_MODE); - - /** - * Remove the given directory, fails when it has contents. - * - * @param string $path - * @return PromiseInterface - */ - public function rmdir($path); - - /** - * Remove the given file. - * - * @param string $filename - * @return PromiseInterface - */ - public function unlink($filename); - - /** - * Change the mode of the given path. - * - * @param string $path - * @param int $mode - * @return PromiseInterface - */ - public function chmod($path, $mode); - /** - * Change the owner of the given path. - * - * @param string $path - * @param int $uid - * @param int $gid - * @return PromiseInterface + * @return PromiseInterface */ - public function chown($path, $uid, $gid); + public function detect(string $path): PromiseInterface; - /** - * Stat the node, returning information such as the file, c/m/a-time, mode, g/u-id, and more. - * - * @param string $filename - * @return PromiseInterface - */ - public function stat($filename); - - /** - * List contents of the given path. - * - * @param string $path - * @return PromiseInterface - */ - public function ls($path); - - /** - * List contents of the given path. - * - * @param string $path - * @return ObjectStream - */ - public function lsStream($path); + public function directory(string $path): Node\DirectoryInterface; - /** - * Touch the given path, either creating a file, or updating mtime on the file. - * - * @param string $path - * @param $mode - * @return PromiseInterface - */ - public function touch($path, $mode = self::CREATION_MODE); - - /** - * Open a file for reading or writing at the given path. This will return a file descriptor, - * which can be used to read or write to the file. And ultimately close the file descriptor. - * - * @param string $path - * @param string $flags - * @param $mode - * @return PromiseInterface - */ - public function open($path, $flags, $mode = self::CREATION_MODE); - - /** - * Read from the given file descriptor. - * - * @param mixed $fileDescriptor - * @param int $length - * @param int $offset - * @return PromiseInterface - */ - public function read($fileDescriptor, $length, $offset); - - /** - * Write to the given file descriptor. - * - * @param mixed $fileDescriptor - * @param string $data - * @param int $length - * @param int $offset - * @return PromiseInterface - */ - public function write($fileDescriptor, $data, $length, $offset); - - /** - * Close the given file descriptor. - * - * @param mixed $fd - * @return PromiseInterface - */ - 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. - * - * @param string $fromPath - * @param string $toPath - * @return PromiseInterface - */ - public function rename($fromPath, $toPath); - - /** - * Read link information from the given path (has to be a symlink). - * - * @param string $path - * @return PromiseInterface - */ - public function readlink($path); - - /** - * Create a symlink from $fromPath to $toPath. - * - * @param string $fromPath - * @param string $toPath - * @return PromiseInterface - */ - public function symlink($fromPath, $toPath); - - /** - * Detect the type of the given path. - * - * @param string $path - * @return PromiseInterface - */ - public function detectType($path); + public function file(string $path): Node\FileInterface; } diff --git a/src/ChildProcess/Adapter.php b/src/ChildProcess/Adapter.php index 0a58e090..3b0dcd8b 100644 --- a/src/ChildProcess/Adapter.php +++ b/src/ChildProcess/Adapter.php @@ -2,496 +2,51 @@ namespace React\Filesystem\ChildProcess; -use DateTime; -use Exception; -use Throwable; -use React\EventLoop\LoopInterface; -use React\Filesystem\ObjectStream; -use React\Filesystem\ObjectStreamSink; +use React\EventLoop\ExtUvLoop; use React\Filesystem\AdapterInterface; -use React\Filesystem\FilesystemInterface; -use React\Filesystem\MappedTypeDetector; use React\Filesystem\ModeTypeDetector; -use React\Filesystem\OpenFileLimiter; -use React\Filesystem\TypeDetectorInterface; -use React\Filesystem\PermissionFlagResolver; -use React\Filesystem\Node\NodeInterface; +use React\Filesystem\PollInterface; +use React\Filesystem\Stat; use React\Promise\PromiseInterface; -use WyriHaximus\React\ChildProcess\Messenger\Messages\Factory; -use WyriHaximus\React\ChildProcess\Messenger\Messages\Payload; -use WyriHaximus\React\ChildProcess\Messenger\Messenger; -use WyriHaximus\React\ChildProcess\Pool\Options; -use WyriHaximus\React\ChildProcess\Pool\PoolInterface; +use RuntimeException; +use React\EventLoop\LoopInterface; +use React\Filesystem\Node; -class Adapter implements AdapterInterface +/** + * @internal + */ +final class Adapter implements AdapterInterface { - const DEFAULT_POOL = 'WyriHaximus\React\ChildProcess\Pool\Factory\Flexible'; - const POOL_INTERFACE = 'WyriHaximus\React\ChildProcess\Pool\PoolFactoryInterface'; - const CHILD_CLASS_NAME = 'React\Filesystem\ChildProcess\Process'; - - /** - * @var LoopInterface - */ - protected $loop; - - /** - * @var FilesystemInterface - */ - protected $filesystem; - - /** - * @var PoolInterface - */ - protected $pool; - - /** - * @var OpenFileLimiter - */ - protected $openFileLimiter; - - /** - * @var array - */ - protected $fileDescriptors = []; - - /** - * @var TypeDetectorInterface[] - */ - protected $typeDetectors = []; - - /** - * @var PermissionFlagResolver - */ - protected $permissionFlagResolver; - - /** - * @var array - */ - protected $options = [ - 'lsFlags' => SCANDIR_SORT_NONE, - ]; - - /** - * Adapter constructor. - * @param LoopInterface $loop - * @param array $options - */ - public function __construct(LoopInterface $loop, array $options = []) - { - $this->loop = $loop; - - $this->openFileLimiter = new OpenFileLimiter(\React\Filesystem\getOpenFileLimit($options)); - $this->permissionFlagResolver = new PermissionFlagResolver(); - - $this->setUpPool($options); - - $this->options = array_merge_recursive($this->options, $options); - } - - protected function setUpPool($options) - { - $poolOptions = [ - Options::MIN_SIZE => 0, - Options::MAX_SIZE => 50, - Options::TTL => 3, - ]; - $poolClass = static::DEFAULT_POOL; - - if (isset($options['pool']['class']) && is_subclass_of($options['pool']['class'], static::POOL_INTERFACE)) { - $poolClass = $options['pool']['class']; - } - - call_user_func_array($poolClass . '::createFromClass', [ - self::CHILD_CLASS_NAME, - $this->loop, - $poolOptions, - ])->then(function (PoolInterface $pool) { - $this->pool = $pool; - }); - } - - /** - * @return bool - */ - public static function isSupported() - { - return substr(strtolower(PHP_OS), 0, 3) !== 'win' && function_exists('proc_open'); - } - - /** - * @return LoopInterface - */ - public function getLoop() - { - return $this->loop; - } + use StatTrait; - /** - * {@inheritDoc} - */ - public function getFilesystem() + public function detect(string $path): PromiseInterface { - return $this->filesystem; - } - - /** - * {@inheritDoc} - */ - public function setFilesystem(FilesystemInterface $filesystem) - { - $this->filesystem = $filesystem; - - $this->typeDetectors = [ - MappedTypeDetector::createDefault($this->filesystem), - new ModeTypeDetector($this->filesystem), - ]; - } - - /** - * @param string $function - * @param array $args - * @param int $errorResultCode - * @return PromiseInterface - */ - 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 $this->internalStat($path)->then(function (?Stat $stat) use ($path) { + if ($stat === null) { + return new NotExist($this, dirname($path) . DIRECTORY_SEPARATOR, basename($path)); } - return \React\Promise\reject(new Exception($payload['error']['message'])); - }); - } - - /** - * @param string $path - * @param int $mode - * @return PromiseInterface - */ - public function chmod($path, $mode) - { - return $this->callFilesystem('chmod', [ - 'path' => $path, - 'mode' => decoct($mode), - ]); - } - - /** - * @param string $path - * @param $mode - * @return PromiseInterface - */ - public function mkdir($path, $mode = self::CREATION_MODE) - { - return $this->callFilesystem('mkdir', [ - 'path' => $path, - 'mode' => decoct($this->permissionFlagResolver->resolve($mode)), - ]); - } - - /** - * @param string $path - * @param string $flags - * @param $mode - * @return PromiseInterface - */ - public function open($path, $flags, $mode = self::CREATION_MODE) - { - return \WyriHaximus\React\ChildProcess\Messenger\Factory::parentFromClass(self::CHILD_CLASS_NAME, $this->loop)->then(function (Messenger $messenger) use ($path, $flags, $mode) { - $this->fileDescriptors[] = $messenger; - \end($this->fileDescriptors); - $id = \key($this->fileDescriptors); - - return $this->fileDescriptors[$id]->rpc(Factory::rpc('open', [ - 'path' => $path, - 'flags' => $flags, - 'mode' => $mode, - ]))->then(function () use ($id) { - return $id; - }); - }); - } - - /** - * @param string $fileDescriptor - * @param int $length - * @param int $offset - * @return PromiseInterface - */ - public function read($fileDescriptor, $length, $offset) - { - return $this->fileDescriptors[$fileDescriptor]->rpc(Factory::rpc('read', [ - 'length' => $length, - 'offset' => $offset, - ]))->then(function ($payload) { - return \React\Promise\resolve(base64_decode($payload['chunk'])); - }); - } - - /** - * @param string $fileDescriptor - * @param string $data - * @param int $length - * @param int $offset - * @return PromiseInterface - */ - public function write($fileDescriptor, $data, $length, $offset) - { - return $this->fileDescriptors[$fileDescriptor]->rpc(Factory::rpc('write', [ - 'chunk' => base64_encode($data), - 'length' => $length, - 'offset' => $offset, - ])); - } - - /** - * @param string $fd - * @return PromiseInterface - */ - public function close($fd) - { - $fileDescriptor = $this->fileDescriptors[$fd]; - unset($this->fileDescriptors[$fd]); - return $fileDescriptor->rpc(Factory::rpc('close'))->then(function () use ($fileDescriptor) { - return $fileDescriptor->softTerminate(); - }, function () use ($fileDescriptor) { - 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->callFilesystem('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->callFilesystem('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->callFilesystem('putContents', [ - 'path' => $path, - 'chunk' => base64_encode($content), - 'flags' => FILE_APPEND, - ])->then(function ($payload) { - return \React\Promise\resolve($payload['written']); - }); - } - - /** - * @param string $path - * @return PromiseInterface - */ - public function rmdir($path) - { - return $this->callFilesystem('rmdir', [ - 'path' => $path, - ]); - } - - /** - * @param string $path - * @return PromiseInterface - */ - public function unlink($path) - { - return $this->callFilesystem('unlink', [ - 'path' => $path, - ]); - } - - /** - * @param string $path - * @param int $uid - * @param int $gid - * @return PromiseInterface - */ - public function chown($path, $uid, $gid) - { - return $this->callFilesystem('chown', [ - 'path' => $path, - 'uid' => $uid, - 'gid' => $gid, - ]); - } - - /** - * @param string $filename - * @return PromiseInterface - */ - public function stat($filename) - { - return $this->callFilesystem('stat', [ - 'path' => $filename, - ])->then(function ($stat) { - $stat['atime'] = new DateTime('@' . $stat['atime']); - $stat['mtime'] = new DateTime('@' . $stat['mtime']); - $stat['ctime'] = new DateTime('@' . $stat['ctime']); - return \React\Promise\resolve($stat); - }); - } - - /** - * @param string $path - * @return PromiseInterface - */ - public function ls($path) - { - return ObjectStreamSink::promise($this->lsStream($path)); - } - - /** - * @param string $path - * @return ObjectStream - */ - public function lsStream($path) - { - $stream = new ObjectStream(); - - $this->callFilesystem('readdir', [ - 'path' => $path, - 'flags' => $this->options['lsFlags'], - ])->then(function ($result) use ($path, $stream) { - $this->processLsContents($path, $result, $stream); - }); - - return $stream; - } - - protected function processLsContents($basePath, $result, ObjectStream $stream) - { - $promises = []; - - foreach ($result as $entry) { - $path = $basePath . DIRECTORY_SEPARATOR . $entry['name']; - $node = [ - 'path' => $path, - 'type' => $entry['type'], - ]; - $promises[] = \React\Filesystem\detectType($this->typeDetectors, $node)->then(function (NodeInterface $node) use ($stream) { - $stream->write($node); - }); - } - - \React\Promise\all($promises)->then(function () use ($stream) { - $stream->close(); - }); - } - - /** - * @param string $path - * @param $mode - * @return PromiseInterface - */ - public function touch($path, $mode = self::CREATION_MODE) - { - return $this->callFilesystem('touch', [ - 'path' => $path, - 'mode' => decoct($this->permissionFlagResolver->resolve($mode)), - ]); - } - - /** - * @param string $fromPath - * @param string $toPath - * @return PromiseInterface - */ - public function rename($fromPath, $toPath) - { - return $this->callFilesystem('rename', [ - 'from' => $fromPath, - 'to' => $toPath, - ]); - } - - /** - * @param string $path - * @return PromiseInterface - */ - public function readlink($path) - { - return $this->callFilesystem('readlink', [ - 'path' => $path, - ])->then(function ($result) { - return \React\Promise\resolve($result['path']); + switch (ModeTypeDetector::detect($stat->mode())) { + case Node\DirectoryInterface::class: + return $this->directory($stat->path()); + break; + case Node\FileInterface::class: + return $this->file($stat->path()); + break; + default: + return new Node\Unknown($stat->path(), $stat->path()); + break; + } }); } - /** - * @param string $fromPath - * @param string $toPath - * @return PromiseInterface - */ - public function symlink($fromPath, $toPath) + public function directory(string $path): Node\DirectoryInterface { - return $this->callFilesystem('symlink', [ - 'from' => $fromPath, - 'to' => $toPath, - ])->then(function ($result) { - return \React\Promise\resolve($result['result']); - }); + return new Directory($this,dirname($path) . DIRECTORY_SEPARATOR, basename($path)); } - /** - * @inheritDoc - */ - public function detectType($path) + public function file(string $path): Node\FileInterface { - return \React\Filesystem\detectType($this->typeDetectors, [ - 'path' => $path, - ]); + return new File(dirname($path) . DIRECTORY_SEPARATOR, basename($path)); } } diff --git a/src/ChildProcess/Directory.php b/src/ChildProcess/Directory.php new file mode 100644 index 00000000..df0f6100 --- /dev/null +++ b/src/ChildProcess/Directory.php @@ -0,0 +1,75 @@ +filesystem = $filesystem; + $this->path = $path; + $this->name = $name; + } + + public function stat(): PromiseInterface + { + return $this->internalStat($this->path . $this->name); + } + + public function ls(): PromiseInterface + { + $path = $this->path . $this->name; + return childProcessPromiseClosure(Loop::get(), function () use ($path): array { + return scandir($path); + })->then( function (array $contents): PromiseInterface { + $promises = []; + foreach ($contents as $node) { + if (in_array($node, ['.', '..'])) { + continue; + } + + $promises[] = $this->filesystem->detect($this->path . $this->name . DIRECTORY_SEPARATOR . $node); + } + + return all($promises); + }); + } + + public function unlink(): PromiseInterface + { + $path = $this->path . $this->name; + return childProcessPromiseClosure(Loop::get(), function () use ($path): array { + if (count(scandir($path)) > 0) { + return ['unlinked' => false]; + } + + return ['unlinked' => rmdir($path)]; + })->then(static fn (array $data) => (bool)$data['unlinked']); + } + + public function path(): string + { + return $this->path; + } + + public function name(): string + { + return $this->name; + } +} diff --git a/src/ChildProcess/File.php b/src/ChildProcess/File.php new file mode 100644 index 00000000..dc3eabcd --- /dev/null +++ b/src/ChildProcess/File.php @@ -0,0 +1,71 @@ +path = $path; + $this->name = $name; + } + + public function stat(): PromiseInterface + { + return $this->internalStat($this->path . $this->name); + } + + public function getContents(int $offset = 0 , ?int $maxlen = null): PromiseInterface + { + $path = $this->path . $this->name; + return childProcessPromiseClosure(Loop::get(), function () use ($path, $offset, $maxlen): array { + return ['contents' => file_get_contents($path, false, null, $offset, $maxlen ?? (int)stat($path)['size'])]; + })->then(static fn (array $data): string => $data['contents']); + } + + public function putContents(string $contents, int $flags = 0): PromiseInterface + { + // Making sure we only pass in one flag for security reasons + if (($flags & \FILE_APPEND) == \FILE_APPEND) { + $flags = \FILE_APPEND; + } else { + $flags = 0; + } + + $path = $this->path . $this->name; + return childProcessPromiseClosure(Loop::get(), function () use ($path, $contents, $flags): array { + return ['size_written' => file_put_contents($path, $contents, $flags)]; + })->then(static fn (array $data) => (int)$data['size_written']); + } + + public function unlink(): PromiseInterface + { + $path = $this->path . $this->name; + return childProcessPromiseClosure(Loop::get(), function () use ($path): array { + return ['unlinked' => unlink($path)]; + })->then(static fn (array $data) => (bool)$data['unlinked']); + } + + public function path(): string + { + return $this->path; + } + + public function name(): string + { + return $this->name; + } +} diff --git a/src/ChildProcess/NotExist.php b/src/ChildProcess/NotExist.php new file mode 100644 index 00000000..1040b063 --- /dev/null +++ b/src/ChildProcess/NotExist.php @@ -0,0 +1,75 @@ +filesystem = $filesystem; + $this->path = $path; + $this->name = $name; + } + + public function stat(): PromiseInterface + { + return $this->internalStat($this->path . $this->name); + } + + public function createDirectory(): PromiseInterface + { + $path = $this->path . $this->name; + return childProcessPromiseClosure(Loop::get(), function () use ($path): array { + return ['mkdir' => mkdir($path,0777, true)]; + })->then(fn (array $data): Node\DirectoryInterface => new Directory($this->filesystem, $this->path, $this->name)); + } + + public function createFile(): PromiseInterface + { + $file = new File($this->path, $this->name); + + return $this->filesystem->detect($this->path)->then(function (Node\NodeInterface $node): PromiseInterface { + if ($node instanceof Node\NotExistInterface) { + return $node->createDirectory(); + } + + return resolve($node); + })->then(function () use ($file): PromiseInterface { + return $file->putContents(''); + })->then(function () use ($file): Node\FileInterface { + return $file; + }); + } + + public function unlink(): PromiseInterface + { + // Essentially a No-OP since it doesn't exist anyway + return resolve(true); + } + + public function path(): string + { + return $this->path; + } + + public function name(): string + { + return $this->name; + } +} diff --git a/src/ChildProcess/Process.php b/src/ChildProcess/Process.php deleted file mode 100644 index 63afd4ed..00000000 --- a/src/ChildProcess/Process.php +++ /dev/null @@ -1,316 +0,0 @@ -registerRpc($method, $this->wrapper($method)); - } - } - - protected function wrapper($function) - { - return function (Payload $payload, Messenger $messenger) use ($function) { - return $this->$function($payload->getPayload()); - }; - } - - /** - * @param array $payload - * @return PromiseInterface - */ - public function mkdir(array $payload) - { - if ( - @mkdir( - $payload['path'], - octdec($payload['mode']) - ) - ) { - return \React\Promise\resolve([]); - } - - return \React\Promise\reject([ - 'error' => error_get_last(), - ]); - } - - /** - * @param array $payload - * @return PromiseInterface - */ - public function rmdir(array $payload) - { - if (@rmdir($payload['path'])) { - return \React\Promise\resolve([]); - } - - return \React\Promise\reject([ - 'error' => error_get_last(), - ]); - } - - /** - * @param array $payload - * @return PromiseInterface - */ - public function unlink(array $payload) - { - if (unlink($payload['path'])) { - return \React\Promise\resolve([]); - } - - return \React\Promise\reject([ - 'error' => error_get_last(), - ]); - } - - /** - * @param array $payload - * @return PromiseInterface - */ - public function chmod(array $payload) - { - if (chmod($payload['path'], octdec($payload['mode']))) { - return \React\Promise\resolve([]); - } - - return \React\Promise\reject([ - 'error' => error_get_last(), - ]); - } - - /** - * @param array $payload - * @return PromiseInterface - */ - public function chown(array $payload) - { - if (chown($payload['path'], $payload['uid']) && - chgrp($payload['path'], $payload['gid']) - ) { - return \React\Promise\resolve([]); - } - - return \React\Promise\reject([ - 'error' => error_get_last(), - ]); - } - - /** - * @param array $payload - * @return PromiseInterface - */ - public function stat(array $payload) - { - if (!file_exists($payload['path'])) { - return \React\Promise\reject([ - 'error' => ['message' => 'Path doesn\'t exist'], - ]); - } - - $stat = lstat($payload['path']); - return \React\Promise\resolve([ - 'dev' => $stat['dev'], - 'ino' => $stat['ino'], - 'mode' => $stat['mode'], - 'nlink' => $stat['nlink'], - 'uid' => $stat['uid'], - 'size' => $stat['size'], - 'gid' => $stat['gid'], - 'rdev' => $stat['rdev'], - 'blksize' => $stat['blksize'], - 'blocks' => $stat['blocks'], - 'atime' => $stat['atime'], - 'mtime' => $stat['mtime'], - 'ctime' => $stat['ctime'], - ]); - } - - /** - * @param array $payload - * @return PromiseInterface - */ - public function readdir(array $payload) - { - $list = []; - foreach (scandir($payload['path'], $payload['flags']) as $node) { - $path = $payload['path'] . DIRECTORY_SEPARATOR . $node; - if ($node == '.' || $node == '..' || (!is_dir($path) && !is_file($path))) { - continue; - } - - $list[] = [ - 'type' => is_dir($path) ? 'dir' : 'file', - 'name' => $node, - ]; - } - return \React\Promise\resolve($list); - } - - /** - * @param array $payload - * @return PromiseInterface - */ - public function open(array $payload) - { - $this->fd = @fopen($payload['path'], $payload['flags']); - return \React\Promise\resolve([ - 'result' => (string)$this->fd, - ]); - } - - /** - * @param array $payload - * @return PromiseInterface - */ - public function touch(array $payload) - { - return \React\Promise\resolve([ - touch($payload['path']) && chmod($payload['path'], octdec($payload['mode'])), - ]); - } - - /** - * @param array $payload - * @return PromiseInterface - */ - public function read(array $payload) - { - fseek($this->fd, $payload['offset']); - return \React\Promise\resolve([ - 'chunk' => base64_encode(fread($this->fd, $payload['length'])), - ]); - } - - /** - * @param array $payload - * @return PromiseInterface - */ - public function write(array $payload) - { - fseek($this->fd, $payload['offset']); - return \React\Promise\resolve([ - 'written' => fwrite($this->fd, base64_decode($payload['chunk']), $payload['length']), - ]); - } - - /** - * @param array $payload - * @return PromiseInterface - */ - public function close(array $payload) - { - $closed = fclose($this->fd); - $this->fd = null; - return \React\Promise\resolve([ - $closed, - ]); - } - - /** - * @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 - */ - public function rename(array $payload) - { - if (rename($payload['from'], $payload['to'])) { - return \React\Promise\resolve([]); - } - - return \React\Promise\reject([ - 'error' => error_get_last(), - ]); - } - - /** - * @param array $payload - * @return PromiseInterface - */ - public function readlink(array $payload) - { - return \React\Promise\resolve([ - 'path' => readlink($payload['path']), - ]); - } - - /** - * @param array $payload - * @return PromiseInterface - */ - public function symlink(array $payload) - { - return \React\Promise\resolve([ - 'result' => symlink($payload['from'], $payload['to']), - ]); - } -} diff --git a/src/ChildProcess/StatTrait.php b/src/ChildProcess/StatTrait.php new file mode 100644 index 00000000..287a197c --- /dev/null +++ b/src/ChildProcess/StatTrait.php @@ -0,0 +1,31 @@ +then(function (array $stat) use ($path): ?Stat { + if (count($stat) > 0) { + return new Stat($path, $stat); + } + + return null; + }); + } +} diff --git a/src/Factory.php b/src/Factory.php new file mode 100644 index 00000000..b204ec8f --- /dev/null +++ b/src/Factory.php @@ -0,0 +1,26 @@ +internalStat($path)->then(function (?Stat $stat) use ($path) { + if ($stat === null) { + return new NotExist($this, dirname($path) . DIRECTORY_SEPARATOR, basename($path)); + } + + switch (ModeTypeDetector::detect($stat->mode())) { + case Node\DirectoryInterface::class: + return $this->directory($stat->path()); + break; + case Node\FileInterface::class: + return $this->file($stat->path()); + break; + default: + return new Node\Unknown($stat->path(), $stat->path()); + break; + } + }); + } + + public function directory(string $path): Node\DirectoryInterface + { + return new Directory($this, dirname($path) . DIRECTORY_SEPARATOR, basename($path)); + } + + public function file(string $path): Node\FileInterface + { + return new File(dirname($path) . DIRECTORY_SEPARATOR, basename($path)); + } + + +} diff --git a/src/Fallback/Directory.php b/src/Fallback/Directory.php new file mode 100644 index 00000000..7efcdc54 --- /dev/null +++ b/src/Fallback/Directory.php @@ -0,0 +1,67 @@ +filesystem = $filesystem; + $this->path = $path; + $this->name = $name; + } + + public function stat(): PromiseInterface + { + return $this->internalStat($this->path . $this->name); + } + + public function ls(): PromiseInterface + { + $path = $this->path . $this->name; + $promises = []; + foreach (scandir($path) as $node) { + if (in_array($node, ['.', '..'])) { + continue; + } + + $promises[] = $this->filesystem->detect($this->path . $this->name . DIRECTORY_SEPARATOR . $node); + } + + return all($promises); + } + + public function unlink(): PromiseInterface + { + $path = $this->path . $this->name; + if (count(scandir($path)) > 0) { + return resolve(false); + } + + return resolve(rmdir($path)); + } + + public function path(): string + { + return $this->path; + } + + public function name(): string + { + return $this->name; + } +} diff --git a/src/Fallback/File.php b/src/Fallback/File.php new file mode 100644 index 00000000..25c26e7c --- /dev/null +++ b/src/Fallback/File.php @@ -0,0 +1,63 @@ +path = $path; + $this->name = $name; + } + + public function stat(): PromiseInterface + { + return $this->internalStat($this->path . $this->name); + } + + public function getContents(int $offset = 0 , ?int $maxlen = null): PromiseInterface + { + $path = $this->path . $this->name; + return resolve(file_get_contents($path, false, null, $offset, $maxlen ?? (int)stat($path)['size'])); + } + + public function putContents(string $contents, int $flags = 0): PromiseInterface + { + // Making sure we only pass in one flag for security reasons + if (($flags & \FILE_APPEND) == \FILE_APPEND) { + $flags = \FILE_APPEND; + } else { + $flags = 0; + } + + $path = $this->path . $this->name; + return resolve(file_put_contents($path, $contents, $flags)); + } + + public function unlink(): PromiseInterface + { + $path = $this->path . $this->name; + return resolve(unlink($path)); + } + + public function path(): string + { + return $this->path; + } + + public function name(): string + { + return $this->name; + } +} diff --git a/src/Fallback/NotExist.php b/src/Fallback/NotExist.php new file mode 100644 index 00000000..66473b74 --- /dev/null +++ b/src/Fallback/NotExist.php @@ -0,0 +1,76 @@ +filesystem = $filesystem; + $this->path = $path; + $this->name = $name; + } + + public function stat(): PromiseInterface + { + return $this->internalStat($this->path . $this->name); + } + + public function createDirectory(): PromiseInterface + { + $path = $this->path . $this->name; + mkdir($path,0777, true); + return resolve(new Directory($this->filesystem, $this->path, $this->name)); + } + + public function createFile(): PromiseInterface + { + $file = new File($this->path, $this->name); + + return $this->filesystem->detect($this->path)->then(function (Node\NodeInterface $node): PromiseInterface { + if ($node instanceof Node\NotExistInterface) { + return $node->createDirectory(); + } + + return resolve($node); + })->then(function () use ($file): PromiseInterface { + return $file->putContents(''); + })->then(function () use ($file): Node\FileInterface { + return $file; + }); + } + + public function unlink(): PromiseInterface + { + // Essentially a No-OP since it doesn't exist anyway + return resolve(true); + } + + public function path(): string + { + return $this->path; + } + + public function name(): string + { + return $this->name; + } +} diff --git a/src/Fallback/StatTrait.php b/src/Fallback/StatTrait.php new file mode 100644 index 00000000..47148aaf --- /dev/null +++ b/src/Fallback/StatTrait.php @@ -0,0 +1,27 @@ +getAdapter()->setFilesystem($filesystem); - return $filesystem; - } - - /** - * @return string[] - */ - public static function getSupportedAdapters() - { - $adapters = []; - - if (ChildProcess\Adapter::isSupported()) { - $adapters[] = 'ChildProcess'; - } - - return $adapters; - } - - /** - * Filesystem constructor. - * @param AdapterInterface $adapter - */ - private function __construct(AdapterInterface $adapter) - { - $this->adapter = $adapter; - } - - /** - * @return AdapterInterface - */ - public function getAdapter() - { - return $this->adapter; - } - - /** - * @param string $filename - * @return Node\FileInterface - */ - public function file($filename) - { - return new Node\File($filename, $this); - } - - /** - * @param string $path - * @return Node\DirectoryInterface - */ - public function dir($path) - { - return new Node\Directory($path, $this); - } - - /** - * @param string $path - * @param Node\NodeInterface $destination - * @return Node\LinkInterface - */ - public function link($path, Node\NodeInterface $destination) - { - return new Node\Link($path, $destination, $this); - } - - /** - * @param string $path - * @return \React\Promise\PromiseInterface - */ - public function constructLink($path) - { - return $this->adapter->readlink($path)->then(function ($linkPath) { - return $this->adapter->detectType($linkPath); - })->then(function (Node\NodeInterface $destination) use ($path) { - return \React\Promise\resolve($this->link($path, $destination)); - }); - } - - /** - * @param string $filename - * @return \React\Promise\PromiseInterface - */ - public function getContents($filename) - { - $file = $this->file($filename); - return $file->exists()->then(function () use ($file) { - return $file->getContents(); - }); - } -} diff --git a/src/FilesystemInterface.php b/src/FilesystemInterface.php deleted file mode 100644 index d92c0d45..00000000 --- a/src/FilesystemInterface.php +++ /dev/null @@ -1,65 +0,0 @@ - 'dir', - 'file' => 'file', - 'link' => 'constructLink', - ]; - - /** - * @var array - */ - protected $mapping = []; - - /** - * @var FilesystemInterface - */ - protected $filesystem; - - public static function createDefault(FilesystemInterface $filesystem) - { - return new static($filesystem, [ - 'mapping' => static::$defaultMapping, - ]); - } - - /** - * @param FilesystemInterface $filesystem - * @param array $options - */ - public function __construct(FilesystemInterface $filesystem, $options = []) - { - $this->filesystem = $filesystem; - - if (isset($options['mapping']) && is_array($options['mapping']) && count($options['mapping']) > 0) { - $this->mapping = $options['mapping']; - } - } - - /** - * @param array $node - * @return \React\Promise\PromiseInterface - */ - public function detect(array $node) - { - if (!isset($node['type']) || !isset($this->mapping[$node['type']])) { - return \React\Promise\reject(new Exception('Unknown type')); - } - - return \React\Promise\resolve([ - $this->filesystem, - $this->mapping[$node['type']], - ]); - } -} diff --git a/src/ModeTypeDetector.php b/src/ModeTypeDetector.php index de2f5594..23c32ab8 100644 --- a/src/ModeTypeDetector.php +++ b/src/ModeTypeDetector.php @@ -2,65 +2,28 @@ namespace React\Filesystem; -use Exception; -use React\Filesystem\FilesystemInterface; -use React\Filesystem\TypeDetectorInterface; +use React\EventLoop\ExtUvLoop; +use React\EventLoop\LoopInterface; +use React\Filesystem\Node\DirectoryInterface; +use React\Filesystem\Node\FileInterface; +use React\Filesystem\Node\Unknown; -class ModeTypeDetector implements TypeDetectorInterface +final class ModeTypeDetector { - /** - * @var array - */ - protected $mapping = [ - 0xa000 => 'constructLink', - 0x4000 => 'dir', - 0x8000 => 'file', - ]; + private const FILE = 0x8000; + private const DIRECTORY = 0x4000; + private const LINK = 0xa000; - /** - * @var FilesystemInterface - */ - protected $filesystem; - - /** - * @param FilesystemInterface $filesystem - */ - public function __construct(FilesystemInterface $filesystem) + public static function detect(int $mode): string { - $this->filesystem = $filesystem; - } - - /** - * @param array $node - * @return \React\Promise\PromiseInterface - */ - public function detect(array $node) - { - return $this->filesystem->getAdapter()->stat($node['path'])->then(function ($stat) { - return $this->walkMapping($stat); - }); - } - - protected function walkMapping($stat) - { - $promiseChain = \React\Promise\reject(new Exception('Unknown type')); - foreach ($this->mapping as $mappingMode => $method) { - $promiseChain = $promiseChain->otherwise(function () use ($stat, $mappingMode, $method) { - return $this->matchMapping($stat['mode'], $mappingMode, $method); - }); + if (($mode & self::FILE) == self::FILE) { + return FileInterface::class; } - return $promiseChain; - } - protected function matchMapping($mode, $mappingMode, $method) - { - if (($mode & $mappingMode) == $mappingMode) { - return \React\Promise\resolve([ - $this->filesystem, - $method, - ]); + if (($mode & self::DIRECTORY) == self::DIRECTORY) { + return DirectoryInterface::class; } - return \React\Promise\reject(new Exception('Unknown filesystem method for type')); + return Unknown::class; } } diff --git a/src/Node/Directory.php b/src/Node/Directory.php deleted file mode 100644 index 3556254a..00000000 --- a/src/Node/Directory.php +++ /dev/null @@ -1,362 +0,0 @@ -path . NodeInterface::DS; - } - - /** - * @return RecursiveInvoker - */ - protected function getRecursiveInvoker() - { - if ($this->recursiveInvoker instanceof RecursiveInvoker) { - return $this->recursiveInvoker; - } - - $this->recursiveInvoker = new RecursiveInvoker($this); - return $this->recursiveInvoker; - } - - /** - * @param $path - * @param FilesystemInterface $filesystem - * @param RecursiveInvoker $recursiveInvoker - */ - public function __construct($path, FilesystemInterface $filesystem, RecursiveInvoker $recursiveInvoker = null) - { - $this->filesystem = $filesystem; - $this->adapter = $filesystem->getAdapter(); - - $this->createNameNParentFromFilename($path); - $this->recursiveInvoker = $recursiveInvoker; - } - - /** - * {@inheritDoc} - */ - public function ls() - { - return $this->adapter->ls($this->path); - } - - /** - * {@inheritDoc} - */ - public function lsStreaming() - { - return $this->adapter->lsStream($this->path); - } - - /** - * {@inheritDoc} - */ - public function size($recursive = false) - { - return $this->ls()->then(function ($result) use ($recursive) { - return $this->processSizeContents($result, $recursive); - }); - } - - /** - * @param $nodes - * @param $recursive - * @return \React\Promise\Promise - */ - protected function processSizeContents($nodes, $recursive) - { - $numbers = [ - 'directories' => 0, - 'files' => 0, - 'size' => 0, - ]; - - $promises = []; - foreach ($nodes as $node) { - switch (true) { - case $node instanceof Directory: - $numbers['directories']++; - if ($recursive) { - $promises[] = $node->size()->then(function ($size) use (&$numbers) { - $numbers['directories'] += $size['directories']; - $numbers['files'] += $size['files']; - $numbers['size'] += $size['size']; - }); - } - break; - case $node instanceof File: - $numbers['files']++; - $promises[] = $node->size()->then(function ($size) use (&$numbers) { - $numbers['size'] += $size; - }); - break; - } - } - - return \React\Promise\all($promises)->then(function () use (&$numbers) { - return $numbers; - }); - } - - /** - * {@inheritDoc} - */ - public function create($mode = AdapterInterface::CREATION_MODE) - { - return $this->adapter->mkdir($this->path, $mode)->then(function () { - $deferred = new Deferred(); - - $check = function () use (&$check, $deferred) { - $this->stat()->then(function () use ($deferred) { - $deferred->resolve(); - }, function () use (&$check) { - $this->adapter->getLoop()->addTimer(0.1, $check); - }); - }; - - $check(); - - return $deferred->promise(); - }); - } - - /** - * {@inheritDoc} - */ - public function remove() - { - return $this->adapter->rmdir($this->path); - } - - - /** - * {@inheritdoc} - */ - public function rename($toDirectoryName) - { - return $this->adapter->rename($this->path, $toDirectoryName)->then(function () use ($toDirectoryName) { - return $this->filesystem->dir($toDirectoryName); - }); - } - - /** - * {@inheritDoc} - */ - public function createRecursive($mode = AdapterInterface::CREATION_MODE) - { - $parentPath = explode(DIRECTORY_SEPARATOR, $this->path); - array_pop($parentPath); - $parentPath = implode(DIRECTORY_SEPARATOR, $parentPath); - - $parentDirectory = $this->filesystem->dir($parentPath); - return $parentDirectory->stat()->then(null, function () use ($parentDirectory, $mode) { - return $parentDirectory->createRecursive($mode); - })->then(function () use ($mode) { - return $this->create($mode); - })->then(function () { - return null; - }); - } - - /** - * {@inheritDoc} - */ - public function chmodRecursive($mode) - { - return $this->getRecursiveInvoker()->execute('chmod', [$mode]); - } - - /** - * {@inheritDoc} - */ - public function chownRecursive($uid = -1, $gid = -1) - { - return $this->getRecursiveInvoker()->execute('chown', [$uid, $gid]); - } - - /** - * {@inheritDoc} - */ - public function removeRecursive() - { - return $this->getRecursiveInvoker()->execute('remove', []); - } - - /** - * {@inheritDoc} - */ - public function sizeRecursive() - { - return $this->size(true); - } - - /** - * {@inheritDoc} - */ - public function lsRecursive() - { - return ObjectStreamSink::promise($this->lsRecursiveStreaming()); - } - - /** - * {@inheritDoc} - */ - public function lsRecursiveStreaming() - { - return $this->processLsRecursiveContents($this->lsStreaming()); - } - - /** - * @param $sourceStream - * @return ObjectStream - */ - protected function processLsRecursiveContents($sourceStream) - { - $stream = new ObjectStream(); - $closeCount = 0; - $sourceStream->on('data', function (NodeInterface $node) use (&$closeCount, $stream) { - $stream->write($node); - if ($node instanceof Directory) { - $this->streamLsIntoStream($node, $stream, $closeCount); - } - }); - - $sourceStream->on('end', function () use (&$closeCount, $stream) { - $this->adapter->getLoop()->addPeriodicTimer(0.01, function ($timer) use (&$closeCount, $stream) { - if ($closeCount === 0) { - $this->adapter->getLoop()->cancelTimer($timer); - $stream->close(); - } - }); - }); - - return $stream; - } - - /** - * @param DirectoryInterface $node - * @param $stream - * @param $closeCount - */ - protected function streamLsIntoStream(DirectoryInterface $node, $stream, &$closeCount) - { - $closeCount++; - $nodeStream = $node->lsRecursiveStreaming(); - $nodeStream->on('end', function () use (&$closeCount) { - $closeCount--; - }); - $nodeStream->pipe($stream, [ - 'end' => false, - ]); - } - - /** - * @param NodeInterface $node - * @return \React\Promise\Promise - */ - public function copy(NodeInterface $node) - { - return ObjectStreamSink::promise($this->copyStreaming($node)); - } - - /** - * @param NodeInterface $node - * @return ObjectStream - */ - public function copyStreaming(NodeInterface $node) - { - if ($node instanceof DirectoryInterface) { - return $this->copyToDirectory($node); - } - - throw new \UnexpectedValueException('Unsupported node type'); - } - - /** - * @param DirectoryInterface $targetNode - * @return ObjectStream - */ - protected function copyToDirectory(DirectoryInterface $targetNode) - { - $promises = []; - $objectStream = new ObjectStream(); - - $stream = $this->lsStreaming(); - $stream->on('data', function (NodeInterface $node) use ($targetNode, &$promises, $objectStream) { - $deferred = new Deferred(); - $promises[] = $deferred->promise(); - - $stream = $this->handleStreamingCopyNode($node, $targetNode); - $stream->on('end', function () use ($deferred) { - $deferred->resolve(); - }); - $stream->pipe($objectStream , [ - 'end' => false, - ]); - }); - $stream->on('end', function () use ($objectStream, &$promises, $targetNode) { - \React\Promise\all($promises)->then(function () use ($objectStream, $targetNode) { - $objectStream->end(); - }); - }); - - return $objectStream; - } - - /** - * @param NodeInterface $node - * @param DirectoryInterface $targetNode - * @return ObjectStream - */ - protected function handleStreamingCopyNode(NodeInterface $node, DirectoryInterface $targetNode) - { - if ($node instanceof FileInterface) { - return $node->copyStreaming($targetNode); - } - - if ($node instanceof DirectoryInterface) { - $stream = new ObjectStream(); - $newDir = $targetNode->getFilesystem()->dir($targetNode->getPath() . $node->getName()); - - $newDir->stat()->then(null, function () use ($newDir) { - return $newDir->createRecursive(); - })->then(function () use ($node, $newDir, $stream) { - $node->copyStreaming($newDir)->pipe($stream); - }); - - return $stream; - } - } -} diff --git a/src/Node/DirectoryInterface.php b/src/Node/DirectoryInterface.php index fea6bc97..8796f367 100644 --- a/src/Node/DirectoryInterface.php +++ b/src/Node/DirectoryInterface.php @@ -4,77 +4,12 @@ use React\Filesystem\AdapterInterface; use React\Promise\PromiseInterface; +use Rx\Observable; interface DirectoryInterface extends NodeInterface { /** - * Create the directory, fails when directory already exists or when parent doesn't exist. - * - * @return PromiseInterface + * @return PromiseInterface> */ - public function create($mode = AdapterInterface::CREATION_MODE); - - /** - * Create the directory, creating any parent that doesn't exist. - * - * @return PromiseInterface - */ - public function createRecursive($mode = AdapterInterface::CREATION_MODE); - - /** - * Remove the directory, fails when it has contents. - * - * @return PromiseInterface - */ - public function remove(); - - /** - * Rename the directory and return the new directory through a promise - * - * @param string $toDirectoryName - * @return PromiseInterface - */ - public function rename($toDirectoryName); - - /** - * List contents of the directory. - * - * @return PromiseInterface - */ - public function ls(); - - /** - * List contents of the directory and any child directories recursively. - * - * @return PromiseInterface - */ - public function lsRecursive(); - - /** - * Change mode recursively. - * - * @param int $mode - * @return PromiseInterface - */ - public function chmodRecursive($mode); - - /** - * Change owner recursively. - * - * @return PromiseInterface - */ - public function chownRecursive(); - - /** - * Remove the directory and all its contents recursively. - * - * @return PromiseInterface - */ - public function removeRecursive(); - - /** - * @param DirectoryInterface $directory - * @return PromiseInterface - */ - //public function rsync(DirectoryInterface $directory); + public function ls(): PromiseInterface; } diff --git a/src/Node/File.php b/src/Node/File.php deleted file mode 100644 index 55a684c4..00000000 --- a/src/Node/File.php +++ /dev/null @@ -1,228 +0,0 @@ -filesystem = $filesystem; - $this->adapter = $filesystem->getAdapter(); - $this->createNameNParentFromFilename($filename); - } - - /** - * {@inheritDoc} - */ - public function exists() - { - return $this->stat()->then(function () { - return null; - }, function () { - throw new Exception('Not found'); - }); - } - - /** - * {@inheritDoc} - */ - public function size() - { - return $this->adapter->stat($this->path)->then(function ($result) { - return $result['size']; - }); - } - - /** - * {@inheritDoc} - */ - public function time() - { - return $this->adapter->stat($this->path)->then(function ($result) { - return [ - 'atime' => $result['atime'], - 'ctime' => $result['ctime'], - 'mtime' => $result['mtime'], - ]; - }); - } - - /** - * {@inheritDoc} - */ - public function rename($toFilename) - { - return $this->adapter->rename($this->path, $toFilename)->then(function () use ($toFilename) { - return $this->filesystem->file($toFilename); - }); - } - - /** - * {@inheritDoc} - */ - public function create($mode = AdapterInterface::CREATION_MODE, $time = null) - { - return $this->stat()->then(function () { - throw new \Exception('File exists already'); - }, function () use ($mode, $time) { - return $this->adapter->touch($this->path, $mode, $time); - }); - } - - /** - * {@inheritDoc} - */ - public function touch($mode = AdapterInterface::CREATION_MODE, $time = null) - { - return $this->adapter->touch($this->path, $mode, $time); - } - - /** - * {@inheritDoc} - */ - public function open($flags, $mode = AdapterInterface::CREATION_MODE) - { - if ($this->open === true) { - return \React\Promise\reject(new Exception('File is already open')); - } - - return $this->adapter->open($this->path, $flags, $mode)->then(function ($fd) use ($flags) { - $this->open = true; - $this->fileDescriptor = $fd; - return StreamFactory::create($this->path, $fd, $flags, $this->adapter); - }); - } - - /** - * {@inheritDoc} - */ - public function close() - { - if ($this->open === false) { - return \React\Promise\reject(new Exception('File is already closed')); - } - - return $this->adapter->close($this->fileDescriptor)->then(function () { - $this->open = false; - $this->fileDescriptor = null; - }); - } - - /** - * {@inheritDoc} - */ - public function getContents() - { - return $this->adapter->getContents($this->path); - } - - /** - * {@inheritDoc} - */ - public function putContents($contents) - { - return $this->adapter->putContents($this->path, $contents); - } - - /** - * {@inheritDoc} - */ - public function remove() - { - return $this->adapter->unlink($this->path); - } - - /** - * @param NodeInterface $node - * @return \React\Promise\PromiseInterface - */ - public function copy(NodeInterface $node) - { - return ObjectStreamSink::promise($this->copyStreaming($node)); - } - - /** - * @param NodeInterface $node - * @return ObjectStream - */ - public function copyStreaming(NodeInterface $node) - { - if ($node instanceof FileInterface) { - return $this->copyToFile($node); - } - - if ($node instanceof DirectoryInterface) { - return $this->copyToDirectory($node); - } - - throw new \UnexpectedValueException('Unsupported node type'); - } - - /** - * @param FileInterface $node - * @return ObjectStream - */ - protected function copyToFile(FileInterface $node) - { - $stream = new ObjectStream(); - - $this->open('r')->then(function (ReadableStreamInterface $readStream) use ($node) { - $readStream->pause(); - - return \React\Promise\all([ - 'read' => $readStream, - 'write' => $node->open('ctw'), - ]); - })->then(function (array $streams) use ($stream, $node) { - $streams['read']->pipe($streams['write']); - $streams['read']->on('close', function () use ($streams, $stream, $node) { - $streams['write']->close(); - $stream->end($node); - }); - $streams['read']->resume(); - })->done(); - - return $stream; - } - - /** - * @param DirectoryInterface $node - * @return ObjectStream - */ - protected function copyToDirectory(DirectoryInterface $node) - { - return $this->copyToFile($node->getFilesystem()->file($node->getPath() . $this->getName())); - } -} diff --git a/src/Node/FileInterface.php b/src/Node/FileInterface.php index 0deb1300..017ec23f 100644 --- a/src/Node/FileInterface.php +++ b/src/Node/FileInterface.php @@ -2,89 +2,22 @@ namespace React\Filesystem\Node; -use React\Filesystem\AdapterInterface; use React\Promise\PromiseInterface; interface FileInterface extends NodeInterface { - /** - * Returns true when the file exists - * - * @return PromiseInterface - */ - public function exists(); - - /** - * Remove the file - * - * @return PromiseInterface - */ - public function remove(); - - /** - * Open the file and return a promise resolve into a stream that can be read from or written to. - * - * @param $flags - * @param string $mode - * @return PromiseInterface - */ - public function open($flags, $mode = AdapterInterface::CREATION_MODE); - - /** - * Return the change time, access time, and modification time - * - * @return PromiseInterface - */ - public function time(); - - /** - * Rename the file and return the new file through a promise - * - * @param string $toFilename - * @return PromiseInterface - */ - public function rename($toFilename); - - /** - * Return the size of the file. - * - * @return PromiseInterface - */ - public function size(); - - /** - * Create the file - * - * @param string $mode - * @param null $time - * @return PromiseInterface - * - * @throws \Exception When the file already exists - */ - public function create($mode = AdapterInterface::CREATION_MODE, $time = null); - - /** - * Touch the file, modifying it's mtime when it exists, - * or creating the file when it doesn't it exists. - * - * @param string $mode - * @param null $time - * @return PromiseInterface - */ - public function touch($mode = AdapterInterface::CREATION_MODE, $time = null); - /** * Open the file and read all its contents returning those through a promise. * * @return PromiseInterface */ - public function getContents(); + public function getContents(int $offset = 0 , ?int $maxlen = null); /** * Write the given contents to the file, overwriting any existing contents or creating the file. * * @param string $contents - * @return PromiseInterface + * @return PromiseInterface */ - public function putContents($contents); + public function putContents(string $contents, int $flags = 0); } diff --git a/src/Node/GenericOperationInterface.php b/src/Node/GenericOperationInterface.php deleted file mode 100644 index 08d9f6df..00000000 --- a/src/Node/GenericOperationInterface.php +++ /dev/null @@ -1,40 +0,0 @@ -path = $path; - - $path = rtrim($path, NodeInterface::DS); - - $pathBits = explode(NodeInterface::DS, $path); - $this->name = array_pop($pathBits); - - if (count($pathBits) > 0) { - $this->parent = $this->filesystem->dir(implode(NodeInterface::DS, $pathBits)); - $this->path = $this->parent->getPath() . $this->getName(); - } - } - - /** - * {@inheritDoc} - */ - public function getPath() - { - return $this->path; - } - - /** - * {@inheritDoc} - */ - public function getName() - { - return $this->name; - } - - /** - * {@inheritDoc} - */ - public function getParent() - { - return $this->parent; - } - - /** - * {@inheritDoc} - */ - public function __toString() - { - return $this->getPath(); - } - - /** - * {@inheritDoc} - */ - public function getFilesystem() - { - return $this->filesystem; - } - - /** - * {@inheritDoc} - */ - public function stat() - { - return $this->adapter->stat($this->getPath()); - } - - /** - * {@inheritDoc} - */ - public function chmod($mode) - { - return $this->adapter->chmod($this->getPath(), $mode); - } - - /** - * {@inheritDoc} - */ - public function chown($uid = -1, $gid = -1) - { - return $this->adapter->chown($this->getPath(), $uid, $gid); - } -} diff --git a/src/Node/Link.php b/src/Node/Link.php deleted file mode 100644 index fd86df52..00000000 --- a/src/Node/Link.php +++ /dev/null @@ -1,63 +0,0 @@ -node = $node; - $this->filesystem = $filesystem; - $this->adapter = $filesystem->getAdapter(); - $this->createNameNParentFromFilename($path); - } - - /** - * @inheritDoc - */ - public function getDestination() - { - return $this->node; - } - - /** - * @inheritDoc - */ - public function copy(NodeInterface $node) - { - return $this->node->copy($node); - } - - /** - * @inheritDoc - */ - public function copyStreaming(NodeInterface $node) - { - return $this->node->copyStreaming($node); - } - - /** - * @param string $name - * @param array $arguments - * @return mixed - */ - public function __call($name, $arguments) - { - return $this->node->$name(...$arguments); - } -} diff --git a/src/Node/LinkInterface.php b/src/Node/LinkInterface.php deleted file mode 100644 index ff5566b4..00000000 --- a/src/Node/LinkInterface.php +++ /dev/null @@ -1,13 +0,0 @@ - */ - public function copy(NodeInterface $node); + public function stat(): PromiseInterface; /** - * Copy this node to the given node streaming. The returned object is a stream, - * that emits events for each copy node during this operation. + * Remove the node from the filesystem, errors on non-empty directories * - * @param NodeInterface $node - * @return ObjectStream + * @return PromiseInterface */ - public function copyStreaming(NodeInterface $node); + public function unlink(): PromiseInterface; } diff --git a/src/Node/NotExistInterface.php b/src/Node/NotExistInterface.php new file mode 100644 index 00000000..ca19b5d8 --- /dev/null +++ b/src/Node/NotExistInterface.php @@ -0,0 +1,19 @@ + + */ + public function createDirectory(): PromiseInterface; + + /** + * @return PromiseInterface + */ + public function createFile(): PromiseInterface; +} diff --git a/src/Node/RecursiveInvoker.php b/src/Node/RecursiveInvoker.php deleted file mode 100644 index d6b1c6a5..00000000 --- a/src/Node/RecursiveInvoker.php +++ /dev/null @@ -1,54 +0,0 @@ -node = $node; - } - - /** - * @param string $method - * @param array $args - * @return \React\Promise\Promise - */ - public function execute($method, $args) - { - return $this->node->ls()->then(function ($list) use ($method, $args) { - return $this->iterateNode($list, $method, $args); - }); - } - - /** - * @param $list - * @param $method - * @param $args - * @return \React\Promise\PromiseInterface - */ - protected function iterateNode($list, $method, $args) - { - $promises = []; - - foreach ($list as $node) { - if ($node instanceof Directory) { - $promises[] = call_user_func_array([$node, $method . 'Recursive'], $args); - } else { - $promises[] = call_user_func_array([$node, $method], $args); - } - } - - return \React\Promise\all($promises)->then(function () use ($method, $args) { - return call_user_func_array([$this->node, $method], $args); - }); - } -} diff --git a/src/NodeNotFound.php b/src/NodeNotFound.php new file mode 100644 index 00000000..08f2b729 --- /dev/null +++ b/src/NodeNotFound.php @@ -0,0 +1,18 @@ +closed; - } - - public function pause() - { - } - - public function resume() - { - } - - public function pipe(WritableStreamInterface $dest, array $options = array()) - { - Util::pipe($this, $dest, $options); - - return $dest; - } - public function write($data) - { - $this->emit('data', array($data, $this)); - } - - public function end($data = null) - { - if (null !== $data) { - $this->write($data); - } - - $this->close(); - } - - public function isWritable() - { - return !$this->closed; - } - - public function close() - { - if ($this->closed) { - return; - } - - $this->closed = true; - $this->emit('end', array($this)); - $this->emit('close', array($this)); - $this->removeAllListeners(); - } -} diff --git a/src/ObjectStreamSink.php b/src/ObjectStreamSink.php deleted file mode 100644 index 74613639..00000000 --- a/src/ObjectStreamSink.php +++ /dev/null @@ -1,27 +0,0 @@ -on('data', function ($object) use (&$list) { - $list[] = $object; - }); - $stream->on('end', function () use ($deferred, &$list) { - $deferred->resolve($list); - }); - - return $deferred->promise(); - } -} diff --git a/src/OpenFileLimiter.php b/src/OpenFileLimiter.php deleted file mode 100644 index 0f52063a..00000000 --- a/src/OpenFileLimiter.php +++ /dev/null @@ -1,85 +0,0 @@ -limit = $limit; - $this->promises = new \SplQueue(); - } - - /** - * @return \React\Promise\PromiseInterface - */ - public function open() - { - if ($this->current < $this->limit) { - $this->current++; - return \React\Promise\resolve(); - } - - $deferred = new Deferred(); - $this->promises->enqueue($deferred); - return $deferred->promise(); - } - - public function close() - { - if (!$this->promises->isEmpty()) { - $this->promises->dequeue()->resolve(); - } else { - $this->current--; - } - } - - /** - * @return int - */ - public function getLimit() - { - return $this->limit; - } - - /** - * @return int - */ - public function getOutstanding() - { - return $this->current; - } - - /** - * @return int - */ - public function getQueueSize() - { - return $this->promises->count(); - } -} diff --git a/src/PermissionFlagResolver.php b/src/PermissionFlagResolver.php deleted file mode 100644 index 8cdaa9c0..00000000 --- a/src/PermissionFlagResolver.php +++ /dev/null @@ -1,66 +0,0 @@ - [ - 'w' => 128, - 'x' => 64, - 'r' => 256, - ], - 'group' => [ - 'w' => 16, - 'x' => 8, - 'r' => 32, - ], - 'universe' => [ - 'w' => 2, - 'x' => 1, - 'r' => 4, - ], - ]; - - /** - * {@inheritDoc} - */ - public function defaultFlags() - { - return static::DEFAULT_FLAG; - } - - /** - * {@inheritDoc} - */ - public function flagMapping() - { - return $this->flagMapping[$this->currentScope]; - } - - /** - * {@inheritDoc} - */ - public function resolve($flag, $flags = null, $mapping = null) - { - $resultFlags = 0; - $start = 0; - - foreach ([ - 'universe', - 'group', - 'user', - ] as $scope) { - $this->currentScope = $scope; - $start -= 3; - $chunk = substr($flag, $start, 3); - $resultFlags |= parent::resolve($chunk, $flags, $mapping); - } - - return $resultFlags; - } -} diff --git a/src/PollInterface.php b/src/PollInterface.php new file mode 100644 index 00000000..18f04241 --- /dev/null +++ b/src/PollInterface.php @@ -0,0 +1,12 @@ +deferred = $deferred; - $this->function = $function; - $this->args = $args; - $this->errorResultCode = $errorResultCode; - } - - /** - * @return Deferred - */ - public function getDeferred() - { - return $this->deferred; - } - - /** - * @return string - */ - public function getFunction() - { - return $this->function; - } - - /** - * @return array - */ - public function getArgs() - { - return $this->args; - } - - /** - * @return int - */ - public function getErrorResultCode() - { - return $this->errorResultCode; - } -} diff --git a/src/Stat.php b/src/Stat.php new file mode 100644 index 00000000..e834bae5 --- /dev/null +++ b/src/Stat.php @@ -0,0 +1,59 @@ + */ + private array $data; + + public function __construct(string $path, array $data) + { + $this->path = $path; + $this->data = $data; + } + + public function path(): string + { + return $this->path; + } + + public function mode(): ?int + { + return array_key_exists('mode', $this->data) ? $this->data['mode'] : null; + } + + public function uid(): ?int + { + return array_key_exists('uid', $this->data) ? $this->data['uid'] : null; + } + + public function gid(): ?int + { + return array_key_exists('gid', $this->data) ? $this->data['gid'] : null; + } + + public function size(): ?int + { + return array_key_exists('size', $this->data) ? $this->data['size'] : null; + } + + public function atime(): ?\DateTimeImmutable + { + return array_key_exists('atime', $this->data) ? new \DateTimeImmutable('@' . $this->data['atime']) : null; + } + + public function mtime(): ?\DateTimeImmutable + { + return array_key_exists('mtime', $this->data) ? new \DateTimeImmutable('@' . $this->data['mtime']) : null; + } + + public function ctime(): ?\DateTimeImmutable + { + return array_key_exists('ctime', $this->data) ? new \DateTimeImmutable('@' . $this->data['ctime']) : null; + } +} diff --git a/src/Stream/DuplexStream.php b/src/Stream/DuplexStream.php deleted file mode 100644 index 37aec595..00000000 --- a/src/Stream/DuplexStream.php +++ /dev/null @@ -1,48 +0,0 @@ -path = $path; - $this->setFilesystem($filesystem); - $this->fileDescriptor = $fileDescriptor; - } - - protected function readChunk() - { - if ($this->pause) { - return; - } - - $this->resolveSize()->then(function () { - $this->performRead($this->calculateChunkSize()); - }); - } - - protected function resolveSize() - { - if ($this->readCursor < $this->size) { - return \React\Promise\resolve(); - } - - return $this->getFilesystem()->stat($this->path)->then(function ($stat) { - $this->size = $stat['size']; - }); - } -} diff --git a/src/Stream/GenericStreamInterface.php b/src/Stream/GenericStreamInterface.php deleted file mode 100644 index 061fdbd8..00000000 --- a/src/Stream/GenericStreamInterface.php +++ /dev/null @@ -1,11 +0,0 @@ -path = $path; - $this->filesystem = $filesystem; - $this->fileDescriptor = $fileDescriptor; - } - - /** - * @return AdapterInterface - */ - public function getFilesystem() - { - return $this->filesystem; - } - - /** - * @param AdapterInterface $filesystem - */ - public function setFilesystem($filesystem) - { - $this->filesystem = $filesystem; - } - - /** - * {@inheritDoc} - */ - public function getFiledescriptor() - { - return $this->fileDescriptor; - } - - /** - * @return bool - */ - public function isClosed() - { - return $this->closed; - } - - /** - * @param bool $closed - */ - public function setClosed($closed) - { - $this->closed = $closed; - } - - /** - * @return string - */ - public function getPath() - { - return $this->path; - } - - /** - * @param string $path - */ - public function setPath($path) - { - $this->path = $path; - } - - /** - * {@inheritDoc} - */ - public function close() - { - if ($this->closed) { - return; - } - - $this->closed = true; - - $this->filesystem->close($this->fileDescriptor)->then(function () { - $this->emit('close', [$this]); - $this->removeAllListeners(); - }); - } -} diff --git a/src/Stream/ReadableStream.php b/src/Stream/ReadableStream.php deleted file mode 100644 index cdb59867..00000000 --- a/src/Stream/ReadableStream.php +++ /dev/null @@ -1,27 +0,0 @@ -path = $path; - $this->filesystem = $filesystem; - $this->fileDescriptor = $fileDescriptor; - - $this->resume(); - } -} diff --git a/src/Stream/ReadableStreamTrait.php b/src/Stream/ReadableStreamTrait.php deleted file mode 100644 index 378a0644..00000000 --- a/src/Stream/ReadableStreamTrait.php +++ /dev/null @@ -1,108 +0,0 @@ -pause || $this->isReading) { - return; - } - - $this->pause = false; - - if ($this->size === null && $this->sizeLookupPromise === null) { - $this->sizeLookupPromise = $this->getFilesystem()->stat($this->getPath())->then(function ($info) { - if ($this->size !== null) { - throw new Exception('File was already stat-ed'); - } - - $this->size = $info['size']; - $this->readCursor = 0; - }); - } - - $this->sizeLookupPromise->then(function () { - $this->readChunk(); - }); - } - - public function pause() - { - $this->pause = true; - } - - public function pipe(WritableStreamInterface $dest, array $options = []) - { - if ($this === $dest) { - throw new \Exception('Can\'t pipe stream into itself!'); - } - - Util::pipe($this, $dest, $options); - - return $dest; - } - - public function isReadable() - { - return !$this->isClosed(); - } - - protected function readChunk() - { - if ($this->pause || $this->isReading) { - return; - } - - $this->performRead($this->calculateChunkSize()); - } - - protected function calculateChunkSize() - { - if ($this->readCursor + $this->chunkSize > $this->size) { - return $this->size - $this->readCursor; - } - - return $this->chunkSize; - } - - protected function performRead($chunkSize) - { - $this->isReading = true; - $this->getFilesystem()->read($this->getFileDescriptor(), $chunkSize, $this->readCursor)->then(function ($data) use ($chunkSize) { - $this->isReading = false; - if ($this->pause) { - return; - } - - // If chunk size can be set make sure to copy it before running this operation so - // that used can't change it mid operation and cause funkyness. - $this->readCursor += $chunkSize; - $this->emit('data', [ - $data, - $this, - ]); - - if ($this->readCursor < $this->size) { - $this->readChunk(); - } else { - $this->emit('end', [$this]); - $this->close(); - } - }); - } - - abstract function close(); -} diff --git a/src/Stream/StreamFactory.php b/src/Stream/StreamFactory.php deleted file mode 100644 index e2fe2e17..00000000 --- a/src/Stream/StreamFactory.php +++ /dev/null @@ -1,28 +0,0 @@ -path = $path; - $this->filesystem = $filesystem; - $this->fileDescriptor = $fileDescriptor; - } -} diff --git a/src/Stream/WritableStreamTrait.php b/src/Stream/WritableStreamTrait.php deleted file mode 100644 index 446dd47a..00000000 --- a/src/Stream/WritableStreamTrait.php +++ /dev/null @@ -1,42 +0,0 @@ -writeCursor; - $this->writeCursor += $length; - - return $this->getFilesystem()->write($this->getFileDescriptor(), $data, $length, $offset); - } - - /** - * {@inheritDoc} - */ - public function end($data = null) - { - if (null !== $data) { - $this->write($data); - } - - $this->close(); - } - - /** - * {@inheritDoc} - */ - public function isWritable() - { - return !$this->isClosed(); - } - - abstract function close(); -} diff --git a/src/TypeDetectorInterface.php b/src/TypeDetectorInterface.php index 16a73711..300af831 100644 --- a/src/TypeDetectorInterface.php +++ b/src/TypeDetectorInterface.php @@ -5,9 +5,9 @@ interface TypeDetectorInterface { /** - * @param FilesystemInterface $filesystem + * @param AdapterInterface $filesystem */ - public function __construct(FilesystemInterface $filesystem); + public function __construct(AdapterInterface $filesystem); /** * @param array $node diff --git a/src/Uv/Adapter.php b/src/Uv/Adapter.php new file mode 100644 index 00000000..e5e49e5b --- /dev/null +++ b/src/Uv/Adapter.php @@ -0,0 +1,79 @@ +loop = $loop; + $this->poll = new Poll($this->loop); + $this->uvLoop = $loop->getUvLoop(); + } + + public function detect(string $path): PromiseInterface + { + return $this->internalStat($path)->then(function (?Stat $stat) use ($path) { + if ($stat === null) { + return new NotExist($this->poll, $this, $this->loop, dirname($path) . DIRECTORY_SEPARATOR, basename($path)); + } + + switch (ModeTypeDetector::detect($stat->mode())) { + case Node\DirectoryInterface::class: + return $this->directory($stat->path()); + break; + case Node\FileInterface::class: + return $this->file($stat->path()); + break; + default: + return new Node\Unknown($stat->path(), $stat->path()); + break; + } + }); + } + + public function directory(string $path): Node\DirectoryInterface + { + return new Directory($this->poll, $this, $this->loop, dirname($path) . DIRECTORY_SEPARATOR, basename($path)); + } + + public function file(string $path): Node\FileInterface + { + return new File($this->poll, $this->loop, dirname($path) . DIRECTORY_SEPARATOR, basename($path)); + } + + + protected function uvLoop() + { + return $this->uvLoop; + } + + protected function activate(): void + { + $this->poll->activate(); + } + + protected function deactivate(): void + { + $this->poll->deactivate(); + } +} diff --git a/src/Uv/Directory.php b/src/Uv/Directory.php new file mode 100644 index 00000000..5e66295b --- /dev/null +++ b/src/Uv/Directory.php @@ -0,0 +1,113 @@ +poll = $poll; + $this->filesystem = $filesystem; + $this->loop = $loop; + $this->uvLoop = $loop->getUvLoop(); + $this->path = $path; + $this->name = $name; + } + + public function stat(): PromiseInterface + { + return $this->internalStat($this->path . $this->name); + } + + public function ls(): PromiseInterface + { + $this->activate(); + $deferred = new Deferred(); + uv_fs_scandir($this->uvLoop, $this->path . $this->name, function (array $contents) use ($deferred): void { + $promises = []; + foreach ($contents as $node) { + $promises[] = $this->filesystem->detect($this->path . $this->name . DIRECTORY_SEPARATOR . $node)->then(null, function (\Throwable $throwable) use ($deferred, &$promises) { + $deferred->reject($throwable); + foreach ($promises as $promise) { + if ($promise instanceof CancellablePromiseInterface) { + $promise->cancel(); + } + } + }); + } + + $deferred->resolve(all($promises)); + $this->deactivate(); + }); + + + return $deferred->promise(); + } + + public function unlink(): PromiseInterface + { + $this->activate(); + $deferred = new Deferred(); + uv_fs_scandir($this->uvLoop, $this->path . $this->name, function (array $contents) use ($deferred): void { + $this->deactivate(); + if (count($contents) > 0) { + $deferred->resolve(false); + + return; + } + + $this->activate(); + uv_fs_rmdir($this->uvLoop, $this->path . $this->name, function () use ($deferred): void { + $this->deactivate(); + $deferred->resolve(true); + }); + }); + + + return $deferred->promise(); + } + + public function path(): string + { + return $this->path; + } + + public function name(): string + { + return $this->name; + } + + protected function uvLoop() + { + return $this->uvLoop; + } + + protected function activate(): void + { + $this->poll->activate(); + } + + protected function deactivate(): void + { + $this->poll->deactivate(); + } +} diff --git a/src/Uv/File.php b/src/Uv/File.php new file mode 100644 index 00000000..f423ef9b --- /dev/null +++ b/src/Uv/File.php @@ -0,0 +1,109 @@ +poll = $poll; + $this->loop = $loop; + $this->uvLoop = $loop->getUvLoop(); + $this->path = $path; + $this->name = $name; + } + + public function stat(): PromiseInterface + { + return $this->internalStat($this->path . $this->name); + } + + public function getContents(int $offset = 0 , ?int $maxlen = null): PromiseInterface + { + $this->activate(); + return new Promise(function (callable $resolve) use ($offset, $maxlen): void { + uv_fs_open($this->uvLoop, $this->path . DIRECTORY_SEPARATOR . $this->name, UV::O_RDONLY, 0, function ($fileDescriptor) use ($resolve, $offset, $maxlen): void { + uv_fs_fstat($this->uvLoop, $fileDescriptor, function ($fileDescriptor, array $stat) use ($resolve, $offset, $maxlen): void { + uv_fs_read($this->uvLoop, $fileDescriptor, $offset, $maxlen ?? (int)$stat['size'], function ($fileDescriptor, string $buffer) use ($resolve): void { + $resolve($buffer); + uv_fs_close($this->uvLoop, $fileDescriptor, function () { + $this->deactivate(); + }); + }); + }); + }); + }); + } + + public function putContents(string $contents, int $flags = 0) + { + $this->activate(); + return new Promise(function (callable $resolve) use ($contents, $flags): void { + uv_fs_open( + $this->uvLoop, + $this->path . DIRECTORY_SEPARATOR . $this->name, + (($flags & \FILE_APPEND) == \FILE_APPEND) ? UV::O_RDWR | UV::O_CREAT | UV::O_APPEND : UV::O_RDWR | UV::O_CREAT, + 0644, + function ($fileDescriptor) use ($resolve, $contents, $flags): void { + uv_fs_write($this->uvLoop, $fileDescriptor, $contents, 0, function ($fileDescriptor, int $bytesWritten) use ($resolve): void { + $resolve($bytesWritten); + uv_fs_close($this->uvLoop, $fileDescriptor, function () { + $this->deactivate(); + }); + }); + } + ); + }); + } + + public function unlink(): PromiseInterface + { + $this->activate(); + return new Promise(function (callable $resolve): void { + uv_fs_unlink($this->uvLoop, $this->path . DIRECTORY_SEPARATOR . $this->name, function () use ($resolve): void { + $resolve(true); + $this->deactivate(); + }); + }); + } + + public function path(): string + { + return $this->path; + } + + public function name(): string + { + return $this->name; + } + + protected function uvLoop() + { + return $this->uvLoop; + } + + protected function activate(): void + { + $this->poll->activate(); + } + + protected function deactivate(): void + { + $this->poll->deactivate(); + } +} diff --git a/src/Uv/NotExist.php b/src/Uv/NotExist.php new file mode 100644 index 00000000..41c2561d --- /dev/null +++ b/src/Uv/NotExist.php @@ -0,0 +1,108 @@ +poll = $poll; + $this->filesystem = $filesystem; + $this->loop = $loop; + $this->uvLoop = $loop->getUvLoop(); + $this->path = $path; + $this->name = $name; + } + + public function stat(): PromiseInterface + { + return $this->internalStat($this->path . $this->name); + } + + public function createDirectory(): PromiseInterface + { + $this->activate(); + return $this->filesystem->detect($this->path)->then(function (Node\NodeInterface $node): PromiseInterface { + if ($node instanceof Node\NotExistInterface) { + return $node->createDirectory(); + } + + return resolve($node); + })->then(function (Node\DirectoryInterface $directory): PromiseInterface { + return new Promise(function (callable $resolve): void { + uv_fs_mkdir($this->uvLoop, $this->path . $this->name, 0777, function () use ($resolve): void { + $resolve(new Directory($this->poll, $this->filesystem, $this->loop, $this->path, $this->name)); + $this->deactivate(); + }); + }); + }); + } + + public function createFile(): PromiseInterface + { + $file = new File($this->poll, $this->loop, $this->path, $this->name); + + return $this->filesystem->detect($this->path . DIRECTORY_SEPARATOR)->then(function (Node\NodeInterface $node): PromiseInterface { + if ($node instanceof Node\NotExistInterface) { + return $node->createDirectory(); + } + + return resolve($node); + })->then(function () use ($file): PromiseInterface { + return $file->putContents(''); + })->then(function () use ($file): Node\FileInterface { + return $file; + }); + } + + public function unlink(): PromiseInterface + { + // Essentially a No-OP since it doesn't exist anyway + return resolve(true); + } + + public function path(): string + { + return $this->path; + } + + public function name(): string + { + return $this->name; + } + + protected function uvLoop() + { + return $this->uvLoop; + } + + protected function activate(): void + { + $this->poll->activate(); + } + + protected function deactivate(): void + { + $this->poll->deactivate(); + } +} diff --git a/src/Uv/Poll.php b/src/Uv/Poll.php new file mode 100644 index 00000000..f8a19c78 --- /dev/null +++ b/src/Uv/Poll.php @@ -0,0 +1,43 @@ +loop = $loop; + } + + public function activate(): void + { + if ($this->workInProgress++ === 0) { + $this->workInProgressTimer = $this->loop->addPeriodicTimer($this->workInterval, static function () {}); + } + } + + public function deactivate(): void + { + if (--$this->workInProgress <= 0) { + $this->loop->cancelTimer($this->workInProgressTimer); + } + } +} diff --git a/src/Uv/StatTrait.php b/src/Uv/StatTrait.php new file mode 100644 index 00000000..95b1a2ed --- /dev/null +++ b/src/Uv/StatTrait.php @@ -0,0 +1,36 @@ +activate(); + uv_fs_stat($this->uvLoop(), $path, function ($stat) use ($path, $resolve, $reject): void { + $this->deactivate(); + if (is_array($stat)) { + $resolve(new Stat($path, $stat)); + } else { + $resolve(null); + } + }); + }); + } + + abstract protected function uvLoop(); // phpcs:disabled + abstract protected function activate(): void; + abstract protected function deactivate(): void; +} diff --git a/src/functions.php b/src/functions.php deleted file mode 100644 index 741f70e2..00000000 --- a/src/functions.php +++ /dev/null @@ -1,37 +0,0 @@ -otherwise(function () use ($node, $detector) { - return $detector->detect($node); - }); - } - - return $promiseChain->then(function ($callable) use ($node) { - return \React\Promise\resolve($callable($node['path'])); - }); -} diff --git a/src/functions_include.php b/src/functions_include.php deleted file mode 100644 index 9152e111..00000000 --- a/src/functions_include.php +++ /dev/null @@ -1,7 +0,0 @@ -> + */ + final public function provideFilesystems(): iterable + { + $loop = EventLoop\Loop::get(); + + yield 'fallback' => [new Fallback\Adapter()]; + + if (DIRECTORY_SEPARATOR !== '\\') { + yield 'childprocess' => [new ChildProcess\Adapter()]; + } + + if (\function_exists('uv_loop_new') && $loop instanceof ExtUvLoop) { + yield 'uv' => [new Uv\Adapter()]; + } + + yield 'factory' => [Factory::create()]; + } + + public function await(PromiseInterface $promise) + { + return await($promise, EventLoop\Loop::get(), 30); + } +} diff --git a/tests/AbstractFlagResolverTest.php b/tests/AbstractFlagResolverTest.php deleted file mode 100644 index f60f38fd..00000000 --- a/tests/AbstractFlagResolverTest.php +++ /dev/null @@ -1,17 +0,0 @@ -assertInstanceOf('React\Filesystem\FlagResolver', $this->resolver); - $this->assertInstanceOf('React\Filesystem\FlagResolverInterface', $this->resolver); - } - - public function testFlagMappingType() - { - $this->assertInternalType('array', $this->resolver->flagMapping()); - } -} diff --git a/tests/Adapters/AbstractAdaptersTest.php b/tests/Adapters/AbstractAdaptersTest.php deleted file mode 100644 index 2846994c..00000000 --- a/tests/Adapters/AbstractAdaptersTest.php +++ /dev/null @@ -1,96 +0,0 @@ -adapterFactory($adapters, 'libevent', function () { - return new EventLoop\LibEventLoop(); - }); - } - - if (class_exists('libev\EventLoop', false)) - { - $this->adapterFactory($adapters, 'libev', function () { - return new EventLoop\LibEvLoop; - }); - } - - if (class_exists('EventBase', false)) - { - $this->adapterFactory($adapters, 'extevent', function () { - return new EventLoop\ExtEventLoop; - }); - } - - $this->adapterFactory($adapters, 'streamselect', function () { - return new EventLoop\StreamSelectLoop(); - }); - - $this->adapterFactory($adapters, 'factory', function () { - return EventLoop\Factory::create(); - }); - - return $adapters; - } - - protected function adapterFactory(&$adapters, $loopSlug, callable $loopFactory) - { - $adapters[$loopSlug . '-factory'] = $this->getFacoryProvider($loopFactory); - $adapters[$loopSlug . '-child-process'] = $this->getChildProcessProvider($loopFactory); - - if (extension_loaded('pthreads')) { - $adapters[$loopSlug . '-pthreads'] = $this->getPthreadsProvider($loopFactory); - } - } - - protected function getChildProcessProvider(callable $loopFactory) - { - $loop = $loopFactory(); - return [ - $loop, - new ChildProcess\Adapter($loop, [Options::TTL => 0.01,]), - ]; - } - - protected function getFacoryProvider(callable $loopFactory) - { - $loop = $loopFactory(); - return [ - $loop, - Filesystem::create($loop)->getAdapter(), - ]; - } - - public function filesystemProvider() - { - $filesystems = []; - - foreach ($this->adapterProvider() as $name => $adapter) { - $filesystems[$name] = [ - $adapter[0], - Filesystem::createFromAdapter($adapter[1]), - ]; - } - - return $filesystems; - } -} diff --git a/tests/Adapters/DirectoryTest.php b/tests/Adapters/DirectoryTest.php deleted file mode 100644 index c0bc5cc3..00000000 --- a/tests/Adapters/DirectoryTest.php +++ /dev/null @@ -1,218 +0,0 @@ -tmpDir . 'path'; - touch($path); - $listing = $this->await($filesystem->dir($this->tmpDir)->ls(), $loop); - $this->assertSame(1, count($listing)); - $this->assertSame($path, reset($listing)->getPath()); - } - - /** - * @dataProvider filesystemProvider - */ - public function testSize(LoopInterface $loop, FilesystemInterface $filesystem) - { - $contents = str_repeat('a', 100); - $path = $this->tmpDir . 'path'; - file_put_contents($path, $contents); - mkdir($this->tmpDir . 'subPath'); - file_put_contents($this->tmpDir . 'subPath/file', $contents); - $size = $this->await($filesystem->dir($this->tmpDir)->size(), $loop); - $this->assertSame([ - 'directories' => 1, - 'files' => 1, - 'size' => 100, - ], $size); - } - - /** - * @dataProvider filesystemProvider - */ - public function testSizeRecursive(LoopInterface $loop, FilesystemInterface $filesystem) - { - $contents = str_repeat('a', 100); - $path = $this->tmpDir . 'path'; - file_put_contents($path, $contents); - mkdir($this->tmpDir . 'subPath'); - file_put_contents($this->tmpDir . 'subPath/file', $contents); - $size = $this->await($filesystem->dir($this->tmpDir)->sizeRecursive(), $loop); - $this->assertSame([ - 'directories' => 1, - 'files' => 2, - 'size' => 200, - ], $size); - } - - /** - * @dataProvider filesystemProvider - */ - public function testCreate(LoopInterface $loop, FilesystemInterface $filesystem) - { - $dir = $this->tmpDir . 'path'; - $this->await($filesystem->dir($dir)->createRecursive(), $loop); - $this->assertFileExists($dir); - $this->assertSame('0760', substr(sprintf('%o', fileperms($dir)), -4)); - } - - /** - * @dataProvider filesystemProvider - */ - public function testCreateRecursive(LoopInterface $loop, FilesystemInterface $filesystem) - { - $dir = $this->tmpDir . 'path' . DIRECTORY_SEPARATOR . 'to' . DIRECTORY_SEPARATOR . 'reactphp' . DIRECTORY_SEPARATOR . 'filesystem'; - $this->await($filesystem->dir($dir)->createRecursive(), $loop); - $this->assertFileExists($dir); - } - - /** - * @dataProvider filesystemProvider - */ - public function testRemove(LoopInterface $loop, FilesystemInterface $filesystem) - { - $dir = $this->tmpDir . 'path'; - mkdir($dir); - $this->assertFileExists($dir); - $this->await($filesystem->dir($dir)->remove(), $loop); - $this->assertFileNotExists($dir); - } - - /** - * @dataProvider filesystemProvider - */ - public function testRemoveSubDir(LoopInterface $loop, FilesystemInterface $filesystem) - { - $dir = $this->tmpDir . 'path'; - $subDir = $this->tmpDir . 'path' . DIRECTORY_SEPARATOR . 'sub'; - mkdir($dir); - mkdir($subDir); - $this->assertFileExists($dir); - $this->assertFileExists($subDir); - $this->await($filesystem->dir($subDir)->remove(), $loop); - $this->assertFileExists($dir); - $this->assertFileNotExists($subDir); - } - - /** - * @dataProvider filesystemProvider - */ - public function testRemoveRecursive(LoopInterface $loop, FilesystemInterface $filesystem) - { - $dir = $this->tmpDir . 'path'; - $subDir = $this->tmpDir . 'path' . DIRECTORY_SEPARATOR . 'sub'; - mkdir($dir); - mkdir($subDir); - $this->assertFileExists($dir); - $this->assertFileExists($subDir); - $this->await($filesystem->dir($dir)->removeRecursive(), $loop); - $this->assertFileNotExists($subDir); - $this->assertFileNotExists($dir); - } - - /** - * @dataProvider filesystemProvider - * @group permissions - */ - public function testChmod(LoopInterface $loop, FilesystemInterface $filesystem) - { - $dir = $this->tmpDir . 'path'; - $subDir = $this->tmpDir . 'path' . DIRECTORY_SEPARATOR . 'sub'; - mkdir($dir); - mkdir($subDir); - chmod($dir, 0777); - chmod($subDir, 0777); - $this->assertFileExists($dir); - $this->assertFileExists($subDir); - $this->assertSame('0777', substr(sprintf('%o', fileperms($dir)), -4)); - $this->assertSame('0777', substr(sprintf('%o', fileperms($subDir)), -4)); - clearstatcache(); - $this->await($filesystem->dir($dir)->chmod(0555), $loop); - clearstatcache(); - $this->assertSame('0555', substr(sprintf('%o', fileperms($dir)), -4)); - $this->assertSame('0777', substr(sprintf('%o', fileperms($subDir)), -4)); - clearstatcache(); - } - - /** - * @dataProvider filesystemProvider - * @group permissions - */ - public function testChmodRecursive(LoopInterface $loop, FilesystemInterface $filesystem) - { - $dir = $this->tmpDir . 'path'; - $subDir = $this->tmpDir . 'path' . DIRECTORY_SEPARATOR . 'sub'; - mkdir($dir); - mkdir($subDir); - chmod($dir, 0777); - chmod($subDir, 0777); - $this->assertFileExists($dir); - $this->assertFileExists($subDir); - $this->assertSame('0777', substr(sprintf('%o', fileperms($dir)), -4)); - $this->assertSame('0777', substr(sprintf('%o', fileperms($subDir)), -4)); - clearstatcache(); - $this->await($filesystem->dir($dir)->chmodRecursive(0555), $loop); - clearstatcache(); - $this->assertSame('0555', substr(sprintf('%o', fileperms($dir)), -4)); - $this->assertSame('0555', substr(sprintf('%o', fileperms($subDir)), -4)); - clearstatcache(); - } - - /** - * @dataProvider filesystemProvider - * @group permissions - */ - public function testChown(LoopInterface $loop, FilesystemInterface $filesystem) - { - $dir = $this->tmpDir . 'path'; - $subDir = $this->tmpDir . 'path' . DIRECTORY_SEPARATOR . 'sub'; - mkdir($dir, 0777); - mkdir($subDir, 0777); - clearstatcache(); - $this->assertFileExists($dir); - $this->assertFileExists($subDir); - clearstatcache(); - $stat = stat($dir); - sleep(2); - $this->await($filesystem->dir($dir)->chown($stat['uid'], getmyuid()), $loop, 5); - clearstatcache(); - $this->assertNotSame($stat, stat($dir)); - clearstatcache(); - } - - /** - * @dataProvider filesystemProvider - * @group permissions - */ - public function testChownRecursive(LoopInterface $loop, FilesystemInterface $filesystem) - { - $dir = $this->tmpDir . 'path'; - $subDir = $this->tmpDir . 'path' . DIRECTORY_SEPARATOR . 'sub'; - mkdir($dir, 0777); - mkdir($subDir, 0777); - $this->assertFileExists($dir); - $this->assertFileExists($subDir); - clearstatcache(); - $stat = stat($dir); - $subStat = stat($subDir); - sleep(2); - $this->await($filesystem->dir($dir)->chownRecursive(-1, getmyuid()), $loop); - clearstatcache(); - $this->assertNotSame($stat, stat($dir)); - $this->assertNotSame($subStat, stat($subDir)); - clearstatcache(); - } -} diff --git a/tests/Adapters/FileTest.php b/tests/Adapters/FileTest.php deleted file mode 100644 index 754ee463..00000000 --- a/tests/Adapters/FileTest.php +++ /dev/null @@ -1,280 +0,0 @@ -await($filesystem->file(__FILE__)->stat(), $loop); - foreach ($actualStat as $key => $value) { - if (!is_string($key) || in_array($key, ['atime', 'mtime', 'ctime'])) { - continue; - } - - $this->assertSame($actualStat[$key], $result[$key]); - } - - $this->assertInstanceOf('DateTime', $result['atime']); - $this->assertEquals($actualStat['atime'], $result['atime']->format('U')); - $this->assertInstanceOf('DateTime', $result['mtime']); - $this->assertEquals($actualStat['mtime'], $result['mtime']->format('U')); - $this->assertInstanceOf('DateTime', $result['atime']); - $this->assertEquals($actualStat['ctime'], $result['ctime']->format('U')); - } - - /** - * @dataProvider filesystemProvider - */ - public function testTime(LoopInterface $loop, FilesystemInterface $filesystem) - { - $actualStat = lstat(__FILE__); - $result = $this->await($filesystem->file(__FILE__)->time(), $loop); - $this->assertCount(3, $result); - $this->assertInstanceOf('DateTime', $result['atime']); - $this->assertEquals($actualStat['atime'], $result['atime']->format('U')); - $this->assertInstanceOf('DateTime', $result['mtime']); - $this->assertEquals($actualStat['mtime'], $result['mtime']->format('U')); - $this->assertInstanceOf('DateTime', $result['atime']); - $this->assertEquals($actualStat['ctime'], $result['ctime']->format('U')); - } - - /** - * @dataProvider filesystemProvider - */ - public function testSize(LoopInterface $loop, FilesystemInterface $filesystem) - { - $actualStat = lstat(__FILE__); - $result = $this->await($filesystem->file(__FILE__)->size(), $loop); - $this->assertEquals($actualStat['size'], $result); - } - - /** - * @dataProvider filesystemProvider - */ - public function testExists(LoopInterface $loop, FilesystemInterface $filesystem) - { - $result = true; - try { - $this->await($filesystem->file(__FILE__)->exists(), $loop); - } catch (\Exception $e) { - $result = false; - } - $this->assertTrue($result); - } - - /** - * @dataProvider filesystemProvider - */ - public function testDoesntExist(LoopInterface $loop, FilesystemInterface $filesystem) - { - $this->setLoopTimeout($loop); - $rejectionReason = null; - - try { - $this->await($filesystem->file(__FILE__ . '.' . time())->stat(), $loop); - } catch (\Exception $e) { - $rejectionReason = $e->getMessage(); - } - - $this->assertTrue( - $rejectionReason === 'Path doesn\'t exist' || - $rejectionReason === 'No such file or directory' - ); - } - - /** - * @dataProvider filesystemProvider - */ - public function testRemove(LoopInterface $loop, FilesystemInterface $filesystem) - { - $tempFile = $this->tmpDir . uniqid('', true); - touch($tempFile); - do { - usleep(500); - $this->checkIfTimedOut(); - } while (!file_exists($tempFile)); - $this->await($filesystem->file($tempFile)->remove(), $loop); - $this->assertFileNotExists($tempFile); - } - - /** - * @dataProvider filesystemProvider - */ - public function testCreate(LoopInterface $loop, FilesystemInterface $filesystem) - { - $tempFile = $this->tmpDir . uniqid('', true); - $this->assertFileNotExists($tempFile); - $this->await($filesystem->file($tempFile)->create(), $loop); - $this->assertFileExists($tempFile); - $this->assertSame('0760', substr(sprintf('%o', fileperms($tempFile)), -4)); - } - - /** - * @dataProvider filesystemProvider - */ - public function testTouch(LoopInterface $loop, FilesystemInterface $filesystem) - { - $tempFile = $this->tmpDir . uniqid('', true); - $this->assertFileNotExists($tempFile); - $this->await($filesystem->file($tempFile)->touch(), $loop); - $this->assertFileExists($tempFile); - } - - /** - * @dataProvider filesystemProvider - */ - public function testGetContents(LoopInterface $loop, FilesystemInterface $filesystem) - { - $tempFile = $this->tmpDir . uniqid('', true); - $contents = str_pad('a', 1024*512); - file_put_contents($tempFile, $contents); - do { - usleep(500); - $this->checkIfTimedOut(); - } while (!file_exists($tempFile)); - $this->assertFileExists($tempFile); - $fileContents = $this->await($filesystem->file($tempFile)->getContents(), $loop); - $this->assertSame($contents, $fileContents); - } - - /** - * @dataProvider filesystemProvider - */ - public function testGetBinaryContents(LoopInterface $loop, FilesystemInterface $filesystem) - { - $file = __DIR__ . DIRECTORY_SEPARATOR . 'reactphp-logo.png'; - $this->assertFileExists($file); - $fileContents = $this->await($filesystem->file($file)->getContents(), $loop); - $this->assertSame(file_get_contents($file), $fileContents); - } - - /** - * @dataProvider filesystemProvider - */ - public function testCopy(LoopInterface $loop, FilesystemInterface $filesystem) - { - $tempFileSource = $this->tmpDir . uniqid('source', true); - $tempFileDestination = $this->tmpDir . uniqid('destination', true); - $contents = str_pad('a', 33, 'b'); - file_put_contents($tempFileSource, $contents); - do { - usleep(500); - $this->checkIfTimedOut(); - } while (!file_exists($tempFileSource)); - $this->assertFileExists($tempFileSource); - $this->assertSame($contents, file_get_contents($tempFileSource)); - $this->await($filesystem->file($tempFileSource)->copy($filesystem->file($tempFileDestination)), $loop); - $this->assertFileEquals($tempFileSource, $tempFileDestination); - } - - /** - * @dataProvider filesystemProvider - */ - public function testCopyToDirectory(LoopInterface $loop, FilesystemInterface $filesystem) - { - $filename = uniqid('source', true); - $tempFileSource = $this->tmpDir . $filename; - $tempFileDestination = $this->tmpDir . uniqid('destination', true) . DIRECTORY_SEPARATOR; - $contents = str_pad('a', 33, 'b'); - file_put_contents($tempFileSource, $contents); - mkdir($tempFileDestination); - do { - usleep(500); - $this->checkIfTimedOut(); - } while (!file_exists($tempFileSource) && !file_exists($tempFileDestination)); - $this->assertFileExists($tempFileSource); - $this->assertSame($contents, file_get_contents($tempFileSource)); - $this->await($filesystem->file($tempFileSource)->copy($filesystem->dir($tempFileDestination)), $loop); - do { - usleep(500); - $this->checkIfTimedOut(); - } while (!file_exists($tempFileDestination . $filename) || stat($tempFileDestination . $filename)['size'] == 0); - $this->assertFileEquals($tempFileSource, $tempFileDestination . $filename); - } - - /** - * @dataProvider filesystemProvider - * @group permissions - */ - public function testChmod(LoopInterface $loop, FilesystemInterface $filesystem) - { - $filename = uniqid('', true); - $tempFile = $this->tmpDir . $filename; - touch($tempFile); - do { - usleep(500); - $this->checkIfTimedOut(); - } while (!file_exists($tempFile)); - chmod($tempFile, 0777); - $this->await($filesystem->file($tempFile)->chmod(0666), $loop); - $this->assertSame('0666', substr(sprintf('%o', fileperms($tempFile)), -4)); - } - - /** - * @dataProvider filesystemProvider - * @group permissions - */ - public function testChownUid(LoopInterface $loop, FilesystemInterface $filesystem) - { - $filename = uniqid('', true); - $tempFile = $this->tmpDir . $filename; - touch($tempFile); - do { - usleep(500); - $this->checkIfTimedOut(); - } while (!file_exists($tempFile)); - $this->await($filesystem->file($tempFile)->chown(1000), $loop); - $this->assertSame(1000, fileowner($tempFile)); - } - - /** - * @dataProvider filesystemProvider - */ - public function testRename(LoopInterface $loop, FilesystemInterface $filesystem) - { - $filenameFrom = uniqid('', true); - $tempFileFrom = $this->tmpDir . $filenameFrom; - file_put_contents($tempFileFrom, $filenameFrom); - $filenameTo = uniqid('', true); - $tempFileTo = $this->tmpDir . $filenameTo; - $this->await($filesystem->file($tempFileFrom)->rename($tempFileTo), $loop); - $this->assertFileExists($tempFileTo); - $this->assertSame($filenameFrom, file_get_contents($tempFileTo)); - } - - /** - * @dataProvider filesystemProvider - */ - public function testPutContents(LoopInterface $loop, FilesystemInterface $filesystem) - { - $contents = str_repeat('abc', 1024 * 1024 * 5); - $filename = uniqid('', true); - $tempFile = $this->tmpDir . $filename; - $this->await($filesystem->file($tempFile)->putContents($contents), $loop); - $this->assertFileExists($tempFile); - $this->assertSame($contents, file_get_contents($tempFile)); - } - /** - * @dataProvider filesystemProvider - */ - public function testPutBinaryContents(LoopInterface $loop, FilesystemInterface $filesystem) - { - $contents = file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'reactphp-logo.png'); - $filename = uniqid('', true); - $tempFile = $this->tmpDir . $filename; - $this->await($filesystem->file($tempFile)->putContents($contents), $loop); - $this->assertFileExists($tempFile); - $this->assertSame($contents, file_get_contents($tempFile)); - } -} diff --git a/tests/Adapters/InterfaceTest.php b/tests/Adapters/InterfaceTest.php deleted file mode 100644 index 04ac3ffb..00000000 --- a/tests/Adapters/InterfaceTest.php +++ /dev/null @@ -1,25 +0,0 @@ -assertInstanceOf('React\Filesystem\AdapterInterface', $adapter); - } - - /** - * @dataProvider adapterProvider - */ - public function testLoop(LoopInterface $loop, AdapterInterface $adapter) - { - $this->assertInstanceOf('React\EventLoop\LoopInterface', $adapter->getLoop()); - } -} diff --git a/tests/Adapters/reactphp-logo.png b/tests/Adapters/reactphp-logo.png deleted file mode 100644 index 001a8c8d17557f09a66e00e918284d4eb8c73768..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 58832 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%0A!=F~{~Dw#J8)*Z#Ku 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 diff --git a/tests/ChildProcess/AdapterTest.php b/tests/ChildProcess/AdapterTest.php deleted file mode 100644 index 98b4a498..00000000 --- a/tests/ChildProcess/AdapterTest.php +++ /dev/null @@ -1,374 +0,0 @@ -assertInstanceOf( - 'React\Filesystem\AdapterInterface', - new Adapter($this->getMock('React\EventLoop\LoopInterface'), [ - 'pool' => [ - 'class' => 'WyriHaximus\React\ChildProcess\Pool\Pool\Dummy', - ], - ]) - ); - } - - public function testGetLoop() - { - $loop = $this->getMock('React\EventLoop\LoopInterface'); - $filesystem = new Adapter($loop, [ - 'pool' => [ - 'class' => 'WyriHaximus\React\ChildProcess\Pool\Pool\Dummy', - ], - ]); - $this->assertSame($loop, $filesystem->getLoop()); - } - - public function testGetSetFilesystem() - { - $loop = $this->getMock('React\EventLoop\LoopInterface'); - $filesystem = new Adapter($loop, [ - 'pool' => [ - 'class' => 'WyriHaximus\React\ChildProcess\Pool\Pool\Dummy', - ], - ]); - - $this->assertNull($filesystem->getFilesystem()); - $fs = \React\Filesystem\Filesystem::createFromAdapter($this->mockAdapter()); - $filesystem->setFilesystem($fs); - - $this->assertSame($fs, $filesystem->getFilesystem()); - } - - public function callFilesystemProvider() - { - return [ - [ - 'mkdir', - [ - 'foo.bar', - ], - [ - 'mkdir', - [ - 'path' => 'foo.bar', - 'mode' => '760', - ], - ], - ], - [ - 'mkdir', - [ - 'foo.bar', - 'rwxrwxrwx', - ], - [ - 'mkdir', - [ - 'path' => 'foo.bar', - 'mode' => '777', - ], - ], - ], - [ - 'rmdir', - [ - 'foo.bar', - ], - [ - 'rmdir', - [ - 'path' => 'foo.bar', - ], - ], - ], - [ - 'unlink', - [ - 'foo.bar', - ], - [ - 'unlink', - [ - 'path' => 'foo.bar', - ], - ], - ], - [ - 'touch', - [ - 'foo.bar', - ], - [ - 'touch', - [ - 'path' => 'foo.bar', - 'mode' => '760', - ], - ], - ], - [ - 'rename', - [ - 'foo.bar', - 'bar.foo', - ], - [ - 'rename', - [ - 'from' => 'foo.bar', - 'to' => 'bar.foo', - ], - ], - ], - [ - 'chown', - [ - 'foo.bar', - 0, - 2, - ], - [ - 'chown', - [ - 'path' => 'foo.bar', - 'uid' => 0, - 'gid' => 2, - ], - ], - ], - [ - 'chmod', - [ - 'foo.bar', - 0123, - ], - [ - 'chmod', - [ - 'path' => 'foo.bar', - 'mode' => '123', - ], - ], - ], - [ - 'readlink', - [ - 'foo.bar', - ], - [ - 'readlink', - [ - 'path' => 'foo.bar', - ], - ], - ], - [ - 'stat', - [ - 'foo.bar', - ], - [ - 'stat', - [ - 'path' => 'foo.bar', - ], - ], - ], - [ - 'symlink', - [ - 'foo.bar', - 'bar.foo', - ], - [ - 'symlink', - [ - 'from' => 'foo.bar', - 'to' => 'bar.foo', - ], - ], - ], - ]; - } - - /** - * @dataProvider callFilesystemProvider - */ - public function testCallFilesystem($method, $arguments, $mockArguments) - { - $loop = $this->getMock('React\EventLoop\LoopInterface'); - $filesystem = new Adapter($loop, [ - 'pool' => [ - 'class' => 'React\Tests\Filesystem\ChildProcess\SingletonPoolStub', - ], - ]); - - call_user_func_array([$filesystem, $method], $arguments); - - $calls = SingletonPoolStub::getCalls(); - self::assertCount(1, $calls); - /** @var array $call */ - $call = $calls[0][1][0]->jsonSerialize(); - self::assertSame($mockArguments[0], $call['target']); - self::assertSame($mockArguments[1], $call['payload']->getPayload()); - } - - public function testLs() - { - $loop = \React\EventLoop\Factory::create(); - $adapter = new Adapter($loop, [ - 'pool' => [ - 'class' => 'React\Tests\Filesystem\ChildProcess\SingletonPoolStub', - ], - ]); - - $deferred = new Deferred(); - SingletonPoolStub::setRpcResponse($deferred->promise()); - - $fs = Filesystem::createFromAdapter($adapter); - - $promise = $adapter->ls('foo.bar'); - $this->assertInstanceOf('React\Promise\PromiseInterface', $promise); - $deferred->resolve(new Payload([ - [ - 'type' => 'file', - 'name' => 'bar.foo', - ], - ])); - - $nodes = $this->await($promise, $loop); - - $calls = SingletonPoolStub::getCalls(); - self::assertCount(1, $calls); - /** @var array $call */ - $call = $calls[0][1][0]->jsonSerialize(); - self::assertSame('readdir', $call['target']); - self::assertSame([ - 'path' => 'foo.bar', - 'flags' => 2, - ], $call['payload']->getPayload()); - - $this->assertEquals(new File('foo.bar/bar.foo', $fs), reset($nodes)); - } - - public function testLsStream() - { - $loop = $this->getMock('React\EventLoop\LoopInterface'); - $adapter = new Adapter($loop, [ - 'pool' => [ - 'class' => 'React\Tests\Filesystem\ChildProcess\SingletonPoolStub', - ], - ]); - - $deferred = new Deferred(); - SingletonPoolStub::setRpcResponse($deferred->promise()); - - Filesystem::createFromAdapter($adapter); - - $stream = $adapter->lsStream('foo.bar'); - $this->assertInstanceOf('React\Filesystem\ObjectStream', $stream); - - $calledOnData = false; - $stream->on('data', function (NodeInterface $file) use (&$calledOnData) { - $this->assertInstanceOf('React\Filesystem\Node\File', $file); - $this->assertSame('foo.bar/bar.foo', $file->getPath()); - $calledOnData = true; - }); - - $deferred->resolve(new Payload([ - [ - 'type' => 'file', - 'name' => 'bar.foo', - ], - ])); - - $calls = SingletonPoolStub::getCalls(); - self::assertCount(1, $calls); - /** @var array $call */ - $call = $calls[0][1][0]->jsonSerialize(); - self::assertSame('readdir', $call['target']); - self::assertSame([ - 'path' => 'foo.bar', - 'flags' => 2, - ], $call['payload']->getPayload()); - - $this->assertTrue($calledOnData); - } - - public function testErrorFromPool() - { - $this->setExpectedException('\Exception', 'oops'); - - $loop = Factory::create(); - $adapter = new Adapter($loop, [ - 'pool' => [ - 'class' => 'React\Tests\Filesystem\ChildProcess\PoolRpcErrorMockFactory', - ], - ]); - $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)); - } -} diff --git a/tests/ChildProcess/PoolRpcErrorMock.php b/tests/ChildProcess/PoolRpcErrorMock.php deleted file mode 100644 index d029a836..00000000 --- a/tests/ChildProcess/PoolRpcErrorMock.php +++ /dev/null @@ -1,44 +0,0 @@ - [ - 'message' => 'oops', - ], - ]); - } - - public function message(Message $message) - { - return new FulfilledPromise(); - } - - public function terminate(Message $message, $timeout = 5, $signal = null) - { - return new FulfilledPromise(); - } - - public function info() - { - return []; - } -} diff --git a/tests/ChildProcess/PoolRpcErrorMockFactory.php b/tests/ChildProcess/PoolRpcErrorMockFactory.php deleted file mode 100644 index 4ff77615..00000000 --- a/tests/ChildProcess/PoolRpcErrorMockFactory.php +++ /dev/null @@ -1,32 +0,0 @@ -getMockBuilder('WyriHaximus\React\ChildProcess\Messenger\Messenger') - ->disableOriginalConstructor() - ->getMock() - ; - - $messenger - ->expects($this->atLeastOnce()) - ->method('registerRpc') - ; - - new Process($messenger); - } - - public function testStat() - { - - $messenger = $this->getMockBuilder('WyriHaximus\React\ChildProcess\Messenger\Messenger') - ->disableOriginalConstructor() - ->getMock() - ; - - $resultCallbackRan = false; - (new Process($messenger))->stat([ - 'path' => __FILE__, - ])->then(function ($result) use (&$resultCallbackRan) { - foreach ([ - 'dev', - 'ino', - 'mode', - 'nlink', - 'uid', - 'size', - 'gid', - 'rdev', - 'blksize', - 'blocks', - 'atime', - 'mtime', - 'ctime', - ] as $item) { - $this->assertArrayHasKey($item, $result); - } - $resultCallbackRan = true; - }); - $this->assertTrue($resultCallbackRan); - } -} diff --git a/tests/ChildProcess/SingletonPoolStub.php b/tests/ChildProcess/SingletonPoolStub.php deleted file mode 100644 index 1cf7c022..00000000 --- a/tests/ChildProcess/SingletonPoolStub.php +++ /dev/null @@ -1,80 +0,0 @@ -await($filesystem->detect(__DIR__)->then(static function (DirectoryInterface $directory): PromiseInterface { + return $directory->stat(); + })); + + self::assertInstanceOf(Stat::class, $stat); + self::assertSame(stat(__DIR__)['size'], $stat->size()); + } + + /** + * @test + * + * @dataProvider provideFilesystems + */ + public function ls(AdapterInterface $filesystem): void + { + $expectedListing = []; + + $d = dir(__DIR__); + while (false !== ($entry = $d->read())) { + if ($entry === '.' || $entry === '..') { + continue; + } + + $expectedListing[__DIR__ . DIRECTORY_SEPARATOR . $entry] = is_file(__DIR__ . DIRECTORY_SEPARATOR . $entry) ? FileInterface::class : DirectoryInterface::class; + } + $d->close(); + + ksort($expectedListing); + + $directoryListing = $this->await($filesystem->detect(__DIR__)->then(static function (DirectoryInterface $directory): PromiseInterface { + return $directory->ls(); + })); + + $listing = []; + foreach ($directoryListing as $node) { + $listing[$node->path() . $node->name()] = $node instanceof FileInterface ? FileInterface::class : DirectoryInterface::class; + } + ksort($listing); + + self::assertSame($expectedListing, $listing); + } +} diff --git a/tests/FactoryTest.php b/tests/FactoryTest.php new file mode 100644 index 00000000..a87fd0e0 --- /dev/null +++ b/tests/FactoryTest.php @@ -0,0 +1,24 @@ +await(Factory::create()->detect(__FILE__)); + + self::assertInstanceOf(FileInterface::class, $node); + } +} diff --git a/tests/FileTest.php b/tests/FileTest.php new file mode 100644 index 00000000..438b62fa --- /dev/null +++ b/tests/FileTest.php @@ -0,0 +1,276 @@ +await($filesystem->detect(__FILE__)->then(static function (FileInterface $file): PromiseInterface { + return $file->stat(); + })); + + self::assertInstanceOf(Stat::class, $stat); + self::assertSame(filesize(__FILE__), $stat->size()); + } + + /** + * @test + * + * @dataProvider provideFilesystems + */ + public function getContents(AdapterInterface $filesystem): void + { + $fileContents = $this->await($filesystem->detect(__FILE__)->then(static function (FileInterface $file): PromiseInterface { + return $file->getContents(); + })); + + self::assertSame(file_get_contents(__FILE__), $fileContents); + } + + /** + * @test + * + * @dataProvider provideFilesystems + */ + public function getContents34and5thCharacterFromFile(AdapterInterface $filesystem): void + { + $directoryName = sys_get_temp_dir() . DIRECTORY_SEPARATOR . bin2hex(random_bytes(13)) . DIRECTORY_SEPARATOR; + $fileName = $directoryName . bin2hex(random_bytes(13)); + mkdir($directoryName); + \file_put_contents($fileName, 'abcdefghijklmnopqrstuvwxyz'); + $fileContents = $this->await($filesystem->detect($fileName)->then(static function (FileInterface $file): PromiseInterface { + return $file->getContents(3, 3); + })); + + self::assertSame('def', $fileContents); + } + + /** + * @test + * + * @dataProvider provideFilesystems + */ + public function putContents(AdapterInterface $filesystem): void + { + $fileName = sys_get_temp_dir() . DIRECTORY_SEPARATOR . bin2hex(random_bytes(13)) . DIRECTORY_SEPARATOR . bin2hex(random_bytes(9)); + $fileContents = bin2hex(random_bytes(128)); + + $writtenLength = $this->await($filesystem->detect($fileName)->then(static fn (NotExistInterface $notExist): PromiseInterface => $notExist->createFile())->then(function (FileInterface $file) use ($fileContents): PromiseInterface { + return $file->putContents($fileContents); + })); + + self::assertSame($writtenLength, strlen(file_get_contents($fileName))); + self::assertSame($fileContents, file_get_contents($fileName)); + + unlink($fileName); + } + + /** + * @test + * + * @dataProvider provideFilesystems + */ + public function putContentsMultipleBigFiles(AdapterInterface $filesystem): void + { + $directoryName = sys_get_temp_dir() . DIRECTORY_SEPARATOR . bin2hex(random_bytes(13)) . DIRECTORY_SEPARATOR; + $this->await($filesystem->detect($directoryName)->then(static fn(NotExistInterface $notExist): PromiseInterface => $notExist->createDirectory())); + $fileNames = []; + $fileContents = []; + for ($i = 0; $i < 25; $i++) { + $fileNames[] = $directoryName . bin2hex(random_bytes(13)); + } + foreach ($fileNames as $fileName) { + $fileContents[$fileName] = bin2hex(random_bytes(4096)); + touch($fileName); + } + + $promises = []; + foreach ($fileContents as $fileName => $fileContent) { + $promises[$fileName] = $filesystem->detect($fileName)->then(static function (FileInterface $file) use ($fileContent): PromiseInterface { + return $file->putContents($fileContent); + }); + } + + $writtenLengths = $this->await(all($promises)); + + foreach ($writtenLengths as $fileName => $writtenLength) { + self::assertSame($writtenLength, strlen(file_get_contents($fileName))); + self::assertSame($fileContents[$fileName], file_get_contents($fileName)); + unlink($fileName); + } + } + + /** + * @test + * + * @dataProvider provideFilesystems + */ + public function putContentsAppend(AdapterInterface $filesystem): void + { + $fileName = sys_get_temp_dir() . DIRECTORY_SEPARATOR . bin2hex(random_bytes(13)) . DIRECTORY_SEPARATOR . bin2hex(random_bytes(9)); + $fileContentsFirst = bin2hex(random_bytes(128)); + $fileContentsSecond = bin2hex(random_bytes(128)); + $writtenLengthFirst = $this->await($filesystem->detect($fileName)->then(static fn (NotExistInterface $notExist): PromiseInterface => $notExist->createFile())->then(static function (FileInterface $file) use ($fileContentsFirst): PromiseInterface { + return $file->putContents($fileContentsFirst); + })); + + self::assertSame($writtenLengthFirst, strlen(file_get_contents($fileName))); + self::assertSame($fileContentsFirst, file_get_contents($fileName)); + + $writtenLengthSecond = $this->await($filesystem->detect($fileName)->then(static function (FileInterface $file) use ($fileContentsSecond): PromiseInterface { + return $file->putContents($fileContentsSecond, \FILE_APPEND); + })); + + self::assertSame($writtenLengthFirst + $writtenLengthSecond, strlen(file_get_contents($fileName))); + self::assertSame($fileContentsFirst . $fileContentsSecond, file_get_contents($fileName)); + + unlink($fileName); + } + + /** + * @test + * + * @dataProvider provideFilesystems + */ + public function putContentsAppendBigFile(AdapterInterface $filesystem): void + { + $fileName = sys_get_temp_dir() . DIRECTORY_SEPARATOR . bin2hex(random_bytes(13)) . DIRECTORY_SEPARATOR . bin2hex(random_bytes(9)); + $this->await($filesystem->detect($fileName)->then(static fn(NotExistInterface $notExist): PromiseInterface => $notExist->createFile())); + + $fileContents = []; + $writtenLength = 0; + for ($i = 0; $i < 13; $i++) { + $fileContents[] = bin2hex(random_bytes(4096)); + } + + foreach ($fileContents as $fileContent) { + $writtenLength += $this->await($filesystem->detect($fileName)->then(static function (FileInterface $file) use ($fileContent): PromiseInterface { + return $file->putContents($fileContent, \FILE_APPEND); + })); + } + + self::assertSame($writtenLength, strlen(file_get_contents($fileName))); + self::assertSame(implode('', $fileContents), file_get_contents($fileName)); + + unlink($fileName); + } + + /** + * @test + * + * @dataProvider provideFilesystems + */ + public function putContentsAppendMultipleBigFiles(AdapterInterface $filesystem): void + { + $this->runMultipleFilesTests($filesystem, 8, 4096, 4); + } + + /** + * @test + * + * @dataProvider provideFilesystems + */ + public function putContentsAppendLotsOfSmallFiles(AdapterInterface $filesystem): void + { + $this->runMultipleFilesTests($filesystem, 16, 16, 4); + } + + /** + * @test + * + * @dataProvider provideFilesystems + */ + public function putContentsAppendLoadsOfSmallFiles(AdapterInterface $filesystem): void + { + $this->runMultipleFilesTests($filesystem, 32, 8, 8); + } + + public function runMultipleFilesTests(AdapterInterface $filesystem, int $fileCount, int $fileSize, int $chunkCount): void + { + $directoryName = sys_get_temp_dir() . DIRECTORY_SEPARATOR . bin2hex(random_bytes(13)) . DIRECTORY_SEPARATOR; + mkdir($directoryName, 0777, true); + $fileNames = []; + $fileContents = []; + for ($i = 0; $i < $fileCount; $i++) { + $fileNames[] = $directoryName . bin2hex(random_bytes(13)); + } + foreach ($fileNames as $fileName) { + $fileContents[$fileName] = []; + touch($fileName); + } + + foreach ($fileNames as $fileName) { + for ($i = 0; $i < $chunkCount; $i++) { + $fileContents[$fileName][] = bin2hex(random_bytes($fileSize)); + } + } + + $promises = []; + foreach ($fileContents as $fileName => $fileContent) { + $queue = new \SplQueue(); + foreach ($fileContent as $chunk) { + $queue->enqueue($chunk); + } + $promises[$fileName] = $filesystem->detect($fileName)->then(static function (FileInterface $file) use ($queue): PromiseInterface { + return new Promise(function (callable $resolve, callable $reject) use ($queue, $file): void { + $bytesWritten = 0; + $writeFunction = function () use (&$writeFunction, &$bytesWritten, $queue, $file, $resolve, $reject) { + if ($queue->count() > 0) { + $file->putContents($queue->dequeue(), \FILE_APPEND)->then(function (int $writtenBytes) use (&$writeFunction, &$bytesWritten): void { + $bytesWritten += $writtenBytes; + $writeFunction(); + }, $reject); + return; + } + + $resolve($bytesWritten); + }; + $writeFunction(); + }); + }); + } + + $writtenLengths = $this->await(all($promises)); + + foreach ($writtenLengths as $fileName => $writtenLength) { + self::assertSame($writtenLength, strlen(file_get_contents($fileName))); + self::assertSame(implode('', $fileContents[$fileName]), file_get_contents($fileName)); + unlink($fileName); + } + } + + /** + * @test + * + * @dataProvider provideFilesystems + */ + public function unlink(AdapterInterface $filesystem): void + { + $fileName = __FILE__ . '.' . time(); + $fileContents = bin2hex(random_bytes(2048)); + file_put_contents($fileName, $fileContents); + self::assertFileExists($fileName); + $this->await($filesystem->detect($fileName)->then(static function (FileInterface $file): PromiseInterface { + return $file->unlink(); + })); + + + self::assertFileDoesNotExist($fileName); + } +} diff --git a/tests/FilesystemTest.php b/tests/FilesystemTest.php index 5a7ea944..fb91c620 100644 --- a/tests/FilesystemTest.php +++ b/tests/FilesystemTest.php @@ -2,84 +2,48 @@ namespace React\Tests\Filesystem; -use React\Filesystem\Filesystem; -use React\Promise\FulfilledPromise; -use React\Promise\RejectedPromise; +use React\EventLoop\LoopInterface; +use React\Filesystem\AdapterInterface; +use React\Filesystem\Node\DirectoryInterface; +use React\Filesystem\Node\FileInterface; +use React\Filesystem\Node\NotExistInterface; +use function Clue\React\Block\await; -class FilesystemTest extends TestCase +final class FilesystemTest extends AbstractFilesystemTestCase { - public function testCreate() + /** + * @test + * + * @dataProvider provideFilesystems + */ + public function file(AdapterInterface $filesystem): void { - $this->assertInstanceOf( - 'React\Filesystem\Filesystem', - Filesystem::create($this->getMock('React\EventLoop\LoopInterface'), [ - 'pool' => [ - 'class' => 'WyriHaximus\React\ChildProcess\Pool\Pool\Dummy', - ], - ]) - ); - } - public function testCreateWithAdapter() - { - $this->assertInstanceOf( - 'React\Filesystem\Filesystem', - Filesystem::createFromAdapter($this->mockAdapter()) - ); - } + $node = $this->await($filesystem->detect(__FILE__)); - public function testFactory() - { - try { - $this->assertInstanceOf('React\Filesystem\Filesystem', Filesystem::create(null, [ - 'pool' => [ - 'class' => 'WyriHaximus\React\ChildProcess\Pool\Pool\Dummy', - ], - ])); - } catch (\PHPUnit_Framework_Error $typeError) { - $this->assertTrue(true); - } catch (\TypeError $typeError) { - $this->assertTrue(true); - } + self::assertInstanceOf(FileInterface::class, $node); } - public function testFile() + /** + * @test + * + * @dataProvider provideFilesystems + */ + public function directory(AdapterInterface $filesystem): void { - $file = Filesystem::create($this->getMock('React\EventLoop\LoopInterface'), [ - 'pool' => [ - 'class' => 'WyriHaximus\React\ChildProcess\Pool\Pool\Dummy', - ], - ])->file('foo.bar'); - $this->assertInstanceOf('React\Filesystem\Node\File', $file); - $this->assertInstanceOf('React\Filesystem\Node\GenericOperationInterface', $file); - } + $node = $this->await($filesystem->detect(__DIR__)); - public function testDir() - { - $directory = Filesystem::create($this->getMock('React\EventLoop\LoopInterface'), [ - 'pool' => [ - 'class' => 'WyriHaximus\React\ChildProcess\Pool\Pool\Dummy', - ], - ])->dir('foo.bar'); - $this->assertInstanceOf('React\Filesystem\Node\Directory', $directory); - $this->assertInstanceOf('React\Filesystem\Node\GenericOperationInterface', $directory); + self::assertInstanceOf(DirectoryInterface::class, $node); } - public function testGetContents() + /** + * @test + * + * @dataProvider provideFilesystems + */ + public function notExists(AdapterInterface $filesystem): void { - $adapter = $this->mockAdapter(); - $adapter - ->expects($this->any()) - ->method('stat') - ->will($this->returnValue(new FulfilledPromise([]))) - ; - $adapter - ->expects($this->any()) - ->method('open') - ->will($this->returnValue(new RejectedPromise())) - ; - $this->assertInstanceOf( - 'React\Promise\PromiseInterface', - Filesystem::createFromAdapter($adapter)->getContents('foo.bar') - ); + $node = $this->await($filesystem->detect(bin2hex(random_bytes(13)))); + + self::assertInstanceOf(NotExistInterface::class, $node); } } diff --git a/tests/FlagResolverTest.php b/tests/FlagResolverTest.php deleted file mode 100644 index 9d4b03ab..00000000 --- a/tests/FlagResolverTest.php +++ /dev/null @@ -1,35 +0,0 @@ -getMock('React\Filesystem\FlagResolver', [ - 'defaultFlags', - 'flagMapping', - ]); - - $resolver - ->expects($this->once()) - ->method('defaultFlags') - ->with() - ->will($this->returnValue(0)) - ; - - $resolver - ->expects($this->once()) - ->method('flagMapping') - ->with() - ->will($this->returnValue([ - 'b' => 1, - 'a' => 2, - 'r' => 4, - ])) - ; - - $this->assertSame(7, $resolver->resolve('bar')); - } -} diff --git a/tests/FunctionsTest.php b/tests/FunctionsTest.php deleted file mode 100644 index f1abbd3d..00000000 --- a/tests/FunctionsTest.php +++ /dev/null @@ -1,20 +0,0 @@ -assertSame($limit, \React\Filesystem\getOpenFileLimit([ - 'open_file_limit' => $limit, - ])); - } - public function testGetOpenFileLimitFallback() - { - $this->assertSame(OpenFileLimiter::DEFAULT_LIMIT, \React\Filesystem\getOpenFileLimit([])); - } -} diff --git a/tests/ModeTypeDetectorTest.php b/tests/ModeTypeDetectorTest.php deleted file mode 100644 index b8f1e497..00000000 --- a/tests/ModeTypeDetectorTest.php +++ /dev/null @@ -1,79 +0,0 @@ -mockAdapter(); - $adapter - ->expects($this->any()) - ->method('stat') - ->with('foo.bar') - ->will($this->returnValue(new FulfilledPromise([ - 'mode' => $mode, - ]))) - ; - $filesystem = Filesystem::createFromAdapter($adapter); - (new ModeTypeDetector($filesystem))->detect([ - 'path' => 'foo.bar', - ])->then(function ($result) use ($filesystem, $method, &$callbackFired) { - $this->assertSame([ - $filesystem, - $method, - ], $result); - $callbackFired = true; - }); - - $this->assertTrue($callbackFired); - } - - public function testDetectUnknown() - { - $callbackFired = false; - - $adapter = $this->mockAdapter(); - $adapter - ->expects($this->any()) - ->method('stat') - ->with('foo.bar') - ->will($this->returnValue(new FulfilledPromise([ - 'mode' => 0x3000, - ]))) - ; - $filesystem = Filesystem::createFromAdapter($adapter); - (new ModeTypeDetector($filesystem))->detect([ - 'path' => 'foo.bar', - ])->otherwise(function ($result) use (&$callbackFired) { - $this->assertInstanceOf('Exception', $result); - $callbackFired = true; - }); - - $this->assertTrue($callbackFired); - } -} diff --git a/tests/Node/DirectoryTest.php b/tests/Node/DirectoryTest.php deleted file mode 100644 index 34a8c8a4..00000000 --- a/tests/Node/DirectoryTest.php +++ /dev/null @@ -1,405 +0,0 @@ -assertSame($path . NodeInterface::DS, (new Directory($path, Filesystem::createFromAdapter($this->mockAdapter())))->getPath()); - } - - public function testLs() - { - $path = '/home/foo/bar'; - $loop = $this->getMock('React\EventLoop\LoopInterface'); - - $filesystem = $this->mockAdapter($loop); - - $ls = \React\Promise\resolve(); - - $filesystem - ->expects($this->once()) - ->method('ls') - ->with() - ->will($this->returnValue($ls)) - ; - - $directory = new Directory($path, Filesystem::createFromAdapter($filesystem)); - - $this->assertInstanceOf('React\Promise\PromiseInterface', $directory->ls()); - } - - public function testLsStream() - { - $path = '/home/foo/bar'; - $loop = $this->getMock('React\EventLoop\LoopInterface'); - - $filesystem = $this->mockAdapter($loop); - - $lsStream = $this->getMock('React\Filesystem\ObjectStream'); - - $filesystem - ->expects($this->once()) - ->method('lsStream') - ->with() - ->will($this->returnValue($lsStream)) - ; - - $directory = new Directory($path, Filesystem::createFromAdapter($filesystem)); - - $this->assertInstanceOf('React\Filesystem\ObjectStream', $directory->lsStreaming()); - } - - public function testCreate() - { - $path = 'foo.bar'; - $filesystem = $this->mockAdapter(); - $promise = new FulfilledPromise(); - - $filesystem - ->expects($this->once()) - ->method('mkdir') - ->with($path) - ->will($this->returnValue($promise)) - ; - - $filesystem - ->expects($this->once()) - ->method('stat') - ->with($path . NodeInterface::DS) - ->will($this->returnValue(new FulfilledPromise())) - ; - - $this->assertInstanceOf('React\Promise\PromiseInterface', (new Directory($path, Filesystem::createFromAdapter($filesystem)))->create()); - } - - public function testRename() - { - $pathFrom = 'foo.bar'; - $pathTo = 'bar.foo'; - $filesystem = $this->mockAdapter(); - - $filesystem - ->expects($this->once()) - ->method('rename') - ->with($pathFrom, $pathTo) - ->will($this->returnValue(new FulfilledPromise())) - ; - - $newDirectory = \Clue\React\Block\await((new Directory($pathFrom, Filesystem::createFromAdapter($filesystem)))->rename($pathTo), Factory::create()); - $this->assertInstanceOf('React\Filesystem\Node\DirectoryInterface', $newDirectory); - $this->assertSame($pathTo . NodeInterface::DS, $newDirectory->getPath()); - } - - public function testRemove() - { - $path = 'foo.bar'; - $filesystem = $this->mockAdapter(); - $promise = $this->getMock('React\Promise\PromiseInterface'); - - $filesystem - ->expects($this->once()) - ->method('rmdir') - ->with($path) - ->will($this->returnValue($promise)) - ; - - $this->assertSame($promise, (new Directory($path, Filesystem::createFromAdapter($filesystem)))->remove()); - } - public function testSize() - { - $path = '/home/foo/bar'; - $loop = $this->getMock('React\EventLoop\LoopInterface'); - - $filesystem = $this->mockAdapter($loop); - - $lsPromise = $this->getMock('React\Promise\PromiseInterface', [ - 'then', - ]); - - $lsPromise - ->expects($this->once()) - ->method('then') - ->with($this->isType('callable')) - ->will($this->returnCallback(function ($callback) { - return $callback('foo.bar'); - })) - ; - - $directory = $this->getMock('React\Filesystem\Node\Directory', [ - 'ls', - 'processSizeContents', - ], [ - $path, - Filesystem::createFromAdapter($filesystem), - ]); - - $directory - ->expects($this->once()) - ->method('ls') - ->with() - ->will($this->returnValue($lsPromise)) - ; - - $directory - ->expects($this->once()) - ->method('processSizeContents') - ->with('foo.bar', $this->isType('boolean')) - ->will($this->returnValue($this->getMock('React\Promise\PromiseInterface'))) - ; - - $this->assertInstanceOf('React\Promise\PromiseInterface', $directory->size()); - } - - public function testChmodRecursive() - { - $filesystem = $this->mockAdapter(); - - $recursiveInvoker = $this->getMock('React\Filesystem\Node\RecursiveInvoker', [ - 'execute', - ], [ - $this->getMock('React\Filesystem\Node\DirectoryInterface', [], [ - 'foo/bar/', - Filesystem::createFromAdapter($filesystem), - ]), - ]); - - $promise = $this->getMock('React\Promise\PromiseInterface'); - - $recursiveInvoker - ->expects($this->once()) - ->method('execute') - ->with('chmod', [123]) - ->will($this->returnValue($promise)) - ; - - $this->assertSame($promise, (new Directory('foo/bar/', Filesystem::createFromAdapter($filesystem), $recursiveInvoker))->chmodRecursive(123)); - } - - public function testChownRecursive() - { - $filesystem = $this->mockAdapter(); - - $recursiveInvoker = $this->getMock('React\Filesystem\Node\RecursiveInvoker', [ - 'execute', - ], [ - $this->getMock('React\Filesystem\Node\DirectoryInterface', [], [ - 'foo/bar/', - Filesystem::createFromAdapter($filesystem), - ]), - ]); - - $promise = $this->getMock('React\Promise\PromiseInterface'); - - $recursiveInvoker - ->expects($this->once()) - ->method('execute') - ->with('chown', [1, 2]) - ->will($this->returnValue($promise)) - ; - - $this->assertSame($promise, (new Directory('foo/bar/', Filesystem::createFromAdapter($filesystem), $recursiveInvoker))->chownRecursive(1, 2)); - } - - public function testRemoveRecursive() - { - $filesystem = $this->mockAdapter(); - - $recursiveInvoker = $this->getMock('React\Filesystem\Node\RecursiveInvoker', [ - 'execute', - ], [ - $this->getMock('React\Filesystem\Node\DirectoryInterface', [], [ - 'foo/bar/', - Filesystem::createFromAdapter($filesystem), - ]), - ]); - - $promise = $this->getMock('React\Promise\PromiseInterface'); - - $recursiveInvoker - ->expects($this->once()) - ->method('execute') - ->with('remove', []) - ->will($this->returnValue($promise)) - ; - - $this->assertSame($promise, (new Directory('foo/bar/', Filesystem::createFromAdapter($filesystem), $recursiveInvoker))->removeRecursive()); - } - - public function testCopy() - { - $filesystem = $this->mockAdapter(); - - $directoryFrom = $this->getMock('React\Filesystem\Node\Directory', [ - 'copyStreaming', - ], [ - 'foo.bar', - Filesystem::createFromAdapter($filesystem), - ]); - - $fileTo = new Directory('bar.foo', Filesystem::createFromAdapter($filesystem)); - - $directoryFrom - ->expects($this->once()) - ->method('copyStreaming') - ->with($fileTo) - ->will($this->returnValue(new ObjectStream())) - ; - - $promise = $directoryFrom->copy($fileTo); - $this->assertInstanceOf('React\Promise\PromiseInterface', $promise); - } - - public function providerCopyStreamingUnknownNode() - { - return [ - [ - new UnknownNodeType(), - ], - [ - new File('foo.bar', Filesystem::createFromAdapter($this->mockAdapter())), - ], - ]; - } - - /** - * @dataProvider providerCopyStreamingUnknownNode - * @expectedException UnexpectedValueException - */ - public function testCopyStreamingUnknownNode($type) - { - $filesystem = Filesystem::createFromAdapter($this->mockAdapter()); - - (new Directory('foo.bar', $filesystem))->copyStreaming($type); - } - - public function testCopyStreamingABC() - { - - $adapter = $this->mockAdapter(); - - $filesystem = Filesystem::createFromAdapter($adapter); - - $adapter - ->expects($this->at(0)) - ->method('stat') - ->with('bar.foo/foo.bar/') - ->will($this->returnValue(new RejectedPromise())) - ; - - $adapter - ->expects($this->at(1)) - ->method('stat') - ->with('bar.foo/') - ->will($this->returnValue(new FulfilledPromise())) - ; - - $adapter - ->expects($this->at(3)) - ->method('stat') - ->with('bar.foo/foo.bar/') - ->will($this->returnValue(new FulfilledPromise())) - ; - - $adapter - ->expects($this->any()) - ->method('mkdir') - ->with($this->isType('string')) - ->will($this->returnValue(new FulfilledPromise())) - ; - - $directoryFrom = $this->getMock('React\Filesystem\Node\Directory', [ - 'lsStreaming', - ], [ - 'foo.bar', - $filesystem, - ]); - - $stream = new ObjectStream(); - $directoryTo = new Directory('bar.foo', $filesystem); - - $directoryFrom - ->expects($this->once()) - ->method('lsStreaming') - ->with() - ->will($this->returnValue($stream)) - ; - - $returnedStream = $directoryFrom->copyStreaming($directoryTo); - $this->assertInstanceOf('React\Filesystem\ObjectStream', $returnedStream); - - $file = $this->getMock('React\Filesystem\Node\File', [ - 'copyStreaming', - ], [ - 'foo.bar', - $filesystem, - ]); - - $fileStream = new ObjectStream(); - - $file - ->expects($this->once()) - ->method('copyStreaming') - ->with($directoryTo) - ->will($this->returnValue($fileStream)) - ; - - $stream->emit('data', [ - $file, - ]); - - $directory = $this->getMock('React\Filesystem\Node\Directory', [ - 'copyStreaming', - ], [ - 'foo.bar', - $filesystem, - ]); - - $directoryStream = new ObjectStream(); - - $directory - ->expects($this->once()) - ->method('copyStreaming') - ->with($this->isInstanceOf('React\Filesystem\Node\Directory')) - ->will($this->returnValue($directoryStream)) - ; - - $stream->emit('data', [ - $directory, - ]); - - $directoryStream->end($directory); - - $stream->end(); - } -} diff --git a/tests/Node/FileTest.php b/tests/Node/FileTest.php deleted file mode 100644 index 1d0fedbf..00000000 --- a/tests/Node/FileTest.php +++ /dev/null @@ -1,424 +0,0 @@ -assertSame($path, (new File($path, Filesystem::createFromAdapter($this->mockAdapter())))->getPath()); - } - - public function testRemove() - { - $path = 'foo.bar'; - $filesystem = $this->mockAdapter(); - $promise = $this->getMock('React\Promise\PromiseInterface'); - - $filesystem - ->expects($this->once()) - ->method('unlink') - ->with($path) - ->will($this->returnValue($promise)) - ; - - $this->assertSame($promise, (new File($path, Filesystem::createFromAdapter($filesystem)))->remove()); - } - - public function testRename() - { - $pathFrom = 'foo.bar'; - $pathTo = 'bar.foo'; - $filesystem = $this->mockAdapter(); - - $filesystem - ->expects($this->once()) - ->method('rename') - ->with($pathFrom, $pathTo) - ->will($this->returnValue(new FulfilledPromise())) - ; - - $newFile = \Clue\React\Block\await((new File($pathFrom, Filesystem::createFromAdapter($filesystem)))->rename($pathTo), Factory::create()); - $this->assertInstanceOf('React\Filesystem\Node\FileInterface', $newFile); - $this->assertSame($pathTo, $newFile->getPath()); - } - - public function testExists() - { - $path = 'foo.bar'; - $filesystem = $this->mockAdapter(); - - $file = $this->getMock('React\Filesystem\Node\File', [ - 'stat', - ], [ - $path, - Filesystem::createFromAdapter($filesystem), - ]); - - $promise = \React\Promise\resolve(); - - $file - ->expects($this->once()) - ->method('stat') - ->with() - ->will($this->returnValue($promise)) - ; - - $this->assertInstanceOf('React\Promise\PromiseInterface', $file->exists()); - } - - public function testDoesntExists() - { - $path = 'foo.bar'; - $filesystem = $this->mockAdapter(); - - $file = $this->getMock('React\Filesystem\Node\File', [ - 'stat', - ], [ - $path, - Filesystem::createFromAdapter($filesystem), - ]); - - $promise = \React\Promise\resolve(); - - $file - ->expects($this->once()) - ->method('stat') - ->with() - ->will($this->returnValue($promise)) - ; - - $this->assertInstanceOf('React\Promise\PromiseInterface', $file->exists()); - } - - public function testSize() - { - $size = 1337; - $path = 'foo.bar'; - $filesystem = $this->mockAdapter(); - $deferred = new Deferred(); - $promise = $deferred->promise(); - - $filesystem - ->expects($this->once()) - ->method('stat') - ->with($path) - ->will($this->returnValue($promise)) - ; - - $sizePromise = (new File($path, Filesystem::createFromAdapter($filesystem)))->size(); - $this->assertInstanceOf('React\Promise\PromiseInterface', $sizePromise); - - $callbackFired = false; - $sizePromise->then(function ($resultSize) use ($size, &$callbackFired) { - $this->assertSame($size, $resultSize); - $callbackFired = true; - }); - $deferred->resolve([ - 'size' => $size, - ]); - $this->assertTrue($callbackFired); - } - - public function testTime() - { - $times = [ - 'atime' => 1, - 'ctime' => 2, - 'mtime' => 3, - ]; - $path = 'foo.bar'; - $filesystem = $this->mockAdapter(); - $deferred = new Deferred(); - $promise = $deferred->promise(); - - $filesystem - ->expects($this->once()) - ->method('stat') - ->with($path) - ->will($this->returnValue($promise)) - ; - - $timePromise = (new File($path, Filesystem::createFromAdapter($filesystem)))->time(); - $this->assertInstanceOf('React\Promise\PromiseInterface', $timePromise); - - $callbackFired = false; - $timePromise->then(function ($time) use ($times, &$callbackFired) { - $this->assertSame($times, $time); - $callbackFired = true; - }); - $deferred->resolve($times); - $this->assertTrue($callbackFired); - } - - public function testCreate() - { - $path = 'foo.bar'; - $filesystem = $this->mockAdapter(); - - $filesystem - ->expects($this->once()) - ->method('stat') - ->with($path) - ->will($this->returnValue(new RejectedPromise())) - ; - - $filesystem - ->expects($this->once()) - ->method('touch') - ->with($path) - ->will($this->returnValue(new FulfilledPromise())) - ; - - $callbackFired = false; - (new File($path, Filesystem::createFromAdapter($filesystem)))->create()->then(function () use (&$callbackFired) { - $callbackFired = true; - }); - - $this->assertTrue($callbackFired); - } - - public function testCreateFail() - { - $path = 'foo.bar'; - $filesystem = $this->mockAdapter(); - - $filesystem - ->expects($this->once()) - ->method('stat') - ->with($path) - ->will($this->returnValue(new FulfilledPromise())) - ; - - $callbackFired = false; - (new File($path, Filesystem::createFromAdapter($filesystem)))->create()->then(null, function ($e) use (&$callbackFired) { - $this->assertInstanceOf('Exception', $e); - $this->assertSame('File exists already', $e->getMessage()); - $callbackFired = true; - }); - - $this->assertTrue($callbackFired); - } - - public function testOpen() - { - $path = 'foo.bar'; - $filesystem = $this->mockAdapter(); - - $fd = 'foo:bar'; - $flags = 'abc'; - - $filesystem - ->expects($this->once()) - ->method('open') - ->with($path, $flags) - ->will($this->returnValue(new FulfilledPromise($fd))) - ; - - $fs = Filesystem::createFromAdapter($filesystem); - $pass = $this->await((new File($path, $fs))->open($flags), $fs->getAdapter()->getLoop()); - - $this->assertInstanceOf('\React\Filesystem\Stream\GenericStreamInterface', $pass); - $this->assertSame($fd, $pass->getFiledescriptor()); - } - - - public function testOpenTwice() - { - $path = 'foo.bar'; - $filesystem = $this->mockAdapter(); - - $fd = 'foo:bar'; - $flags = 'abc'; - - $filesystem - ->expects($this->once()) - ->method('open') - ->with($path, $flags) - ->will($this->returnValue(new FulfilledPromise($fd))) - ; - - $file = new File($path, Filesystem::createFromAdapter($filesystem)); - $file->open($flags); - $this->assertInstanceOf('React\Promise\RejectedPromise', $file->open($flags)); - } - - public function testGetContents() - { - $path = 'foo.bar'; - $fd = '0123456789abcdef'; - - $filesystem = $this->mockAdapter(); - - $filesystem - ->expects($this->once()) - ->method('getContents') - ->with($path) - ->will($this->returnValue(new FulfilledPromise('a'))) - ; - - $getContentsPromise = (new File($path, Filesystem::createFromAdapter($filesystem)))->getContents(); - $this->assertInstanceOf('React\Promise\PromiseInterface', $getContentsPromise); - } - - public function testClose() - { - $path = 'foo.bar'; - $fd = '0123456789abcdef'; - $filesystem = $this->mockAdapter(); - - $openPromise = new FulfilledPromise($fd); - - $filesystem - ->method('stat') - ->with($path) - ->will($this->returnValue(new FulfilledPromise([ - 'size' => 1, - ]))) - ; - - $filesystem - ->method('read') - ->with($path) - ->will($this->returnValue(new FulfilledPromise('a'))) - ; - - $filesystem - ->expects($this->once()) - ->method('open') - ->with($path, 'r') - ->will($this->returnValue($openPromise)) - ; - - $filesystem - ->expects($this->once()) - ->method('close') - ->with($fd) - ->will($this->returnValue(\React\Promise\resolve())) - ; - - $file = new File($path, Filesystem::createFromAdapter($filesystem)); - $file->open('r'); - $file->close(); - } - - public function testCloseNeverOpened() - { - $path = 'foo.bar'; - $filesystem = $this->mockAdapter(); - $this->assertInstanceOf('React\Promise\RejectedPromise', (new File($path, Filesystem::createFromAdapter($filesystem)))->close()); - } - - public function testTouch() - { - $path = 'foo.bar'; - $filesystem = $this->mockAdapter(); - - $filesystem - ->expects($this->once()) - ->method('touch') - ->with($path) - ->will($this->returnValue($this->getMock('React\Promise\PromiseInterface'))) - ; - - $this->assertInstanceOf('React\Promise\PromiseInterface', (new File($path, Filesystem::createFromAdapter($filesystem)))->touch()); - } - - public function testCopy() - { - $filesystem = $this->mockAdapter(); - - $fileFrom = $this->getMock('React\Filesystem\Node\File', [ - 'copyStreaming', - ], [ - 'foo.bar', - Filesystem::createFromAdapter($filesystem), - ]); - - $fileTo = new File('bar.foo', Filesystem::createFromAdapter($filesystem)); - - $fileFrom - ->expects($this->once()) - ->method('copyStreaming') - ->with($fileTo) - ->will($this->returnValue(new ObjectStream())) - ; - - $stream = $fileFrom->copy($fileTo); - $this->assertInstanceOf('React\Promise\PromiseInterface', $stream); - } - - /** - * @expectedException UnexpectedValueException - */ - public function testCopyUnknownNode() - { - $filesystem = $this->mockAdapter(); - - (new File('foo.bar', Filesystem::createFromAdapter($filesystem)))->copy(new UnknownNodeType()); - } - - public function testCopyDirectory() - { - $filesystem = $this->mockAdapter(); - - $file = $this->getMock('React\Filesystem\Node\File', [ - 'copyToFile', - ], [ - 'foo.bar', - Filesystem::createFromAdapter($filesystem), - ]); - - $directoryTo = new Directory('bar.foo', Filesystem::createFromAdapter($filesystem)); - - $file - ->expects($this->once()) - ->method('copyToFile') - ->with($this->isInstanceOf('React\Filesystem\Node\File')) - ->will($this->returnValue(new ObjectStream())) - ; - - $stream = $file->copyStreaming($directoryTo); - $this->assertInstanceOf('React\Filesystem\ObjectStream', $stream); - } - - /** - * @expectedException UnexpectedValueException - */ - public function testCopyStreamingUnknownNode() - { - $filesystem = $this->mockAdapter(); - - (new File('foo.bar', Filesystem::createFromAdapter($filesystem)))->copyStreaming(new UnknownNodeType()); - } -} diff --git a/tests/Node/GenericOperationTraitTest.php b/tests/Node/GenericOperationTraitTest.php deleted file mode 100644 index 09dde74c..00000000 --- a/tests/Node/GenericOperationTraitTest.php +++ /dev/null @@ -1,152 +0,0 @@ -getMockForTrait('React\Filesystem\Node\GenericOperationTrait'); - - $got->adapter = $this->mockAdapter(); - - $got->filesystem = Filesystem::createFromAdapter($got->adapter); - - $this->assertSame($got->filesystem, $got->getFilesystem()); - $this->assertSame($got->adapter, $got->getFilesystem()->getAdapter()); - } - - public function testStat() - { - $got = $this->getMockForTrait('React\Filesystem\Node\GenericOperationTrait', [], '', true, true, true, [ - 'getPath', - ]); - $got->expects($this->once()) - ->method('getPath') - ->will($this->returnValue('foo.bar')); - - $promise = new FulfilledPromise(); - - $got->adapter = $this->mockAdapter(); - $got->adapter->expects($this->once()) - ->method('stat') - ->with('foo.bar') - ->will($this->returnValue($promise)); - - $got->filesystem = Filesystem::createFromAdapter($got->adapter); - - $this->assertSame($promise, $got->stat()); - } - - public function testChmod() - { - $got = $this->getMockForTrait('React\Filesystem\Node\GenericOperationTrait', [], '', true, true, true, [ - 'getPath', - ]); - $got->expects($this->once()) - ->method('getPath') - ->will($this->returnValue('foo.bar')); - - $promise = new FulfilledPromise(); - - $got->adapter = $this->mockAdapter(); - $got->adapter->expects($this->once()) - ->method('chmod') - ->with('foo.bar', 'abc') - ->will($this->returnValue($promise)); - - $got->filesystem = Filesystem::createFromAdapter($got->adapter); - - $this->assertSame($promise, $got->chmod('abc')); - } - - public function testChown() - { - $got = $this->getMockForTrait('React\Filesystem\Node\GenericOperationTrait', [], '', true, true, true, [ - 'getPath', - ]); - $got->expects($this->once()) - ->method('getPath') - ->will($this->returnValue('foo.bar')); - - $promise = new FulfilledPromise(); - - $got->adapter = $this->mockAdapter(); - $got->adapter->expects($this->once()) - ->method('chown') - ->with('foo.bar', 1, 2) - ->will($this->returnValue($promise)); - - $got->filesystem = Filesystem::createFromAdapter($got->adapter); - - $this->assertSame($promise, $got->chown(1, 2)); - } - - public function testChownDefaults() - { - $got = $this->getMockForTrait('React\Filesystem\Node\GenericOperationTrait', [], '', true, true, true, [ - 'getPath', - ]); - $got->expects($this->once()) - ->method('getPath') - ->will($this->returnValue('foo.bar')); - - $promise = new FulfilledPromise(); - - $got->adapter = $this->mockAdapter(); - $got->adapter->expects($this->once()) - ->method('chown') - ->with('foo.bar', -1, -1) - ->will($this->returnValue($promise)); - - $got->filesystem = Filesystem::createFromAdapter($got->adapter); - - $this->assertSame($promise, $got->chown()); - } - - public function testCreateNameNParentFromFilename() - { - $node = new File('/foo/bar/baz/rabbit/kitten/index.php', Filesystem::createFromAdapter($this->mockAdapter())); - - foreach ([ - [ - 'index.php', - '/foo/bar/baz/rabbit/kitten/index.php', - ], - [ - 'kitten', - '/foo/bar/baz/rabbit/kitten/', - ], - [ - 'rabbit', - '/foo/bar/baz/rabbit/', - ], - [ - 'baz', - '/foo/bar/baz/', - ], - [ - 'bar', - '/foo/bar/', - ], - [ - 'foo', - '/foo/', - ], - [ - '', - '/', - ], - ] as $names) { - $this->assertSame($names[0], $node->getName()); - $this->assertSame($names[1], $node->getPath()); - $node = $node->getParent(); - } - } -} diff --git a/tests/Node/NodeTestTrait.php b/tests/Node/NodeTestTrait.php deleted file mode 100644 index 0e2c8e82..00000000 --- a/tests/Node/NodeTestTrait.php +++ /dev/null @@ -1,21 +0,0 @@ -getNodeClass(); - $this->assertSame($out, (string) (new $nodeClass($in, Filesystem::createFromAdapter($this->mockAdapter())))); - } - - abstract protected function getNodeClass(); -} diff --git a/tests/Node/RecursiveInvokerTest.php b/tests/Node/RecursiveInvokerTest.php deleted file mode 100644 index f04c9c38..00000000 --- a/tests/Node/RecursiveInvokerTest.php +++ /dev/null @@ -1,89 +0,0 @@ -mockAdapter(); - - $node = $this->getMock('React\Filesystem\Node\Directory', [ - 'ls', - 'chmod', - ], [ - 'foo.bar', - Filesystem::createFromAdapter($filesystem), - ]); - - $promise = $this->getMock('React\Promise\PromiseInterface'); - - $node - ->expects($this->once()) - ->method('ls') - ->with() - ->will($this->returnValue($promise)) - ; - - $fileDent = $this->getMock('React\Filesystem\Node\File', [ - 'chmod', - ], [ - 'foo', - Filesystem::createFromAdapter($filesystem), - ]); - - $node - ->expects($this->once()) - ->method('chmod') - ->with(123) - ->will($this->returnValue(new FulfilledPromise())) - ; - - $directoryDent = $this->getMock('React\Filesystem\Node\Directory', [ - 'chmodRecursive', - ], [ - 'foo', - Filesystem::createFromAdapter($filesystem), - ]); - - $directoryDent - ->expects($this->once()) - ->method('chmodRecursive') - ->with(123) - ->will($this->returnValue(new FulfilledPromise())) - ; - - $finalPromise = $this->getMock('React\Promise\PromiseInterface'); - - $node - ->expects($this->once()) - ->method('chmod') - ->with(123) - ->will($this->returnValue($finalPromise)) - ; - - $dents = [ - $fileDent, - $directoryDent, - ]; - - $promise - ->expects($this->once()) - ->method('then') - ->with($this->isType('callable')) - ->will($this->returnCallback(function ($resolveCb) use ($dents) { - return $resolveCb($dents); - })) - ; - - $this->assertInstanceOf('React\Promise\PromiseInterface', (new RecursiveInvoker($node))->execute('chmod', [ - 123, - ])); - } -} diff --git a/tests/NotExistTest.php b/tests/NotExistTest.php new file mode 100644 index 00000000..ada8fc75 --- /dev/null +++ b/tests/NotExistTest.php @@ -0,0 +1,25 @@ +await($filesystem->detect($dirName)->then(static function (NotExistInterface $notExist): PromiseInterface { + return $notExist->createDirectory(); + })); + + self::assertDirectoryExists($dirName); + } +} diff --git a/tests/ObjectStreamSinkTest.php b/tests/ObjectStreamSinkTest.php deleted file mode 100644 index efb0dd4d..00000000 --- a/tests/ObjectStreamSinkTest.php +++ /dev/null @@ -1,27 +0,0 @@ -getMock('React\Filesystem\Node\NodeInterface'); - $stream = new ObjectStream(); - $sink = ObjectStreamSink::promise($stream); - $this->assertInstanceOf('React\Promise\PromiseInterface', $sink); - $stream->emit('data', [$node]); - $stream->close(); - - $nodes = null; - $sink->then(function ($list) use (&$nodes) { - $nodes = $list; - }); - - $this->assertSame(1, count($nodes)); - $this->assertSame($node, reset($nodes)); - } -} diff --git a/tests/ObjectStreamTest.php b/tests/ObjectStreamTest.php deleted file mode 100644 index 164cfe42..00000000 --- a/tests/ObjectStreamTest.php +++ /dev/null @@ -1,31 +0,0 @@ -getMock('React\Filesystem\Node\NodeInterface'); - $stream = new ObjectStream(); - - $this->assertTrue($stream->isWritable()); - $this->assertTrue($stream->isReadable()); - - $stream->on('data', function (NodeInterface $data) use ($node) { - $this->assertEquals($node, $data); - }); - $stream->end($node); - - $this->assertFalse($stream->isWritable()); - $this->assertFalse($stream->isReadable()); - - $stream->close(); - - $this->assertFalse($stream->isWritable()); - $this->assertFalse($stream->isReadable()); - } -} diff --git a/tests/OpenFileLimiterTest.php b/tests/OpenFileLimiterTest.php deleted file mode 100644 index 14b67e4c..00000000 --- a/tests/OpenFileLimiterTest.php +++ /dev/null @@ -1,44 +0,0 @@ -assertSame(1, $limiter->getLimit()); - $this->assertSame(0, $limiter->getOutstanding()); - $this->assertSame(0, $limiter->getQueueSize()); - - $promise1 = $limiter->open(); - $this->assertInstanceOf('React\Promise\PromiseInterface', $promise1); - $promiseCallbackCalled1 = false; - $promise1->then(function () use (&$promiseCallbackCalled1) { - $promiseCallbackCalled1 = true; - }); - $this->assertSame(1, $limiter->getOutstanding()); - $this->assertSame(0, $limiter->getQueueSize()); - - $promise2 = $limiter->open(); - $this->assertInstanceOf('React\Promise\PromiseInterface', $promise2); - $promiseCallbackCalled2 = false; - $promise2->then(function () use (&$promiseCallbackCalled2) { - $promiseCallbackCalled2 = true; - }); - $this->assertSame(1, $limiter->getOutstanding()); - $this->assertSame(1, $limiter->getQueueSize()); - - $limiter->close(); - $this->assertSame(1, $limiter->getOutstanding()); - $this->assertSame(0, $limiter->getQueueSize()); - $this->assertTrue($promiseCallbackCalled1); - - $limiter->close(); - $this->assertSame(0, $limiter->getOutstanding()); - $this->assertSame(0, $limiter->getQueueSize()); - $this->assertTrue($promiseCallbackCalled2); - } -} diff --git a/tests/PermissionFlagResolverTest.php b/tests/PermissionFlagResolverTest.php deleted file mode 100644 index 097c86fb..00000000 --- a/tests/PermissionFlagResolverTest.php +++ /dev/null @@ -1,115 +0,0 @@ -assertSame($result, $resolver->resolve($flags)); - } -} diff --git a/tests/Stream/DuplexStreamTest.php b/tests/Stream/DuplexStreamTest.php deleted file mode 100644 index ab22db9e..00000000 --- a/tests/Stream/DuplexStreamTest.php +++ /dev/null @@ -1,24 +0,0 @@ -mockAdapter(); - - $stream = new DuplexStream($path, $fileDescriptor, $filesystem); - - $this->assertSame($stream, $stream->pipe($stream)); - } -} diff --git a/tests/Stream/GenericStreamTraitTest.php b/tests/Stream/GenericStreamTraitTest.php deleted file mode 100644 index 927d7773..00000000 --- a/tests/Stream/GenericStreamTraitTest.php +++ /dev/null @@ -1,19 +0,0 @@ -getMockForTrait('React\Filesystem\Stream\GenericStreamTrait', [ - 'foo.bar', - 'abc', - $this->mockAdapter(), - ]); - - $this->assertSame('abc', $gst->getFiledescriptor()); - } -} diff --git a/tests/Stream/ReadableStreamTest.php b/tests/Stream/ReadableStreamTest.php deleted file mode 100644 index 9aded959..00000000 --- a/tests/Stream/ReadableStreamTest.php +++ /dev/null @@ -1,275 +0,0 @@ -mockAdapter(); - - $filesystem - ->expects($this->once()) - ->method('stat') - ->with($path) - ->will($this->returnValue(\React\Promise\resolve([ - 'size' => 123, - ]))) - ; - - $mock = $this->getMock($className, [ - 'readChunk', - ], [ - $path, - $fileDescriptor, - $filesystem, - ]); - - if ($className == 'React\Filesystem\Stream\DuplexStream') { - $mock->resume(); - } - } - - /** - * @dataProvider classNamesProvider - */ - public function testResume($className) - { - $path = 'foo.bar'; - $fileDescriptor = '0123456789abcdef'; - - $filesystem = $this->mockAdapter(); - - $filesystem - ->expects($this->once()) - ->method('stat') - ->with($path) - ->will($this->returnValue(\React\Promise\resolve([ - 'size' => 123, - ]))) - ; - - $mock = $this->getMock($className, [ - 'readChunk', - ], [ - $path, - $fileDescriptor, - $filesystem, - ]); - - if ($className == 'React\Filesystem\Stream\DuplexStream') { - $mock->resume(); - } - } - - /** - * @dataProvider classNamesProvider - */ - public function testClose($className, $stat) - { - $path = 'foo.bar'; - $fd = '0123456789abcdef'; - - $filesystem = $this->mockAdapter(); - - if ($stat) { - $filesystem - ->expects($this->once()) - ->method('stat') - ->with($path) - ->will($this->returnValue(new RejectedPromise())) - ; - } - - $promise = $this->getMock('React\Promise\PromiseInterface', [ - 'then', - ]); - - $promise - ->expects($this->once()) - ->method('then') - ->with($this->isType('callable')) - ->will($this->returnCallback(function ($resolveCb) { - $resolveCb(); - })) - ; - - $filesystem - ->expects($this->once()) - ->method('close') - ->with($fd) - ->will($this->returnValue($promise)) - ; - - $stream = $this->getMock($className, [ - 'emit', - 'removeAllListeners', - ], [ - $path, - $fd, - $filesystem, - ]); - - $stream - ->expects($this->at(0)) - ->method('emit') - ->with('close', [$stream]) - ; - - $stream - ->expects($this->at(1)) - ->method('removeAllListeners') - ->with() - ; - - $stream->close(); - } - - /** - * @dataProvider classNamesProvider - */ - public function testAlreadyClosed($className, $stat) - { - $path = 'foo.bar'; - $fd = '0123456789abcdef'; - $filesystem = $this->mockAdapter(); - - if ($stat) { - $filesystem - ->expects($this->once()) - ->method('stat') - ->with($path) - ->will($this->returnValue(new RejectedPromise())) - ; - } - - $filesystem - ->expects($this->once()) - ->method('close') - ->with($fd) - ->will($this->returnValue(new RejectedPromise())) - ; - - - $stream = (new $className($path, $fd, $filesystem)); - $stream->close(); - $stream->close(); - } - - /** - * @dataProvider classNamesProvider - */ - public function testPipe($className, $stat) - { - $path = 'foo.bar'; - $fileDescriptor = '0123456789abcdef'; - - $filesystem = $this->mockAdapter(); - - if ($stat) { - $filesystem - ->expects($this->once()) - ->method('stat') - ->with($path) - ->will($this->returnValue(new RejectedPromise())) - ; - } - - $stream = new $className($path, $fileDescriptor, $filesystem); - $destination = new WritableStream($path, $fileDescriptor, $filesystem); - - $this->assertSame($destination, $stream->pipe($destination)); - } - - /** - * @dataProvider classNamesProvider - */ - public function testReadChunk($className, $stat) - { - $path = 'foo.bar'; - $fileDescriptor = '0123456789abcdef'; - - $readPromise = $this->getMock('React\Promise\PromiseInterface', [ - 'then', - ]); - - $readPromise - ->expects($this->exactly(2)) - ->method('then') - ->with($this->isType('callable')) - ->will($this->returnCallback(function ($resolveCb) { - $resolveCb('foo.bar' . (string)microtime(true)); - })) - ; - - $filesystem = $this->mockAdapter(); - - $filesystem - ->expects($this->at(0)) - ->method('stat') - ->with($path) - ->will($this->returnValue(\React\Promise\resolve([ - 'size' => 16384, - ]))) - ; - - $filesystem - ->expects($this->at(1)) - ->method('read') - ->with($fileDescriptor, 8192, 0) - ->will($this->returnValue($readPromise)) - ; - - $filesystem - ->expects($this->at(2)) - ->method('read') - ->with($fileDescriptor, 8192, 8192) - ->will($this->returnValue($readPromise)) - ; - - $filesystem - ->expects($this->at(3)) - ->method('close') - ->with($fileDescriptor) - ->will($this->returnValue(new FulfilledPromise())) - ; - - $mock = $this->getMock($className, [ - 'isReadable', - 'emit', - ], [ - $path, - $fileDescriptor, - $filesystem, - ]); - - if ($className == 'React\Filesystem\Stream\DuplexStream') { - $mock->resume(); - } - } -} diff --git a/tests/Stream/StreamFactoryTest.php b/tests/Stream/StreamFactoryTest.php deleted file mode 100644 index 8afc236f..00000000 --- a/tests/Stream/StreamFactoryTest.php +++ /dev/null @@ -1,46 +0,0 @@ -mockAdapter(); - $filesystem - ->expects($this->once()) - ->method('stat') - ->with('foo.bar') - ->will($this->returnValue(\React\Promise\resolve([]))) - ; - - $this->assertInstanceOf( - 'React\Filesystem\Stream\ReadableStream', - StreamFactory::create('foo.bar', null, 'r', $filesystem) - ); - } - - public function testCreateWrite() - { - $filesystem = $this->mockAdapter(); - - $this->assertInstanceOf( - 'React\Filesystem\Stream\WritableStream', - StreamFactory::create('foo.bar', null, 'w', $filesystem) - ); - } - - public function testCreateDuplex() - { - $filesystem = $this->mockAdapter(); - - $this->assertInstanceOf( - 'React\Filesystem\Stream\DuplexStream', - StreamFactory::create('foo.bar', null, '+', $filesystem) - ); - } -} diff --git a/tests/Stream/WritableStreamTest.php b/tests/Stream/WritableStreamTest.php deleted file mode 100644 index 3b732972..00000000 --- a/tests/Stream/WritableStreamTest.php +++ /dev/null @@ -1,231 +0,0 @@ -mockAdapter(); - - $filesystem - ->expects($this->at($offset + 0)) - ->method('write') - ->with($fd, 'abc', 3, 0) - ->will($this->returnValue($fd)) - ; - - $filesystem - ->expects($this->at($offset + 1)) - ->method('write') - ->with($fd, 'def', 3, 3) - ->will($this->returnValue($fd)) - ; - - $filesystem - ->expects($this->at($offset + 2)) - ->method('write') - ->with($fd, 'ghijklmnopqrstuvwxyz', 20, 6) - ->will($this->returnValue($fd)) - ; - - $stream = (new $className($path, $fd, $filesystem)); - $stream->write('abc'); - $stream->write('def'); - $stream->write('ghijklmnopqrstuvwxyz'); - } - - /** - * @dataProvider classNamesProvider - */ - public function testIsWritable($className) - { - $path = 'foo.bar'; - $fd = '0123456789abcdef'; - $filesystem = $this->mockAdapter(); - - $this->assertTrue((new $className($path, $fd, $filesystem))->isWritable()); - } - - /** - * @dataProvider classNamesProvider - */ - public function testIsNotWritable($className) - { - $path = 'foo.bar'; - $fd = '0123456789abcdef'; - $filesystem = $this->mockAdapter(); - - - $filesystem - ->expects($this->once()) - ->method('close') - ->with($fd) - ->will($this->returnValue(new RejectedPromise())) - ; - - - $stream = (new $className($path, $fd, $filesystem)); - $stream->close(); - $this->assertFalse($stream->isWritable()); - } - - /** - * @dataProvider classNamesProvider - */ - public function testEnd($className) - { - $data = 'iahbfeq'; - $stream = $this->getMock($className, [ - 'write', - 'close', - ], [ - 'foo.bar', - '0123456789abcdef', - $this->mockAdapter(), - ]); - - $stream - ->expects($this->once()) - ->method('write') - ->with($data) - ; - - $stream - ->expects($this->once()) - ->method('close') - ->with() - ; - - $stream->end($data); - } - - /** - * @dataProvider classNamesProvider - */ - public function testEndNoWrite($className) - { - $stream = $this->getMock($className, [ - 'write', - 'close', - ], [ - 'foo.bar', - '0123456789abcdef', - $this->mockAdapter(), - ]); - - $stream - ->expects($this->never()) - ->method('write') - ->with() - ; - - $stream - ->expects($this->once()) - ->method('close') - ->with() - ; - - $stream->end(); - } - - /** - * @dataProvider classNamesProvider - */ - public function testClose($className) - { - $path = 'foo.bar'; - $fd = '0123456789abcdef'; - - $filesystem = $this->mockAdapter(); - - - $promise = $this->getMock('React\Promise\PromiseInterface', [ - 'then', - ]); - - $promise - ->expects($this->once()) - ->method('then') - ->with($this->isType('callable')) - ->will($this->returnCallback(function ($resolveCb) { - $resolveCb(); - })) - ; - - $filesystem - ->expects($this->once()) - ->method('close') - ->with($fd) - ->will($this->returnValue($promise)) - ; - - $stream = $this->getMock($className, [ - 'emit', - 'removeAllListeners', - ], [ - $path, - $fd, - $filesystem, - ]); - - $stream - ->expects($this->at(0)) - ->method('emit') - ->with('close', [$stream]) - ; - - $stream - ->expects($this->at(1)) - ->method('removeAllListeners') - ->with() - ; - - $stream->close(); - } - - /** - * @dataProvider classNamesProvider - */ - public function testAlreadyClosed($className) - { - $path = 'foo.bar'; - $fd = '0123456789abcdef'; - $filesystem = $this->mockAdapter(); - - - $filesystem - ->expects($this->once()) - ->method('close') - ->with($fd) - ->will($this->returnValue(new RejectedPromise())) - ; - - - $stream = (new $className($path, $fd, $filesystem)); - $stream->close(); - $stream->close(); - } -} diff --git a/tests/TestCase.php b/tests/TestCase.php deleted file mode 100644 index 06b973cb..00000000 --- a/tests/TestCase.php +++ /dev/null @@ -1,119 +0,0 @@ -getMock('React\EventLoop\LoopInterface'); - } - - $mock = $this->getMock('React\Filesystem\AdapterInterface', [ - '__construct', - 'getLoop', - 'getFilesystem', - 'setFilesystem', - 'callFilesystem', - 'isSupported', - 'mkdir', - 'rmdir', - 'unlink', - 'chmod', - 'chown', - 'stat', - 'ls', - 'lsStream', - 'touch', - 'open', - 'read', - 'write', - 'close', - 'getContents', - 'putContents', - 'appendContents', - 'rename', - 'readlink', - 'symlink', - 'detectType', - ], [ - $loop, - ]); - - $mock - ->expects($this->any()) - ->method('getLoop') - ->with() - ->will($this->returnValue($loop)) - ; - - return $mock; - } - - public function setUp() - { - $this->tmpDir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'react-filesystem-tests' . DIRECTORY_SEPARATOR . uniqid('', true) . DIRECTORY_SEPARATOR; - mkdir($this->tmpDir, 0777, true); - $this->startTime = time(); - } - - protected function checkIfTimedOut($maxExecutionTime = self::TIMEOUT) - { - if (($this->startTime + $maxExecutionTime) <= time()) { - $this->fail('Manual timeout'); - } - } - - protected function setLoopTimeout(LoopInterface $loop, $maxExecutionTime = self::TIMEOUT) - { - $loop->addTimer($maxExecutionTime, function () use ($loop) { - $loop->stop(); - $this->fail('Event loop timeout'); - }); - } - - public function tearDown() - { - $this->rmdir($this->tmpDir); - } - - protected function rmdir($dir) - { - $directory = dir($dir); - while (false !== ($entry = $directory->read())) { - if (in_array($entry, ['.', '..'])) { - continue; - } - - if (is_dir($dir . $entry)) { - $this->rmdir($dir . $entry . DIRECTORY_SEPARATOR); - continue; - } - - if (is_file($dir . $entry)) { - unlink($dir . $entry); - continue; - } - } - $directory->close(); - } - - protected function await(PromiseInterface $promise, LoopInterface $loop, $timeout = self::TIMEOUT) - { - $result = Block\await($promise, $loop, $timeout); - $loop->run(); // Ensure we let the loop run it's course to clean up - return $result; - } -} diff --git a/tests/UnknownNodeType.php b/tests/UnknownNodeType.php deleted file mode 100644 index b62eacce..00000000 --- a/tests/UnknownNodeType.php +++ /dev/null @@ -1,94 +0,0 @@ -getPath(); - } - - /** - * @return \React\Filesystem\AdapterInterface - */ - public function getFilesystem() - { - // TODO: Implement getFilesystem() method. - } - - /** - * @return \React\Promise\PromiseInterface - */ - public function stat() - { - // TODO: Implement stat() method. - } - - /** - * @param int $mode - * @return \React\Promise\PromiseInterface - */ - public function chmod($mode) - { - // TODO: Implement chmod() method. - } - - /** - * @param int $uid - * @param int $gid - * @return \React\Promise\PromiseInterface - */ - public function chown($uid = -1, $gid = -1) - { - // TODO: Implement chown() method. - } - - /** - * @param NodeInterface $node - * @return \React\Promise\PromiseInterface - */ - public function copy(NodeInterface $node) - { - // TODO: Implement copy() method. - } - - /** - * @param NodeInterface $node - * @return ObjectStream - */ - public function copyStreaming(NodeInterface $node) - { - // TODO: Implement copyStreaming() method. - } -}