diff --git a/composer.json b/composer.json index 065ed6268..673942f03 100644 --- a/composer.json +++ b/composer.json @@ -22,14 +22,12 @@ "ext-mbstring": "*", "ext-phar": "*", "composer-plugin-api": "^2.2", - "amphp/parallel-functions": "^1.1", "composer/semver": "^3.3.2", "composer/xdebug-handler": "^3.0.3", "fidry/console": "^0.6.0", "fidry/filesystem": "^1.1", "humbug/php-scoper": "^0.18.6", "justinrainbow/json-schema": "^5.2.12", - "laravel/serializable-closure": "^1.2.2", "nikic/iter": "^2.2", "nikic/php-parser": "^4.15.2", "phpdocumentor/reflection-docblock": "^5.3", @@ -44,7 +42,8 @@ "symfony/polyfill-mbstring": "^1.28", "symfony/process": "^6.1.3", "symfony/var-dumper": "^6.1.6", - "webmozart/assert": "^1.11" + "webmozart/assert": "^1.11", + "webmozarts/console-parallelization": "^2.1" }, "require-dev": { "ext-xml": "*", diff --git a/composer.lock b/composer.lock index 27118bd1b..9b89e03f9 100644 --- a/composer.lock +++ b/composer.lock @@ -4,559 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "83b93ad0a784e79fa33a67425d43c9ab", + "content-hash": "6cc0cf89ef861f5992e68aeecb210e47", "packages": [ - { - "name": "amphp/amp", - "version": "v2.6.2", - "source": { - "type": "git", - "url": "https://github.com/amphp/amp.git", - "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/amp/zipball/9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", - "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "amphp/php-cs-fixer-config": "dev-master", - "amphp/phpunit-util": "^1", - "ext-json": "*", - "jetbrains/phpstorm-stubs": "^2019.3", - "phpunit/phpunit": "^7 | ^8 | ^9", - "psalm/phar": "^3.11@dev", - "react/promise": "^2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - } - }, - "autoload": { - "files": [ - "lib/functions.php", - "lib/Internal/functions.php" - ], - "psr-4": { - "Amp\\": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Lowrey", - "email": "rdlowrey@php.net" - }, - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Bob Weinand", - "email": "bobwei9@hotmail.com" - }, - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - } - ], - "description": "A non-blocking concurrency framework for PHP applications.", - "homepage": "https://amphp.org/amp", - "keywords": [ - "async", - "asynchronous", - "awaitable", - "concurrency", - "event", - "event-loop", - "future", - "non-blocking", - "promise" - ], - "support": { - "irc": "irc://irc.freenode.org/amphp", - "issues": "https://github.com/amphp/amp/issues", - "source": "https://github.com/amphp/amp/tree/v2.6.2" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2022-02-20T17:52:18+00:00" - }, - { - "name": "amphp/byte-stream", - "version": "v1.8.1", - "source": { - "type": "git", - "url": "https://github.com/amphp/byte-stream.git", - "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/byte-stream/zipball/acbd8002b3536485c997c4e019206b3f10ca15bd", - "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd", - "shasum": "" - }, - "require": { - "amphp/amp": "^2", - "php": ">=7.1" - }, - "require-dev": { - "amphp/php-cs-fixer-config": "dev-master", - "amphp/phpunit-util": "^1.4", - "friendsofphp/php-cs-fixer": "^2.3", - "jetbrains/phpstorm-stubs": "^2019.3", - "phpunit/phpunit": "^6 || ^7 || ^8", - "psalm/phar": "^3.11.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "files": [ - "lib/functions.php" - ], - "psr-4": { - "Amp\\ByteStream\\": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - } - ], - "description": "A stream abstraction to make working with non-blocking I/O simple.", - "homepage": "http://amphp.org/byte-stream", - "keywords": [ - "amp", - "amphp", - "async", - "io", - "non-blocking", - "stream" - ], - "support": { - "irc": "irc://irc.freenode.org/amphp", - "issues": "https://github.com/amphp/byte-stream/issues", - "source": "https://github.com/amphp/byte-stream/tree/v1.8.1" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2021-03-30T17:13:30+00:00" - }, - { - "name": "amphp/parallel", - "version": "v1.4.3", - "source": { - "type": "git", - "url": "https://github.com/amphp/parallel.git", - "reference": "3aac213ba7858566fd83d38ccb85b91b2d652cb0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/parallel/zipball/3aac213ba7858566fd83d38ccb85b91b2d652cb0", - "reference": "3aac213ba7858566fd83d38ccb85b91b2d652cb0", - "shasum": "" - }, - "require": { - "amphp/amp": "^2", - "amphp/byte-stream": "^1.6.1", - "amphp/parser": "^1", - "amphp/process": "^1", - "amphp/serialization": "^1", - "amphp/sync": "^1.0.1", - "php": ">=7.1" - }, - "require-dev": { - "amphp/php-cs-fixer-config": "dev-master", - "amphp/phpunit-util": "^1.1", - "phpunit/phpunit": "^8 || ^7" - }, - "type": "library", - "autoload": { - "files": [ - "lib/Context/functions.php", - "lib/Sync/functions.php", - "lib/Worker/functions.php" - ], - "psr-4": { - "Amp\\Parallel\\": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Stephen Coakley", - "email": "me@stephencoakley.com" - } - ], - "description": "Parallel processing component for Amp.", - "homepage": "https://github.com/amphp/parallel", - "keywords": [ - "async", - "asynchronous", - "concurrent", - "multi-processing", - "multi-threading" - ], - "support": { - "issues": "https://github.com/amphp/parallel/issues", - "source": "https://github.com/amphp/parallel/tree/v1.4.3" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2023-03-23T08:04:23+00:00" - }, - { - "name": "amphp/parallel-functions", - "version": "v1.1.0", - "source": { - "type": "git", - "url": "https://github.com/amphp/parallel-functions.git", - "reference": "04e92fcacfc921a56dfe12c23b3265e62593a7cb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/parallel-functions/zipball/04e92fcacfc921a56dfe12c23b3265e62593a7cb", - "reference": "04e92fcacfc921a56dfe12c23b3265e62593a7cb", - "shasum": "" - }, - "require": { - "amphp/amp": "^2.0.3", - "amphp/parallel": "^1.4", - "amphp/serialization": "^1.0", - "laravel/serializable-closure": "^1.0", - "php": ">=7.4" - }, - "require-dev": { - "amphp/php-cs-fixer-config": "v2.x-dev", - "amphp/phpunit-util": "^2.0", - "phpunit/phpunit": "^9.5.11" - }, - "type": "library", - "autoload": { - "files": [ - "src/functions.php" - ], - "psr-4": { - "Amp\\ParallelFunctions\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - } - ], - "description": "Parallel processing made simple.", - "support": { - "issues": "https://github.com/amphp/parallel-functions/issues", - "source": "https://github.com/amphp/parallel-functions/tree/v1.1.0" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2022-02-03T19:32:41+00:00" - }, - { - "name": "amphp/parser", - "version": "v1.1.0", - "source": { - "type": "git", - "url": "https://github.com/amphp/parser.git", - "reference": "ff1de4144726c5dad5fab97f66692ebe8de3e151" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/parser/zipball/ff1de4144726c5dad5fab97f66692ebe8de3e151", - "reference": "ff1de4144726c5dad5fab97f66692ebe8de3e151", - "shasum": "" - }, - "require": { - "php": ">=7.4" - }, - "require-dev": { - "amphp/php-cs-fixer-config": "^2", - "phpunit/phpunit": "^9", - "psalm/phar": "^5.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Amp\\Parser\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - } - ], - "description": "A generator parser to make streaming parsers simple.", - "homepage": "https://github.com/amphp/parser", - "keywords": [ - "async", - "non-blocking", - "parser", - "stream" - ], - "support": { - "issues": "https://github.com/amphp/parser/issues", - "source": "https://github.com/amphp/parser/tree/v1.1.0" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2022-12-30T18:08:47+00:00" - }, - { - "name": "amphp/process", - "version": "v1.1.4", - "source": { - "type": "git", - "url": "https://github.com/amphp/process.git", - "reference": "76e9495fd6818b43a20167cb11d8a67f7744ee0f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/process/zipball/76e9495fd6818b43a20167cb11d8a67f7744ee0f", - "reference": "76e9495fd6818b43a20167cb11d8a67f7744ee0f", - "shasum": "" - }, - "require": { - "amphp/amp": "^2", - "amphp/byte-stream": "^1.4", - "php": ">=7" - }, - "require-dev": { - "amphp/php-cs-fixer-config": "dev-master", - "amphp/phpunit-util": "^1", - "phpunit/phpunit": "^6" - }, - "type": "library", - "autoload": { - "files": [ - "lib/functions.php" - ], - "psr-4": { - "Amp\\Process\\": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bob Weinand", - "email": "bobwei9@hotmail.com" - }, - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - } - ], - "description": "Asynchronous process manager.", - "homepage": "https://github.com/amphp/process", - "support": { - "issues": "https://github.com/amphp/process/issues", - "source": "https://github.com/amphp/process/tree/v1.1.4" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2022-07-06T23:50:12+00:00" - }, - { - "name": "amphp/serialization", - "version": "v1.0.0", - "source": { - "type": "git", - "url": "https://github.com/amphp/serialization.git", - "reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/serialization/zipball/693e77b2fb0b266c3c7d622317f881de44ae94a1", - "reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "amphp/php-cs-fixer-config": "dev-master", - "phpunit/phpunit": "^9 || ^8 || ^7" - }, - "type": "library", - "autoload": { - "files": [ - "src/functions.php" - ], - "psr-4": { - "Amp\\Serialization\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - } - ], - "description": "Serialization tools for IPC and data storage in PHP.", - "homepage": "https://github.com/amphp/serialization", - "keywords": [ - "async", - "asynchronous", - "serialization", - "serialize" - ], - "support": { - "issues": "https://github.com/amphp/serialization/issues", - "source": "https://github.com/amphp/serialization/tree/master" - }, - "time": "2020-03-25T21:39:07+00:00" - }, - { - "name": "amphp/sync", - "version": "v1.4.2", - "source": { - "type": "git", - "url": "https://github.com/amphp/sync.git", - "reference": "85ab06764f4f36d63b1356b466df6111cf4b89cf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/sync/zipball/85ab06764f4f36d63b1356b466df6111cf4b89cf", - "reference": "85ab06764f4f36d63b1356b466df6111cf4b89cf", - "shasum": "" - }, - "require": { - "amphp/amp": "^2.2", - "php": ">=7.1" - }, - "require-dev": { - "amphp/php-cs-fixer-config": "dev-master", - "amphp/phpunit-util": "^1.1", - "phpunit/phpunit": "^9 || ^8 || ^7" - }, - "type": "library", - "autoload": { - "files": [ - "src/functions.php", - "src/ConcurrentIterator/functions.php" - ], - "psr-4": { - "Amp\\Sync\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Stephen Coakley", - "email": "me@stephencoakley.com" - } - ], - "description": "Mutex, Semaphore, and other synchronization tools for Amp.", - "homepage": "https://github.com/amphp/sync", - "keywords": [ - "async", - "asynchronous", - "mutex", - "semaphore", - "synchronization" - ], - "support": { - "issues": "https://github.com/amphp/sync/issues", - "source": "https://github.com/amphp/sync/tree/v1.4.2" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2021-10-25T18:29:10+00:00" - }, { "name": "composer/pcre", "version": "3.1.1", @@ -907,6 +356,67 @@ ], "time": "2023-11-18T22:57:23+00:00" }, + { + "name": "fidry/cpu-core-counter", + "version": "0.5.1", + "source": { + "type": "git", + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "b58e5a3933e541dc286cc91fc4f3898bbc6f1623" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/b58e5a3933e541dc286cc91fc4f3898bbc6f1623", + "reference": "b58e5a3933e541dc286cc91fc4f3898bbc6f1623", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "fidry/makefile": "^0.2.0", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^1.9.2", + "phpstan/phpstan-deprecation-rules": "^1.0.0", + "phpstan/phpstan-phpunit": "^1.2.2", + "phpstan/phpstan-strict-rules": "^1.4.4", + "phpunit/phpunit": "^9.5.26 || ^8.5.31", + "theofidry/php-cs-fixer-config": "^1.0", + "webmozarts/strict-phpunit": "^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Tiny utility to get the number of CPU cores.", + "keywords": [ + "CPU", + "core" + ], + "support": { + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/0.5.1" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2022-12-24T12:35:10+00:00" + }, { "name": "fidry/filesystem", "version": "1.1.0", @@ -1177,66 +687,6 @@ }, "time": "2023-09-26T02:20:38+00:00" }, - { - "name": "laravel/serializable-closure", - "version": "v1.3.3", - "source": { - "type": "git", - "url": "https://github.com/laravel/serializable-closure.git", - "reference": "3dbf8a8e914634c48d389c1234552666b3d43754" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/3dbf8a8e914634c48d389c1234552666b3d43754", - "reference": "3dbf8a8e914634c48d389c1234552666b3d43754", - "shasum": "" - }, - "require": { - "php": "^7.3|^8.0" - }, - "require-dev": { - "nesbot/carbon": "^2.61", - "pestphp/pest": "^1.21.3", - "phpstan/phpstan": "^1.8.2", - "symfony/var-dumper": "^5.4.11" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Laravel\\SerializableClosure\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - }, - { - "name": "Nuno Maduro", - "email": "nuno@laravel.com" - } - ], - "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.", - "keywords": [ - "closure", - "laravel", - "serializable" - ], - "support": { - "issues": "https://github.com/laravel/serializable-closure/issues", - "source": "https://github.com/laravel/serializable-closure" - }, - "time": "2023-11-08T14:08:06+00:00" - }, { "name": "nikic/iter", "version": "v2.3.0", @@ -1981,6 +1431,87 @@ ], "time": "2023-10-31T08:09:35+00:00" }, + { + "name": "symfony/dependency-injection", + "version": "v6.3.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/dependency-injection.git", + "reference": "1f30f545c4151f611148fc19e28d54d39e0a00bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/1f30f545c4151f611148fc19e28d54d39e0a00bc", + "reference": "1f30f545c4151f611148fc19e28d54d39e0a00bc", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/service-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.2.10" + }, + "conflict": { + "ext-psr": "<1.1|>=2", + "symfony/config": "<6.1", + "symfony/finder": "<5.4", + "symfony/proxy-manager-bridge": "<6.3", + "symfony/yaml": "<5.4" + }, + "provide": { + "psr/container-implementation": "1.1|2.0", + "symfony/service-implementation": "1.1|2.0|3.0" + }, + "require-dev": { + "symfony/config": "^6.1", + "symfony/expression-language": "^5.4|^6.0", + "symfony/yaml": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\DependencyInjection\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows you to standardize and centralize the way objects are constructed in your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/dependency-injection/tree/v6.3.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-10-31T08:07:48+00:00" + }, { "name": "symfony/deprecation-contracts", "version": "v3.4.0", @@ -2977,6 +2508,80 @@ ], "time": "2023-11-08T10:42:36+00:00" }, + { + "name": "symfony/var-exporter", + "version": "v6.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-exporter.git", + "reference": "374d289c13cb989027274c86206ddc63b16a2441" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/374d289c13cb989027274c86206ddc63b16a2441", + "reference": "374d289c13cb989027274c86206ddc63b16a2441", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "symfony/var-dumper": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\VarExporter\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows exporting any serializable PHP data structure to plain PHP code", + "homepage": "https://symfony.com", + "keywords": [ + "clone", + "construct", + "export", + "hydrate", + "instantiate", + "lazy-loading", + "proxy", + "serialize" + ], + "support": { + "source": "https://github.com/symfony/var-exporter/tree/v6.3.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-10-13T09:16:49+00:00" + }, { "name": "thecodingmachine/safe", "version": "v2.5.0", @@ -3173,6 +2778,83 @@ "source": "https://github.com/webmozarts/assert/tree/1.11.0" }, "time": "2022-06-03T18:03:27+00:00" + }, + { + "name": "webmozarts/console-parallelization", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/console-parallelization.git", + "reference": "863b07275a626065591cac7a31a6a66ec09fd7b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/console-parallelization/zipball/863b07275a626065591cac7a31a6a66ec09fd7b1", + "reference": "863b07275a626065591cac7a31a6a66ec09fd7b1", + "shasum": "" + }, + "require": { + "fidry/cpu-core-counter": "^0.5.0", + "nikic/iter": "^2.2", + "php": "^8.1", + "psr/log": "^1.1 || ^2.0 || ^3.0", + "symfony/console": "^5.4 || ^6.0", + "symfony/dependency-injection": "^5.4 || ^6.0", + "symfony/deprecation-contracts": "^2.5 || ^3.1", + "symfony/process": "^5.4 || ^6.0", + "symfony/service-contracts": "^3.3", + "thecodingmachine/safe": "^1.3.3 || ^2.4", + "webmozart/assert": "^1.5" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8", + "ergebnis/composer-normalize": "^2.28", + "ext-json": "*", + "fidry/makefile": "^1.0", + "infection/infection": "^0.27.0", + "jangregor/phpstan-prophecy": "^1.0", + "phpspec/prophecy-phpunit": "^2.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-symfony": "^1.2", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.0", + "symfony/framework-bundle": "^5.4 || ^6.0", + "webmozarts/strict-phpunit": "^7.3" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": false, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "Webmozarts\\Console\\Parallelization\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bernhard.schussek@webmozarts.com" + }, + { + "name": "Théo Fidry", + "email": "theo.fidry@webmozarts.com" + } + ], + "description": "Enables parallelization of Symfony Console commands", + "support": { + "issues": "https://github.com/webmozarts/console-parallelization/issues", + "source": "https://github.com/webmozarts/console-parallelization/tree/2.1.1" + }, + "time": "2023-10-13T22:11:00+00:00" } ], "packages-dev": [ @@ -5656,16 +5338,16 @@ }, { "name": "theseer/tokenizer", - "version": "1.2.1", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96", + "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96", "shasum": "" }, "require": { @@ -5694,7 +5376,7 @@ "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" + "source": "https://github.com/theseer/tokenizer/tree/1.2.2" }, "funding": [ { @@ -5702,24 +5384,24 @@ "type": "github" } ], - "time": "2021-07-28T10:34:58+00:00" + "time": "2023-11-20T00:12:19+00:00" }, { "name": "webmozarts/strict-phpunit", - "version": "7.7.7", + "version": "7.8.0", "source": { "type": "git", "url": "https://github.com/webmozarts/strict-phpunit.git", - "reference": "bec31848d596536af8119a37de61ba23e0599377" + "reference": "cb3fcea4e1978671db3870324734ad1db3c4d5e3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/strict-phpunit/zipball/bec31848d596536af8119a37de61ba23e0599377", - "reference": "bec31848d596536af8119a37de61ba23e0599377", + "url": "https://api.github.com/repos/webmozarts/strict-phpunit/zipball/cb3fcea4e1978671db3870324734ad1db3c4d5e3", + "reference": "cb3fcea4e1978671db3870324734ad1db3c4d5e3", "shasum": "" }, "require": { - "php": ">=7.4.0", + "php": "^8.1", "phpunit/phpunit": "^9.4.3" }, "require-dev": { @@ -5727,6 +5409,11 @@ "infection/infection": "^0.26.6" }, "type": "library", + "extra": { + "symfony": { + "require": "6.*.*" + } + }, "autoload": { "psr-4": { "Webmozarts\\StrictPHPUnit\\": "src" @@ -5745,9 +5432,9 @@ "description": "Enables type-safe comparisons of objects in PHPUnit", "support": { "issues": "https://github.com/webmozarts/strict-phpunit/issues", - "source": "https://github.com/webmozarts/strict-phpunit/tree/7.7.7" + "source": "https://github.com/webmozarts/strict-phpunit/tree/7.8.0" }, - "time": "2023-03-29T20:04:11+00:00" + "time": "2023-11-03T17:34:29+00:00" } ], "aliases": [], diff --git a/src/Amp/FailureCollector.php b/src/Amp/FailureCollector.php deleted file mode 100644 index ba8f3efd1..000000000 --- a/src/Amp/FailureCollector.php +++ /dev/null @@ -1,39 +0,0 @@ - - * Théo Fidry - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -namespace KevinGH\Box\Amp; - -use Amp\MultiReasonException; -use KevinGH\Box\NotInstantiable; -use Throwable; -use function array_map; -use function array_unique; - -final class FailureCollector -{ - use NotInstantiable; - - /** - * @return list - */ - public static function collectReasons(MultiReasonException $exception): array - { - return array_unique( - array_map( - static fn (Throwable $throwable) => $throwable->getMessage(), - $exception->getReasons(), - ), - ); - } -} diff --git a/src/Box.php b/src/Box.php index 39d3fb747..482ea4b50 100644 --- a/src/Box.php +++ b/src/Box.php @@ -14,7 +14,6 @@ namespace KevinGH\Box; -use Amp\MultiReasonException; use BadMethodCallException; use Countable; use DateTimeImmutable; @@ -23,6 +22,8 @@ use KevinGH\Box\Compactor\Compactors; use KevinGH\Box\Compactor\PhpScoper; use KevinGH\Box\Compactor\Placeholder; +use KevinGH\Box\Parallelization\ParallelFileProcessor; +use KevinGH\Box\Parallelization\ParallelizationDecider; use KevinGH\Box\Phar\CompressionAlgorithm; use KevinGH\Box\Phar\SigningAlgorithm; use KevinGH\Box\PhpScoper\NullScoper; @@ -33,12 +34,10 @@ use Seld\PharUtils\Timestamps; use SplFileInfo; use Webmozart\Assert\Assert; -use function Amp\ParallelFunctions\parallelMap; -use function Amp\Promise\wait; -use function array_filter; use function array_map; use function array_unshift; use function chdir; +use function count; use function dirname; use function extension_loaded; use function file_exists; @@ -288,8 +287,6 @@ public function registerStub(string $file): void /** * @param array $files - * - * @throws MultiReasonException */ public function addFiles(array $files, bool $binary): void { @@ -446,73 +443,25 @@ public function signUsingKey(string $key, ?string $password): void /** * @param string[] $files * - * @throws MultiReasonException - * - * @return array array of tuples where the first element is the local file path (path inside the PHAR) and the - * second element is the processed contents + * @return array{string, string} array of tuples where the first element is the local file path (path inside the PHAR) and the + * second element is the processed contents */ private function processContents(array $files): array { - $mapFile = $this->mapFile; - $compactors = $this->compactors; - $cwd = getcwd(); - $enableParallelization = $this->enableParallelization; - - $processFile = static function (string $file) use ($cwd, $mapFile, $compactors, $enableParallelization): array { - chdir($cwd); - - // Keep the fully qualified call here since this function may be executed without the right autoloading - // mechanism - \KevinGH\Box\register_aliases(); - if ($enableParallelization) { - \KevinGH\Box\register_error_handler(); - } - - $contents = \Fidry\FileSystem\FS::getFileContents($file); - - $local = $mapFile($file); - - $processedContents = $compactors->compact($local, $contents); - - return [$local, $processedContents, $compactors->getScoperSymbolsRegistry()]; - }; - - if ($this->scoper instanceof NullScoper || !$enableParallelization) { - return array_map($processFile, $files); - } - - // In the case of parallel processing, an issue is caused due to the statefulness nature of the PhpScoper - // symbols registry. - // - // Indeed, the PhpScoper symbols registry stores the records of exposed/excluded classes and functions. If nothing is done, - // then the symbols registry retrieved in the end will here will be "blank" since the updated symbols registries are the ones - // from the workers used for the parallel processing. - // - // In order to avoid that, the symbols registries will be returned as a result as well in order to be able to merge - // all the symbols registries into one. - // - // This process is allowed thanks to the nature of the state of the symbols registries: having redundant classes or - // functions registered can easily be deal with so merging all those different states is actually - // straightforward. - $tuples = wait(parallelMap($files, $processFile)); - - if ([] === $tuples) { - return []; - } - - $filesWithContents = []; - $symbolRegistries = []; + $shouldProcessFilesInParallel = $this->enableParallelization && ParallelizationDecider::shouldProcessFilesInParallel( + $this->scoper, + count($files), + ); - foreach ($tuples as [$local, $processedContents, $symbolRegistry]) { - $filesWithContents[] = [$local, $processedContents]; - $symbolRegistries[] = $symbolRegistry; - } + $processFiles = $shouldProcessFilesInParallel + ? ParallelFileProcessor::processFilesInParallel(...) + : self::processFilesSynchronously(...); - $this->compactors->registerSymbolsRegistry( - SymbolsRegistry::createFromRegistries(array_filter($symbolRegistries)), + return $processFiles( + $files, + $this->mapFile, + $this->compactors, ); - - return $filesWithContents; } public function count(): int @@ -521,4 +470,30 @@ public function count(): int return $this->phar->count(); } + + /** + * @param string[] $files + * + * @return list + */ + private static function processFilesSynchronously( + array $files, + MapFile $mapFile, + Compactors $compactors, + ): array { + $processFile = static function (string $file) use ($mapFile, $compactors): array { + $contents = FS::getFileContents($file); + + $local = $mapFile($file); + + $processedContents = $compactors->compact($local, $contents); + + return [ + $local, + $processedContents, + ]; + }; + + return array_map($processFile, $files); + } } diff --git a/src/Console/Application.php b/src/Console/Application.php index 47d6e209b..eed76bd5a 100644 --- a/src/Console/Application.php +++ b/src/Console/Application.php @@ -15,6 +15,7 @@ namespace KevinGH\Box\Console; use Fidry\Console\Application\Application as FidryApplication; +use KevinGH\Box\Parallelization\ProcessFileCommand; use function KevinGH\Box\get_box_version; use function sprintf; use function trim; @@ -89,6 +90,7 @@ public function getCommands(): array new Command\Verify(), new Command\GenerateDockerFile(), new Command\Namespace_(), + new ProcessFileCommand(), ]; } diff --git a/src/Console/Command/Compile.php b/src/Console/Command/Compile.php index 33d231208..45e271379 100644 --- a/src/Console/Command/Compile.php +++ b/src/Console/Command/Compile.php @@ -14,7 +14,6 @@ namespace KevinGH\Box\Console\Command; -use Amp\MultiReasonException; use DateTimeImmutable; use DateTimeInterface; use Fidry\Console\Command\Command; @@ -26,7 +25,6 @@ use Fidry\FileSystem\FileSystem; use Fidry\FileSystem\FS; use Humbug\PhpScoper\Symbol\SymbolsRegistry; -use KevinGH\Box\Amp\FailureCollector; use KevinGH\Box\Box; use KevinGH\Box\Compactor\Compactor; use KevinGH\Box\Composer\CompilerPsrLogger; @@ -484,7 +482,7 @@ private static function addFiles(Configuration $config, Box $box, CompilerLogger $count = count($config->getFiles()); - self::addFilesWithErrorHandling($config, $box, $io); + $box->addFiles($config->getFiles(), false); $logger->log( CompilerLogger::CHEVRON_PREFIX, @@ -494,26 +492,6 @@ private static function addFiles(Configuration $config, Box $box, CompilerLogger ); } - private static function addFilesWithErrorHandling(Configuration $config, Box $box, IO $io): void - { - try { - $box->addFiles($config->getFiles(), false); - - return; - } catch (MultiReasonException $ampFailure) { - // Continue - } - - // This exception is handled a different way to give me meaningful feedback to the user - $io->error([ - 'An Amp\Parallel error occurred. To diagnostic if it is an Amp error related, you may try again with "--no-parallel".', - 'Reason(s) of the failure:', - ...FailureCollector::collectReasons($ampFailure), - ]); - - throw $ampFailure; - } - private static function registerMainScript(Configuration $config, Box $box, CompilerLogger $logger): ?string { if (false === $config->hasMainScript()) { diff --git a/src/Parallelization/BatchResult.php b/src/Parallelization/BatchResult.php new file mode 100644 index 000000000..7cd58aa9b --- /dev/null +++ b/src/Parallelization/BatchResult.php @@ -0,0 +1,59 @@ + + * Théo Fidry + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace KevinGH\Box\Parallelization; + +use Humbug\PhpScoper\Symbol\SymbolsRegistry; + +/** + * @private + */ +final readonly class BatchResult +{ + /** + * @param array{string, string} $processedFilesWithContents + */ + public function __construct( + public array $processedFilesWithContents, + public SymbolsRegistry $symbolsRegistry, + ) { + } + + public function serialize(): string + { + return serialize($this); + } + + /** + * @return list + */ + public static function unserialize(string $serialized): self + { + return unserialize($serialized); + } + + /** + * @return array{ + * array{string, string}, + * SymbolsRegistry, + * } + */ + public function toArray(): array + { + return [ + $this->processedFilesWithContents, + $this->symbolsRegistry, + ]; + } +} diff --git a/src/Parallelization/BatchResults.php b/src/Parallelization/BatchResults.php new file mode 100644 index 000000000..3375178c9 --- /dev/null +++ b/src/Parallelization/BatchResults.php @@ -0,0 +1,44 @@ + + * Théo Fidry + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace KevinGH\Box\Parallelization; + +use KevinGH\Box\NotInstantiable; +use Symfony\Component\Finder\Finder; +use Symfony\Component\Finder\SplFileInfo; +use function KevinGH\Box\unique_id; + +/** + * @private + */ +final class BatchResults +{ + use NotInstantiable; + + public static function createFilename(): string + { + return unique_id('batch-').'.json'; + } + + /** + * @return iterable + */ + public static function collect(string $source): iterable + { + return Finder::create() + ->files() + ->in($source) + ->name('batch-*.json'); + } +} diff --git a/src/Parallelization/Configuration.php b/src/Parallelization/Configuration.php new file mode 100644 index 000000000..d94654eb9 --- /dev/null +++ b/src/Parallelization/Configuration.php @@ -0,0 +1,46 @@ + + * Théo Fidry + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace KevinGH\Box\Parallelization; + +use KevinGH\Box\Compactor\Compactors; +use KevinGH\Box\MapFile; +use function serialize; +use function unserialize; + +/** + * @private + */ +final readonly class Configuration +{ + public function __construct( + public array $filePaths, + public MapFile $mapFile, + public Compactors $compactors, + ) { + } + + public function serialize(): string + { + return serialize($this); + } + + /** + * @return list + */ + public static function unserialize(string $serialized): self + { + return unserialize($serialized); + } +} diff --git a/src/Parallelization/ParallelFileProcessor.php b/src/Parallelization/ParallelFileProcessor.php new file mode 100644 index 000000000..f7f022378 --- /dev/null +++ b/src/Parallelization/ParallelFileProcessor.php @@ -0,0 +1,126 @@ + + * Théo Fidry + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace KevinGH\Box\Parallelization; + +use Fidry\FileSystem\FS; +use Humbug\PhpScoper\Symbol\SymbolsRegistry; +use KevinGH\Box\Compactor\Compactors; +use KevinGH\Box\ExecutableFinder; +use KevinGH\Box\MapFile; +use Symfony\Component\Finder\SplFileInfo; +use Symfony\Component\Process\Exception\ProcessFailedException; +use Symfony\Component\Process\Process; +use function iter\toArray; + +/** + * @private + */ +final class ParallelFileProcessor +{ + /** + * @param string[] $filePaths + * + * @return list + */ + public static function processFilesInParallel( + array $filePaths, + MapFile $mapFile, + Compactors $compactors, + ): array { + $tmp = FS::makeTmpDir('BoxProcessFile', self::class); + FS::mkdir($tmp); + + $configPath = self::createConfig( + $filePaths, + $mapFile, + $compactors, + $tmp, + ); + + $processFilesProcess = self::createProcess($configPath, $tmp); + $processFilesProcess->run(); + + if (false === $processFilesProcess->isSuccessful()) { + throw new ProcessFailedException($processFilesProcess); + } + + $processedResults = self::getProcessedResults($tmp); + + $filesWithContents = array_merge( + ...array_column($processedResults, 0), + ); + $mergedSymbolsRegistry = SymbolsRegistry::createFromRegistries( + array_column($processedResults, 1), + ); + + FS::remove($tmp); + + $compactors->registerSymbolsRegistry($mergedSymbolsRegistry); + + return $filesWithContents; + } + + /** + * @param string[] $filePaths + * + * @return list + */ + private static function createConfig( + array $filePaths, + MapFile $mapFile, + Compactors $compactors, + string $tmp + ): string { + $config = new Configuration($filePaths, $mapFile, $compactors); + $configPath = $tmp.'/config.json'; + + FS::dumpFile($configPath, $config->serialize()); + + return $configPath; + } + + /** + * @param array|string $configPath + */ + private static function createProcess( + string $configPath, + string $tmp, + ): Process { + $process = new Process([ + ExecutableFinder::findPhpExecutable(), + ExecutableFinder::findBoxExecutable(), + ProcessFileCommand::COMMAND_NAME, + $configPath, + $tmp, + '--no-interaction', + ]); + + $process->setTimeout(3 * 60.); + + return $process; + } + + /** + * @return list + */ + public static function getProcessedResults(string $tmp): array + { + return array_map( + static fn (SplFileInfo $batchResultFileInfo) => BatchResult::unserialize($batchResultFileInfo->getContents()) + ->toArray(), + toArray(BatchResults::collect($tmp)), + ); + } +} diff --git a/src/Parallelization/ParallelizationDecider.php b/src/Parallelization/ParallelizationDecider.php new file mode 100644 index 000000000..778f4a4d8 --- /dev/null +++ b/src/Parallelization/ParallelizationDecider.php @@ -0,0 +1,27 @@ + + * Théo Fidry + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace KevinGH\Box\Parallelization; + +use Fidry\FileSystem\FS; +use Humbug\PhpScoper\Symbol\SymbolsRegistry; +use KevinGH\Box\ExecutableFinder; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Webmozarts\Console\Parallelization\Input\ParallelizationInput; +use Webmozarts\Console\Parallelization\ParallelCommand; +use Webmozarts\Console\Parallelization\ParallelExecutorFactory; +use function count; + +/** + * @private + */ +final class ProcessFileCommand extends ParallelCommand +{ + public const COMMAND_NAME = 'internal:process:files'; + + private const SEGMENT_SIZE = 100; + + private const CONFIG_ARGUMENT = 'file'; + private const TMP_DIR = 'tmp'; + + private Configuration $configuration; + private string $tmp; + private array $processesFilesWithContents = []; + + public function __construct() + { + parent::__construct(self::COMMAND_NAME); + } + + public function configure(): void + { + $this->addArgument( + self::CONFIG_ARGUMENT, + InputArgument::REQUIRED, + 'Path to the file processing configuration.', + ); + $this->addArgument( + self::TMP_DIR, + InputArgument::REQUIRED, + 'Temporary directory that can be used for dumping artifacts that can be collected later.', + ); + + ParallelizationInput::configureCommand($this); + + $this->setHidden(); + } + + protected function fetchItems(InputInterface $input, OutputInterface $output): iterable + { + if (!isset($this->configuration)) { + $this->configuration = self::getConfiguration($input); + } + + return $this->configuration->filePaths; + } + + protected function configureParallelExecutableFactory(ParallelExecutorFactory $parallelExecutorFactory, InputInterface $input, OutputInterface $output): ParallelExecutorFactory + { + return $parallelExecutorFactory + ->withScriptPath(ExecutableFinder::findBoxExecutable()) + // Ensure the batch & segment size are identical: we want the one and only one batch per process. + ->withBatchSize(self::SEGMENT_SIZE) + ->withSegmentSize(self::SEGMENT_SIZE) + ->withRunBeforeBatch($this->runBeforeFirstBatch(...)) + ->withRunAfterBatch($this->runAfterLastBatch(...)); + } + + private function runBeforeFirstBatch(InputInterface $input): void + { + if (!isset($this->configuration)) { + $this->configuration = self::getConfiguration($input); + } + + if (!isset($this->tmp)) { + $this->tmp = $input->getArgument(self::TMP_DIR); + } + } + + protected function runSingleCommand(string $file, InputInterface $input, OutputInterface $output): void + { + $contents = FS::getFileContents($file); + + $local = ($this->configuration->mapFile)($file); + + $processedContents = $this->configuration->compactors->compact($local, $contents); + + $this->processesFilesWithContents[] = [$local, $processedContents]; + } + + private function runAfterLastBatch(): void + { + $symbols = $this->configuration->compactors->getScoperSymbolsRegistry(); + $processedFilesWithContents = $this->processesFilesWithContents; + + if (0 === count($processedFilesWithContents) + && (null === $symbols || 0 === count($symbols)) + ) { + return; + } + + $batchResult = new BatchResult( + $processedFilesWithContents, + $symbols, + ); + + FS::dumpFile( + $this->tmp.'/'.BatchResults::createFilename(), + $batchResult->serialize(), + ); + + $this->resetState(); + } + + protected function getItemName(?int $count): string + { + return 1 === $count ? 'file' : 'files'; + } + + private static function getConfiguration(InputInterface $input): Configuration + { + $configPath = $input->getArgument(self::CONFIG_ARGUMENT); + + return Configuration::unserialize( + FS::getFileContents($configPath), + ); + } + + private function resetState(): void + { + $this->processesFilesWithContents = []; + $this->configuration->compactors->registerSymbolsRegistry(new SymbolsRegistry()); + } +} diff --git a/src/PhpScoper/PatcherFactory.php b/src/PhpScoper/PatcherFactory.php deleted file mode 100644 index 9afa8be0c..000000000 --- a/src/PhpScoper/PatcherFactory.php +++ /dev/null @@ -1,44 +0,0 @@ - - * Théo Fidry - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -namespace KevinGH\Box\PhpScoper; - -use Humbug\PhpScoper\Patcher\Patcher; -use Humbug\PhpScoper\Patcher\PatcherChain; -use KevinGH\Box\NotInstantiable; -use Laravel\SerializableClosure\SerializableClosure; - -final class PatcherFactory -{ - use NotInstantiable; - - /** - * @param callable[] $patcher - * - * @return SerializableClosure[] - */ - public static function createSerializablePatchers(Patcher $patcher): Patcher - { - if (!($patcher instanceof PatcherChain)) { - return $patcher; - } - - $serializablePatchers = array_map( - static fn (callable $patcher) => SerializablePatcher::create($patcher), - $patcher->getPatchers(), - ); - - return new PatcherChain($serializablePatchers); - } -} diff --git a/src/PhpScoper/SerializablePatcher.php b/src/PhpScoper/SerializablePatcher.php deleted file mode 100644 index 14f51d7c0..000000000 --- a/src/PhpScoper/SerializablePatcher.php +++ /dev/null @@ -1,47 +0,0 @@ - - * Théo Fidry - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -namespace KevinGH\Box\PhpScoper; - -use Closure; -use Humbug\PhpScoper\Patcher\Patcher; -use Laravel\SerializableClosure\SerializableClosure; -use function func_get_args; - -/** - * @var PatcherCallable = (string $filePath, string $prefix, string $contents): string - */ -final class SerializablePatcher implements Patcher -{ - public static function create(callable $patcher): self - { - if ($patcher instanceof Patcher) { - $patcher = static fn (mixed ...$args) => $patcher(...$args); - } - - return new self(new SerializableClosure($patcher)); - } - - /** - * @param PatcherCallable $patch - */ - private function __construct(private Closure|SerializableClosure $patch) - { - } - - public function __invoke(string $filePath, string $prefix, string $contents): string - { - return ($this->patch)(...func_get_args()); - } -} diff --git a/src/PhpScoper/SerializableScoper.php b/src/PhpScoper/SerializableScoper.php index 152c00e87..0d7d4e111 100644 --- a/src/PhpScoper/SerializableScoper.php +++ b/src/PhpScoper/SerializableScoper.php @@ -25,7 +25,6 @@ */ final class SerializableScoper implements Scoper { - private PhpScoperConfiguration $scoperConfig; private PhpScoperContainer $scoperContainer; private PhpScoperScoper $scoper; private SymbolsRegistry $symbolsRegistry; @@ -36,12 +35,9 @@ final class SerializableScoper implements Scoper public array $excludedFilePaths; public function __construct( - PhpScoperConfiguration $scoperConfig, + private PhpScoperConfiguration $scoperConfig, string ...$excludedFilePaths, ) { - $this->scoperConfig = $scoperConfig->withPatcher( - PatcherFactory::createSerializablePatchers($scoperConfig->getPatcher()) - ); $this->excludedFilePaths = $excludedFilePaths; $this->symbolsRegistry = new SymbolsRegistry(); } @@ -86,16 +82,6 @@ private function getScoper(): PhpScoperScoper return $this->scoper; } - public function __wakeup(): void - { - // We need to make sure that a fresh Scoper & PHP-Parser Parser/Lexer - // is used within a sub-process. - // Otherwise, there is a risk of data corruption or that a compatibility - // layer of some sorts (such as the tokens for PHP-Paser) is not - // triggered in the sub-process resulting in obscure errors - unset($this->scoper, $this->scoperContainer); - } - private function createScoper(): PhpScoperScoper { $scoper = $this->scoperContainer @@ -119,4 +105,25 @@ public function getExcludedFilePaths(): array { return $this->excludedFilePaths; } + + public function __serialize(): array + { + return [ + $this->scoperConfig->getPath(), + $this->scoperConfig->getPrefix(), + $this->excludedFilePaths, + ]; + } + + public function __unserialize(array $data): void + { + [$configPath, $configPrefix, $excludedFilePaths] = $data; + + $config = ConfigurationFactory::create($configPath)->withPrefix($configPrefix); + + $this->__construct( + $config, + ...$excludedFilePaths, + ); + } } diff --git a/tests/Console/Command/CompileTest.php b/tests/Console/Command/CompileTest.php index b77aedee7..427cc913a 100644 --- a/tests/Console/Command/CompileTest.php +++ b/tests/Console/Command/CompileTest.php @@ -191,7 +191,7 @@ public function test_it_can_build_a_phar_file(): void FS::dumpFile('composer.lock', '{}'); FS::dumpFile('vendor/composer/installed.json', '{}'); - $shebang = sprintf('#!%s', (new PhpExecutableFinder())->find()); + $shebang = self::getExpectedShebang(); $numberOfFiles = self::NUMBER_OF_FILES; @@ -355,7 +355,7 @@ public function test_it_can_build_a_phar_from_a_different_directory(): void { FS::mirror(self::FIXTURES_DIR.'/dir000', $this->tmp); - $shebang = sprintf('#!%s', (new PhpExecutableFinder())->find()); + $shebang = self::getExpectedShebang(); FS::dumpFile( 'box.json', @@ -788,7 +788,7 @@ public function test_it_can_build_a_phar_file_in_verbose_mode(): void FS::mirror(self::FIXTURES_DIR.'/dir000', $this->tmp); - $shebang = sprintf('#!%s', (new PhpExecutableFinder())->find()); + $shebang = self::getExpectedShebang(); $expectedNumberOfClasses = 1; $expectedNumberOfFiles = self::NUMBER_OF_FILES; @@ -915,7 +915,7 @@ public function test_it_can_build_a_phar_file_in_very_verbose_mode(): void FS::mirror(self::FIXTURES_DIR.'/dir000', $this->tmp); - $shebang = sprintf('#!%s', (new PhpExecutableFinder())->find()); + $shebang = self::getExpectedShebang(); $expectedNumberOfClasses = 1; $expectedNumberOfFiles = self::NUMBER_OF_FILES; @@ -1315,7 +1315,7 @@ public function test_it_can_build_a_phar_file_in_quiet_mode(): void { FS::mirror(self::FIXTURES_DIR.'/dir000', $this->tmp); - $shebang = sprintf('#!%s', (new PhpExecutableFinder())->find()); + $shebang = self::getExpectedShebang(); FS::dumpFile( 'box.json', @@ -1382,7 +1382,7 @@ public function test_it_can_build_a_phar_file_using_the_phar_default_stub(): voi { FS::mirror(self::FIXTURES_DIR.'/dir000', $this->tmp); - $shebang = sprintf('#!%s', (new PhpExecutableFinder())->find()); + $shebang = self::getExpectedShebang(); FS::dumpFile( 'box.json', @@ -1431,7 +1431,7 @@ public function test_it_can_build_a_phar_file_using_a_custom_stub(): void { FS::mirror(self::FIXTURES_DIR.'/dir000', $this->tmp); - $shebang = sprintf('#!%s', (new PhpExecutableFinder())->find()); + $shebang = self::getExpectedShebang(); FS::dumpFile( 'custom_stub', @@ -3143,4 +3143,9 @@ private function skipIfDefaultStubNotFound(): void self::markTestSkipped('The default stub file could not be found. Run the tests via the make commands or manually generate the stub file with `$ make generate_default_stub`.'); } } + + private static function getExpectedShebang(): string + { + return sprintf('#!%s', (new PhpExecutableFinder())->find()); + } } diff --git a/tests/Parallelization/BatchResultTest.php b/tests/Parallelization/BatchResultTest.php new file mode 100644 index 000000000..97d36f98e --- /dev/null +++ b/tests/Parallelization/BatchResultTest.php @@ -0,0 +1,62 @@ + + * Théo Fidry + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace KevinGH\Box\Parallelization; + +use Humbug\PhpScoper\Symbol\SymbolsRegistry; +use PhpParser\Node\Name\FullyQualified; +use PHPUnit\Framework\TestCase; + +/** + * @covers \KevinGH\Box\Parallelization\BatchResult + * @internal + */ +final class BatchResultTest extends TestCase +{ + /** + * @dataProvider batchResultProvider + */ + public function test_it_can_be_serialized_and_deserialized(BatchResult $batchResult): void + { + $unserializedBatchResult = BatchResult::unserialize($batchResult->serialize()); + + self::assertEquals($batchResult, $unserializedBatchResult); + } + + public static function batchResultProvider(): iterable + { + yield 'empty' => [ + new BatchResult( + [], + new SymbolsRegistry(), + ), + ]; + + $symbolsRegistry = new SymbolsRegistry(); + $symbolsRegistry->recordClass( + new FullyQualified('Box\Func'), + new FullyQualified('Scoped\Box\Func'), + ); + + yield 'nominal' => [ + new BatchResult( + [ + '/path/to/file.php', + ' + * Théo Fidry + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace KevinGH\Box\Parallelization; + +use Humbug\PhpScoper\Symbol\SymbolsRegistry; +use KevinGH\Box\Compactor\Compactors; +use KevinGH\Box\MapFile; +use PhpParser\Node\Name\FullyQualified; +use PHPUnit\Framework\TestCase; + +/** + * @covers \KevinGH\Box\Parallelization\Configuration + * @internal + */ +final class ConfigurationTest extends TestCase +{ + /** + * @dataProvider configurationProvider + */ + public function test_it_can_be_serialized_and_deserialized(Configuration $configuration): void + { + $unserializedConfiguration = Configuration::unserialize($configuration->serialize()); + + self::assertEquals($configuration, $unserializedConfiguration); + } + + public static function configurationProvider(): iterable + { + yield 'nominal' => [ + new Configuration( + [ + '/path/to/file1.php', + '/path/to/file2.php', + ], + new MapFile('/path/to/base', []), + new Compactors(), + ), + ]; + } +} diff --git a/tests/PhpScoper/PatcherFactoryTest.php b/tests/PhpScoper/PatcherFactoryTest.php deleted file mode 100644 index 5ab769e1a..000000000 --- a/tests/PhpScoper/PatcherFactoryTest.php +++ /dev/null @@ -1,58 +0,0 @@ - - * Théo Fidry - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -namespace PhpScoper; - -use Humbug\PhpScoper\Patcher\PatcherChain; -use KevinGH\Box\PhpScoper\DummyPatcher; -use KevinGH\Box\PhpScoper\PatcherFactory; -use KevinGH\Box\PhpScoper\SerializablePatcher; -use PHPUnit\Framework\TestCase; - -/** - * @covers \KevinGH\Box\PhpScoper\PatcherFactory - * - * @internal - */ -final class PatcherFactoryTest extends TestCase -{ - public function test_it_create_a_new_chain_of_serializable_patchers(): void - { - $patcherChain = new PatcherChain([ - new DummyPatcher(), - new DummyPatcher(), - ]); - - $patcherChainOfSerializablePatchers = PatcherFactory::createSerializablePatchers($patcherChain); - - self::assertInstanceOf(PatcherChain::class, $patcherChainOfSerializablePatchers); - - $serializedPatchers = $patcherChainOfSerializablePatchers->getPatchers(); - - self::assertCount(2, $serializedPatchers); - - foreach ($serializedPatchers as $serializedPatcher) { - self::assertInstanceOf(SerializablePatcher::class, $serializedPatcher); - } - } - - public function test_it_leaves_patcher_unchanged_if_is_not_a_patcher_chain(): void - { - $patcher = new DummyPatcher(); - - $serializablePatcher = PatcherFactory::createSerializablePatchers($patcher); - - self::assertSame($patcher, $serializablePatcher); - } -} diff --git a/tests/PhpScoper/SerializablePatcherTest.php b/tests/PhpScoper/SerializablePatcherTest.php deleted file mode 100644 index 165568e57..000000000 --- a/tests/PhpScoper/SerializablePatcherTest.php +++ /dev/null @@ -1,66 +0,0 @@ - - * Théo Fidry - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -namespace KevinGH\Box\PhpScoper; - -use PHPUnit\Framework\TestCase; -use function serialize; -use function sprintf; -use function unserialize; - -/** - * @covers \KevinGH\Box\PhpScoper\SerializablePatcher - * - * @internal - */ -final class SerializablePatcherTest extends TestCase -{ - protected function setUp(): void - { - self::markTestSkipped('This is causing serialization issues'); - } - - /** - * @dataProvider patchProvider - */ - public function test_it_can_be_serialized(callable $patch, string $expected): void - { - $serializablePatcher = SerializablePatcher::create($patch); - $serializedPatcher = unserialize(serialize($serializablePatcher)); - - $actual1 = $serializablePatcher('filePath', '_Humbug', 'content'); - $actual2 = $serializedPatcher('filePath', '_Humbug', 'content'); - - self::assertSame($expected, $actual1); - self::assertSame($expected, $actual2); - } - - public static function patchProvider(): iterable - { - $expected = 'scopedContent(content)'; - - yield 'closure' => [ - static fn (string $filePath, string $prefix, string $contents) => sprintf( - 'scopedContent(%s)', - $contents, - ), - $expected, - ]; - - yield 'patcher' => [ - new DummyPatcher(), - $expected, - ]; - } -} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 88bb618dd..857c717c4 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -12,7 +12,6 @@ * with this source code in the file LICENSE. */ -use Laravel\SerializableClosure\Support\ClosureStream; use org\bovigo\vfs\vfsStreamWrapper; use Symfony\Component\Filesystem\Path; use function KevinGH\Box\register_aliases; @@ -21,7 +20,6 @@ register_aliases(); vfsStreamWrapper::register(); -ClosureStream::register(); $binBoxPath = Path::normalize(__DIR__.'/../bin/box');