From 8e7b912f07a174fe6e215e6e96bc85c687af701e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Felix=20=C5=A0ulc?= Date: Thu, 30 May 2019 06:57:10 +0200 Subject: [PATCH] New version with Nette v3 --- .docs/README.md | 25 +++-- .travis.yml | 47 ++++---- Makefile | 27 +++++ README.md | 11 +- composer.json | 49 +++------ phpstan.neon | 10 +- ruleset.xml | 32 +++--- src/CommandLoader/ContainerCommandLoader.php | 3 +- src/DI/ConsoleExtension.php | 103 +++++++++++------- .../{ => Unit}/Command/Command.HelperSet.phpt | 2 +- .../CommandLoader/CommandLoader.phpt | 2 +- .../DI/ConsoleExtension.HelperSet.phpt | 2 +- .../{ => Unit}/DI/ConsoleExtension.lazy.phpt | 7 +- .../cases/{ => Unit}/DI/ConsoleExtension.phpt | 2 +- tests/php-unix.ini | 8 -- 15 files changed, 179 insertions(+), 151 deletions(-) create mode 100644 Makefile rename tests/cases/{ => Unit}/Command/Command.HelperSet.phpt (96%) rename tests/cases/{ => Unit}/CommandLoader/CommandLoader.phpt (93%) rename tests/cases/{ => Unit}/DI/ConsoleExtension.HelperSet.phpt (97%) rename tests/cases/{ => Unit}/DI/ConsoleExtension.lazy.phpt (80%) rename tests/cases/{ => Unit}/DI/ConsoleExtension.phpt (97%) delete mode 100644 tests/php-unix.ini diff --git a/.docs/README.md b/.docs/README.md index 9db4963..9fc1621 100644 --- a/.docs/README.md +++ b/.docs/README.md @@ -145,16 +145,27 @@ You can copy & paste it to your project, for example to `/bin/console`. Make sure to set it as executable. `chmod +x /bin/console`. +##### Nette 3.0+ + ```php #!/usr/bin/env php -createContainer() + ->getByType(Contributte\Console\Application::class) + ->run()); +``` + +##### Nette <= 2.4 -// Get application from DI container. -$application = $container->getByType(Contributte\Console\Application::class); +```php +#!/usr/bin/env php +run()); +exit($container->getByType(Contributte\Console\Application::class)->run()); ``` diff --git a/.travis.yml b/.travis.yml index c369e70..f6927d1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,62 +1,55 @@ language: php php: - - 7.1 - 7.2 - 7.3 + - 7.4snapshot + - nightly before_install: - # Turn off XDebug - - phpenv config-rm xdebug.ini || return 0 + - phpenv config-rm xdebug.ini || return 0 # Turn off XDebug install: - # Composer - - travis_retry composer install --no-progress --prefer-dist + - travis_retry composer install --no-progress --prefer-dist # Install dependencies script: - # Tests - - composer run-script tests + - make tests # Tests after_failure: - # Print *.actual content - - for i in $(find tests -name \*.actual); do echo "--- $i"; cat $i; echo; echo; done + - for i in $(find tests -name \*.actual); do echo "--- $i"; cat $i; echo; echo; done # Print *.actual content jobs: include: - - env: title="Lowest Dependencies 7.1" - php: 7.1 + - env: title="Lowest Dependencies 7.2" + php: 7.2 install: - - travis_retry composer update --no-progress --prefer-dist --prefer-lowest + - travis_retry composer update --no-progress --prefer-dist --prefer-lowest --prefer-stable script: - - composer run-script tests + - make tests - stage: Quality Assurance - php: 7.1 + php: 7.3 script: - - composer run-script qa - - - stage: Phpstan - php: 7.1 - script: - - composer run-script phpstan-install - - composer run-script phpstan + - make qa - stage: Test Coverage if: branch = master AND type = push - php: 7.1 + php: 7.3 script: - - composer run-script coverage + - make coverage after_script: - - wget https://github.com/php-coveralls/php-coveralls/releases/download/v2.1.0/php-coveralls.phar - - php php-coveralls.phar --verbose --config tests/.coveralls.yml + - composer global require php-coveralls/php-coveralls ^2.1.0 + - ~/.composer/vendor/bin/php-coveralls --verbose --config tests/.coveralls.yml - stage: Outdated Dependencies if: branch = master AND type = cron - php: 7.1 + php: 7.3 script: - - composer outdated --direct --strict + - composer outdated --direct allow_failures: - stage: Test Coverage + - php: 7.4snapshot + - php: nightly sudo: false diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1f8c6b4 --- /dev/null +++ b/Makefile @@ -0,0 +1,27 @@ +.PHONY: qa lint cs csf phpstan tests coverage + +all: + @$(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | sort | egrep -v -e '^[^[:alnum:]]' -e '^$@$$' | xargs + +vendor: composer.json composer.lock + composer install + +qa: lint phpstan cs + +lint: vendor + vendor/bin/linter src tests + +cs: vendor + vendor/bin/codesniffer src tests + +csf: vendor + vendor/bin/codefixer src tests + +phpstan: vendor + vendor/bin/phpstan analyse -l max -c phpstan.neon src + +tests: vendor + vendor/bin/tester -s -p php --colors 1 -C tests/cases + +coverage: vendor + vendor/bin/tester -s -p phpdbg --colors 1 -C --coverage ./coverage.xml --coverage-src ./src tests/cases diff --git a/README.md b/README.md index 925b9df..c0bc679 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Ultra easy-to-use [`Symfony\Console`](https://github.com/symfony/console) implem [![Downloads this Month](https://img.shields.io/packagist/dm/contributte/console.svg?style=flat-square)](https://packagist.org/packages/contributte/console) [![Downloads total](https://img.shields.io/packagist/dt/contributte/console.svg?style=flat-square)](https://packagist.org/packages/contributte/console) [![Latest stable](https://img.shields.io/packagist/v/contributte/console.svg?style=flat-square)](https://packagist.org/packages/contributte/console) -[![PHPStan](https://img.shields.io/badge/PHPStan-enabled-brightgreen.svg?style=flat)](https://github.com/phpstan/phpstan) +[![PHPStan](https://img.shields.io/badge/PHPStan-enabled-brightgreen.svg?style=flat-square)](https://github.com/phpstan/phpstan) ## Discussion / Help @@ -24,11 +24,10 @@ composer require contributte/console ## Versions -| State | Version | Branch | PHP | -|-------------|--------------|----------|----------| -| dev | `^0.6.0` | `master` | `>= 7.1` | -| stable | `^0.5.0` | `master` | `>= 7.1` | -| stable | `^0.4.0` | `master` | `>= 5.6` | +| State | Version | Branch | Nette | PHP | +|-------------|--------------|----------|--------|--------| +| development | `^0.7.0` | `master` | `3.0+` | `^7.2` | +| stable | `^0.6.0` | `master` | `2.4` | `^7.1` | ## Overview diff --git a/composer.json b/composer.json index d8f559b..433fae4 100644 --- a/composer.json +++ b/composer.json @@ -16,23 +16,22 @@ } ], "require": { - "php": ">= 7.1", - "symfony/console": "^4.0.0" + "php": "^7.2", + "symfony/console": "^4.2.9" }, "require-dev": { - "ninjify/qa": "^0.8.0", - "ninjify/nunjuck": "^0.2.0", - "nette/di": "~2.4.11", - "nette/http": "~2.4.8" + "ninjify/qa": "^0.9.0", + "ninjify/nunjuck": "^0.3.0", + "nette/di": "~3.0.0", + "nette/http": "~3.0.1", + "phpstan/phpstan-shim": "^0.11.8", + "phpstan/phpstan-deprecation-rules": "^0.11.2", + "phpstan/phpstan-nette": "^0.11.1", + "phpstan/phpstan-strict-rules": "^0.11.1" }, "suggest": { "nette/di": "to use ConsoleExtension[CompilerExtension]" }, - "conflict": { - "nette/utils": "<2.5.2" - }, - "minimum-stability": "dev", - "prefer-stable": true, "autoload": { "psr-4": { "Contributte\\Console\\": "src" @@ -40,34 +39,18 @@ }, "autoload-dev": { "psr-4": { + "Tests\\Cases\\": "tests/cases", "Tests\\Fixtures\\": "tests/fixtures" } }, - "scripts": { - "qa": [ - "linter src tests", - "codesniffer src tests" - ], - "tests": [ - "tester -s -p php --colors 1 -C tests/cases" - ], - "coverage": [ - "tester -s -p phpdbg --colors 1 -C --coverage ./coverage.xml --coverage-src ./src tests/cases" - ], - "phpstan-install": [ - "mkdir -p temp/phpstan", - "composer require -d temp/phpstan phpstan/phpstan:^0.10", - "composer require -d temp/phpstan phpstan/phpstan-deprecation-rules:^0.10", - "composer require -d temp/phpstan phpstan/phpstan-nette:^0.10", - "composer require -d temp/phpstan phpstan/phpstan-strict-rules:^0.10" - ], - "phpstan": [ - "temp/phpstan/vendor/bin/phpstan analyse -l max -c phpstan.neon src" - ] + "minimum-stability": "dev", + "prefer-stable": true, + "config": { + "sort-packages": true }, "extra": { "branch-alias": { - "dev-master": "0.6.x-dev" + "dev-master": "0.7.x-dev" } } } diff --git a/phpstan.neon b/phpstan.neon index 81e6d8a..7f12438 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,9 +1,9 @@ includes: - - temp/phpstan/vendor/phpstan/phpstan-deprecation-rules/rules.neon - - temp/phpstan/vendor/phpstan/phpstan-nette/extension.neon - - temp/phpstan/vendor/phpstan/phpstan-nette/rules.neon - - temp/phpstan/vendor/phpstan/phpstan-strict-rules/rules.neon + - vendor/phpstan/phpstan-deprecation-rules/rules.neon + - vendor/phpstan/phpstan-nette/extension.neon + - vendor/phpstan/phpstan-nette/rules.neon + - vendor/phpstan/phpstan-strict-rules/rules.neon parameters: ignoreErrors: - - '#Accessing property $arguments on possibly null value of type Nette\DI\Statement|null#' + - '#^Parameter \#1 \$function of function call_user_func expects callable\(\): mixed, array\(string\|null, .+\) given.$#' diff --git a/ruleset.xml b/ruleset.xml index ef350c3..166c03f 100644 --- a/ruleset.xml +++ b/ruleset.xml @@ -1,18 +1,24 @@ - - - - - - - - + + + + + + - - /tests/tmp + + + + + + + + + /tests/tmp diff --git a/src/CommandLoader/ContainerCommandLoader.php b/src/CommandLoader/ContainerCommandLoader.php index 42f142a..da55a35 100644 --- a/src/CommandLoader/ContainerCommandLoader.php +++ b/src/CommandLoader/ContainerCommandLoader.php @@ -4,6 +4,7 @@ use Contributte\Console\Exception\Runtime\CommandNotFoundException; use Nette\DI\Container; +use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\CommandLoader\CommandLoaderInterface; class ContainerCommandLoader implements CommandLoaderInterface @@ -28,7 +29,7 @@ public function __construct(Container $container, array $commandMap) * Loads a command. * * @param string $name - * @return object + * @return Command * @throws CommandNotFoundException * @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingParameterTypeHint * @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingReturnTypeHint diff --git a/src/DI/ConsoleExtension.php b/src/DI/ConsoleExtension.php index 26ad52b..ab6e366 100644 --- a/src/DI/ConsoleExtension.php +++ b/src/DI/ConsoleExtension.php @@ -5,35 +5,28 @@ use Contributte\Console\Application; use Contributte\Console\CommandLoader\ContainerCommandLoader; use Contributte\Console\Exception\Logical\InvalidArgumentException; -use Nette\DI\Compiler; use Nette\DI\CompilerExtension; +use Nette\DI\Definitions\ServiceDefinition; +use Nette\DI\Definitions\Statement; use Nette\DI\ServiceCreationException; -use Nette\DI\Statement; use Nette\Http\Request; use Nette\Http\UrlScript; +use Nette\Schema\Expect; +use Nette\Schema\Schema; use Nette\Utils\Arrays; use Nette\Utils\Strings; -use Nette\Utils\Validators; +use stdClass; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\CommandLoader\CommandLoaderInterface; +/** + * @property-read stdClass $config + */ class ConsoleExtension extends CompilerExtension { public const COMMAND_TAG = 'console.command'; - /** @var mixed[] */ - private $defaults = [ - 'url' => null, - 'name' => null, - 'version' => null, - 'catchExceptions' => null, - 'autoExit' => null, - 'helperSet' => null, - 'helpers' => [], - 'lazy' => false, - ]; - /** @var bool */ private $cliMode; @@ -46,18 +39,32 @@ public function __construct(bool $cliMode = false) $this->cliMode = $cliMode; } + public function getConfigSchema(): Schema + { + return Expect::structure([ + 'url' => Expect::string(), + 'name' => Expect::string(), + 'version' => Expect::string(), + 'catchExceptions' => Expect::bool(), + 'autoExit' => Expect::bool(), + 'helperSet' => Expect::string(), + 'helpers' => Expect::listOf('string'), + 'lazy' => Expect::bool(true), + ]); + } + /** * Register services */ public function loadConfiguration(): void { - $builder = $this->getContainerBuilder(); - $config = $this->validateConfig($this->defaults); - // Skip if isn't CLI - if ($this->cliMode !== true) return; + if ($this->cliMode !== true) { + return; + } - Validators::assertField($config, 'helpers', 'array|null'); + $builder = $this->getContainerBuilder(); + $config = (array) $this->config; $application = $builder->addDefinition($this->prefix('application')) ->setFactory(Application::class); @@ -82,20 +89,29 @@ public function loadConfiguration(): void if (is_string($config['helperSet']) && Strings::startsWith($config['helperSet'], '@')) { // Add already defined service $application->addSetup('setHelperSet', [$config['helperSet']]); - } else { + } elseif (is_string($config['helperSet'])) { // Parse service definition - $helperSetDef = $builder->addDefinition($this->prefix('helperSet')); - Compiler::loadDefinition($helperSetDef, $config['helperSet']); + $helperSetDef = $builder->addDefinition($this->prefix('helperSet')) + ->setFactory($config['helperSet']); $application->addSetup('setHelperSet', [$helperSetDef]); + } else { + throw new ServiceCreationException(sprintf('Unsupported definition of helperSet')); } } - if (is_array($config['helpers'])) { - $helpers = 1; - foreach ($config['helpers'] as $helper) { - $helperDef = $builder->addDefinition($this->prefix('helper.' . $helpers++)); - Compiler::loadDefinition($helperDef, $helper); - $application->addSetup(new Statement('$service->getHelperSet()->set(?)', [$helperDef])); + if ($config['helpers']) { + foreach ($config['helpers'] as $n => $helper) { + if (is_string($helper) && Strings::startsWith($helper, '@')) { + // Add already defined service + $application->addSetup(new Statement('$service->getHelperSet()->set(?)', [$helper])); + } elseif (is_string($helper)) { + // Parse service definition + $helperDef = $builder->addDefinition($this->prefix('helperSet')) + ->setFactory($helper); + $application->addSetup(new Statement('$service->getHelperSet()->set(?)', [$helperDef])); + } else { + throw new ServiceCreationException(sprintf('Unsupported definition of helper')); + } } } @@ -113,18 +129,22 @@ public function loadConfiguration(): void */ public function beforeCompile(): void { - $builder = $this->getContainerBuilder(); - $config = $this->validateConfig($this->defaults); - // Skip if isn't CLI - if ($this->cliMode !== true) return; + if ($this->cliMode !== true) { + return; + } + + $builder = $this->getContainerBuilder(); + $config = (array) $this->config; - $application = $builder->getDefinition($this->prefix('application')); + /** @var ServiceDefinition $applicationDef */ + $applicationDef = $builder->getDefinition($this->prefix('application')); // Setup URL for CLI if ($builder->hasDefinition('http.request') && $config['url'] !== null) { - $builder->getDefinition('http.request') - ->setFactory(Request::class, [new Statement(UrlScript::class, [$config['url']])]); + /** @var ServiceDefinition $httpDef */ + $httpDef = $builder->getDefinition('http.request'); + $httpDef->setFactory(Request::class, [new Statement(UrlScript::class, [$config['url']])]); } // Register all commands (if they are not lazy-loaded) @@ -134,7 +154,7 @@ public function beforeCompile(): void if ($config['lazy'] === false) { // Iterate over all commands and add to console foreach ($commands as $serviceName => $service) { - $application->addSetup('add', [$service]); + $applicationDef->addSetup('add', [$service]); } } else { $commandMap = []; @@ -153,7 +173,7 @@ public function beforeCompile(): void } } else { // Parse it from static property - $entry['name'] = call_user_func([$service->getEntity(), 'getDefaultName']); + $entry['name'] = call_user_func([$service->getType(), 'getDefaultName']); } // Validate command name @@ -161,7 +181,7 @@ public function beforeCompile(): void throw new ServiceCreationException( sprintf( 'Command "%s" missing tag "%s[name]" or variable "$defaultName".', - $service->getEntity(), + $service->getType(), self::COMMAND_TAG ) ); @@ -171,8 +191,9 @@ public function beforeCompile(): void $commandMap[$entry['name']] = $serviceName; } - $builder->getDefinition($this->prefix('commandLoader')) - ->getFactory()->arguments = ['@container', $commandMap]; + /** @var ServiceDefinition $commandLoaderDef */ + $commandLoaderDef = $builder->getDefinition($this->prefix('commandLoader')); + $commandLoaderDef->getFactory()->arguments = ['@container', $commandMap]; } } diff --git a/tests/cases/Command/Command.HelperSet.phpt b/tests/cases/Unit/Command/Command.HelperSet.phpt similarity index 96% rename from tests/cases/Command/Command.HelperSet.phpt rename to tests/cases/Unit/Command/Command.HelperSet.phpt index f68278e..725b0a4 100644 --- a/tests/cases/Command/Command.HelperSet.phpt +++ b/tests/cases/Unit/Command/Command.HelperSet.phpt @@ -14,7 +14,7 @@ use Tester\Assert; use Tester\FileMock; use Tests\Fixtures\HelperSetCommand; -require_once __DIR__ . '/../../bootstrap.php'; +require_once __DIR__ . '/../../../bootstrap.php'; // Test auto filling helperSet in command test(function (): void { diff --git a/tests/cases/CommandLoader/CommandLoader.phpt b/tests/cases/Unit/CommandLoader/CommandLoader.phpt similarity index 93% rename from tests/cases/CommandLoader/CommandLoader.phpt rename to tests/cases/Unit/CommandLoader/CommandLoader.phpt index eaf9b57..299df1e 100644 --- a/tests/cases/CommandLoader/CommandLoader.phpt +++ b/tests/cases/Unit/CommandLoader/CommandLoader.phpt @@ -10,7 +10,7 @@ use Nette\DI\Container; use Tester\Assert; use Tester\Environment; -require_once __DIR__ . '/../../bootstrap.php'; +require_once __DIR__ . '/../../../bootstrap.php'; if (!interface_exists('Symfony\Component\Console\CommandLoader\CommandLoaderInterface', true)) { Environment::skip('CommandLoaderInterface is available from symfony/console 3.4'); diff --git a/tests/cases/DI/ConsoleExtension.HelperSet.phpt b/tests/cases/Unit/DI/ConsoleExtension.HelperSet.phpt similarity index 97% rename from tests/cases/DI/ConsoleExtension.HelperSet.phpt rename to tests/cases/Unit/DI/ConsoleExtension.HelperSet.phpt index bb71536..4a779c5 100644 --- a/tests/cases/DI/ConsoleExtension.HelperSet.phpt +++ b/tests/cases/Unit/DI/ConsoleExtension.HelperSet.phpt @@ -13,7 +13,7 @@ use Tester\Assert; use Tester\FileMock; use Tests\Fixtures\FooHelperSet; -require_once __DIR__ . '/../../bootstrap.php'; +require_once __DIR__ . '/../../../bootstrap.php'; // Default helperSet test(function (): void { diff --git a/tests/cases/DI/ConsoleExtension.lazy.phpt b/tests/cases/Unit/DI/ConsoleExtension.lazy.phpt similarity index 80% rename from tests/cases/DI/ConsoleExtension.lazy.phpt rename to tests/cases/Unit/DI/ConsoleExtension.lazy.phpt index 8cbfeaf..6bf19e5 100644 --- a/tests/cases/DI/ConsoleExtension.lazy.phpt +++ b/tests/cases/Unit/DI/ConsoleExtension.lazy.phpt @@ -11,15 +11,10 @@ use Nette\DI\Container; use Nette\DI\ContainerLoader; use Symfony\Component\Console\Command\Command; use Tester\Assert; -use Tester\Environment; use Tester\FileMock; use Tests\Fixtures\FooCommand; -require_once __DIR__ . '/../../bootstrap.php'; - -if (!interface_exists('Symfony\Component\Console\CommandLoader\CommandLoaderInterface', true)) { - Environment::skip('CommandLoaderInterface is available from symfony/console 3.4'); -} +require_once __DIR__ . '/../../../bootstrap.php'; // 1 command of type FooCommand lazy-loading test(function (): void { diff --git a/tests/cases/DI/ConsoleExtension.phpt b/tests/cases/Unit/DI/ConsoleExtension.phpt similarity index 97% rename from tests/cases/DI/ConsoleExtension.phpt rename to tests/cases/Unit/DI/ConsoleExtension.phpt index 2b0ca08..a71eb1c 100644 --- a/tests/cases/DI/ConsoleExtension.phpt +++ b/tests/cases/Unit/DI/ConsoleExtension.phpt @@ -16,7 +16,7 @@ use Tester\Assert; use Tester\FileMock; use Tests\Fixtures\FooCommand; -require_once __DIR__ . '/../../bootstrap.php'; +require_once __DIR__ . '/../../../bootstrap.php'; // No commands test(function (): void { diff --git a/tests/php-unix.ini b/tests/php-unix.ini deleted file mode 100644 index 89ec497..0000000 --- a/tests/php-unix.ini +++ /dev/null @@ -1,8 +0,0 @@ -[PHP] -;extension_dir = "./ext" -extension=tokenizer.so -;extension=json.so -;extension=mcrypt.so - -[Zend] -;zend_extension="./ext/zend_extension"