From 8d40c814f2bf21528be0b643e2041b47f0a840cd Mon Sep 17 00:00:00 2001 From: "justus.graf" Date: Sun, 1 Nov 2020 13:38:35 +0100 Subject: [PATCH] PHP Upgrade - dropped support for older unmaintained php versions - updated dependencies - cleaned up old config files - introduced strict types where possible --- .scrutinizer.yml | 19 - .travis.yml | 27 - CHANGELOG.md | 5 + README.md | 6 +- cli-config.php | 21 +- composer.json | 40 +- composer.lock | 1971 ++++++++++++++----- phpunit.xml | 31 +- src/AbstractJob.php | 60 +- src/Client/ClientInterface.php | 20 +- src/Client/CredisClient.php | 10 +- src/Console/Command.php | 13 +- src/Console/ConsoleRunner.php | 16 +- src/Console/EnqueueCommand.php | 11 +- src/Console/Helper/LoggerHelper.php | 15 +- src/Console/Helper/RedisHelper.php | 7 +- src/Console/QueueClearCommand.php | 10 +- src/Console/QueueListCommand.php | 8 +- src/Console/WorkerCommand.php | 10 +- src/Exception/DirtyExitException.php | 2 + src/Exception/JobClassNotFoundException.php | 2 + src/Exception/JobIdException.php | 2 + src/Exception/JobInvalidException.php | 2 + src/Exception/JobLogicException.php | 2 + src/Failure/BackendInterface.php | 13 +- src/Failure/RedisBackend.php | 45 +- src/Job/Status.php | 199 +- src/Job/StatusFactory.php | 14 +- src/JobInterface.php | 27 +- src/Resque.php | 198 +- src/ResqueException.php | 6 +- src/Statistic.php | 38 +- src/Version.php | 4 +- src/Worker.php | 778 ++++---- test/AbstractJobTest.php | 70 +- test/ClientTest.php | 6 +- test/Job/StatusTest.php | 78 +- test/ResqueTest.php | 6 +- test/StatisticTest.php | 24 +- test/Test.php | 31 +- test/Test/FailingJob.php | 9 +- test/Test/Job.php | 25 +- test/Test/MinimalJob.php | 17 +- test/Test/NoPerformJob.php | 2 + test/Test/Settings.php | 157 +- test/WorkerTest.php | 209 +- test/bootstrap.php | 2 + 47 files changed, 2649 insertions(+), 1619 deletions(-) delete mode 100644 .scrutinizer.yml delete mode 100644 .travis.yml diff --git a/.scrutinizer.yml b/.scrutinizer.yml deleted file mode 100644 index f19137c..0000000 --- a/.scrutinizer.yml +++ /dev/null @@ -1,19 +0,0 @@ -# vim: set sw=4 ts=4 et : -imports: - - php - -filter: - # Trailing slash is converted to /* - excluded_paths: - - test/ - - build/ - -tools: - # Inherited: - # php_analyzer - # php_pdepend - # php_sim - - external_code_coverage: - timeout: 600 - diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index ac4868b..0000000 --- a/.travis.yml +++ /dev/null @@ -1,27 +0,0 @@ -language: php - -php: - - 5.3 - - 5.4 - - 5.5 - -env: - - RESQUE_CLIENT_TYPE=predis - - RESQUE_CLIENT_TYPE=credis -# - RESQUE_CLIENT_TYPE=phpredis (broken by https://github.com/nicolasff/phpredis/issues/474) - - RESQUE_CLIENT_TYPE=phpiredis - -before_script: - - sh -c "if [ \"$RESQUE_CLIENT_TYPE\" = 'phpredis' ]; then ant phpredis; fi" - - sh -c "if [ \"$RESQUE_CLIENT_TYPE\" = 'phpredis' ]; then echo 'extension=redis.so' >> `php --ini | grep 'Loaded Configuration' | sed -e \"s|.*:\s*||\"`; fi" - - sh -c "if [ \"$RESQUE_CLIENT_TYPE\" = 'phpiredis' ]; then ant phpiredis; fi" - - sh -c "if [ \"$RESQUE_CLIENT_TYPE\" = 'phpiredis' ]; then echo 'extension=phpiredis.so' >> `php --ini | grep 'Loaded Configuration' | sed -e \"s|.*:\s*||\"`; fi" - - composer install - -script: sh -c "if [ \"$RESQUE_CLIENT_TYPE\" = 'predis' -a \"$TRAVIS_PHP_VERSION\" = '5.5' ]; then phpunit --coverage-clover=coverage.clover; else phpunit; fi" - -after_script: - - sh -c "if [ \"$RESQUE_CLIENT_TYPE\" = 'predis' -a \"$TRAVIS_PHP_VERSION\" = '5.5' ]; then wget https://scrutinizer-ci.com/ocular.phar; fi" - - sh -c "if [ \"$RESQUE_CLIENT_TYPE\" = 'predis' -a \"$TRAVIS_PHP_VERSION\" = '5.5' ]; then php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi" - - cat build/redis.log - - cat build/test.log diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f3b828..d5f5424 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## Forked version to add namespacing +### 4.0.0 ## +* Introduced strict types where possible +* Added support for PHP 8 +* Dropped support for PHP versions < 7.3 + ### 3.0.2 ## * Remove upper bound on supported symfony/console version to support Symfony v3 and v4 based projects diff --git a/README.md b/README.md index 506a8ad..2800084 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ decoupling. This makes it easier to use and extend. ## Getting Started -Add `grepolis/php-resque` to your application's composer.json. +Add `innogames/php-resque` to your application's composer.json. ```json { @@ -26,9 +26,9 @@ Add `grepolis/php-resque` to your application's composer.json. ## Requirements -* PHP 5.3+ +* PHP 7.3+ (incl. PHP 8) * A Redis client library (for instance, [Predis](https://github.com/nrk/predis) or [Credis](https://github.com/colinmollenhour/credis)) -* The PHP pcntl library for proper forking is recommended (not available under Windows) but it still runs without it +* The PHP pcntl extension for proper forking is recommended (not available under Windows) but it still runs without it ## Jobs diff --git a/cli-config.php b/cli-config.php index 1ce80dd..226e56f 100644 --- a/cli-config.php +++ b/cli-config.php @@ -1,5 +1,7 @@ 'tcp', - 'host' => '127.0.0.1', - 'port' => 6379 -)); +/* @var ClientInterface $predis */ +$predis = new Client( + [ + 'scheme' => 'tcp', + 'host' => '127.0.0.1', + 'port' => 6379, + ] +); /* * You can optionally customize the PSR3 logger used on the CLI. The @@ -35,4 +42,4 @@ * $logger = new Monolog\Logger('resque'); */ -return \Resque\Console\ConsoleRunner::createHelperSet($predis/*, $logger*/); +return ConsoleRunner::createHelperSet($predis/*, $logger*/); diff --git a/composer.json b/composer.json index a7b5d76..fc994f8 100644 --- a/composer.json +++ b/composer.json @@ -1,9 +1,7 @@ { - "_-x-vim-formatting": " vim: set ft=javascript ts=4 sw=4 et : ", - - "name": "innogames/php-resque", + "name": "innogames/php-resque", "description": "Port of vend/resque which is the namespaced port of chrisbolton/php-resque, supports Predis, more DI", - "license": "MIT", + "license": "MIT", "minimum-stability": "stable", "authors": [ { @@ -17,39 +15,43 @@ { "name": "Grepolis Devs", "email": "grepolis_browser@innogames.de" + }, + { + "name": "Justus Graf", + "email": "justus.graf@innogames.com" } ], - "autoload": { "psr-4": { - "Resque\\": ["src/", "test/"] + "Resque\\": [ + "src/", + "test/" + ] } }, - "bin": [ "resque" ], - "require": { - "php": ">=5.3.7", - "psr/log": "1.0.0", - "symfony/console": ">=2.7" + "php": "^7.3 || ^8.0", + "psr/log": "^1.1", + "symfony/console": "^5.0", + "ext-pcntl": "*", + "ext-posix": "*", + "ext-json": "*" }, - "require-dev": { - "colinmollenhour/credis": "1.3.*", - "predis/predis": ">=0.8.6", - "phpunit/phpunit": "^7.0", - "monolog/monolog": "1.7.*" + "colinmollenhour/credis": "^1.11", + "predis/predis": ">=1.1", + "phpunit/phpunit": "^9.4", + "monolog/monolog": "^2.0" }, - "extra": { "branch-alias": { "dev-master": "2.1.x-dev", - "dev-2.0": "2.0.x-dev" + "dev-2.0": "2.0.x-dev" } }, - "suggest": { "ext-proctitle": "Allows php-resque to rename the title of UNIX processes to show the status of a worker.", "predis/predis": "Suggested Redis client", diff --git a/composer.lock b/composer.lock index 416dd66..b6d49ce 100644 --- a/composer.lock +++ b/composer.lock @@ -4,26 +4,83 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "074c5994271bef54f28da72e8c3c963d", + "content-hash": "c6515caff15fddb014a126ee9fbd3001", "packages": [ { - "name": "psr/log", + "name": "psr/container", "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "time": "2017-02-14T16:28:37+00:00" + }, + { + "name": "psr/log", + "version": "1.1.3", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b" + "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", - "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b", + "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc", + "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc", "shasum": "" }, + "require": { + "php": ">=5.3.0" + }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, "autoload": { - "psr-0": { - "Psr\\Log\\": "" + "psr-4": { + "Psr\\Log\\": "Psr/Log/" } }, "notification-url": "https://packagist.org/downloads/", @@ -37,46 +94,54 @@ } ], "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", "keywords": [ "log", "psr", "psr-3" ], - "time": "2012-12-21T11:40:51+00:00" + "time": "2020-03-23T09:12:05+00:00" }, { "name": "symfony/console", - "version": "v4.2.5", + "version": "v5.1.8", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "24206aff3efe6962593297e57ef697ebb220e384" + "reference": "e0b2c29c0fa6a69089209bbe8fcff4df2a313d0e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/24206aff3efe6962593297e57ef697ebb220e384", - "reference": "24206aff3efe6962593297e57ef697ebb220e384", + "url": "https://api.github.com/repos/symfony/console/zipball/e0b2c29c0fa6a69089209bbe8fcff4df2a313d0e", + "reference": "e0b2c29c0fa6a69089209bbe8fcff4df2a313d0e", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/contracts": "^1.0", - "symfony/polyfill-mbstring": "~1.0" + "php": ">=7.2.5", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php73": "^1.8", + "symfony/polyfill-php80": "^1.15", + "symfony/service-contracts": "^1.1|^2", + "symfony/string": "^5.1" }, "conflict": { - "symfony/dependency-injection": "<3.4", - "symfony/process": "<3.3" + "symfony/dependency-injection": "<4.4", + "symfony/dotenv": "<5.1", + "symfony/event-dispatcher": "<4.4", + "symfony/lock": "<4.4", + "symfony/process": "<4.4" }, "provide": { "psr/log-implementation": "1.0" }, "require-dev": { "psr/log": "~1.0", - "symfony/config": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/event-dispatcher": "~3.4|~4.0", - "symfony/lock": "~3.4|~4.0", - "symfony/process": "~3.4|~4.0" + "symfony/config": "^4.4|^5.0", + "symfony/dependency-injection": "^4.4|^5.0", + "symfony/event-dispatcher": "^4.4|^5.0", + "symfony/lock": "^4.4|^5.0", + "symfony/process": "^4.4|^5.0", + "symfony/var-dumper": "^4.4|^5.0" }, "suggest": { "psr/log": "For using the console logger", @@ -85,11 +150,6 @@ "symfony/process": "" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Console\\": "" @@ -114,54 +174,530 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2019-04-01T07:32:59+00:00" + "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": "2020-10-24T12:01:57+00:00" }, { - "name": "symfony/contracts", - "version": "v1.0.2", + "name": "symfony/polyfill-ctype", + "version": "v1.20.0", "source": { "type": "git", - "url": "https://github.com/symfony/contracts.git", - "reference": "1aa7ab2429c3d594dd70689604b5cf7421254cdf" + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "f4ba089a5b6366e453971d3aad5fe8e897b37f41" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/contracts/zipball/1aa7ab2429c3d594dd70689604b5cf7421254cdf", - "reference": "1aa7ab2429c3d594dd70689604b5cf7421254cdf", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f4ba089a5b6366e453971d3aad5fe8e897b37f41", + "reference": "f4ba089a5b6366e453971d3aad5fe8e897b37f41", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": ">=7.1" }, - "require-dev": { - "psr/cache": "^1.0", - "psr/container": "^1.0" + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.20-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "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": "2020-10-23T14:02:19+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.20.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "c7cf3f858ec7d70b89559d6e6eb1f7c2517d479c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/c7cf3f858ec7d70b89559d6e6eb1f7c2517d479c", + "reference": "c7cf3f858ec7d70b89559d6e6eb1f7c2517d479c", + "shasum": "" + }, + "require": { + "php": ">=7.1" }, "suggest": { - "psr/cache": "When using the Cache contracts", - "psr/container": "When using the Service contracts", - "symfony/cache-contracts-implementation": "", - "symfony/service-contracts-implementation": "", - "symfony/translation-contracts-implementation": "" + "ext-intl": "For best performance" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "1.20-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { "psr-4": { - "Symfony\\Contracts\\": "" + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" }, - "exclude-from-classmap": [ - "**/Tests/" + "files": [ + "bootstrap.php" + ] + }, + "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": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "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": "2020-10-23T14:02:19+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.20.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "727d1096295d807c309fb01a851577302394c897" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/727d1096295d807c309fb01a851577302394c897", + "reference": "727d1096295d807c309fb01a851577302394c897", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.20-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "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": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "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": "2020-10-23T14:02:19+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.20.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "39d483bdf39be819deabf04ec872eb0b2410b531" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/39d483bdf39be819deabf04ec872eb0b2410b531", + "reference": "39d483bdf39be819deabf04ec872eb0b2410b531", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.20-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "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": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "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": "2020-10-23T14:02:19+00:00" + }, + { + "name": "symfony/polyfill-php73", + "version": "v1.20.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "8ff431c517be11c78c48a39a66d37431e26a6bed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/8ff431c517be11c78c48a39a66d37431e26a6bed", + "reference": "8ff431c517be11c78c48a39a66d37431e26a6bed", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.20-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php73\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "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": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "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": "2020-10-23T14:02:19+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.20.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "e70aa8b064c5b72d3df2abd5ab1e90464ad009de" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/e70aa8b064c5b72d3df2abd5ab1e90464ad009de", + "reference": "e70aa8b064c5b72d3df2abd5ab1e90464ad009de", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.20-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "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": "2020-10-23T14:02:19+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v2.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d15da7ba4957ffb8f1747218be9e1a121fd298a1", + "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/container": "^1.0" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], "authors": [ { "name": "Nicolas Grekas", @@ -172,7 +708,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "A set of abstractions extracted out of the Symfony components", + "description": "Generic abstractions related to writing services", "homepage": "https://symfony.com", "keywords": [ "abstractions", @@ -182,40 +718,60 @@ "interoperability", "standards" ], - "time": "2018-12-05T08:06:11+00:00" + "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": "2020-09-07T11:33:47+00:00" }, { - "name": "symfony/polyfill-mbstring", - "version": "v1.11.0", + "name": "symfony/string", + "version": "v5.1.8", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "fe5e94c604826c35a32fa832f35bd036b6799609" + "url": "https://github.com/symfony/string.git", + "reference": "a97573e960303db71be0dd8fda9be3bca5e0feea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fe5e94c604826c35a32fa832f35bd036b6799609", - "reference": "fe5e94c604826c35a32fa832f35bd036b6799609", + "url": "https://api.github.com/repos/symfony/string/zipball/a97573e960303db71be0dd8fda9be3bca5e0feea", + "reference": "a97573e960303db71be0dd8fda9be3bca5e0feea", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php80": "~1.15" }, - "suggest": { - "ext-mbstring": "For best performance" + "require-dev": { + "symfony/error-handler": "^4.4|^5.0", + "symfony/http-client": "^4.4|^5.0", + "symfony/translation-contracts": "^1.1|^2", + "symfony/var-exporter": "^4.4|^5.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.11-dev" - } - }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" + "Symfony\\Component\\String\\": "" }, "files": [ - "bootstrap.php" + "Resources/functions.php" + ], + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -232,41 +788,58 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for the Mbstring extension", + "description": "Symfony String component", "homepage": "https://symfony.com", "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "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": "2019-02-06T07:57:58+00:00" + "time": "2020-10-24T12:01:57+00:00" } ], "packages-dev": [ { "name": "colinmollenhour/credis", - "version": "1.3", + "version": "v1.11.4", "source": { "type": "git", "url": "https://github.com/colinmollenhour/credis.git", - "reference": "af95629093b98eb8193b3ee18b277109a25e30a2" + "reference": "b458b7c65d156744f5f0c4667c0f8ce45d955435" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/af95629093b98eb8193b3ee18b277109a25e30a2", - "reference": "af95629093b98eb8193b3ee18b277109a25e30a2", + "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/b458b7c65d156744f5f0c4667c0f8ce45d955435", + "reference": "b458b7c65d156744f5f0c4667c0f8ce45d955435", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=5.4.0" }, "type": "library", "autoload": { "classmap": [ "Client.php", - "Cluster.php" + "Cluster.php", + "Sentinel.php", + "Module.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -281,24 +854,24 @@ ], "description": "Credis is a lightweight interface to the Redis key-value store which wraps the phpredis library when available for better performance.", "homepage": "https://github.com/colinmollenhour/credis", - "time": "2013-11-01T22:46:58+00:00" + "time": "2020-10-13T23:55:13+00:00" }, { "name": "doctrine/instantiator", - "version": "1.2.0", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "a2c590166b2133a4633738648b6b064edae0814a" + "reference": "f350df0268e904597e3bd9c4685c53e0e333feea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/a2c590166b2133a4633738648b6b064edae0814a", - "reference": "a2c590166b2133a4633738648b6b064edae0814a", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/f350df0268e904597e3bd9c4685c53e0e333feea", + "reference": "f350df0268e904597e3bd9c4685c53e0e333feea", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.1 || ^8.0" }, "require-dev": { "doctrine/coding-standard": "^6.0", @@ -337,52 +910,81 @@ "constructor", "instantiate" ], - "time": "2019-03-17T17:37:11+00:00" + "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-05-29T17:27:14+00:00" }, { "name": "monolog/monolog", - "version": "1.7.0", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "6225b22de9dcf36546be3a0b2fa8e3d986153f57" + "reference": "f9eee5cec93dfb313a38b6b288741e84e53f02d5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/6225b22de9dcf36546be3a0b2fa8e3d986153f57", - "reference": "6225b22de9dcf36546be3a0b2fa8e3d986153f57", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/f9eee5cec93dfb313a38b6b288741e84e53f02d5", + "reference": "f9eee5cec93dfb313a38b6b288741e84e53f02d5", "shasum": "" }, "require": { - "php": ">=5.3.0", - "psr/log": "~1.0" + "php": ">=7.2", + "psr/log": "^1.0.1" + }, + "provide": { + "psr/log-implementation": "1.0.0" }, "require-dev": { - "aws/aws-sdk-php": "~2.4.8", - "doctrine/couchdb": "dev-master", - "mlehner/gelf-php": "1.0.*", - "phpunit/phpunit": "~3.7.0", - "raven/raven": "0.5.*", - "ruflin/elastica": "0.90.*" + "aws/aws-sdk-php": "^2.4.9 || ^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^6.0", + "graylog2/gelf-php": "^1.4.2", + "php-amqplib/php-amqplib": "~2.4", + "php-console/php-console": "^3.1.3", + "php-parallel-lint/php-parallel-lint": "^1.0", + "phpspec/prophecy": "^1.6.1", + "phpunit/phpunit": "^8.5", + "predis/predis": "^1.1", + "rollbar/rollbar": "^1.3", + "ruflin/elastica": ">=0.90 <3.0", + "swiftmailer/swiftmailer": "^5.3|^6.0" }, "suggest": { "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", - "ext-mongo": "Allow sending log messages to a MongoDB server", - "mlehner/gelf-php": "Allow sending log messages to a GrayLog2 server", - "raven/raven": "Allow sending log messages to a Sentry server", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "php-console/php-console": "Allow sending log messages to Google Chrome", + "rollbar/rollbar": "Allow sending log messages to Rollbar", "ruflin/elastica": "Allow sending log messages to an Elastic Search server" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { - "psr-0": { - "Monolog": "src/" + "psr-4": { + "Monolog\\": "src/Monolog" } }, "notification-url": "https://packagist.org/downloads/", @@ -393,8 +995,7 @@ { "name": "Jordi Boggiano", "email": "j.boggiano@seld.be", - "homepage": "http://seld.be", - "role": "Developer" + "homepage": "http://seld.be" } ], "description": "Sends your logs to files, sockets, inboxes, databases and various web services", @@ -404,24 +1005,34 @@ "logging", "psr-3" ], - "time": "2013-11-14T19:48:31+00:00" + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2020-07-23T08:41:23+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.9.1", + "version": "1.10.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72" + "reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72", - "reference": "e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/969b211f9a51aa1f6c01d1d2aef56d3bd91598e5", + "reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.1 || ^8.0" }, "replace": { "myclabs/deep-copy": "self.version" @@ -452,32 +1063,91 @@ "object", "object graph" ], - "time": "2019-04-07T13:18:21+00:00" + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2020-06-29T13:22:24+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.10.2", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "658f1be311a230e0907f5dfe0213742aff0596de" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/658f1be311a230e0907f5dfe0213742aff0596de", + "reference": "658f1be311a230e0907f5dfe0213742aff0596de", + "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" + ], + "time": "2020-09-26T10:30:38+00:00" }, { "name": "phar-io/manifest", - "version": "1.0.3", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" + "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", - "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/85265efd3af7ba3ca4b2a2c34dbfc5788dd29133", + "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133", "shasum": "" }, "require": { "ext-dom": "*", "ext-phar": "*", - "phar-io/version": "^2.0", - "php": "^5.6 || ^7.0" + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -507,24 +1177,24 @@ } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "time": "2018-07-08T19:23:20+00:00" + "time": "2020-06-27T14:33:11+00:00" }, { "name": "phar-io/version", - "version": "2.0.1", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", - "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" + "reference": "c6bb6825def89e0a32220f88337f8ceaf1975fa0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", - "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "url": "https://api.github.com/repos/phar-io/version/zipball/c6bb6825def89e0a32220f88337f8ceaf1975fa0", + "reference": "c6bb6825def89e0a32220f88337f8ceaf1975fa0", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { @@ -554,39 +1224,34 @@ } ], "description": "Library for handling version information and constraints", - "time": "2018-07-08T19:19:57+00:00" + "time": "2020-06-27T14:39:04+00:00" }, { "name": "phpdocumentor/reflection-common", - "version": "1.0.1", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", "shasum": "" }, "require": { - "php": ">=5.5" - }, - "require-dev": { - "phpunit/phpunit": "^4.6" + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-2.x": "2.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src" - ] + "phpDocumentor\\Reflection\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -608,44 +1273,41 @@ "reflection", "static analysis" ], - "time": "2017-09-11T18:02:19+00:00" + "time": "2020-06-27T09:03:43+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "4.3.0", + "version": "5.2.2", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "94fd0001232e47129dd3504189fa1c7225010d08" + "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08", - "reference": "94fd0001232e47129dd3504189fa1c7225010d08", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/069a785b2141f5bcf49f3e353548dc1cce6df556", + "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556", "shasum": "" }, "require": { - "php": "^7.0", - "phpdocumentor/reflection-common": "^1.0.0", - "phpdocumentor/type-resolver": "^0.4.0", - "webmozart/assert": "^1.0" + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" }, "require-dev": { - "doctrine/instantiator": "~1.0.5", - "mockery/mockery": "^1.0", - "phpunit/phpunit": "^6.4" + "mockery/mockery": "~1.3.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.x-dev" + "dev-master": "5.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] + "phpDocumentor\\Reflection\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -656,44 +1318,45 @@ { "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.", - "time": "2017-11-30T07:14:17+00:00" + "time": "2020-09-03T19:13:55+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "0.4.0", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" + "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", + "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0", - "phpdocumentor/reflection-common": "^1.0" + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^5.2||^4.8.24" + "ext-tokenizer": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-1.x": "1.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] + "phpDocumentor\\Reflection\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -706,42 +1369,43 @@ "email": "me@mikevanriel.com" } ], - "time": "2017-07-14T14:27:02+00:00" + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "time": "2020-09-17T18:55:26+00:00" }, { "name": "phpspec/prophecy", - "version": "1.8.0", + "version": "1.12.1", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06" + "reference": "8ce87516be71aae9b956f81906aaf0338e0d8a2d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06", - "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/8ce87516be71aae9b956f81906aaf0338e0d8a2d", + "reference": "8ce87516be71aae9b956f81906aaf0338e0d8a2d", "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|^3.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0" + "doctrine/instantiator": "^1.2", + "php": "^7.2 || ~8.0, <8.1", + "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 || ^7.1" + "phpspec/phpspec": "^6.0", + "phpunit/phpunit": "^8.0 || ^9.0 <9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.8.x-dev" + "dev-master": "1.11.x-dev" } }, "autoload": { - "psr-0": { - "Prophecy\\": "src/" + "psr-4": { + "Prophecy\\": "src/Prophecy" } }, "notification-url": "https://packagist.org/downloads/", @@ -769,44 +1433,48 @@ "spy", "stub" ], - "time": "2018-08-05T17:53:17+00:00" + "time": "2020-09-29T09:10:42+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "6.1.4", + "version": "9.2.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d" + "reference": "6b20e2055f7c29b56cb3870b3de7cc463d7add41" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", - "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6b20e2055f7c29b56cb3870b3de7cc463d7add41", + "reference": "6b20e2055f7c29b56cb3870b3de7cc463d7add41", "shasum": "" }, "require": { "ext-dom": "*", + "ext-libxml": "*", "ext-xmlwriter": "*", - "php": "^7.1", - "phpunit/php-file-iterator": "^2.0", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^3.0", - "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^3.1 || ^4.0", - "sebastian/version": "^2.0.1", - "theseer/tokenizer": "^1.1" + "nikic/php-parser": "^4.10.2", + "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", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" }, "require-dev": { - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^9.3" }, "suggest": { - "ext-xdebug": "^2.6.0" + "ext-pcov": "*", + "ext-xdebug": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "6.1-dev" + "dev-master": "9.2-dev" } }, "autoload": { @@ -832,32 +1500,38 @@ "testing", "xunit" ], - "time": "2018-10-31T16:06:48+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-30T10:46:41+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "2.0.2", + "version": "3.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "050bedf145a257b1ff02746c31894800e5122946" + "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946", - "reference": "050bedf145a257b1ff02746c31894800e5122946", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/aa4be8575f26070b100fccb67faabb28f21f66f8", + "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^7.1" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -882,26 +1556,44 @@ "filesystem", "iterator" ], - "time": "2018-09-13T20:33:42+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:57:25+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/" @@ -918,37 +1610,43 @@ "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" + ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2015-06-21T13:50:34+00:00" + "time": "2020-09-28T05:58:55+00:00" }, { - "name": "phpunit/php-timer", - "version": "2.1.1", + "name": "phpunit/php-text-template", + "version": "2.0.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "8b389aebe1b8b0578430bda0c7c95a829608e059" + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/8b389aebe1b8b0578430bda0c7c95a829608e059", - "reference": "8b389aebe1b8b0578430bda0c7c95a829608e059", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -967,38 +1665,43 @@ "role": "lead" } ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", "keywords": [ - "timer" + "template" + ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2019-02-20T10:12:59+00:00" + "time": "2020-10-26T05:33:50+00:00" }, { - "name": "phpunit/php-token-stream", - "version": "3.0.1", + "name": "phpunit/php-timer", + "version": "5.0.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18" + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/c99e3be9d3e85f60646f152f9002d46ed7770d18", - "reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", "shasum": "" }, "require": { - "ext-tokenizer": "*", - "php": "^7.1" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -1013,65 +1716,74 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", "keywords": [ - "tokenizer" + "timer" + ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2018-10-30T05:52:18+00:00" + "time": "2020-10-26T13:16:10+00:00" }, { "name": "phpunit/phpunit", - "version": "7.5.8", + "version": "9.4.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "c29c0525cf4572c11efe1db49a8b8aee9dfac58a" + "reference": "3866b2eeeed21b1b099c4bc0b7a1690ac6fd5baa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c29c0525cf4572c11efe1db49a8b8aee9dfac58a", - "reference": "c29c0525cf4572c11efe1db49a8b8aee9dfac58a", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3866b2eeeed21b1b099c4bc0b7a1690ac6fd5baa", + "reference": "3866b2eeeed21b1b099c4bc0b7a1690ac6fd5baa", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.1", + "doctrine/instantiator": "^1.3.1", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", - "myclabs/deep-copy": "^1.7", - "phar-io/manifest": "^1.0.2", - "phar-io/version": "^2.0", - "php": "^7.1", - "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^6.0.7", - "phpunit/php-file-iterator": "^2.0.1", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^2.1", - "sebastian/comparator": "^3.0", - "sebastian/diff": "^3.0", - "sebastian/environment": "^4.0", - "sebastian/exporter": "^3.1", - "sebastian/global-state": "^2.0", - "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^2.0", - "sebastian/version": "^2.0.1" - }, - "conflict": { - "phpunit/phpunit-mock-objects": "*" + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.1", + "phar-io/version": "^3.0.2", + "php": ">=7.3", + "phpspec/prophecy": "^1.12.1", + "phpunit/php-code-coverage": "^9.2", + "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", + "sebastian/version": "^3.0.2" }, "require-dev": { - "ext-pdo": "*" + "ext-pdo": "*", + "phpspec/prophecy-phpunit": "^2.0.1" }, "suggest": { "ext-soap": "*", - "ext-xdebug": "*", - "phpunit/php-invoker": "^2.0" + "ext-xdebug": "*" }, "bin": [ "phpunit" @@ -1079,12 +1791,15 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "7.5-dev" + "dev-master": "9.4-dev" } }, "autoload": { "classmap": [ "src/" + ], + "files": [ + "src/Framework/Assert/Functions.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -1105,82 +1820,221 @@ "testing", "xunit" ], - "time": "2019-03-26T13:23:54+00:00" + "funding": [ + { + "url": "https://phpunit.de/donate.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-19T09:23:29+00:00" }, { "name": "predis/predis", - "version": "v1.1.1", + "version": "v1.1.6", + "source": { + "type": "git", + "url": "https://github.com/predis/predis.git", + "reference": "9930e933c67446962997b05201c69c2319bf26de" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/predis/predis/zipball/9930e933c67446962997b05201c69c2319bf26de", + "reference": "9930e933c67446962997b05201c69c2319bf26de", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "cweagans/composer-patches": "^1.6", + "phpunit/phpunit": "~4.8" + }, + "suggest": { + "ext-curl": "Allows access to Webdis when paired with phpiredis", + "ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol" + }, + "type": "library", + "extra": { + "composer-exit-on-patch-failure": true, + "patches": { + "phpunit/phpunit-mock-objects": { + "Fix PHP 7 and 8 compatibility": "./tests/phpunit_mock_objects.patch" + }, + "phpunit/phpunit": { + "Fix PHP 7 compatibility": "./tests/phpunit_php7.patch", + "Fix PHP 8 compatibility": "./tests/phpunit_php8.patch" + } + } + }, + "autoload": { + "psr-4": { + "Predis\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniele Alessandri", + "email": "suppakilla@gmail.com", + "homepage": "http://clorophilla.net", + "role": "Creator & Maintainer" + }, + { + "name": "Till Krüss", + "homepage": "https://till.im", + "role": "Maintainer" + } + ], + "description": "Flexible and feature-complete Redis client for PHP and HHVM", + "homepage": "http://github.com/predis/predis", + "keywords": [ + "nosql", + "predis", + "redis" + ], + "funding": [ + { + "url": "https://github.com/sponsors/tillkruss", + "type": "github" + } + ], + "time": "2020-09-11T19:18:05+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", + "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/nrk/predis.git", - "reference": "f0210e38881631afeafb56ab43405a92cafd9fd1" + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nrk/predis/zipball/f0210e38881631afeafb56ab43405a92cafd9fd1", - "reference": "f0210e38881631afeafb56ab43405a92cafd9fd1", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "~4.8" - }, - "suggest": { - "ext-curl": "Allows access to Webdis when paired with phpiredis", - "ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol" + "phpunit/phpunit": "^9.3" }, "type": "library", - "autoload": { - "psr-4": { - "Predis\\": "src/" + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" } }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Daniele Alessandri", - "email": "suppakilla@gmail.com", - "homepage": "http://clorophilla.net" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Flexible and feature-complete Redis client for PHP and HHVM", - "homepage": "http://github.com/nrk/predis", - "keywords": [ - "nosql", - "predis", - "redis" + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2016-06-16T16:22:20+00:00" + "time": "2020-10-26T13:08:54+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.1", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^5.7 || ^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -1200,34 +2054,40 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2017-03-04T06:30:41+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" }, { "name": "sebastian/comparator", - "version": "3.0.2", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" + "reference": "55f4261989e546dc112258c7a75935a81a7ce382" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", - "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382", + "reference": "55f4261989e546dc112258c7a75935a81a7ce382", "shasum": "" }, "require": { - "php": "^7.1", - "sebastian/diff": "^3.0", - "sebastian/exporter": "^3.1" + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" }, "require-dev": { - "phpunit/phpunit": "^7.1" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -1240,6 +2100,10 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" @@ -1251,10 +2115,6 @@ { "name": "Bernhard Schussek", "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" } ], "description": "Provides the functionality to compare PHP values for equality", @@ -1264,33 +2124,92 @@ "compare", "equality" ], - "time": "2018-07-12T15:12:46+00:00" + "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", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:52:27+00:00" }, { "name": "sebastian/diff", - "version": "3.0.2", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29" + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29", - "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^7.5 || ^8.0", - "symfony/process": "^2 || ^3.3 || ^4" + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -1303,13 +2222,13 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" } ], "description": "Diff implementation", @@ -1320,27 +2239,33 @@ "unidiff", "unified diff" ], - "time": "2019-02-04T06:01:07+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:10:38+00:00" }, { "name": "sebastian/environment", - "version": "4.1.0", + "version": "5.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "6fda8ce1974b62b14935adc02a9ed38252eca656" + "reference": "388b6ced16caa751030f6a69e588299fa09200ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6fda8ce1974b62b14935adc02a9ed38252eca656", - "reference": "6fda8ce1974b62b14935adc02a9ed38252eca656", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/388b6ced16caa751030f6a69e588299fa09200ac", + "reference": "388b6ced16caa751030f6a69e588299fa09200ac", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^7.5" + "phpunit/phpunit": "^9.3" }, "suggest": { "ext-posix": "*" @@ -1348,7 +2273,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.1-dev" + "dev-master": "5.1-dev" } }, "autoload": { @@ -1373,34 +2298,40 @@ "environment", "hhvm" ], - "time": "2019-02-01T05:27:49+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:52:38+00:00" }, { "name": "sebastian/exporter", - "version": "3.1.0", + "version": "4.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" + "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/d89cc98761b8cb5a1a235a6b703ae50d34080e65", + "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65", "shasum": "" }, "require": { - "php": "^7.0", - "sebastian/recursion-context": "^3.0" + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" }, "require-dev": { "ext-mbstring": "*", - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -1413,6 +2344,10 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" @@ -1421,17 +2356,13 @@ "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" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], "description": "Provides the functionality to export PHP variables for visualization", @@ -1440,27 +2371,36 @@ "export", "exporter" ], - "time": "2017-04-03T13:19:02+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:24:23+00:00" }, { "name": "sebastian/global-state", - "version": "2.0.0", + "version": "5.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" + "reference": "a90ccbddffa067b51f574dea6eb25d5680839455" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/a90ccbddffa067b51f574dea6eb25d5680839455", + "reference": "a90ccbddffa067b51f574dea6eb25d5680839455", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "ext-dom": "*", + "phpunit/phpunit": "^9.3" }, "suggest": { "ext-uopz": "*" @@ -1468,7 +2408,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -1491,34 +2431,93 @@ "keywords": [ "global state" ], - "time": "2017-04-27T15:39:26+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:55:19+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "acf76492a65401babcf5283296fa510782783a7a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/acf76492a65401babcf5283296fa510782783a7a", + "reference": "acf76492a65401babcf5283296fa510782783a7a", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.6", + "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 counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T17:03:56+00:00" }, { "name": "sebastian/object-enumerator", - "version": "3.0.3", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", "shasum": "" }, "require": { - "php": "^7.0", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -1538,32 +2537,38 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-08-03T12:35:26+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" }, { "name": "sebastian/object-reflector", - "version": "1.1.1", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "773f97c67f28de00d397be301821b06708fca0be" + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", - "reference": "773f97c67f28de00d397be301821b06708fca0be", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -1583,32 +2588,38 @@ ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "time": "2017-03-29T09:07:27+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" }, { "name": "sebastian/recursion-context", - "version": "3.0.0", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -1621,14 +2632,14 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, { "name": "Adam Harvey", "email": "aharvey@php.net" @@ -1636,29 +2647,38 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2017-03-03T06:23:57+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:17:30+00:00" }, { "name": "sebastian/resource-operations", - "version": "2.0.1", + "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9" + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9", - "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1678,29 +2698,38 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2018-10-04T04:07:39+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:45:17+00:00" }, { - "name": "sebastian/version", - "version": "2.0.1", + "name": "sebastian/type", + "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "81cd61ab7bbf2de744aba0ea61fae32f721df3d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/81cd61ab7bbf2de744aba0ea61fae32f721df3d2", + "reference": "81cd61ab7bbf2de744aba0ea61fae32f721df3d2", "shasum": "" }, "require": { - "php": ">=5.6" + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "2.3-dev" } }, "autoload": { @@ -1719,87 +2748,84 @@ "role": "lead" } ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "time": "2016-10-03T07:35:21+00:00" + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:18:59+00:00" }, { - "name": "symfony/polyfill-ctype", - "version": "v1.11.0", + "name": "sebastian/version", + "version": "3.0.2", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "82ebae02209c21113908c229e9883c419720738a" + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a", - "reference": "82ebae02209c21113908c229e9883c419720738a", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", "shasum": "" }, "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-ctype": "For best performance" + "php": ">=7.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.11-dev" + "dev-master": "3.0-dev" } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, - "files": [ - "bootstrap.php" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - }, - { - "name": "Gert de Pagter", - "email": "backendtea@gmail.com" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2019-02-06T07:57:58+00:00" + "time": "2020-09-28T06:39:44+00:00" }, { "name": "theseer/tokenizer", - "version": "1.1.2", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "1c42705be2b6c1de5904f8afacef5895cab44bf8" + "reference": "75a63c33a8577608444246075ea0af0d052e452a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/1c42705be2b6c1de5904f8afacef5895cab44bf8", - "reference": "1c42705be2b6c1de5904f8afacef5895cab44bf8", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/75a63c33a8577608444246075ea0af0d052e452a", + "reference": "75a63c33a8577608444246075ea0af0d052e452a", "shasum": "" }, "require": { "ext-dom": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", - "php": "^7.0" + "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { @@ -1819,36 +2845,40 @@ } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "time": "2019-04-04T09:56:43+00:00" + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2020-07-12T23:59:07+00:00" }, { "name": "webmozart/assert", - "version": "1.4.0", + "version": "1.9.1", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9" + "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9", - "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9", + "url": "https://api.github.com/repos/webmozart/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", + "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0", + "php": "^5.3.3 || ^7.0 || ^8.0", "symfony/polyfill-ctype": "^1.8" }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<3.9.1" + }, "require-dev": { - "phpunit/phpunit": "^4.6", - "sebastian/version": "^1.0.1" + "phpunit/phpunit": "^4.8.36 || ^7.5.13" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, "autoload": { "psr-4": { "Webmozart\\Assert\\": "src/" @@ -1870,16 +2900,17 @@ "check", "validate" ], - "time": "2018-12-25T11:19:39+00:00" + "time": "2020-07-08T17:02:28+00:00" } ], "aliases": [], "minimum-stability": "stable", "stability-flags": [], - "prefer-stable": true, + "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=5.3.7" + "php": "^7.3 || ^8.0" }, - "platform-dev": [] + "platform-dev": [], + "plugin-api-version": "1.1.0" } diff --git a/phpunit.xml b/phpunit.xml index 6af55fc..1921f54 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,19 +1,16 @@ - - - - test - - - - - ./src - - ./vendor - - - + + + + ./src + + + ./vendor + + + + + test + + diff --git a/src/AbstractJob.php b/src/AbstractJob.php index 7e70c47..56d0361 100644 --- a/src/AbstractJob.php +++ b/src/AbstractJob.php @@ -1,20 +1,21 @@ + * @author Chris Boulton * @license http://www.opensource.org/licenses/mit-license.php */ abstract class AbstractJob implements ArrayAccess, IteratorAggregate, JobInterface @@ -42,20 +43,17 @@ abstract class AbstractJob implements ArrayAccess, IteratorAggregate, JobInterfa /** * Instantiate a new instance of a job. * - * @param string $queue The queue that the job belongs to. - * @param array $payload array containing details of the job. + * @param string $queue The queue that the job belongs to. + * @param array $payload array containing details of the job. */ - public function __construct($queue, array $payload) + public function __construct(string $queue, array $payload) { - $this->queue = $queue; + $this->queue = $queue; $this->payload = $payload; - $this->id = isset($payload['id']) ? $payload['id'] : null; + $this->id = isset($payload['id']) ? $payload['id'] : null; } - /** - * @param Resque $resque - */ - public function setResque(Resque $resque) + public function setResque(Resque $resque): void { $this->resque = $resque; } @@ -63,7 +61,7 @@ public function setResque(Resque $resque) /** * @return string */ - public function getQueue() + public function getQueue(): string { return $this->queue; } @@ -73,9 +71,8 @@ public function getQueue() * * @throws InvalidArgumentException * @throws JobLogicException - * @return \Resque\Job\Status */ - protected function getStatus() + public function getStatus(): Status { if (!$this->resque) { throw new JobLogicException('Job has no Resque instance: cannot get status'); @@ -84,31 +81,24 @@ protected function getStatus() return $this->resque->getStatusFactory()->forJob($this); } - public function getId() + public function getId(): string { return $this->id; } - public function getPayload() + public function getPayload(): array { return $this->payload; } - /** - * Execute the job - * - * @return bool - */ - abstract public function perform(); - /** * Re-queue the current job. * * @return string ID of the recreated job */ - public function recreate() + public function recreate(): string { - $status = $this->getStatus(); + $status = $this->getStatus(); $tracking = $status->isTracking(); $new = $this->resque->enqueue( @@ -133,17 +123,17 @@ public function recreate() */ public function __toString() { - $name = array( - 'Job{' . $this->queue .'}' - ); + $name = [ + 'Job{' . $this->queue . '}', + ]; - if(!empty($this->id)) { + if (!empty($this->id)) { $name[] = 'ID: ' . $this->id; } $name[] = $this->payload['class']; - if(!empty($this->payload['args'])) { + if (!empty($this->payload['args'])) { $name[] = json_encode($this->payload['args']); } @@ -191,6 +181,6 @@ public function offsetGet($offset) */ public function getIterator() { - return new \ArrayIterator($this->payload); + return new ArrayIterator($this->payload); } } diff --git a/src/Client/ClientInterface.php b/src/Client/ClientInterface.php index 7eaa3df..0bf07e8 100644 --- a/src/Client/ClientInterface.php +++ b/src/Client/ClientInterface.php @@ -28,17 +28,17 @@ public function disconnect(); public function pipeline(); /** - * @return boolean|\Predis\Response\ResponseInterface + * @return bool|\Predis\Response\ResponseInterface */ public function execute(); /** - * @return boolean + * @return bool */ public function isConnected(); /** - * @param integer $db + * @param int $db */ public function flushdb($db = null); @@ -67,19 +67,19 @@ public function exists($key); /** * @param string $key - * @param integer $ttl + * @param int $ttl */ public function expire($key, $ttl); /** * @param string $key - * @param integer $increment + * @param int $increment */ public function incrby($key, $increment); /** * @param string $key - * @param integer $decrement + * @param int $decrement */ public function decrby($key, $decrement); @@ -133,22 +133,22 @@ public function hset($key, $field, $value); /** * @param string $key - * @return array + * @return mixed[] */ public function hgetall($key); /** * @param string $key * @param array $hash - * @return boolean|\Predis\Response\ResponseInterface + * @return bool|\Predis\Response\ResponseInterface */ public function hmset($key, array $hash); /** * @param string $key * @param string $field - * @param integer $increment - * @return integer The value at field after the increment operation + * @param int $increment + * @return int The value at field after the increment operation */ public function hincrby($key, $field, $increment); } diff --git a/src/Client/CredisClient.php b/src/Client/CredisClient.php index 2e2c392..8615576 100644 --- a/src/Client/CredisClient.php +++ b/src/Client/CredisClient.php @@ -1,5 +1,7 @@ connected; } @@ -26,7 +28,7 @@ public function isConnected() /** * Disconnects the client */ - public function disconnect() + public function disconnect(): void { $this->close(); } @@ -34,8 +36,8 @@ public function disconnect() /** * Alias to exec() for pipeline compatibility with Predis */ - public function execute() + public function execute(): void { - $this->__call('exec', array()); + $this->__call('exec', []); } } diff --git a/src/Console/Command.php b/src/Console/Command.php index 45e0c3e..4f3317c 100644 --- a/src/Console/Command.php +++ b/src/Console/Command.php @@ -1,12 +1,15 @@ getHelper('redis')->getClient(); } - /** - * @param OutputInterface $output - * @return \Resque\Resque - */ - public function getResque(OutputInterface $output) + public function getResque(OutputInterface $output): Resque { $resque = new Resque($this->getRedis()); - if (($helper = $this->getHelper('logger'))) { + if ($helper = $this->getHelper('logger')) { /* @var LoggerHelper $helper */ $resque->setLogger($helper->getLogger()); } else { diff --git a/src/Console/ConsoleRunner.php b/src/Console/ConsoleRunner.php index ecd8681..11ccc91 100644 --- a/src/Console/ConsoleRunner.php +++ b/src/Console/ConsoleRunner.php @@ -1,14 +1,16 @@ set(new RedisHelper($client)); @@ -42,9 +43,10 @@ public static function createHelperSet($client, LoggerInterface $logger = null) * calling this method (the actual bin command file). * * @param HelperSet $helperSet + * * @return integer 0 if everything went fine, or an error code */ - public static function run(HelperSet $helperSet) + public static function run(HelperSet $helperSet): int { $application = new Application('Resque Console Tool', Version::VERSION); $application->setCatchExceptions(true); diff --git a/src/Console/EnqueueCommand.php b/src/Console/EnqueueCommand.php index a5a051c..6c7683a 100644 --- a/src/Console/EnqueueCommand.php +++ b/src/Console/EnqueueCommand.php @@ -1,5 +1,7 @@ getResque($output); - $queue = $input->getArgument('queue'); + $queue = $input->getArgument('queue'); $id = $resque->enqueue( $queue, @@ -75,11 +78,13 @@ protected function execute(InputInterface $input, OutputInterface $output) } $output->writeln($message); + return 0; } /** * @param InputInterface $input + * * @return array|mixed */ protected function getPayload(InputInterface $input) diff --git a/src/Console/Helper/LoggerHelper.php b/src/Console/Helper/LoggerHelper.php index 0d31aa3..69a7618 100644 --- a/src/Console/Helper/LoggerHelper.php +++ b/src/Console/Helper/LoggerHelper.php @@ -1,5 +1,7 @@ logger = $logger; } - /** - * @return LoggerInterface - */ - public function getLogger() + public function getLogger(): LoggerInterface { return $this->logger; } - /** - * @return string - */ - public function getName() + public function getName(): string { return 'logger'; } diff --git a/src/Console/Helper/RedisHelper.php b/src/Console/Helper/RedisHelper.php index 6a67e84..16eaf7a 100644 --- a/src/Console/Helper/RedisHelper.php +++ b/src/Console/Helper/RedisHelper.php @@ -1,5 +1,7 @@ client; } - /** - * @return string - */ - public function getName() + public function getName(): string { return 'redis'; } diff --git a/src/Console/QueueClearCommand.php b/src/Console/QueueClearCommand.php index 2193ec6..be4a95e 100644 --- a/src/Console/QueueClearCommand.php +++ b/src/Console/QueueClearCommand.php @@ -1,5 +1,7 @@ getResque($output); - $queue = $input->getArgument('queue'); + $queue = $input->getArgument('queue'); $cleared = $resque->size($queue); $resque->clearQueue($queue); $output->writeln('Cleared ' . $cleared . ' jobs on queue ' . $queue); + return 0; } } diff --git a/src/Console/QueueListCommand.php b/src/Console/QueueListCommand.php index 40152db..3974cb0 100644 --- a/src/Console/QueueListCommand.php +++ b/src/Console/QueueListCommand.php @@ -1,5 +1,7 @@ getResque($output); $queues = $resque->queues(); @@ -36,6 +39,7 @@ protected function execute(InputInterface $input, OutputInterface $output) if (!count($queues)) { $output->writeln('No queues'); + return 1; // If no queues, return error exit code } diff --git a/src/Console/WorkerCommand.php b/src/Console/WorkerCommand.php index 9268103..a1868df 100644 --- a/src/Console/WorkerCommand.php +++ b/src/Console/WorkerCommand.php @@ -1,5 +1,7 @@ getResque($output); $queues = $input->getOption('queue'); $worker = new Worker($resque, $queues); - $worker->work($input->getOption('interval')); + $worker->work((int)$input->getOption('interval')); + + return 0; } } diff --git a/src/Exception/DirtyExitException.php b/src/Exception/DirtyExitException.php index 0c97bca..e367a23 100644 --- a/src/Exception/DirtyExitException.php +++ b/src/Exception/DirtyExitException.php @@ -1,5 +1,7 @@ + * @author Chris Boulton * @license http://www.opensource.org/licenses/mit-license.php */ interface BackendInterface @@ -17,11 +19,12 @@ interface BackendInterface /** * Initialize a failed job class and save it (where appropriate). * - * @param array $payload Object containing details of the failed job. + * @param array $payload Object containing details of the failed job. * @param \Exception $exception Instance of the exception that was thrown by the failed job. - * @param Worker $worker Instance of Worker that received the job. - * @param string $queue The name of the queue the job was fetched from. + * @param Worker $worker Instance of Worker that received the job. + * @param string $queue The name of the queue the job was fetched from. + * * @return void */ - public function receiveFailure($payload, Exception $exception, Worker $worker, $queue); + public function receiveFailure(array $payload, Exception $exception, Worker $worker, string $queue): void; } diff --git a/src/Failure/RedisBackend.php b/src/Failure/RedisBackend.php index 015272c..34eb4b4 100644 --- a/src/Failure/RedisBackend.php +++ b/src/Failure/RedisBackend.php @@ -1,16 +1,18 @@ + * @author Chris Boulton * @license http://www.opensource.org/licenses/mit-license.php */ class RedisBackend implements BackendInterface @@ -18,14 +20,14 @@ class RedisBackend implements BackendInterface /** * Initialize a failed job class and save it (where appropriate). * - * @param object $payload Object containing details of the failed job. + * @param array $payload Object containing details of the failed job. * @param Exception $exception Instance of the exception that was thrown by the failed job. - * @param Worker $worker Instance of Worker that received the job. - * @param string $queue The name of the queue the job was fetched from. + * @param Worker $worker Instance of Worker that received the job. + * @param string $queue The name of the queue the job was fetched from. */ - public function receiveFailure($payload, Exception $exception, Worker $worker, $queue) + public function receiveFailure(array $payload, Exception $exception, Worker $worker, string $queue): void { - $data = new stdClass; + $data = new stdClass(); $data->failed_at = strftime('%a %b %d %H:%M:%S %Z %Y'); $data->payload = $payload; $data->exception = $this->getClass($exception); @@ -45,12 +47,13 @@ public function receiveFailure($payload, Exception $exception, Worker $worker, $ * multiple lines by resque-web. So, we'll also use it to mention the * wrapping exceptions. * - * @param \Exception $exception + * @param Exception $exception + * * @return array */ - protected function getBacktrace(\Exception $exception) + protected function getBacktrace(Exception $exception): array { - $backtrace = array(); + $backtrace = []; $backtrace[] = '---'; $backtrace[] = $this->getErrorMessage($exception); @@ -63,7 +66,7 @@ protected function getBacktrace(\Exception $exception) $backtrace = array_merge($backtrace, explode("\n", $exception->getTraceAsString())); } - if (($previous = $exception->getPrevious())) { + if ($previous = $exception->getPrevious()) { $backtrace = array_merge($backtrace, $this->getBacktrace($previous)); // Recurse } @@ -72,15 +75,13 @@ protected function getBacktrace(\Exception $exception) /** * Find the ultimate cause exception, by following previous members right back - * - * @param Exception $exception - * @return Exception */ - protected function getDistalCause(\Exception $exception) + protected function getDistalCause(Exception $exception): Exception { - if (($previous = $exception->getPrevious())) { + if ($previous = $exception->getPrevious()) { return $this->getDistalCause($previous); } + return $exception; } @@ -88,13 +89,14 @@ protected function getDistalCause(\Exception $exception) * Find the class names of the exceptions * * @param Exception $exception + * * @return string */ - protected function getClass(\Exception $exception) + protected function getClass(Exception $exception): string { $message = ''; - if (($previous = $exception->getPrevious())) { + if ($previous = $exception->getPrevious()) { $message = $this->getClass($previous) . ' < '; // Recurse } @@ -110,11 +112,8 @@ protected function getClass(\Exception $exception) /** * Gets a single string error message from the exception - * - * @param \Exception $exception - * @return string */ - protected function getErrorMessage(\Exception $exception) + protected function getErrorMessage(Exception $exception): string { return $exception->getMessage() . ' at ' . $exception->getFile() . ':' . $exception->getLine(); } diff --git a/src/Job/Status.php b/src/Job/Status.php index 04ada8a..c8159b9 100644 --- a/src/Job/Status.php +++ b/src/Job/Status.php @@ -1,10 +1,12 @@ - * @author Chris Boulton + * @author Dominic Scheirlinck + * @author Chris Boulton * @copyright (c) 2010 Chris Boulton - * @license http://www.opensource.org/licenses/mit-license.php + * @license http://www.opensource.org/licenses/mit-license.php */ class Status { @@ -29,8 +31,8 @@ class Status * * @var int */ - const COMPLETE_TTL = 86400; // 24 hours - const INCOMPLETE_TTL = 604800; // A week + public const COMPLETE_TTL = 86400; // 24 hours + public const INCOMPLETE_TTL = 604800; // A week /**#@-*/ /**#@+ @@ -38,36 +40,40 @@ class Status * * @var int */ - const STATUS_WAITING = 1; - const STATUS_RUNNING = 2; - const STATUS_FAILED = 3; - const STATUS_COMPLETE = 4; - const STATUS_RECREATED = 5; + public const STATUS_WAITING = 1; + public const STATUS_RUNNING = 2; + public const STATUS_FAILED = 3; + public const STATUS_COMPLETE = 4; + public const STATUS_RECREATED = 5; + + private const ATTRIBUTE_STATUS = 'status'; + private const ATTRIBUTE_CREATED = 'created'; + private const ATTRIBUTE_UPDATED = 'updated'; /**#@-*/ /** * An array of valid statuses * - * @var array + * @var string[] */ - public static $valid = array( + public static $valid = [ self::STATUS_WAITING => 'waiting', self::STATUS_RUNNING => 'running', self::STATUS_FAILED => 'failed', self::STATUS_COMPLETE => 'complete', - self::STATUS_RECREATED => 'recreated' - ); + self::STATUS_RECREATED => 'recreated', + ]; /** * An array of complete statuses * - * @var array + * @var int[] */ - public static $complete = array( + public static $complete = [ self::STATUS_FAILED, self::STATUS_COMPLETE, - self::STATUS_RECREATED - ); + self::STATUS_RECREATED, + ]; /** * @var string The ID of the job this status class refers back to. @@ -77,14 +83,14 @@ class Status /** * Whether the status has been loaded from the database * - * @var boolean + * @var bool */ protected $loaded = false; /** - * @var array + * @var mixed[] */ - protected $attributes = array(); + protected $attributes = []; /** * @var \Resque\Client\ClientInterface @@ -92,7 +98,7 @@ class Status protected $client; /** - * @var boolean|null Cache variable if the status of this job is being + * @var bool|null Cache variable if the status of this job is being * monitored or not. True/false when checked at least * once or null if not checked yet. */ @@ -102,18 +108,15 @@ class Status * Setup a new instance of the job monitor class for the supplied job ID. * * @param string $id The ID of the job to manage the status for. - * @param \Resque\Resque $resque + * @param Resque $resque */ - public function __construct($id, Resque $resque) + public function __construct(string $id, Resque $resque) { - $this->id = $id; + $this->id = $id; $this->client = $resque->getClient(); } - /** - * @return string - */ - public function getId() + public function getId(): string { return $this->id; } @@ -122,72 +125,73 @@ public function getId() * Create a new status monitor item for the supplied job ID. Will create * all necessary keys in Redis to monitor the status of a job. */ - public function create() + public function create(): void { $this->isTracking = true; - $this->setAttributes(array( - 'status' => self::STATUS_WAITING, - 'created' => time(), - 'updated' => time() - )); + $this->setAttributes( + [ + self::ATTRIBUTE_STATUS => self::STATUS_WAITING, + self::ATTRIBUTE_CREATED => time(), + self::ATTRIBUTE_UPDATED => time(), + ] + ); } /** * Sets all the given attributes * - * @param array $attributes + * @param mixed[] $attributes + * * @return mixed */ public function setAttributes(array $attributes) { $this->attributes = array_merge($this->attributes, $attributes); - $set = array(); + $set = []; foreach ($attributes as $name => $value) { - if ($name == 'status') { + if ($name == self::ATTRIBUTE_STATUS) { $this->update($value); continue; } $set[$name] = $value; } - return call_user_func(array($this->client, 'hmset'), $this->getHashKey(), $set); + return call_user_func([$this->client, 'hmset'], $this->getHashKey(), $set); } /** - * Sets an attribute - * - * @param string $name - * @param string $value + * @param mixed $value */ - public function setAttribute($name, $value) + public function setAttribute(string $name, $value) { - if ($name == 'status') { + if ($name == self::ATTRIBUTE_STATUS) { $this->update($value); } else { $this->attributes[$name] = $value; - $this->client->hmset($this->getHashKey(), array( - $name => $value, - 'updated' => time() - )); + $this->client->hmset( + $this->getHashKey(), + [ + $name => $value, + self::ATTRIBUTE_UPDATED => time(), + ] + ); } } /** * Increments an attribute * - * The attribute should be an integer field + * The attribute should be an int field * - * @param string $name - * @param integer $by - * @return integer The value after incrementing (see hincrby) + * @return int The value after incrementing (see hincrby) */ - public function incrementAttribute($name, $by = 1) + public function incrementAttribute(string $name, int $by = 1) { $pipeline = $this->client->pipeline(); $pipeline->hincrby($this->getHashKey(), $name, $by); - $pipeline->hset($this->getHashKey(), 'updated', time()); + $pipeline->hset($this->getHashKey(), self::ATTRIBUTE_UPDATED, time()); $result = $pipeline->execute(); return $this->attributes[$name] = $result[0]; @@ -200,10 +204,11 @@ public function incrementAttribute($name, $by = 1) * properly updated. * * @param int $status The status of the job (see constants in Resque\Job\Status) - * @throws \InvalidArgumentException - * @return boolean + * + * @return bool + * @throws InvalidArgumentException */ - public function update($status) + public function update(int $status): bool { if (!isset(self::$valid[$status])) { throw new InvalidArgumentException('Invalid status'); @@ -213,13 +218,16 @@ public function update($status) return false; } - $this->attributes['status'] = $status; - $this->attributes['updated'] = time(); + $this->attributes[self::ATTRIBUTE_STATUS] = $status; + $this->attributes[self::ATTRIBUTE_UPDATED] = time(); - $success = $this->client->hmset($this->getHashKey(), array( - 'status' => $this->attributes['status'], - 'updated' => $this->attributes['updated'] - )); + $success = $this->client->hmset( + $this->getHashKey(), + [ + self::ATTRIBUTE_STATUS => $this->attributes[self::ATTRIBUTE_STATUS], + self::ATTRIBUTE_UPDATED => $this->attributes[self::ATTRIBUTE_UPDATED], + ] + ); // Delete completed jobs and set expire times for the rest. if ($status == self::STATUS_COMPLETE) { @@ -230,23 +238,24 @@ public function update($status) $this->client->expire($this->getHashKey(), self::INCOMPLETE_TTL); } - return (boolean)$success; + return (bool)$success; } /** * Check if we're actually checking the status of the loaded job status * instance. * - * @return boolean True if the status is being monitored, false if not. + * @return bool True if the status is being monitored, false if not. */ - public function isTracking() + public function isTracking(): bool { if ($this->isTracking === null) { - $this->isTracking = (boolean)$this->client->exists($this->getHashKey()); + $this->isTracking = (bool)$this->client->exists($this->getHashKey()); if ($this->isTracking) { $this->load(); } } + return $this->isTracking; } @@ -258,7 +267,7 @@ public function isTracking() public function load() { if ($this->loaded) { - throw new \LogicException('The status is already loaded. Use another instance.'); + throw new LogicException('The status is already loaded. Use another instance.'); } $this->attributes = array_merge($this->attributes, $this->client->hgetall($this->getHashKey())); @@ -266,7 +275,7 @@ public function load() } /** - * @return array + * @return mixed[] */ public function getAll() { @@ -280,17 +289,19 @@ public function getAll() /** * Gets the time this status was updated */ - public function getUpdated() + public function getUpdated(): ?int { - return $this->getAttribute('updated'); + $updated = $this->getAttribute(self::ATTRIBUTE_UPDATED); + return $updated ? (int)$updated : null; } /** * Gets the time this status was created */ - public function getCreated() + public function getCreated(): ?int { - return $this->getAttribute('created'); + $created = $this->getAttribute(self::ATTRIBUTE_CREATED); + return $created ? (int)$created : null; } /** @@ -298,36 +309,29 @@ public function getCreated() * * For consistency, this would be called getStatus(), but for BC, it's * just get(). - * - * @return null|integer */ - public function get() + public function get(): ?int { - return $this->getAttribute('status'); + $status = $this->getAttribute(self::ATTRIBUTE_STATUS); + return $status ? (int)$status : null; } - /** - * @return string - */ - public function getStatus() + public function getStatus(): string { $status = $this->get(); - if (isset(self::$valid[$status])) { - return self::$valid[$status]; - } - - return 'unknown'; + return self::$valid[$status] ?? 'unknown'; } /** * Gets a single attribute value * * @param string $name - * @param mixed $default + * @param mixed $default + * * @return mixed */ - public function getAttribute($name, $default = null) + public function getAttribute(string $name, $default = null) { if ($this->loaded) { return isset($this->attributes[$name]) ? $this->attributes[$name] : $default; @@ -335,15 +339,14 @@ public function getAttribute($name, $default = null) // Could be just hget, but Credis will return false?! $attributes = $this->client->hGetAll($this->getHashKey()); + return isset($attributes[$name]) ? $attributes[$name] : $default; } /** * Stop tracking the status of a job. - * - * @return void */ - public function stop() + public function stop(): void { $this->client->del($this->getHashKey()); } @@ -352,10 +355,8 @@ public function stop() * A new key, because we're now using a hash format to store the status * * Used from outside this class to do status processing more efficiently - * - * @return string */ - public function getHashKey() + public function getHashKey(): string { return 'job:' . $this->id . ':status/hash'; } @@ -363,7 +364,7 @@ public function getHashKey() /** * Accessor to return valid statuses * - * @return array + * @return int[] */ public function getValid() { @@ -373,7 +374,7 @@ public function getValid() /** * Accessor to return complete statuses * - * @return array + * @return int[] */ public function getComplete() { @@ -383,7 +384,7 @@ public function getComplete() /** * Convenience method to to check if a resque job has a complete status */ - public function isComplete() + public function isComplete(): bool { return in_array($this->get(), self::$complete); } diff --git a/src/Job/StatusFactory.php b/src/Job/StatusFactory.php index 033bced..ff85fdc 100644 --- a/src/Job/StatusFactory.php +++ b/src/Job/StatusFactory.php @@ -1,5 +1,7 @@ resque = $resque; } - /** - * @param String $id - * @return \Resque\Job\Status - */ - public function forId($id) + public function forId(string $id): Status { return new Status($id, $this->resque); } /** - * @param JobInterface $job - * @return Status - * @throws \Resque\Exception\JobIdException + * @throws JobIdException */ - public function forJob(JobInterface $job) + public function forJob(JobInterface $job): Status { $payload = $job->getPayload(); diff --git a/src/JobInterface.php b/src/JobInterface.php index 0210f2e..3c717d8 100644 --- a/src/JobInterface.php +++ b/src/JobInterface.php @@ -1,8 +1,8 @@ + * @var mixed[] */ protected $options; @@ -58,9 +61,10 @@ class Resque implements LoggerAwareInterface /** * Constructor - * @param ClientInterface $client + * + * @param ClientInterface $client //fake interface, instances will not actually implement this! */ - public function __construct($client, array $options = array()) + public function __construct($client, array $options = []) { $this->client = $client; $this->logger = new NullLogger(); @@ -71,17 +75,19 @@ public function __construct($client, array $options = array()) /** * Configures the options of the resque background queue system * - * @param array $options - * @return void + * @param mixed[] $options */ - public function configure(array $options) + public function configure(array $options): void { - $this->options = array_merge(array( - 'pgrep' => 'pgrep -f', - 'pgrep_pattern' => '[r]esque[^-]', - 'prefix' => 'resque:', - 'statistic_class' => 'Resque\Statistic' - ), $options); + $this->options = array_merge( + [ + 'pgrep' => 'pgrep -f', + 'pgrep_pattern' => '[r]esque[^-]', + 'prefix' => 'resque:', + 'statistic_class' => 'Resque\Statistic', + ], + $options + ); } /** @@ -97,18 +103,12 @@ public function getClient() return $this->client; } - /** - * @param \Resque\Failure\BackendInterface $backend - */ public function setFailureBackend(BackendInterface $backend) { $this->failures = $backend; } - /** - * @return BackendInterface - */ - public function getFailureBackend() + public function getFailureBackend(): BackendInterface { if (!isset($this->failures)) { $this->failures = new RedisBackend(); @@ -117,18 +117,12 @@ public function getFailureBackend() return $this->failures; } - /** - * @param StatusFactory $factory - */ public function setStatusFactory(StatusFactory $factory) { $this->statuses = $factory; } - /** - * @return StatusFactory - */ - public function getStatusFactory() + public function getStatusFactory(): StatusFactory { if (!isset($this->statuses)) { $this->statuses = new StatusFactory($this); @@ -139,10 +133,8 @@ public function getStatusFactory() /** * Causes the client to reconnect to the Redis server - * - * @return void */ - public function reconnect() + public function reconnect(): void { if ($this->client->isConnected()) { $this->client->disconnect(); @@ -153,7 +145,7 @@ public function reconnect() /** * Causes the client to connect to the Redis server */ - public function connect() + public function connect(): void { $this->client->connect(); } @@ -161,30 +153,20 @@ public function connect() /** * Disconnects the client from the Redis server */ - public function disconnect() + public function disconnect(): void { $this->client->disconnect(); } - /** - * Logs a message - * - * @param string $message - * @param string $priority - * @return void - */ - public function log($message, $priority = LogLevel::INFO) + public function log(string $message, string $priority = LogLevel::INFO): void { $this->logger->log($message, $priority); } /** * Gets a namespaced/prefixed key for the given key suffix - * - * @param string $key - * @return string */ - public function getKey($key) + public function getKey(string $key): string { return $this->options['prefix'] . $key; } @@ -194,9 +176,9 @@ public function getKey($key) * exist, then create it as well. * * @param string $queue The name of the queue to add the job to. - * @param array $item Job description as an array to be JSON encoded. + * @param array $item Job description as an array to be JSON encoded. */ - public function push($queue, $item) + public function push(string $queue, array $item) { // Add the queue to the list of queues $this->getClient()->sadd($this->getKey(self::QUEUES_KEY), $queue); @@ -210,9 +192,10 @@ public function push($queue, $item) * return it. * * @param string $queue The name of the queue to fetch an item from. - * @return array Decoded item from the queue. + * + * @return array|null Decoded item from the queue. */ - public function pop($queue) + public function pop(string $queue): ?array { $item = $this->getClient()->lpop($this->getKey(self::QUEUE_KEY . $queue)); @@ -225,10 +208,8 @@ public function pop($queue) /** * Clears the whole of a queue - * - * @param string $queue */ - public function clearQueue($queue) + public function clearQueue(string $queue) { $this->getClient()->del($this->getKey(self::QUEUE_KEY . $queue)); } @@ -236,11 +217,11 @@ public function clearQueue($queue) /** * Return the size (number of pending jobs) of the specified queue. * - * @param $queue name of the queue to be checked for pending jobs + * @param string $queue name of the queue to be checked for pending jobs * * @return int The size of the queue. */ - public function size($queue) + public function size(string $queue): int { return $this->getClient()->llen($this->getKey(self::QUEUE_KEY . $queue)); } @@ -248,28 +229,26 @@ public function size($queue) /** * Create a new job and save it to the specified queue. * - * @param string $queue The name of the queue to place the job in. - * @param string $class The name of the class that contains the code to execute the job. - * @param array $args Any optional arguments that should be passed when the job is executed. - * @param boolean $trackStatus Set to true to be able to monitor the status of a job. - * @throws \InvalidArgumentException + * @param string $queue The name of the queue to place the job in. + * @param string $class The name of the class that contains the code to execute the job. + * @param null $args Any optional arguments that should be passed when the job is executed. + * @param bool $trackStatus Set to true to be able to monitor the status of a job. + * * @return string + * @throws InvalidArgumentException */ - public function enqueue($queue, $class, $args = null, $trackStatus = false) + public function enqueue(string $queue, string $class, ?array $args = null, bool $trackStatus = false): string { - if ($args !== null && !is_array($args)) { - throw new InvalidArgumentException( - 'Supplied $args must be an array.' - ); - } - $id = md5(uniqid('', true)); - $this->push($queue, array( - 'class' => $class, - 'args' => $args, - 'id' => $id, - )); + $this->push( + $queue, + [ + 'class' => $class, + 'args' => $args, + 'id' => $id, + ] + ); if ($trackStatus) { $status = new Status($id, $this); @@ -282,9 +261,9 @@ public function enqueue($queue, $class, $args = null, $trackStatus = false) /** * Get an array of all known queues. * - * @return array + * @return string[] */ - public function queues() + public function queues(): array { return $this->getSetMembers(self::QUEUES_KEY); } @@ -292,33 +271,14 @@ public function queues() /** * Gets an array of all known worker IDs * - * @return array + * @return string[] */ - public function getWorkerIds() + public function getWorkerIds(): array { return $this->getSetMembers(self::WORKERS_KEY); } - /** - * @param string $suffix Partial key (don't pass to getKey() - let this method do it for you) - * @return array - */ - protected function getSetMembers($suffix) - { - $members = $this->getClient()->smembers($this->getKey($suffix)); - - if (!is_array($members)) { - $members = array(); - } - - return $members; - } - - /** - * @param string $id Worker ID - * @return bool - */ - public function workerExists($id) + public function workerExists(string $id): bool { return in_array($id, $this->getWorkerIds()); } @@ -330,13 +290,13 @@ public function workerExists($id) * Expects pgrep to be in the path, and for it to inspect full argument * lists using -f * - * @return array Array of Resque worker process IDs. + * @return int[] Array of Resque worker process IDs. */ - public function getWorkerPids() + public function getWorkerPids(): array { $command = $this->options['pgrep'] . ' ' . escapeshellarg($this->options['pgrep_pattern']); - $pids = array(); + $pids = []; $output = null; $return = null; @@ -351,7 +311,8 @@ public function getWorkerPids() */ if (($return !== 0 && $return !== 1) || empty($output) || !is_array($output)) { $this->logger->warning('Unable to determine worker PIDs'); - return array(); + + return []; } foreach ($output as $line) { @@ -367,31 +328,34 @@ public function getWorkerPids() return $pids; } - /** - * Gets a statistic - * - * @param string $name - * @return \Resque\Statistic - */ - public function getStatistic($name) + public function getStatistic(string $name): Statistic { return new Statistic($this, $name); } - /** - * @param LoggerInterface $logger - * @return void - */ - public function setLogger(LoggerInterface $logger) + public function setLogger(LoggerInterface $logger): void { $this->logger = $logger; } + public function getLogger(): LoggerInterface + { + return $this->logger; + } + /** - * @return LoggerInterface + * @param string $suffix Partial key (don't pass to getKey() - let this method do it for you) + * + * @return string[] */ - public function getLogger() + protected function getSetMembers(string $suffix): array { - return $this->logger; + $members = $this->getClient()->smembers($this->getKey($suffix)); + + if (!is_array($members)) { + $members = []; + } + + return $members; } } diff --git a/src/ResqueException.php b/src/ResqueException.php index 5a9c7fe..012786a 100644 --- a/src/ResqueException.php +++ b/src/ResqueException.php @@ -1,7 +1,11 @@ + * @author Chris Boulton * @license http://www.opensource.org/licenses/mit-license.php */ class Statistic { - const KEY = 'stat:'; + private const KEY = 'stat:'; protected $resque; protected $statistic; - /** - * Constructor - * - * @param Resque $resque - * @param string $statistic - */ - public function __construct(Resque $resque, $statistic) + public function __construct(Resque $resque, string $statistic) { - $this->resque = $resque; + $this->resque = $resque; $this->statistic = $statistic; } /** * Gets the key for a statistic - * - * @return string */ - public function getKey() + public function getKey(): string { return $this->resque->getKey(self::KEY . $this->statistic); } /** * Get the value of the supplied statistic counter for the specified statistic. - * - * @return integer Value of the statistic. */ - public function get() + public function get(): int { return (int)$this->resque->getClient()->get($this->getKey()); } @@ -52,9 +44,10 @@ public function get() * Increment the value of the specified statistic by a certain amount (default is 1) * * @param int $by The amount to increment the statistic by. - * @return boolean True if successful, false if not. + * + * @return bool True if successful, false if not. */ - public function incr($by = 1) + public function incr(int $by = 1): bool { return (bool)$this->resque->getClient()->incrby($this->getKey(), $by); } @@ -63,9 +56,10 @@ public function incr($by = 1) * Decrement the value of the specified statistic by a certain amount (default is 1) * * @param int $by The amount to decrement the statistic by. - * @return boolean True if successful, false if not. + * + * @return bool True if successful, false if not. */ - public function decr($by = 1) + public function decr(int $by = 1): bool { return (bool)$this->resque->getClient()->decrby($this->getKey(), $by); } @@ -73,9 +67,9 @@ public function decr($by = 1) /** * Delete a statistic with the given name. * - * @return boolean True if successful, false if not. + * @return bool True if successful, false if not. */ - public function clear() + public function clear(): bool { return (bool)$this->resque->getClient()->del($this->getKey()); } diff --git a/src/Version.php b/src/Version.php index 9c5cffc..6cf8437 100644 --- a/src/Version.php +++ b/src/Version.php @@ -1,8 +1,10 @@ + * @var mixed[] */ - protected $options = array(); + protected $options = []; /** * @var int Process ID of child worker processes. @@ -91,174 +117,46 @@ class Worker implements LoggerAwareInterface * Passing a single '*' allows the worker to work on all queues. * You can easily add new queues dynamically and have them worked on using this method. * - * @param Resque $resque + * @param Resque $resque * @param string|array $queues String with a single queue name, array with multiple. - * @param array $options + * @param array $options */ - public function __construct(Resque $resque, $queues, array $options = array()) + public function __construct(Resque $resque, $queues, array $options = []) { $this->configure($options); - $this->queues = is_array($queues) ? $queues : array($queues); + $this->queues = is_array($queues) ? $queues : [$queues]; $this->resque = $resque; $this->logger = $this->resque->getLogger(); if (in_array('*', $this->queues) || empty($this->queues)) { $this->refreshQueues = true; - $this->queues = $resque->queues(); + $this->queues = $resque->queues(); } $this->configureId(); } - /** - * Configures options for the worker - * - * @param array $options - * Including - * Worker identification - * - server_name => string, default is FQDN hostname - * - pid => int, default is current PID - * - id_format => string, suitable for sprintf - * - id_location_preg => string, Perl compat regex, gets hostname and PID - * out of worker ID - * - shuffle_queues => bool, whether to shuffle the queues on reserve, so we evenly check all queues - * - sort_queues => bool, whether to check the queues in alphabetical order (mutually exclusive with shuffle_queues) - * - no_fork => bool, whether to suppress fork if available - */ - protected function configure(array $options) - { - $this->options = array_merge(array( - 'server_name' => null, - 'pid' => null, - 'ps' => '/bin/ps', - 'ps_args' => array('-o', 'pid,state', '-p'), - 'id_format' => '%s:%d:%s', - 'id_location_preg' => '/^([^:]+?):([0-9]+):/', - 'shuffle_queues' => true, - 'sort_queues' => false, - 'pre_perform' => false, - 'post_perform' => false, - 'no_fork' => false, - ), $options); - - if (!$this->options['server_name']) { - $this->options['server_name'] = function_exists('gethostname') ? gethostname() : php_uname('n'); - } - - if (!$this->options['pid']) { - $this->options['pid'] = getmypid(); - } - } - - /** - * Configures the ID of this worker - */ - protected function configureId() - { - $this->id = sprintf( - $this->options['id_format'], - $this->options['server_name'], - $this->options['pid'], - implode(',', $this->queues) - ); - } - - /** - * @return Client\ClientInterface - */ - protected function getClient() - { - return $this->resque->getClient(); - } - - /** - * @param string $queue - * @param array $payload - * @throws JobClassNotFoundException - * @throws JobInvalidException - * @return JobInterface - */ - protected function createJobInstance($queue, array $payload) - { - if (!class_exists($payload['class'])) { - throw new JobClassNotFoundException( - 'Could not find job class ' . $payload['class'] . '.' - ); - } - - if (!is_subclass_of($payload['class'], 'Resque\JobInterface')) { - throw new JobInvalidException(); - } - - $job = new $payload['class']($queue, $payload); - - if (method_exists($job, 'setResque')) { - $job->setResque($this->resque); - } - - return $job; - } - - /** - * Parses a hostname and PID out of a string worker ID - * - * If you change the format of the ID, you should also change the definition - * of this method. - * - * This method *always* parses the ID of the worker, rather than figuring out - * the current processes' PID/hostname. This means you can use setId() to - * interrogate the properties of other workers given their ID. - * - * @throws Exception - * @return array - */ - protected function getLocation() - { - $matches = array(); - - if (!preg_match($this->options['id_location_preg'], $this->getId(), $matches)) { - throw new Exception('Incompatible ID format: unable to determine worker location'); - } - - if (!isset($matches[1]) || !$matches[1]) { - throw new Exception('Invalid ID: invalid hostname'); - } - - if (!isset($matches[2]) || !$matches[2] || !is_numeric($matches[2])) { - throw new Exception('Invalid ID: invalid PID'); - } - - return array($matches[1], (int)$matches[2]); - } - /** * Set the ID of this worker to a given ID string. - * - * @param string $id ID for the worker. */ - public function setId($id) + public function setId(string $id) { $this->id = $id; } /** * Gives access to the main Resque system this worker belongs to - * - * @return \Resque\Resque */ - public function getResque() + public function getResque(): Resque { return $this->resque; } /** * Given a worker ID, check if it is registered/valid. - * - * @param string $id ID of the worker. - * @return boolean True if the worker exists, false if not. */ - public function exists($id) + public function exists(string $id): bool { return (bool)$this->resque->getClient()->sismember('workers', $id); } @@ -271,7 +169,7 @@ public function exists($id) * * @param int $interval How often to check for new jobs across the queues. */ - public function work($interval = 5) + public function work(int $interval = 5) { $this->updateProcLine('Starting'); $this->startup(); @@ -280,6 +178,7 @@ public function work($interval = 5) pcntl_signal_dispatch(); if ($this->shutdown) { $this->unregister(); + return; } @@ -306,7 +205,7 @@ public function work($interval = 5) continue; } - $this->logger->info('got {job}', array('job' => $job)); + $this->logger->info('got {job}', ['job' => $job]); $this->workingOn($job); if ($this->options['no_fork']) { @@ -336,11 +235,14 @@ public function work($interval = 5) $exitStatus = pcntl_wexitstatus($status); if ($exitStatus !== 0) { - $this->failJob($job, new DirtyExitException( - 'Job exited with exit code ' . $exitStatus - )); + $this->failJob( + $job, + new DirtyExitException( + 'Job exited with exit code ' . $exitStatus + ) + ); } else { - $this->logger->debug('Job returned status code {code}', array('code' => $exitStatus)); + $this->logger->debug('Job returned status code {code}', ['code' => $exitStatus]); } } @@ -350,8 +252,6 @@ public function work($interval = 5) /** * Process a single job. - * - * @param JobInterface $job The job to be processed. */ public function perform(JobInterface $job) { @@ -363,101 +263,37 @@ public function perform(JobInterface $job) if ($this->options['post_perform']) { $this->options['post_perform'](); } - } catch (Exception $e) { - $this->logger->notice('{job} failed: {exception}', array( - 'job' => $job, - 'exception' => $e - )); + $this->logger->notice( + '{job} failed: {exception}', + [ + 'job' => $job, + 'exception' => $e, + ] + ); $this->failJob($job, $e); + return; } try { $this->resque->getStatusFactory()->forJob($job)->update(Status::STATUS_COMPLETE); } catch (JobIdException $e) { - $this->logger->warning('Could not mark job complete: no ID in payload - {exception}', array('exception' => $e)); + $this->logger->warning('Could not mark job complete: no ID in payload - {exception}', ['exception' => $e]); } $payload = $job->getPayload(); - $this->logger->notice('Finished job {queue}/{class} (ID: {id})', array( - 'queue' => $job->getQueue(), - 'class' => get_class($job), - 'id' => isset($payload['id']) ? $payload['id'] : 'unknown' - )); - - $this->logger->debug('Done with {job}', array('job' => $job)); - } - - /** - * Marks the given job as failed - * - * This happens whenever the job's perform() method emits an exception - * - * @param JobInterface|array $job - * @param Exception $exception - */ - protected function failJob($job, Exception $exception) - { - if ($job instanceof JobInterface) { - $payload = $job->getPayload(); - $queue = $job->getQueue(); - } else { - $payload = $job; - $queue = isset($job['queue']) ? $job['queue'] : null; - } - - $id = isset($job['id']) ? $job['id'] : null; - - if ($id) { - try { - $status = $this->resque->getStatusFactory()->forId($id); - $status->update(Status::STATUS_FAILED); - } catch (JobIdException $e) { - $this->logger->warning($e); - } - } // else no status to update - - $this->resque->getFailureBackend()->receiveFailure( - $payload, - $exception, - $this, - $queue + $this->logger->notice( + 'Finished job {queue}/{class} (ID: {id})', + [ + 'queue' => $job->getQueue(), + 'class' => get_class($job), + 'id' => isset($payload['id']) ? $payload['id'] : 'unknown', + ] ); - $this->getResque()->getStatistic('failed')->incr(); - $this->getStatistic('failed')->incr(); - } - - /** - * Prepares the list of queues for a job to reserved - * - * Updates/sorts/shuffles the array ahead of the call to reserve a job from one of them - * - * @return void - */ - protected function refreshQueues() - { - if ($this->refreshQueues) { - $this->queues = $this->resque->queues(); - } - - if (!$this->queues) { - if ($this->refreshQueues) { - $this->logger->info('Refreshing queues dynamically, but there are no queues yet'); - } else { - $this->logger->notice('Not listening to any queues, and dynamic queue refreshing is disabled'); - $this->shutdownNow(self::DEFAULT_SIGNO, null); - } - } - - // Each call to reserve, we check the queues in a different order - if ($this->options['shuffle_queues']) { - shuffle($this->queues); - } elseif ($this->options['sort_queues']) { - sort($this->queues); - } + $this->logger->debug('Done with {job}', ['job' => $job]); } /** @@ -465,13 +301,16 @@ protected function refreshQueues() * * @return JobInterface|null Instance of JobInterface if a job is found, null if not. */ - public function reserve() + public function reserve(): ?JobInterface { $this->refreshQueues(); - $this->logger->debug('Attempting to reserve job from {queues}', array( - 'queues' => empty($this->queues) ? 'empty queue list' : implode(', ', $this->queues) - )); + $this->logger->debug( + 'Attempting to reserve job from {queues}', + [ + 'queues' => empty($this->queues) ? 'empty queue list' : implode(', ', $this->queues), + ] + ); foreach ($this->queues as $queue) { $payload = $this->resque->pop($queue); @@ -486,11 +325,13 @@ public function reserve() $job = $this->createJobInstance($queue, $payload); } catch (ResqueException $exception) { $this->failJob($payload, $exception); + return null; } if ($job) { - $this->logger->info('Found job on {queue}', array('queue' => $queue)); + $this->logger->info('Found job on {queue}', ['queue' => $queue]); + return $job; } } @@ -498,98 +339,10 @@ public function reserve() return null; } - /** - * Attempt to fork a child process from the parent to run a job in. - * - * Return values are those of pcntl_fork(). - * - * @throws \RuntimeException - * @throws \Exception - * @return int -1 if the fork failed, 0 for the forked child, the PID of the child for the parent. - */ - private function fork() - { - if ($this->options['no_fork']) { - $this->logger->notice('Forking disabled'); - return false; - } - - if (!function_exists('pcntl_fork')) { - $this->logger->warning('Using non fork version!'); - return false; - } - - // Immediately before a fork, disconnect the redis client - $this->resque->disconnect(); - - $this->logger->notice('Forking...'); - - $pid = (int)pcntl_fork(); - - // And reconnect - $this->resque->connect(); - - if ($pid === -1) { - throw new RuntimeException('Unable to fork child worker.'); - } - - return $pid; - } - - /** - * Perform necessary actions to start a worker. - */ - protected function startup() - { - $this->registerSigHandlers(); - $this->pruneDeadWorkers(); - $this->register(); - } - - /** - * On supported systems (with the PECL proctitle module installed), update - * the name of the currently running process to indicate the current state - * of a worker. - * - * @param string $status The updated process title. - */ - protected function updateProcLine($status) - { - if (function_exists('setproctitle')) { - setproctitle('resque-' . Version::VERSION . ': ' . $status); - } - } - - /** - * Register signal handlers that a worker should respond to. - * - * TERM: Shutdown immediately and stop processing jobs. - * INT: Shutdown immediately and stop processing jobs. - * QUIT: Shutdown after the current job finishes processing. - * USR1: Kill the forked child immediately and continue processing jobs. - */ - protected function registerSigHandlers() - { - if (!function_exists('pcntl_signal')) { - $this->logger->warning('Cannot register signal handlers'); - return; - } - - declare(ticks = 1); - pcntl_signal(SIGTERM, array($this, 'shutDownNow')); - pcntl_signal(SIGINT, array($this, 'shutDownNow')); - pcntl_signal(SIGQUIT, array($this, 'shutdown')); - pcntl_signal(SIGUSR1, array($this, 'killChild')); - pcntl_signal(SIGUSR2, array($this, 'pauseProcessing')); - pcntl_signal(SIGCONT, array($this, 'unPauseProcessing')); - pcntl_signal(SIGPIPE, array($this, 'reestablishRedisConnection')); - - $this->logger->notice('Registered signals'); - } - /** * Signal handler callback for USR2, pauses processing of new jobs. - * @param int $signo + * + * @param int $signo * @param mixed $signinfo */ public function pauseProcessing($signo, $signinfo) @@ -601,7 +354,8 @@ public function pauseProcessing($signo, $signinfo) /** * Signal handler callback for CONT, resumes worker allowing it to pick * up new jobs. - * @param int $signo + * + * @param int $signo * @param mixed $signinfo */ public function unPauseProcessing($signo, $signinfo) @@ -613,7 +367,8 @@ public function unPauseProcessing($signo, $signinfo) /** * Signal handler for SIGPIPE, in the event the redis connection has gone away. * Attempts to reconnect to redis, or raises an Exception. - * @param int $signo + * + * @param int $signo * @param mixed $signinfo */ public function reestablishRedisConnection($signo, $signinfo) @@ -625,7 +380,8 @@ public function reestablishRedisConnection($signo, $signinfo) /** * Schedule a worker for shutdown. Will finish processing the current job * and when the timeout interval is reached, the worker will shut down. - * @param int $signo + * + * @param int $signo * @param mixed $signinfo */ public function shutdown($signo, $signinfo) @@ -637,7 +393,8 @@ public function shutdown($signo, $signinfo) /** * Force an immediate shutdown of the worker, killing any child jobs * currently running. - * @param int $signo + * + * @param int $signo * @param mixed $signinfo */ public function shutdownNow($signo, $signinfo) @@ -649,17 +406,19 @@ public function shutdownNow($signo, $signinfo) /** * Kill a forked child job immediately. The job it is processing will not * be completed. - * @param int $signo + * + * @param int $signo * @param mixed $signinfo */ public function killChild($signo, $signinfo) { if (!$this->child) { $this->logger->notice('No child to kill.'); + return; } - $this->logger->notice('Killing child at {pid}', array('pid' => $this->child)); + $this->logger->notice('Killing child at {pid}', ['pid' => $this->child]); $command = escapeshellcmd($this->options['ps']); @@ -685,7 +444,7 @@ public function killChild($signo, $signinfo) * server may have been killed and the Resque workers did not die gracefully * and therefore leave state information in Redis. */ - public function pruneDeadWorkers() + public function pruneDeadWorkers(): void { $pids = $this->resque->getWorkerPids(); $ids = $this->resque->getWorkerIds(); @@ -694,7 +453,7 @@ public function pruneDeadWorkers() $worker = clone $this; $worker->setId($id); - list($host, $pid) = $worker->getLocation(); + [$host, $pid] = $worker->getLocation(); // Ignore workers on other hosts if ($host != $this->options['server_name']) { @@ -711,7 +470,7 @@ public function pruneDeadWorkers() continue; } - $this->logger->warning('Pruning dead worker: {id}', array('id' => $id)); + $this->logger->warning('Pruning dead worker: {id}', ['id' => $id]); $worker->unregister(); } } @@ -719,7 +478,7 @@ public function pruneDeadWorkers() /** * Gets the ID of this worker */ - public function getId() + public function getId(): string { return $this->id; } @@ -727,7 +486,7 @@ public function getId() /** * Register this worker in Redis. */ - public function register() + public function register(): void { $this->logger->debug('Registering worker ' . $this->getId()); $this->resque->getClient()->sadd($this->resque->getKey(Resque::WORKERS_KEY), $this->getId()); @@ -737,7 +496,7 @@ public function register() /** * Unregister this worker in Redis. (shutdown etc) */ - public function unregister() + public function unregister(): void { $this->logger->debug('Unregistering worker ' . $this->getId()); @@ -759,7 +518,7 @@ public function unregister() * * @param JobInterface $job Job instance we're working on. */ - public function workingOn(JobInterface $job) + public function workingOn(JobInterface $job): void { if (method_exists($job, 'setWorker')) { $job->setWorker($this); @@ -769,11 +528,13 @@ public function workingOn(JobInterface $job) $this->resque->getStatusFactory()->forJob($job)->update(Status::STATUS_RUNNING); - $data = json_encode(array( - 'queue' => $job->getQueue(), - 'run_at' => strftime('%a %b %d %H:%M:%S %Z %Y'), - 'payload' => $job->getPayload() - )); + $data = json_encode( + [ + 'queue' => $job->getQueue(), + 'run_at' => strftime('%a %b %d %H:%M:%S %Z %Y'), + 'payload' => $job->getPayload(), + ] + ); $this->resque->getClient()->set($this->getJobKey(), $data); } @@ -782,7 +543,7 @@ public function workingOn(JobInterface $job) * Notify Redis that we've finished working on a job, clearing the working * state and incrementing the job stats. */ - public function doneWorking() + public function doneWorking(): void { $this->currentJob = null; $this->resque->getStatistic('processed')->incr(); @@ -794,34 +555,23 @@ public function doneWorking() * Generate a string representation of this worker. * * @return string String identifier for this worker instance. - * @deprecated Just use getId(). Explicit, simpler, less magic. */ public function __toString() { return $this->id; } - /** - * Gets the key for where this worker will store its active job - * - * @return string - */ - protected function getJobKey() - { - return $this->getResque()->getKey('worker:' . $this->getId()); - } - /** * Return an object describing the job this worker is currently working on. * * @return array Object with details of current job. */ - public function job() + public function job(): array { $job = $this->resque->getClient()->get($this->getJobKey()); if (!$job) { - return array(); + return []; } else { return json_decode($job, true); } @@ -831,21 +581,317 @@ public function job() * Get a statistic belonging to this worker. * * @param string $name Statistic to fetch. + * * @return Statistic */ - public function getStatistic($name) + public function getStatistic(string $name) { - return new Statistic($this->resque, $name. ':' . $this->getId()); + return new Statistic($this->resque, $name . ':' . $this->getId()); } /** * Inject the logging object into the worker * * @param LoggerInterface $logger + * * @return void */ public function setLogger(LoggerInterface $logger) { $this->logger = $logger; } + + /** + * Configures options for the worker + * + * @param array $options + * Including + * Worker identification + * - server_name => string, default is FQDN hostname + * - pid => int, default is current PID + * - id_format => string, suitable for sprintf + * - id_location_preg => string, Perl compat regex, gets hostname and PID + * out of worker ID + * - shuffle_queues => bool, whether to shuffle the queues on reserve, so we evenly + * check all queues + * - sort_queues => bool, whether to check the queues in alphabetical order + * (mutually exclusive with shuffle_queues) + * - no_fork => bool, whether to suppress fork if available + */ + protected function configure(array $options): void + { + $this->options = array_merge( + [ + 'server_name' => null, + 'pid' => null, + 'ps' => '/bin/ps', + 'ps_args' => ['-o', 'pid,state', '-p'], + 'id_format' => '%s:%d:%s', + 'id_location_preg' => '/^([^:]+?):([0-9]+):/', + 'shuffle_queues' => true, + 'sort_queues' => false, + 'pre_perform' => false, + 'post_perform' => false, + 'no_fork' => false, + ], + $options + ); + + if (!$this->options['server_name']) { + $this->options['server_name'] = function_exists('gethostname') ? gethostname() : php_uname('n'); + } + + if (!$this->options['pid']) { + $this->options['pid'] = getmypid(); + } + } + + /** + * Configures the ID of this worker + */ + protected function configureId(): void + { + $this->id = sprintf( + $this->options['id_format'], + $this->options['server_name'], + $this->options['pid'], + implode(',', $this->queues) + ); + } + + /** + * @return Client\ClientInterface //fake interface! instances will not actually implement this + */ + protected function getClient() + { + return $this->resque->getClient(); + } + + /** + * @throws JobInvalidException + * @throws JobClassNotFoundException + */ + protected function createJobInstance(string $queue, array $payload): JobInterface + { + if (!class_exists($payload['class'])) { + throw new JobClassNotFoundException( + 'Could not find job class ' . $payload['class'] . '.' + ); + } + + if (!is_subclass_of($payload['class'], 'Resque\JobInterface')) { + throw new JobInvalidException(); + } + + $job = new $payload['class']($queue, $payload); + + if (method_exists($job, 'setResque')) { + $job->setResque($this->resque); + } + + return $job; + } + + /** + * Gets the key for where this worker will store its active job + */ + protected function getJobKey(): string + { + return $this->getResque()->getKey('worker:' . $this->getId()); + } + + /** + * Parses a hostname and PID out of a string worker ID + * + * If you change the format of the ID, you should also change the definition + * of this method. + * + * This method *always* parses the ID of the worker, rather than figuring out + * the current processes' PID/hostname. This means you can use setId() to + * interrogate the properties of other workers given their ID. + * + * @return int[] + * @throws Exception + */ + protected function getLocation(): array + { + $matches = []; + + if (!preg_match($this->options['id_location_preg'], $this->getId(), $matches)) { + throw new Exception('Incompatible ID format: unable to determine worker location'); + } + + if (!isset($matches[1]) || !$matches[1]) { + throw new Exception('Invalid ID: invalid hostname'); + } + + if (!isset($matches[2]) || !$matches[2] || !is_numeric($matches[2])) { + throw new Exception('Invalid ID: invalid PID'); + } + + return [$matches[1], (int)$matches[2]]; + } + + /** + * Marks the given job as failed + * + * This happens whenever the job's perform() method emits an exception + * + * @param JobInterface|array $job + * @param Exception $exception + */ + protected function failJob($job, Exception $exception) + { + if ($job instanceof JobInterface) { + $payload = $job->getPayload(); + $queue = $job->getQueue(); + } else { + $payload = $job; + $queue = isset($job['queue']) ? $job['queue'] : null; + } + + $id = isset($job['id']) ? $job['id'] : null; + + if ($id) { + try { + $status = $this->resque->getStatusFactory()->forId($id); + $status->update(Status::STATUS_FAILED); + } catch (JobIdException $e) { + $this->logger->warning($e); + } + } // else no status to update + + $this->resque->getFailureBackend()->receiveFailure( + $payload, + $exception, + $this, + $queue + ); + + $this->getResque()->getStatistic('failed')->incr(); + $this->getStatistic('failed')->incr(); + } + + /** + * Prepares the list of queues for a job to reserved + * + * Updates/sorts/shuffles the array ahead of the call to reserve a job from one of them + */ + protected function refreshQueues(): void + { + if ($this->refreshQueues) { + $this->queues = $this->resque->queues(); + } + + if (!$this->queues) { + if ($this->refreshQueues) { + $this->logger->info('Refreshing queues dynamically, but there are no queues yet'); + } else { + $this->logger->notice('Not listening to any queues, and dynamic queue refreshing is disabled'); + $this->shutdownNow(self::DEFAULT_SIGNO, null); + } + } + + // Each call to reserve, we check the queues in a different order + if ($this->options['shuffle_queues']) { + shuffle($this->queues); + } elseif ($this->options['sort_queues']) { + sort($this->queues); + } + } + + /** + * Perform necessary actions to start a worker. + */ + protected function startup(): void + { + $this->registerSigHandlers(); + $this->pruneDeadWorkers(); + $this->register(); + } + + /** + * On supported systems (with the PECL proctitle module installed), update + * the name of the currently running process to indicate the current state + * of a worker. + * + * @param string $status The updated process title. + */ + protected function updateProcLine(string $status): void + { + if (function_exists('setproctitle')) { + setproctitle('resque-' . Version::VERSION . ': ' . $status); + } + } + + /** + * Register signal handlers that a worker should respond to. + * + * TERM: Shutdown immediately and stop processing jobs. + * INT: Shutdown immediately and stop processing jobs. + * QUIT: Shutdown after the current job finishes processing. + * USR1: Kill the forked child immediately and continue processing jobs. + * USR2: Pause processing jobs. + * CONT: Resume progressing jobs. + * PIPE: Reestablish the redis connection to resolve conenction issues. + */ + protected function registerSigHandlers(): void + { + if (!function_exists('pcntl_signal')) { + $this->logger->warning('Cannot register signal handlers'); + + return; + } + + declare(ticks=1); + pcntl_signal(SIGTERM, [$this, 'shutDownNow']); + pcntl_signal(SIGINT, [$this, 'shutDownNow']); + pcntl_signal(SIGQUIT, [$this, 'shutdown']); + pcntl_signal(SIGUSR1, [$this, 'killChild']); + pcntl_signal(SIGUSR2, [$this, 'pauseProcessing']); + pcntl_signal(SIGCONT, [$this, 'unPauseProcessing']); + pcntl_signal(SIGPIPE, [$this, 'reestablishRedisConnection']); + + $this->logger->notice('Registered signals'); + } + + /** + * Attempt to fork a child process from the parent to run a job in. + * + * Return values are those of pcntl_fork(). + * + * @return bool|int -1 if the fork failed, 0 for the forked child, the PID of the child for the parent. + * @throws Exception + * @throws RuntimeException + */ + private function fork() + { + if ($this->options['no_fork']) { + $this->logger->notice('Forking disabled'); + + return false; + } + + if (!function_exists('pcntl_fork')) { + $this->logger->warning('Using non fork version!'); + + return false; + } + + // Immediately before a fork, disconnect the redis client + $this->resque->disconnect(); + + $this->logger->notice('Forking...'); + + $pid = (int)pcntl_fork(); + + // And reconnect + $this->resque->connect(); + + if ($pid === -1) { + throw new RuntimeException('Unable to fork child worker.'); + } + + return $pid; + } } diff --git a/test/AbstractJobTest.php b/test/AbstractJobTest.php index 1e49560..e36828a 100644 --- a/test/AbstractJobTest.php +++ b/test/AbstractJobTest.php @@ -1,88 +1,84 @@ + * @author Chris Boulton * @license http://www.opensource.org/licenses/mit-license.php */ class AbstractJobTest extends Test { - /** - * @var Worker - */ + private const QUEUE_JOBS = 'jobs'; + + /** @var Worker */ protected $worker; - public function setUp() + public function setUp(): void { parent::setUp(); // Register a worker to test with - $this->worker = new Worker($this->resque, 'jobs'); + $this->worker = new Worker($this->resque, self::QUEUE_JOBS); $this->worker->setLogger($this->logger); $this->worker->register(); } - public function testJobCanBeQueued() + public function testJobCanBeQueued(): void { - $this->assertTrue((bool)$this->resque->enqueue('jobs', 'Resque\Test\Job')); + $this->assertNotEmpty($this->resque->enqueue(self::QUEUE_JOBS, Job::class)); } - public function testQueuedJobCanBeReserved() + public function testQueuedJobCanBeReserved(): void { - $this->resque->enqueue('jobs', 'Resque\Test\Job'); + $this->resque->enqueue(self::QUEUE_JOBS, Job::class); - $worker = $this->getWorker('jobs'); + $worker = $this->getWorker(self::QUEUE_JOBS); - $job = $worker->reserve('jobs'); + $job = $worker->reserve(); - if ($job == false) { + if (!$job) { $this->fail('Job could not be reserved.'); } - $this->assertEquals('jobs', $job->getQueue()); - $this->assertEquals('Resque\Test\Job', $job['class']); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testObjectArgumentsCannotBePassedToJob() - { - $args = new \stdClass; - $args->test = 'somevalue'; - $this->resque->enqueue('jobs', 'Resque\Test\Job', $args); + $this->assertEquals(self::QUEUE_JOBS, $job->getQueue()); + $this->assertEquals(Job::class, $job['class']); } - public function testFailedJobExceptionsAreCaught() + public function testFailedJobExceptionsAreCaught(): void { - $this->resque->clearQueue('jobs'); - - $job = new FailingJob('jobs', array( - 'class' => 'Resque\Test\FailingJob', - 'args' => null, - 'id' => 'failing_test_job' - )); + $this->resque->clearQueue(self::QUEUE_JOBS); + + $job = new FailingJob( + self::QUEUE_JOBS, + [ + 'class' => FailingJob::class, + 'args' => null, + 'id' => 'failing_test_job', + ] + ); $job->setResque($this->resque); $this->worker->perform($job); - $failed = new Statistic($this->resque, 'failed'); + $failed = new Statistic($this->resque, 'failed'); $workerFailed = new Statistic($this->resque, 'failed:' . (string)$this->worker); $this->assertEquals(1, $failed->get()); $this->assertEquals(1, $workerFailed->get()); } - public function testInvalidJobReservesNull() + public function testInvalidJobReservesNull(): void { - $this->resque->enqueue('jobs', 'Resque\Test\NoPerformJob'); + $this->resque->enqueue(self::QUEUE_JOBS, NoPerformJob::class); $job = $this->worker->reserve(); $this->assertNull($job); } diff --git a/test/ClientTest.php b/test/ClientTest.php index d6760f1..de45a93 100644 --- a/test/ClientTest.php +++ b/test/ClientTest.php @@ -1,17 +1,19 @@ 'abc', 'two' => 'def', 'three' => 123, 'four' => 1.0 / 3 - ); + ]; $success = $this->redis->hmset('some_other_key', $values); $hash = $this->redis->hgetall('some_other_key'); diff --git a/test/Job/StatusTest.php b/test/Job/StatusTest.php index 171b046..3a0a72d 100644 --- a/test/Job/StatusTest.php +++ b/test/Job/StatusTest.php @@ -1,54 +1,53 @@ + * @author Chris Boulton * @license http://www.opensource.org/licenses/mit-license.php */ class StatusTest extends Test { - public function tearDown() + public function tearDown(): void { parent::tearDown(); $this->resque->clearQueue('jobs'); } - public function testConstructor() + public function testConstructor(): void { $status = new Status(uniqid(__CLASS__, true), $this->resque); $this->assertNotNull($status); } - public function testJobStatusCanBeTracked() + public function testJobStatusCanBeTracked(): void { $this->resque->clearQueue('jobs'); - $token = $this->resque->enqueue('jobs', 'Resque\Test\Job', null, true); + $token = $this->resque->enqueue('jobs', Job::class, null, true); $status = new Status($token, $this->resque); $this->assertTrue($status->isTracking()); } - public function testJobStatusIsReturnedViaJobInstance() + public function testJobStatusIsReturnedViaJobInstance(): void { $this->resque->clearQueue('jobs'); - $token = $this->resque->enqueue('jobs', 'Resque\Test\Job', null, true); + $this->resque->enqueue('jobs', Job::class, null, true); $worker = $this->getWorker('jobs'); - $job = $worker->reserve(); + $job = $worker->reserve(); if (!$job) { $this->fail('Could not get job'); @@ -60,28 +59,28 @@ public function testJobStatusIsReturnedViaJobInstance() $this->assertEquals(Status::STATUS_WAITING, $status); } - public function testQueuedJobReturnsQueuedStatus() + public function testQueuedJobReturnsQueuedStatus(): void { - $token = $this->resque->enqueue('jobs', 'Resque\Test\Job', null, true); + $token = $this->resque->enqueue('jobs', Job::class, null, true); $status = new Status($token, $this->resque); $this->assertEquals(Status::STATUS_WAITING, $status->get()); } - public function testQueuedJobReturnsCreatedAndUpdatedKeys() + public function testQueuedJobReturnsCreatedAndUpdatedKeys(): void { - $token = $this->resque->enqueue('jobs', 'Resque\Test\Job', null, true); + $token = $this->resque->enqueue('jobs', Job::class, null, true); $status = new Status($token, $this->resque); $this->assertGreaterThan(0, $status->getCreated()); $this->assertGreaterThan(0, $status->getUpdated()); } - public function testStartingQueuedJobUpdatesUpdatedAtStatus() + public function testStartingQueuedJobUpdatesUpdatedAtStatus(): void { $this->resque->clearQueue('jobs'); - $token = $this->resque->enqueue('jobs', 'Resque\Test\Job', null, true); + $token = $this->resque->enqueue('jobs', Job::class, null, true); - $status = new Status($token, $this->resque); + $status = new Status($token, $this->resque); $old_created = $status->getCreated(); $old_updated = $status->getUpdated(); @@ -103,11 +102,11 @@ public function testStartingQueuedJobUpdatesUpdatedAtStatus() $this->assertGreaterThan($old_updated, $status->getUpdated()); } - public function testRunningJobReturnsRunningStatus() + public function testRunningJobReturnsRunningStatus(): void { $this->resque->clearQueue('jobs'); - $token = $this->resque->enqueue('jobs', 'Resque\Test\Job', null, true); + $token = $this->resque->enqueue('jobs', Job::class, null, true); $worker = $this->getWorker('jobs'); @@ -123,35 +122,26 @@ public function testRunningJobReturnsRunningStatus() $this->assertEquals(Status::STATUS_RUNNING, $status->get()); } - public function testFailedJobReturnsFailedStatus() + public function testFailedJobReturnsFailedStatus(): void { - $pid = getmypid(); - $this->resque->clearQueue('jobs'); - $token = $this->resque->enqueue('jobs', 'Resque\Test\FailingJob', null, true); + $token = $this->resque->enqueue('jobs', FailingJob::class, null, true); $worker = $this->getWorker('jobs'); - - $status = new Status($token, $this->resque); - $before = $status->get(); - - $pid2 = getmypid(); - $worker->work(0); - $pid3 = getmypid(); $status = new Status($token, $this->resque); - $after = $status->get(); + $after = $status->get(); $this->assertEquals(Status::STATUS_FAILED, $after); } - public function testCompletedJobIsRemovedAndStatusUnavailable() + public function testCompletedJobIsRemovedAndStatusUnavailable(): void { $this->resque->clearQueue('jobs'); - $token = $this->resque->enqueue('jobs', 'Resque\Test\Job', null, true); + $token = $this->resque->enqueue('jobs', Job::class, null, true); $status = new Status($token, $this->resque); $this->assertEquals(Status::STATUS_WAITING, $status->get()); @@ -162,14 +152,14 @@ public function testCompletedJobIsRemovedAndStatusUnavailable() $this->assertNull($status->get()); } - public function testStatusIsNotTrackedWhenToldNotTo() + public function testStatusIsNotTrackedWhenToldNotTo(): void { - $token = $this->resque->enqueue('jobs', 'Resque\Test\Job', null, false); + $token = $this->resque->enqueue('jobs', Job::class, null, false); $status = new Status($token, $this->resque); $this->assertFalse($status->isTracking()); } - public function testStatusTrackingCanBeStopped() + public function testStatusTrackingCanBeStopped(): void { $status = new Status('test', $this->resque); $status->create(); @@ -179,12 +169,12 @@ public function testStatusTrackingCanBeStopped() $this->assertNull($status->get()); } - public function testRecreatedJobWithTrackingStillTracksStatus() + public function testRecreatedJobWithTrackingStillTracksStatus(): void { - $worker = $this->getWorker('jobs'); - $originalToken = $this->resque->enqueue('jobs', 'Resque\Test\Job', null, true); + $worker = $this->getWorker('jobs'); + $originalToken = $this->resque->enqueue('jobs', Job::class, null, true); - /** @var Test\Job $job */ + /** @var Job $job */ $job = $worker->reserve(); if (!$job) { @@ -202,7 +192,7 @@ public function testRecreatedJobWithTrackingStillTracksStatus() $this->assertNotEquals($originalToken, $newToken); // Now check the status of the new job - /** @var Test\Job $newJob */ + /** @var Job $newJob */ $newJob = $worker->reserve(); if (!$newJob) { diff --git a/test/ResqueTest.php b/test/ResqueTest.php index 6e97e06..0f53d31 100644 --- a/test/ResqueTest.php +++ b/test/ResqueTest.php @@ -1,5 +1,7 @@ assertInternalType('array', $this->resque->getWorkerIds()); + $this->assertIsArray($this->resque->getWorkerIds()); } public function testEmptyWorkerPids() { - $this->assertInternalType('array', $this->resque->getWorkerPids()); + $this->assertIsArray($this->resque->getWorkerPids()); } } diff --git a/test/StatisticTest.php b/test/StatisticTest.php index c6033e1..34767ec 100644 --- a/test/StatisticTest.php +++ b/test/StatisticTest.php @@ -1,5 +1,7 @@ statistic = new Statistic($this->resque, __CLASS__); } - public function tearDown() + public function tearDown(): void { if ($this->statistic) { $this->statistic->clear(); @@ -33,46 +33,46 @@ public function tearDown() parent::tearDown(); } - protected function assertStatisticValueByClient($value, $message = '') + protected function assertStatisticValueByClient($value, $message = ''): void { $this->assertEquals($value, $this->redis->get('resque:stat:' . __CLASS__), $message); } - public function testStatCanBeIncremented() + public function testStatCanBeIncremented(): void { $this->statistic->incr(); $this->statistic->incr(); $this->assertStatisticValueByClient(2); } - public function testStatCanBeIncrementedByX() + public function testStatCanBeIncrementedByX(): void { $this->statistic->incr(10); $this->statistic->incr(11); $this->assertStatisticValueByClient(21); } - public function testStatCanBeDecremented() + public function testStatCanBeDecremented(): void { $this->statistic->incr(22); $this->statistic->decr(); $this->assertStatisticValueByClient(21); } - public function testStatCanBeDecrementedByX() + public function testStatCanBeDecrementedByX(): void { $this->statistic->incr(22); $this->statistic->decr(11); $this->assertStatisticValueByClient(11); } - public function testGetStatByName() + public function testGetStatByName(): void { $this->statistic->incr(100); $this->assertEquals(100, $this->statistic->get()); } - public function testGetUnknownStatReturns0() + public function testGetUnknownStatReturns0(): void { $statistic = new Statistic($this->resque, 'some_unknown_statistic'); $this->assertEquals(0, $statistic->get()); diff --git a/test/Test.php b/test/Test.php index ded453c..a75c2ca 100644 --- a/test/Test.php +++ b/test/Test.php @@ -1,45 +1,38 @@ redis = self::$settings->getClient(); @@ -51,7 +44,7 @@ public function setUp() $this->resque->setLogger($this->logger); } - public function tearDown() + public function tearDown(): void { if ($this->redis) { $this->redis->flushdb(); @@ -63,7 +56,7 @@ public function tearDown() } } - protected function getWorker($queues) + protected function getWorker($queues): Worker { $worker = new Worker($this->resque, $queues); $worker->setLogger($this->logger); diff --git a/test/Test/FailingJob.php b/test/Test/FailingJob.php index 106ea2a..0a3a879 100644 --- a/test/Test/FailingJob.php +++ b/test/Test/FailingJob.php @@ -1,7 +1,10 @@ performed = true; } /** - * @return int See Resque\Job\Status::STATUS_* + * @see Status */ - public function getStatusCode() + public function getStatusCode(): ?int { return $this->getStatus()->get(); } - - /** - * @return string ID of the recreated job - */ - public function recreate() - { - return parent::recreate(); - } - - /** - * @return \Resque\Job\Status - */ - public function getStatus() - { - return parent::getStatus(); - } } diff --git a/test/Test/MinimalJob.php b/test/Test/MinimalJob.php index ca8d07f..bc4f8bb 100644 --- a/test/Test/MinimalJob.php +++ b/test/Test/MinimalJob.php @@ -1,5 +1,7 @@ performed = true; } + + public function getQueue(): string + { + return ''; + } + + public function getPayload(): array + { + return []; + } } diff --git a/test/Test/NoPerformJob.php b/test/Test/NoPerformJob.php index ace2c0f..b201a8c 100644 --- a/test/Test/NoPerformJob.php +++ b/test/Test/NoPerformJob.php @@ -1,5 +1,7 @@ testPid = getmypid(); @@ -41,7 +62,7 @@ protected function fromDefaults() public function fromEnvironment() { - $env = array( + $env = [ 'client_type' => 'clientType', 'host' => 'host', 'port' => 'port', @@ -49,8 +70,8 @@ public function fromEnvironment() 'build_dir' => 'buildDir', 'run' => 'run', 'db' => 'db', - 'prefix' => 'prefix' - ); + 'prefix' => 'prefix', + ]; foreach ($env as $var => $setting) { $name = 'RESQUE_' . strtoupper($var); @@ -61,28 +82,28 @@ public function fromEnvironment() } } - public function setBuildDir($dir) + public function setBuildDir(string $dir) { $this->buildDir = $dir; } - public function startRedis() + public function startRedis(): void { $this->dumpRedisConfig(); $this->registerShutdown(); - $this->logger->notice('Starting redis server in {buildDir}', array('buildDir' => $this->buildDir)); + $this->logger->notice('Starting redis server in {buildDir}', ['buildDir' => $this->buildDir]); exec('cd ' . $this->buildDir . '; redis-server ' . $this->buildDir . '/redis.conf', $output, $return); usleep(500000); if ($return != 0) { - throw new \RuntimeException('Cannot start redis-server'); + throw new RuntimeException('Cannot start redis-server'); } } - protected function getRedisConfig() + protected function getRedisConfig(): array { - return array( + return [ 'daemonize' => 'yes', 'pidfile' => './redis.pid', 'port' => $this->port, @@ -91,45 +112,50 @@ protected function getRedisConfig() 'dbfilename' => 'dump.rdb', 'dir' => $this->buildDir, 'loglevel' => 'debug', - 'logfile' => './redis.log' - ); + 'logfile' => './redis.log', + ]; } /** - * @return ClientInterface - * @throws \InvalidArgumentException + * @return ClientInterface //fake interface to represent expected methods on the actually returned clients + * @throws InvalidArgumentException */ public function getClient() { switch ($this->clientType) { case 'predis': - return new \Predis\Client(array( - 'host' => $this->host, - 'port' => $this->port, - 'db' => $this->db, - 'prefix' => $this->prefix - )); + return new PredisClient( + [ + 'host' => $this->host, + 'port' => $this->port, + 'db' => $this->db, + 'prefix' => $this->prefix, + ] + ); case 'phpiredis': - return new \Predis\Client(array( - 'host' => $this->host, - 'port' => $this->port, - 'db' => $this->db, - 'prefix' => $this->prefix - ), array( - 'tcp' => 'Predis\Connection\PhpiredisStreamConnection', - 'unix' => 'Predis\Connection\PhpiredisSocketConnection' - )); + return new PredisClient( + [ + 'host' => $this->host, + 'port' => $this->port, + 'db' => $this->db, + 'prefix' => $this->prefix, + ], [ + 'tcp' => 'Predis\Connection\PhpiredisStreamConnection', + 'unix' => 'Predis\Connection\PhpiredisSocketConnection', + ] + ); case 'credis': case 'phpredis': - $client = new \Resque\Client\CredisClient($this->host, $this->port); + $client = new CredisClient($this->host, $this->port); $client->setCloseOnDestruct(false); + return $client; default: - throw new \InvalidArgumentException('Invalid or unknown client type: ' . $this->clientType); + throw new InvalidArgumentException('Invalid or unknown client type: ' . $this->clientType); } } - public function checkBuildDir() + public function checkBuildDir(): void { if (!is_dir($this->buildDir)) { mkdir($this->buildDir); @@ -140,7 +166,7 @@ public function checkBuildDir() } } - protected function dumpRedisConfig() + protected function dumpRedisConfig(): void { $file = $this->buildDir . '/redis.conf'; $conf = ''; @@ -149,36 +175,45 @@ protected function dumpRedisConfig() $conf .= "$name $value\n"; } - $this->logger->info('Dumping redis config {config} to {file}', array('file' => $file, 'config' => $conf)); + $this->logger->info('Dumping redis config {config} to {file}', ['file' => $file, 'config' => $conf]); file_put_contents($file, $conf); } - // Override INT and TERM signals, so they do a clean shutdown and also - // clean up redis-server as well. - public function catchSignals() + /** + * Override INT and TERM signals, so they do a clean shutdown and also + * clean up redis-server as well. + */ + public function catchSignals(): void { if (function_exists('pcntl_signal')) { $self = $this; - pcntl_signal(SIGINT, function () use ($self) { - $self->logger->debug('SIGINT received'); - exit; - }); - - pcntl_signal(SIGTERM, function () use ($self) { - $self->logger->debug('SIGTERM received'); - exit; - }); + pcntl_signal( + SIGINT, + function () use ($self) { + $self->logger->debug('SIGINT received'); + exit; + } + ); + + pcntl_signal( + SIGTERM, + function () use ($self) { + $self->logger->debug('SIGTERM received'); + exit; + } + ); } } - public function killRedis() + public function killRedis(): void { $pid = getmypid(); - $this->logger->notice('Attempting to kill redis from {pid}', array('pid' => $pid)); + $this->logger->notice('Attempting to kill redis from {pid}', ['pid' => $pid]); if ($pid === null || $this->testPid !== $pid) { $this->logger->warning('Refusing to kill redis from forked worker'); + return; // don't kill from a forked worker } @@ -186,9 +221,9 @@ public function killRedis() if (file_exists($pidFile)) { $pid = trim(file_get_contents($pidFile)); - posix_kill((int) $pid, 9); + posix_kill((int)$pid, 9); - if(is_file($pidFile)) { + if (is_file($pidFile)) { unlink($pidFile); } } @@ -200,27 +235,21 @@ public function killRedis() } } - protected function registerShutdown() + protected function registerShutdown(): void { $this->logger->info('Registered shutdown function'); - register_shutdown_function(array($this, 'killRedis')); + register_shutdown_function([$this, 'killRedis']); } /** * Sets a logger instance on the object - * - * @param LoggerInterface $logger - * @return null */ public function setLogger(LoggerInterface $logger) { $this->logger = $logger; } - /** - * @return LoggerInterface - */ - public function getLogger() + public function getLogger(): LoggerInterface { return $this->logger; } @@ -232,10 +261,10 @@ public function getLogger() */ public function dumpConfig() { - $file = $this->buildDir . \DIRECTORY_SEPARATOR . 'settings.json'; + $file = $this->buildDir . DIRECTORY_SEPARATOR . 'settings.json'; $config = json_encode(get_object_vars($this), JSON_PRETTY_PRINT); - $this->logger->info('Dumping test config {config} to {file}', array('file' => $file, 'config' => $config)); + $this->logger->info('Dumping test config {config} to {file}', ['file' => $file, 'config' => $config]); file_put_contents($file, $config); } } diff --git a/test/WorkerTest.php b/test/WorkerTest.php index 734a604..ed92540 100644 --- a/test/WorkerTest.php +++ b/test/WorkerTest.php @@ -1,18 +1,25 @@ + * @author Chris Boulton * @license http://www.opensource.org/licenses/mit-license.php */ class WorkerTest extends Test { - public function testWorkerRegistersInList() + private const QUEUE_JOBS = 'jobs'; + private const MESSAGE_CANNOT_RESERVE_JOB = 'Cannot reserve job'; + private const MESSAGE_JOB_FROM_VALID_QUEUES = 'Job from valid queues'; + + public function testWorkerRegistersInList(): void { $worker = $this->getWorker('*'); @@ -20,19 +27,20 @@ public function testWorkerRegistersInList() $this->assertTrue((bool)$this->redis->sismember('resque:workers', (string)$worker)); } - public function testGetAllWorkers() + public function testGetAllWorkers(): void { - $num = 3; + $workerCount = 3; + // Register a few workers - for($i = 0; $i < $num; ++$i) { - $worker = $this->getWorker('queue_' . $i); + for ($i = 0; $i < $workerCount; ++$i) { + $this->getWorker('queue_' . $i); } // Now try to get them - $this->assertEquals($num, count($this->resque->getWorkerIds())); + $this->assertEquals($workerCount, count($this->resque->getWorkerIds())); } - public function testGetWorkerById() + public function testGetWorkerById(): void { $worker = $this->getWorker('*'); @@ -42,25 +50,24 @@ public function testGetWorkerById() $this->assertEquals((string)$worker, (string)$newWorker); } - - public function testWorkerCanUnregister() + public function testWorkerCanUnregister(): void { $worker = $this->getWorker('*'); $worker->unregister(); $this->assertFalse($this->resque->workerExists((string)$worker)); - $this->assertEquals(array(), $this->resque->getWorkerIds()); - $this->assertEquals(array(), $this->redis->smembers('resque:workers')); + $this->assertEquals([], $this->resque->getWorkerIds()); + $this->assertEquals([], $this->redis->smembers('resque:workers')); } - public function testPausedWorkerDoesNotPickUpJobs() + public function testPausedWorkerDoesNotPickUpJobs(): void { - $this->resque->clearQueue('jobs'); + $this->resque->clearQueue(self::QUEUE_JOBS); $worker = $this->getWorker('*'); $worker->pauseProcessing(Worker::DEFAULT_SIGNO, null); - $this->resque->enqueue('jobs', 'Resque\Test\Job'); + $this->resque->enqueue(self::QUEUE_JOBS, Job::class); $worker->work(0); $worker->work(0); @@ -68,14 +75,14 @@ public function testPausedWorkerDoesNotPickUpJobs() $this->assertEquals(0, $worker->getStatistic('processed')->get()); } - public function testResumedWorkerPicksUpJobs() + public function testResumedWorkerPicksUpJobs(): void { - $this->resque->clearQueue('jobs'); + $this->resque->clearQueue(self::QUEUE_JOBS); $worker = $this->getWorker('*'); $worker->pauseProcessing(Worker::DEFAULT_SIGNO, 'pauseProcessing'); - $this->resque->enqueue('jobs', 'Resque\Test\Job'); + $this->resque->enqueue(self::QUEUE_JOBS, Job::class); $worker->work(0); $this->assertEquals(0, $worker->getStatistic('processed')->get()); @@ -86,89 +93,82 @@ public function testResumedWorkerPicksUpJobs() $this->assertEquals(1, $worker->getStatistic('processed')->get()); } - protected function clearQueues(array $queues) + public function testWorkerCanWorkOverMultipleQueues(): void { - foreach ($queues as $queue) { - $this->resque->clearQueue($queue); - } - } - - public function testWorkerCanWorkOverMultipleQueues() - { - $queues = array( + $queues = [ 'queue1', - 'queue2' - ); + 'queue2', + ]; $this->clearQueues($queues); $worker = $this->getWorker($queues); - $this->resque->enqueue($queues[0], 'Resque\Test\Job'); - $this->resque->enqueue($queues[1], 'Resque\Test\Job'); + $this->resque->enqueue($queues[0], Job::class); + $this->resque->enqueue($queues[1], Job::class); $job = $worker->reserve(); if (!$job) { - $this->fail('Cannot reserve job'); + $this->fail(self::MESSAGE_CANNOT_RESERVE_JOB); } - $this->assertTrue(in_array($job->getQueue(), $queues), 'Job from valid queues'); + $this->assertContains($job->getQueue(), $queues, self::MESSAGE_JOB_FROM_VALID_QUEUES); $job = $worker->reserve(); if (!$job) { - $this->fail('Cannot reserve job'); + $this->fail(self::MESSAGE_CANNOT_RESERVE_JOB); } - $this->assertTrue(in_array($job->getQueue(), $queues), 'Job from valid queues'); + $this->assertContains($job->getQueue(), $queues, self::MESSAGE_JOB_FROM_VALID_QUEUES); } - public function testWildcardQueueWorkerWorksAllQueues() + public function testWildcardQueueWorkerWorksAllQueues(): void { - $queues = array( + $queues = [ 'queue1', - 'queue2' - ); + 'queue2', + ]; $this->clearQueues($queues); $worker = $this->getWorker('*'); - $this->resque->enqueue($queues[0], 'Resque\Test\Job'); - $this->resque->enqueue($queues[1], 'Resque\Test\Job'); + $this->resque->enqueue($queues[0], Job::class); + $this->resque->enqueue($queues[1], Job::class); $job = $worker->reserve(); if (!$job) { - $this->fail('Cannot reserve job'); + $this->fail(self::MESSAGE_CANNOT_RESERVE_JOB); } - $this->assertTrue(in_array($job->getQueue(), $queues), 'Job from valid queues'); + $this->assertContains($job->getQueue(), $queues, self::MESSAGE_JOB_FROM_VALID_QUEUES); $job = $worker->reserve(); if (!$job) { - $this->fail('Cannot reserve job'); + $this->fail(self::MESSAGE_CANNOT_RESERVE_JOB); } - $this->assertTrue(in_array($job->getQueue(), $queues), 'Job from valid queues'); + $this->assertContains($job->getQueue(), $queues, self::MESSAGE_JOB_FROM_VALID_QUEUES); } - public function testWorkerDoesNotWorkOnUnknownQueues() + public function testWorkerDoesNotWorkOnUnknownQueues(): void { $worker = $this->getWorker('queue1'); - $this->resque->enqueue('queue2', 'Resque\Test\Job'); + $this->resque->enqueue('queue2', Job::class); $this->assertNull($worker->reserve()); } - public function testWorkerClearsItsStatusWhenNotWorking() + public function testWorkerClearsItsStatusWhenNotWorking(): void { - $this->resque->enqueue('jobs', 'Resque\Test\Job'); - $worker = $this->getWorker('jobs'); - $job = $worker->reserve(); + $this->resque->enqueue(self::QUEUE_JOBS, Job::class); + $worker = $this->getWorker(self::QUEUE_JOBS); + $job = $worker->reserve(); if (!$job) { $this->fail('Could not reserve job'); @@ -176,19 +176,19 @@ public function testWorkerClearsItsStatusWhenNotWorking() $worker->workingOn($job); $worker->doneWorking(); - $this->assertEquals(array(), $worker->job()); + $this->assertEquals([], $worker->job()); } - public function testWorkerRecordsWhatItIsWorkingOn() + public function testWorkerRecordsWhatItIsWorkingOn(): void { - $worker = $this->getWorker('jobs'); + $worker = $this->getWorker(self::QUEUE_JOBS); - $payload = array( - 'class' => 'Resque\Test\Job', - 'id' => 'test' - ); + $payload = [ + 'class' => Job::class, + 'id' => 'test', + ]; - $job = new Job('jobs', $payload); + $job = new Job(self::QUEUE_JOBS, $payload); $worker->workingOn($job); $job = $worker->job(); @@ -197,120 +197,122 @@ public function testWorkerRecordsWhatItIsWorkingOn() $this->fail('Could not get job being worked on'); } - $this->assertEquals('jobs', $job['queue']); + $this->assertEquals(self::QUEUE_JOBS, $job['queue']); - if(!isset($job['run_at'])) { + if (!isset($job['run_at'])) { $this->fail('Job does not have run_at time'); } $this->assertEquals($payload, $job['payload']); } - public function testWorkerErasesItsStatsWhenShutdown() + public function testWorkerErasesItsStatsWhenShutdown(): void { - $this->resque->enqueue('jobs', 'Resque\Test\Job'); - $this->resque->enqueue('jobs', 'Resque\Test\FailingJob'); + $this->resque->enqueue(self::QUEUE_JOBS, Job::class); + $this->resque->enqueue(self::QUEUE_JOBS, 'Resque\Test\FailingJob'); - $worker = $this->getWorker('jobs'); + $worker = $this->getWorker(self::QUEUE_JOBS); $worker->work(0); $worker->shutdown(Worker::DEFAULT_SIGNO, 'shutdown'); $worker->work(0); - $this->resque->clearQueue('jobs'); + $this->resque->clearQueue(self::QUEUE_JOBS); $this->assertEquals(0, $worker->getStatistic('processed')->get()); $this->assertEquals(0, $worker->getStatistic('failed')->get()); } - public function testWorkerCleansUpDeadWorkersOnStartup() + public function testWorkerCleansUpDeadWorkersOnStartup(): void { // Register a good worker - $goodWorker = new Worker($this->resque, 'jobs'); + $goodWorker = new Worker($this->resque, self::QUEUE_JOBS); $goodWorker->setLogger($this->logger); $goodWorker->register(); - $goodWorker = $this->getWorker('jobs'); + $goodWorker = $this->getWorker(self::QUEUE_JOBS); - $workerId = explode(':', $goodWorker); + $workerId = explode(':', (string)$goodWorker); // Register some bad workers - $worker = new Worker($this->resque, 'jobs'); + $worker = new Worker($this->resque, self::QUEUE_JOBS); $worker->setLogger($this->logger); - $worker->setId($workerId[0].':1:jobs'); + $worker->setId($workerId[0] . ':1:jobs'); $worker->register(); - $worker = new Worker($this->resque, array('high', 'low')); + $worker = new Worker($this->resque, ['high', 'low']); $worker->setLogger($this->logger); - $worker->setId($workerId[0].':2:high,low'); + $worker->setId($workerId[0] . ':2:high,low'); $worker->register(); - $this->assertEquals(3, count($this->resque->getWorkerIds())); + $this->assertCount(3, $this->resque->getWorkerIds()); $goodWorker->pruneDeadWorkers(); // There should only be $goodWorker left now - $this->assertEquals(1, count($this->resque->getWorkerIds())); + $this->assertCount(1, $this->resque->getWorkerIds()); } - public function testDeadWorkerCleanUpDoesNotCleanUnknownWorkers() + public function testDeadWorkerCleanUpDoesNotCleanUnknownWorkers(): void { // Register a bad worker on this machine - $worker = new Worker($this->resque, 'jobs'); + $worker = new Worker($this->resque, self::QUEUE_JOBS); $worker->setLogger($this->logger); - $workerId = explode(':', $worker); - $worker->setId($workerId[0].':1:jobs'); + $workerId = explode(':', (string)$worker); + $worker->setId($workerId[0] . ':1:jobs'); $worker->register(); // Register some other false workers - $worker = new Worker($this->resque, 'jobs'); + $worker = new Worker($this->resque, self::QUEUE_JOBS); $worker->setLogger($this->logger); $worker->setId('my.other.host:1:jobs'); $worker->register(); - $this->assertEquals(2, count($this->resque->getWorkerIds())); + $this->assertCount(2, $this->resque->getWorkerIds()); $worker->pruneDeadWorkers(); // my.other.host should be left $workers = $this->resque->getWorkerIds(); - $this->assertEquals(1, count($workers)); + $this->assertCount(1, $workers); $this->assertEquals((string)$worker, (string)$workers[0]); } - public function testWorkerFailsUncompletedJobsOnExit() + public function testWorkerFailsUncompletedJobsOnExit(): void { - $backend = $this->getMockForAbstractClass('Resque\Failure\BackendInterface'); + $backend = $this->getMockForAbstractClass(BackendInterface::class); $backend->expects($this->once()) ->method('receiveFailure'); $this->resque->setFailureBackend($backend); - $worker = $this->getWorker('jobs'); + $worker = $this->getWorker(self::QUEUE_JOBS); - $job = new Job('jobs', array( - 'class' => 'Resque\Test\Job', - 'id' => __METHOD__ - )); + $job = new Job( + self::QUEUE_JOBS, + [ + 'class' => Job::class, + 'id' => __METHOD__, + ] + ); $job->setResque($this->resque); $worker->workingOn($job); $worker->unregister(); } - public function testBlockingListPop() + public function testBlockingListPop(): void { - $worker = $this->getWorker('jobs'); + $worker = $this->getWorker(self::QUEUE_JOBS); - $this->resque->enqueue('jobs', 'Resque\Test\Job'); - $this->resque->enqueue('jobs', 'Resque\Test\Job'); + $this->resque->enqueue(self::QUEUE_JOBS, Job::class); + $this->resque->enqueue(self::QUEUE_JOBS, Job::class); $i = 1; - while($job = $worker->reserve(true, 1)) - { - $this->assertEquals('Resque\Test\Job', $job['class']); + while ($job = $worker->reserve()) { + $this->assertEquals(Job::class, $job['class']); - if($i == 2) { + if ($i == 2) { break; } @@ -320,12 +322,12 @@ public function testBlockingListPop() $this->assertEquals(2, $i); } - public function testReestablishRedisConnection() + public function testReestablishRedisConnection(): void { $client_mock = $this->getMockForAbstractClass('Resque\Client\ClientInterface'); $resque = new Resque($client_mock); - $worker = new Worker($resque, 'jobs'); + $worker = new Worker($resque, self::QUEUE_JOBS); $client_mock->expects($this->once())->method('isConnected')->willReturn(true); $client_mock->expects($this->once())->method('disconnect'); @@ -333,4 +335,11 @@ public function testReestablishRedisConnection() $worker->reestablishRedisConnection(Worker::DEFAULT_SIGNO, null); } + + protected function clearQueues(array $queues): void + { + foreach ($queues as $queue) { + $this->resque->clearQueue($queue); + } + } } diff --git a/test/bootstrap.php b/test/bootstrap.php index 3c4defd..8855592 100644 --- a/test/bootstrap.php +++ b/test/bootstrap.php @@ -1,5 +1,7 @@ addPsr4('Resque\\', __DIR__);