From 4c3f2530da0924294e757d26a83b511e5340df39 Mon Sep 17 00:00:00 2001 From: Stanislau Komar Date: Wed, 18 Jan 2023 15:53:23 +0400 Subject: [PATCH 1/2] v1.6.0 released --- .gitignore | 9 ++- .php-cs-fixer.dist.php | 30 ++++++++ composer.json | 37 ++++++++-- phpcs.xml | 63 ---------------- phpmd.xml | 14 ---- phpstan.neon.dist | 4 + phpunit.xml => phpunit.xml.dist | 4 +- psalm.xml | 22 ++++++ src/Container.php | 74 +++++++++++-------- src/ContainerDecoratorInterface.php | 18 +++-- src/ContainerRegistryInterface.php | 17 ++++- .../ServiceNotRegisteredException.php | 9 +++ .../ServiceRegistrationException.php | 9 +++ tests/{unit => Unit}/ContainerTest.php | 59 ++++++++++----- 14 files changed, 226 insertions(+), 143 deletions(-) mode change 100755 => 100644 .gitignore create mode 100644 .php-cs-fixer.dist.php delete mode 100755 phpcs.xml delete mode 100755 phpmd.xml create mode 100644 phpstan.neon.dist rename phpunit.xml => phpunit.xml.dist (90%) create mode 100644 psalm.xml rename tests/{unit => Unit}/ContainerTest.php (65%) diff --git a/.gitignore b/.gitignore old mode 100755 new mode 100644 index a8fea06..c40cd9d --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,9 @@ -vendor/ +.idea +vendor composer.lock .phpunit.result.cache -test-coverage-report/ +.php-cs-fixer.cache +test-coverage-report +phpunit.xml +.php-cs-fixer.php +phpstan.neon \ No newline at end of file diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..ded2263 --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,30 @@ +in(__DIR__.'/src') + ->in(__DIR__.'/tests') +; + +return (new PhpCsFixer\Config()) + ->setRules(array( + '@Symfony' => true, + '@Symfony:risky' => true, + 'protected_to_private' => false, + 'semicolon_after_instruction' => false, + 'header_comment' => [ + 'header' => << + + For the full copyright and license information, please view the LICENSE + file that was distributed with this source code. +EOF + ] + )) + ->setRiskyAllowed(true) + ->setFinder($finder); \ No newline at end of file diff --git a/composer.json b/composer.json index a6274b8..3971ce1 100755 --- a/composer.json +++ b/composer.json @@ -15,12 +15,11 @@ }, "require-dev": { "ergebnis/composer-normalize": "^2.29", - "phpmd/phpmd": "^2.13", + "friendsofphp/php-cs-fixer": "^3.13", + "phpstan/phpstan": "^1.9", + "phpunit/php-code-coverage": "^9.2", "phpunit/phpunit": "^9.5", - "squizlabs/php_codesniffer": "^3.7" - }, - "conflict": { - "micro/autowire": "<1.1" + "vimeo/psalm": "^5.2" }, "suggest": { "micro/autowire": "Autowire helper for dependency injection" @@ -30,14 +29,40 @@ "Micro\\Component\\DependencyInjection\\": "src/" } }, + "autoload-dev": { + "psr-4": { + "Micro\\Component\\DependencyInjection\\Tests\\Unit\\": "tests/Unit/" + } + }, "config": { "allow-plugins": { "ergebnis/composer-normalize": true - } + }, + "sort-packages": true }, "extra": { "branch-alias": { "dev-master": "1.x-dev" } + }, + "scripts": { + "coverage": "XDEBUG_MODE=coverage ./vendor/bin/phpunit --coverage-text", + "coverage-html": "XDEBUG_MODE=coverage ./vendor/bin/phpunit --coverage-html ./test-coverage-report", + "php-cs-fix": "PHP_CS_FIXER_IGNORE_ENV=1 ./vendor/bin/php-cs-fixer fix --verbose --using-cache=no", + "php-cs-try": "PHP_CS_FIXER_IGNORE_ENV=1 ./vendor/bin/php-cs-fixer fix --verbose --dry-run --using-cache=no", + "phpstan": "./vendor/bin/phpstan analyze --no-progress", + "phpunit": "./vendor/bin/phpunit", + "psalm": "./vendor/bin/psalm --no-progress --show-info=true", + "statics": [ + "@phpstan", + "@psalm", + "@php-cs-try" + ], + "test": [ + "@statics", + "composer validate --strict", + "composer normalize", + "@coverage" + ] } } diff --git a/phpcs.xml b/phpcs.xml deleted file mode 100755 index 6581b2d..0000000 --- a/phpcs.xml +++ /dev/null @@ -1,63 +0,0 @@ - - - src/ - - - - Micro Framework - сode formatting rules. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/phpmd.xml b/phpmd.xml deleted file mode 100755 index dae2715..0000000 --- a/phpmd.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - This class has too many methods, consider refactoring it. - - - diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 0000000..18d77f7 --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,4 @@ +parameters: + level: 7 + paths: + - src \ No newline at end of file diff --git a/phpunit.xml b/phpunit.xml.dist similarity index 90% rename from phpunit.xml rename to phpunit.xml.dist index 209e106..f113387 100755 --- a/phpunit.xml +++ b/phpunit.xml.dist @@ -17,10 +17,10 @@ - tests/unit + tests/Unit - + diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 0000000..5dd061c --- /dev/null +++ b/psalm.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Container.php b/src/Container.php index 9d0ecb4..382f884 100755 --- a/src/Container.php +++ b/src/Container.php @@ -1,42 +1,65 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Micro\Component\DependencyInjection; use Micro\Component\DependencyInjection\Exception\ServiceNotRegisteredException; use Micro\Component\DependencyInjection\Exception\ServiceRegistrationException; use Psr\Container\ContainerInterface; +/** + * @author Stanislau Komar + */ class Container implements ContainerInterface, ContainerRegistryInterface, ContainerDecoratorInterface { /** - * @var array + * @var array */ private array $services = []; /** - * @var array + * @var array */ private array $servicesRaw = []; /** - * @var array> + * @var array>> */ private array $decorators = []; /** - * @template T + * @psalm-suppress MoreSpecificImplementedParamType + * @psalm-suppress MixedPropertyTypeCoercion + * + * @template T of object * * @param class-string $id * - * @return T + * @psalm-return object */ public function get(string $id): object { - return $this->lookup($id); + if (!empty($this->services[$id])) { + return $this->services[$id]; + } + + $this->initializeService($id); + + return $this->services[$id]; } /** - * {@inheritDoc} + * @param class-string $id + * + * @psalm-suppress MoreSpecificImplementedParamType */ public function has(string $id): bool { @@ -46,9 +69,9 @@ public function has(string $id): bool /** * {@inheritDoc} */ - public function register(string $id, \Closure $service): void + public function register(string $id, callable $service): void { - if($this->has($id)) { + if ($this->has($id)) { throw new ServiceRegistrationException(sprintf('Service "%s" already registered', $id)); } @@ -57,42 +80,33 @@ public function register(string $id, \Closure $service): void /** * {@inheritDoc} - */ - public function decorate(string $id, \Closure $service, int $priority = 0): void - { - $this->decorators[$id][$priority][] = $service; - } - - /** - * @param string $id * - * @return object + * @psalm-suppress InvalidPropertyAssignmentValue */ - private function lookup(string $id): object + public function decorate(string $id, callable $service, int $priority = 0): void { - if(!empty($this->services[$id])) { - return $this->services[$id]; + if (!\array_key_exists($id, $this->decorators)) { + $this->decorators[$id] = []; } - - $this->initializeService($id); - - return $this->services[$id]; + $this->decorators[$id][$priority][] = $service; } /** - * @param string $serviceId + * @template T of Object + * + * @param class-string $serviceId */ protected function initializeService(string $serviceId): void { - if(empty($this->servicesRaw[$serviceId])) { + if (empty($this->servicesRaw[$serviceId])) { throw new ServiceNotRegisteredException($serviceId); } - $raw = $this->servicesRaw[$serviceId]; - $service = $raw($this); + $raw = $this->servicesRaw[$serviceId]; + $service = $raw($this); $this->services[$serviceId] = $service; - if(!array_key_exists($serviceId, $this->decorators)) { + if (!\array_key_exists($serviceId, $this->decorators)) { return; } diff --git a/src/ContainerDecoratorInterface.php b/src/ContainerDecoratorInterface.php index e95089c..8763b44 100644 --- a/src/ContainerDecoratorInterface.php +++ b/src/ContainerDecoratorInterface.php @@ -1,15 +1,23 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Micro\Component\DependencyInjection; interface ContainerDecoratorInterface { /** - * @param string $id - * @param \Closure $service - * @param int $priority + * @template T * - * @return void + * @param class-string $id + * @param callable(): T $service */ - public function decorate(string $id, \Closure $service, int $priority = 0): void; + public function decorate(string $id, callable $service, int $priority = 0): void; } diff --git a/src/ContainerRegistryInterface.php b/src/ContainerRegistryInterface.php index 44d76c8..887b008 100755 --- a/src/ContainerRegistryInterface.php +++ b/src/ContainerRegistryInterface.php @@ -1,5 +1,14 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Micro\Component\DependencyInjection; interface ContainerRegistryInterface @@ -7,10 +16,10 @@ interface ContainerRegistryInterface /** * Register new service. * - * @param string $id service alias - * @param \Closure $service service initialization callback + * @template T of Object * - * @return void + * @param class-string $id service alias + * @param callable $service service initialization callback */ - public function register(string $id, \Closure $service): void; + public function register(string $id, callable $service): void; } diff --git a/src/Exception/ServiceNotRegisteredException.php b/src/Exception/ServiceNotRegisteredException.php index b6dca6f..a65e835 100755 --- a/src/Exception/ServiceNotRegisteredException.php +++ b/src/Exception/ServiceNotRegisteredException.php @@ -1,5 +1,14 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Micro\Component\DependencyInjection\Exception; use Psr\Container\NotFoundExceptionInterface; diff --git a/src/Exception/ServiceRegistrationException.php b/src/Exception/ServiceRegistrationException.php index 79a2f04..522a11d 100755 --- a/src/Exception/ServiceRegistrationException.php +++ b/src/Exception/ServiceRegistrationException.php @@ -1,5 +1,14 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Micro\Component\DependencyInjection\Exception; use Psr\Container\ContainerExceptionInterface; diff --git a/tests/unit/ContainerTest.php b/tests/Unit/ContainerTest.php similarity index 65% rename from tests/unit/ContainerTest.php rename to tests/Unit/ContainerTest.php index a1752d1..aa81e8d 100755 --- a/tests/unit/ContainerTest.php +++ b/tests/Unit/ContainerTest.php @@ -1,8 +1,16 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Micro\Component\DependencyInjection\Tests; -use Micro\Component\DependencyInjection\Autowire\ContainerAutowire; use Micro\Component\DependencyInjection\Container; use Micro\Component\DependencyInjection\Exception\ServiceNotRegisteredException; use Micro\Component\DependencyInjection\Exception\ServiceRegistrationException; @@ -31,8 +39,8 @@ public function testRegisterTwoServicesWithEqualAliasesException(): void $this->expectException(ServiceRegistrationException::class); $container = new Container(); - $container->register('test', function () { return new class {}; }); - $container->register('test', function () { return new class {}; }); + $container->register('test', function () { return new class() {}; }); + $container->register('test', function () { return new class() {}; }); } public function testContainerUnresolvedException(): void @@ -40,7 +48,7 @@ public function testContainerUnresolvedException(): void $this->expectException(ServiceNotRegisteredException::class); $container = new Container(); - $container->register('test', function () { + $container->register(NamedInterface::class, function (): NamedInterface { return new NamedService('success'); }); @@ -51,55 +59,72 @@ public function testDecorateService(): void { $container = new Container(); - $container->register('test', function () { + $container->register(NamedInterface::class, function (): NamedInterface { return new NamedService('A'); }); - $container->decorate('test', function (NamedInterface $decorated) { + $container->decorate(NamedInterface::class, function (NamedInterface $decorated) { return new NamedServiceDecorator($decorated, 'D'); }); - $container->decorate('test', function (NamedInterface $decorated) { + $container->decorate(NamedInterface::class, function (NamedInterface $decorated) { return new NamedServiceDecorator($decorated, 'B'); }, 10); - $container->decorate('test', function (NamedInterface $decorated) { + $container->decorate(NamedInterface::class, function (NamedInterface $decorated) { return new NamedServiceDecorator($decorated, 'C'); }, 5); - /** @var NamedInterface $result */ - $result = $container->get('test'); + $result = $container->get(NamedInterface::class); + $this->assertInstanceOf(NamedServiceDecorator::class, $result); $this->assertInstanceOf(NamedInterface::class, $result); $this->assertEquals('ABCD', $result->getName()); + + $container->get(NamedInterface::class); + } + + public function testUnregisteredException() + { + $container = new Container(); + $service = 'UnresolvedService'; + + $this->expectException(ServiceNotRegisteredException::class); + + try { + $container->get($service); + } catch (ServiceNotRegisteredException $exception) { + $this->assertEquals($service, $exception->getServiceId()); + + throw $exception; + } } public function testDecoratorsWithSamePriority(): void { $container = new Container(); - $container->register('test', function () { + $container->register(NamedInterface::class, function () { return new NamedService('A'); }); - $container->decorate('test', function (NamedInterface $decorated) { + $container->decorate(NamedInterface::class, function (NamedInterface $decorated) { return new NamedServiceDecorator($decorated, 'B'); }, 10); - $container->decorate('test', function (NamedInterface $decorated) { + $container->decorate(NamedInterface::class, function (NamedInterface $decorated) { return new NamedServiceDecorator($decorated, 'D'); }); - $container->decorate('test', function (NamedInterface $decorated) { + $container->decorate(NamedInterface::class, function (NamedInterface $decorated) { return new NamedServiceDecorator($decorated, 'E'); }); - $container->decorate('test', function (NamedInterface $decorated) { + $container->decorate(NamedInterface::class, function (NamedInterface $decorated) { return new NamedServiceDecorator($decorated, 'C'); }, 10); - /** @var NamedInterface $result */ - $result = $container->get('test'); + $result = $container->get(NamedInterface::class); $this->assertInstanceOf(NamedServiceDecorator::class, $result); $this->assertInstanceOf(NamedInterface::class, $result); $this->assertEquals('ABCDE', $result->getName()); From 1d5892a883c32e5bc23f7899d259c19c8e183322 Mon Sep 17 00:00:00 2001 From: Stanislau Komar Date: Wed, 18 Jan 2023 16:04:42 +0400 Subject: [PATCH 2/2] Fix guthub actions --- .github/workflows/ci.yaml | 4 ++-- .github/workflows/static.yaml | 24 ++---------------------- 2 files changed, 4 insertions(+), 24 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 08a0740..c3743b9 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -40,5 +40,5 @@ jobs: dependency-versions: "${{ matrix.dependency-versions }}" composer-options: "--prefer-dist --no-progress" - - name: "Run tests" - run: "./vendor/bin/phpunit" + - name: "RUn tests" + run: "composer coverage-html" diff --git a/.github/workflows/static.yaml b/.github/workflows/static.yaml index a17c349..85f7e23 100644 --- a/.github/workflows/static.yaml +++ b/.github/workflows/static.yaml @@ -41,25 +41,5 @@ jobs: - name: Download dependencies uses: ramsey/composer-install@v2 - - name: PHP_CodeSniffer - run: ./vendor/bin/phpcs --standard=phpcs.xml ./src/ - - phpmd: - name: Mess Detector - runs-on: ubuntu-22.04 - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: 8.2 - coverage: none - tools: phpmd/phpmd:^2.13 - - - name: Download dependencies - uses: ramsey/composer-install@v2 - - - name: Mess Detector - run: ./vendor/bin/phpmd ./src/ github ./phpmd.xml + - name: Testing + run: composer statics \ No newline at end of file