From 757e0bf34064da983c608510f9efc49a3094edb2 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sat, 2 Jul 2022 14:17:49 +0900 Subject: [PATCH 1/7] Conditional type with phpstan for getInstance() @see https://phpstan.org/blog/phpstan-1-6-0-with-conditional-return-types --- src/di/AssistedInterceptor.php | 13 ++- src/di/Di/Set.php | 5 +- src/di/InjectorInterface.php | 14 +-- tests/di/InjectorTest.php | 6 -- tests/type/InjectorInterfaceTest.php | 2 +- vendor-bin/tools/composer.json | 3 +- vendor-bin/tools/composer.lock | 144 ++++++++++++++++++--------- 7 files changed, 118 insertions(+), 69 deletions(-) diff --git a/src/di/AssistedInterceptor.php b/src/di/AssistedInterceptor.php index 3d640f1e..63376b4e 100644 --- a/src/di/AssistedInterceptor.php +++ b/src/di/AssistedInterceptor.php @@ -77,9 +77,8 @@ public function injectAssistedParameters(ReflectionMethod $method, Assisted $ass $interface = $this->getInterface($type); $name = $this->getName($method, $parameter); $pos = $parameter->getPosition(); - assert(class_exists($interface) || interface_exists($interface) || $interface === ''); /** @psalm-suppress MixedAssignment */ - $arguments[$pos] = $this->injector->getInstance($interface, $name); + $arguments[$pos] = $this->injector->getInstance($interface, $name); // @phpstan-ignore-line } return $arguments; @@ -104,8 +103,14 @@ private function getName(ReflectionMethod $method, ReflectionParameter $paramete return Name::ANY; } - private function getInterface(?ReflectionType $type): string + /** + * @return ''|class-string + */ + private function getInterface(?ReflectionType $type) { - return $type instanceof ReflectionNamedType && ! in_array($type->getName(), Argument::UNBOUND_TYPE, true) ? $type->getName() : ''; + $interface = $type instanceof ReflectionNamedType && ! in_array($type->getName(), Argument::UNBOUND_TYPE, true) ? $type->getName() : ''; + assert($interface === '' || interface_exists($interface) || class_exists($interface)); + + return $interface; } } diff --git a/src/di/Di/Set.php b/src/di/Di/Set.php index 68cb90ec..52f3ae2e 100644 --- a/src/di/Di/Set.php +++ b/src/di/Di/Set.php @@ -11,18 +11,19 @@ * @Annotation * @Target({"METHOD","PROPERTY"}) * @NamedArgumentConstructor() + * @template T of object */ #[Attribute(Attribute::TARGET_PARAMETER | Attribute::TARGET_PROPERTY)] final class Set { - /** @var ""|class-string */ + /** @var ''|class-string */ public $interface; /** @var string */ public $name; /** - * @param ""|class-string $interface + * @param ''|class-string $interface */ public function __construct(string $interface, string $name = '') { diff --git a/src/di/InjectorInterface.php b/src/di/InjectorInterface.php index a373eed8..7cb02519 100644 --- a/src/di/InjectorInterface.php +++ b/src/di/InjectorInterface.php @@ -16,16 +16,12 @@ interface InjectorInterface /** * Return object graph * - * @param class-string $interface interface name|class name|empty-string - * @param string $name interface name space - * @psalm-param ''|class-string $interface - * @phpstan-param ''|class-string $interface + * @param ''|class-string $interface + * @param string $name * - * @return T - * @psalm-return (T is object ? T : mixed) - * @phpstan-return mixed + * @return ($interface is '' ? mixed : T) * - * @psalm-template T of object + * @template T of object */ - public function getInstance($interface, $name = Name::ANY); // @phpstan-ignore-line + public function getInstance($interface, $name = Name::ANY); } diff --git a/tests/di/InjectorTest.php b/tests/di/InjectorTest.php index 5b7a4f45..4e24f6f3 100644 --- a/tests/di/InjectorTest.php +++ b/tests/di/InjectorTest.php @@ -110,7 +110,6 @@ public function testToBindingPrototype(): void $injector = new Injector(new FakeToBindModule()); $instance1 = $injector->getInstance(FakeRobotInterface::class); $instance2 = $injector->getInstance(FakeRobotInterface::class); - assert(is_object($instance1) && is_object($instance2)); $this->assertNotSame(spl_object_hash($instance1), spl_object_hash($instance2)); } @@ -119,7 +118,6 @@ public function testToBindingSingleton(): void $injector = new Injector(new FakeToBindSingletonModule()); $instance1 = $injector->getInstance(FakeRobotInterface::class); $instance2 = $injector->getInstance(FakeRobotInterface::class); - assert(is_object($instance1) && is_object($instance2)); $this->assertSame(spl_object_hash($instance1), spl_object_hash($instance2)); } @@ -128,7 +126,6 @@ public function testToProviderBinding(): void $injector = new Injector(new FakeToProviderBindModule()); $instance1 = $injector->getInstance(FakeRobotInterface::class); $instance2 = $injector->getInstance(FakeRobotInterface::class); - assert(is_object($instance1) && is_object($instance2)); $this->assertNotSame(spl_object_hash($instance1), spl_object_hash($instance2)); } @@ -144,7 +141,6 @@ public function testToProviderBindingSingleton(): void $injector = new Injector(new FakeToProviderSingletonBindModule()); $instance1 = $injector->getInstance(FakeRobotInterface::class); $instance2 = $injector->getInstance(FakeRobotInterface::class); - assert(is_object($instance1) && is_object($instance2)); $this->assertSame(spl_object_hash($instance1), spl_object_hash($instance2)); } @@ -159,7 +155,6 @@ public function testGetConcreteHavingDependency(): void { $injector = new Injector(); $team = $injector->getInstance(FakeRobotTeam::class); - /** @var FakeRobotTeam $team */ $this->assertInstanceOf(FakeRobotTeam::class, $team); $this->assertInstanceOf(FakeRobot::class, $team->robot1); $this->assertInstanceOf(FakeRobot::class, $team->robot2); @@ -185,7 +180,6 @@ public function testAnnotationBasedInjection(): Injector $this->assertInstanceOf(FakeMirrorInterface::class, $car->spareMirror); $this->assertSame(spl_object_hash($car->rightMirror), spl_object_hash($car->spareMirror)); $this->assertInstanceOf(FakeHandle::class, $car->handle); - assert($car->handle instanceof FakeHandle); $this->assertSame($car->handle->logo, 'momo'); return $injector; diff --git a/tests/type/InjectorInterfaceTest.php b/tests/type/InjectorInterfaceTest.php index 987fb30f..53883f21 100644 --- a/tests/type/InjectorInterfaceTest.php +++ b/tests/type/InjectorInterfaceTest.php @@ -21,7 +21,7 @@ protected function configure() } }); $injector->getInstance(DateTimeInterface::class); - $this->a = $injector->getInstance(DateTimeInterface::class); // @phpstan-ignore-line + $this->a = $injector->getInstance(DateTimeInterface::class); /** @psalm-suppress PropertyTypeCoercion */ $this->b = $injector->getInstance(DateTimeInterface::class); // @phpstan-ignore-line /** @psalm-suppress ArgumentTypeCoercion */ diff --git a/vendor-bin/tools/composer.json b/vendor-bin/tools/composer.json index cdb4455e..62fdb711 100644 --- a/vendor-bin/tools/composer.json +++ b/vendor-bin/tools/composer.json @@ -5,7 +5,8 @@ "phpmetrics/phpmetrics": "^2.7", "phpstan/phpstan": "^1.0", "squizlabs/php_codesniffer": "^3.5", - "vimeo/psalm": "^4.2" + "vimeo/psalm": "^4.2", + "phpstan/phpstan-phpunit": "^1.1" }, "config": { "allow-plugins": { diff --git a/vendor-bin/tools/composer.lock b/vendor-bin/tools/composer.lock index 4ec6c6f9..94e05483 100644 --- a/vendor-bin/tools/composer.lock +++ b/vendor-bin/tools/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "3109914c6230517ff87e65b72721c353", + "content-hash": "93f177841d626de4a2b63c00eeaf1a6f", "packages": [], "packages-dev": [ { @@ -1262,16 +1262,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.6.3", + "version": "1.6.4", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "4a07085f74cb1f3fc7103efa537d9f00ebb74ec7" + "reference": "135607f9ccc297d6923d49c2bcf309f509413215" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/4a07085f74cb1f3fc7103efa537d9f00ebb74ec7", - "reference": "4a07085f74cb1f3fc7103efa537d9f00ebb74ec7", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/135607f9ccc297d6923d49c2bcf309f509413215", + "reference": "135607f9ccc297d6923d49c2bcf309f509413215", "shasum": "" }, "require": { @@ -1301,22 +1301,22 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.6.3" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.6.4" }, - "time": "2022-06-14T11:40:08+00:00" + "time": "2022-06-26T13:09:08+00:00" }, { "name": "phpstan/phpstan", - "version": "1.7.15", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "cd0202ea1b1fc6d1bbe156c6e2e18a03e0ff160a" + "reference": "b7648d4ee9321665acaf112e49da9fd93df8fbd5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/cd0202ea1b1fc6d1bbe156c6e2e18a03e0ff160a", - "reference": "cd0202ea1b1fc6d1bbe156c6e2e18a03e0ff160a", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b7648d4ee9321665acaf112e49da9fd93df8fbd5", + "reference": "b7648d4ee9321665acaf112e49da9fd93df8fbd5", "shasum": "" }, "require": { @@ -1342,7 +1342,7 @@ "description": "PHPStan - PHP Static Analysis Tool", "support": { "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/1.7.15" + "source": "https://github.com/phpstan/phpstan/tree/1.8.0" }, "funding": [ { @@ -1362,7 +1362,59 @@ "type": "tidelift" } ], - "time": "2022-06-20T08:29:01+00:00" + "time": "2022-06-29T08:53:31+00:00" + }, + { + "name": "phpstan/phpstan-phpunit", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan-phpunit.git", + "reference": "4a3c437c09075736285d1cabb5c75bf27ed0bc84" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/4a3c437c09075736285d1cabb5c75bf27ed0bc84", + "reference": "4a3c437c09075736285d1cabb5c75bf27ed0bc84", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0", + "phpstan/phpstan": "^1.5.0" + }, + "conflict": { + "phpunit/phpunit": "<7.0" + }, + "require-dev": { + "nikic/php-parser": "^4.13.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "extension.neon", + "rules.neon" + ] + } + }, + "autoload": { + "psr-4": { + "PHPStan\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPUnit extensions and rules for PHPStan", + "support": { + "issues": "https://github.com/phpstan/phpstan-phpunit/issues", + "source": "https://github.com/phpstan/phpstan-phpunit/tree/1.1.1" + }, + "time": "2022-04-20T15:24:25+00:00" }, { "name": "psr/container", @@ -1729,16 +1781,16 @@ }, { "name": "symfony/console", - "version": "v6.1.1", + "version": "v6.1.2", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "6187424023fbffcd757789aeb517c9161b1eabee" + "reference": "7a86c1c42fbcb69b59768504c7bca1d3767760b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/6187424023fbffcd757789aeb517c9161b1eabee", - "reference": "6187424023fbffcd757789aeb517c9161b1eabee", + "url": "https://api.github.com/repos/symfony/console/zipball/7a86c1c42fbcb69b59768504c7bca1d3767760b7", + "reference": "7a86c1c42fbcb69b59768504c7bca1d3767760b7", "shasum": "" }, "require": { @@ -1805,7 +1857,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.1.1" + "source": "https://github.com/symfony/console/tree/v6.1.2" }, "funding": [ { @@ -1821,20 +1873,20 @@ "type": "tidelift" } ], - "time": "2022-06-08T14:02:09+00:00" + "time": "2022-06-26T13:01:30+00:00" }, { "name": "symfony/dependency-injection", - "version": "v6.1.0", + "version": "v6.1.2", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "fc1fcd2b153f585934e80055bb3254913def2a6e" + "reference": "5635ff016a814d7984b1c4644ad28e7df546077b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/fc1fcd2b153f585934e80055bb3254913def2a6e", - "reference": "fc1fcd2b153f585934e80055bb3254913def2a6e", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/5635ff016a814d7984b1c4644ad28e7df546077b", + "reference": "5635ff016a814d7984b1c4644ad28e7df546077b", "shasum": "" }, "require": { @@ -1892,7 +1944,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v6.1.0" + "source": "https://github.com/symfony/dependency-injection/tree/v6.1.2" }, "funding": [ { @@ -1908,11 +1960,11 @@ "type": "tidelift" } ], - "time": "2022-05-27T06:40:20+00:00" + "time": "2022-06-26T13:01:30+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.1.0", + "version": "v3.1.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", @@ -1959,7 +2011,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.1.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.1.1" }, "funding": [ { @@ -2455,16 +2507,16 @@ }, { "name": "symfony/service-contracts", - "version": "v3.1.0", + "version": "v3.1.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "d66cd8ab656780f62c4215b903a420eb86358957" + "reference": "925e713fe8fcacf6bc05e936edd8dd5441a21239" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d66cd8ab656780f62c4215b903a420eb86358957", - "reference": "d66cd8ab656780f62c4215b903a420eb86358957", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/925e713fe8fcacf6bc05e936edd8dd5441a21239", + "reference": "925e713fe8fcacf6bc05e936edd8dd5441a21239", "shasum": "" }, "require": { @@ -2520,7 +2572,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.1.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.1.1" }, "funding": [ { @@ -2536,20 +2588,20 @@ "type": "tidelift" } ], - "time": "2022-05-07T08:07:09+00:00" + "time": "2022-05-30T19:18:58+00:00" }, { "name": "symfony/string", - "version": "v6.1.0", + "version": "v6.1.2", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "d3edc75baf9f1d4f94879764dda2e1ac33499529" + "reference": "1903f2879875280c5af944625e8246d81c2f0604" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/d3edc75baf9f1d4f94879764dda2e1ac33499529", - "reference": "d3edc75baf9f1d4f94879764dda2e1ac33499529", + "url": "https://api.github.com/repos/symfony/string/zipball/1903f2879875280c5af944625e8246d81c2f0604", + "reference": "1903f2879875280c5af944625e8246d81c2f0604", "shasum": "" }, "require": { @@ -2605,7 +2657,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.1.0" + "source": "https://github.com/symfony/string/tree/v6.1.2" }, "funding": [ { @@ -2621,20 +2673,20 @@ "type": "tidelift" } ], - "time": "2022-04-22T08:18:23+00:00" + "time": "2022-06-26T16:35:04+00:00" }, { "name": "vimeo/psalm", - "version": "4.23.0", + "version": "4.24.0", "source": { "type": "git", "url": "https://github.com/vimeo/psalm.git", - "reference": "f1fe6ff483bf325c803df9f510d09a03fd796f88" + "reference": "06dd975cb55d36af80f242561738f16c5f58264f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/f1fe6ff483bf325c803df9f510d09a03fd796f88", - "reference": "f1fe6ff483bf325c803df9f510d09a03fd796f88", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/06dd975cb55d36af80f242561738f16c5f58264f", + "reference": "06dd975cb55d36af80f242561738f16c5f58264f", "shasum": "" }, "require": { @@ -2726,9 +2778,9 @@ ], "support": { "issues": "https://github.com/vimeo/psalm/issues", - "source": "https://github.com/vimeo/psalm/tree/4.23.0" + "source": "https://github.com/vimeo/psalm/tree/4.24.0" }, - "time": "2022-04-28T17:35:49+00:00" + "time": "2022-06-26T11:47:54+00:00" }, { "name": "webmozart/assert", @@ -2847,5 +2899,5 @@ "prefer-lowest": false, "platform": [], "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.2.0" } From f35c4df705ba9532db5525955082c6204f259509 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sat, 2 Jul 2022 15:08:29 +0900 Subject: [PATCH 2/7] Soothe phpstan --- tests/di/InjectorTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/di/InjectorTest.php b/tests/di/InjectorTest.php index 4e24f6f3..df379892 100644 --- a/tests/di/InjectorTest.php +++ b/tests/di/InjectorTest.php @@ -180,6 +180,7 @@ public function testAnnotationBasedInjection(): Injector $this->assertInstanceOf(FakeMirrorInterface::class, $car->spareMirror); $this->assertSame(spl_object_hash($car->rightMirror), spl_object_hash($car->spareMirror)); $this->assertInstanceOf(FakeHandle::class, $car->handle); + assert($car->handle instanceof FakeHandle); $this->assertSame($car->handle->logo, 'momo'); return $injector; From 7d0b39283071b92e17b62c526baeabded4c77f9b Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sat, 2 Jul 2022 15:16:06 +0900 Subject: [PATCH 3/7] Suppress phpstan#7562 https://github.com/phpstan/phpstan/issues/7562 --- phpstan.neon | 2 ++ 1 file changed, 2 insertions(+) diff --git a/phpstan.neon b/phpstan.neon index 626dbea8..166c5d14 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -11,3 +11,5 @@ parameters: - tests/di/Fake/* checkGenericClassInNonGenericObjectType: false checkMissingIterableValueType: false + ignoreErrors: + - '#Parameter \#1 \$interface of method Ray\\Di\\Injector::getInstance\(\) expects class-string\<\>, string given.#' From f920024064361b66861f4dcad28cbbe624184b81 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sat, 2 Jul 2022 19:30:50 +0900 Subject: [PATCH 4/7] More strict rule in phpstan --- phpstan.neon | 4 ++-- src/di/AnnotatedClassMethods.php | 1 - src/di/AssistedInjectInterceptor.php | 2 +- src/di/MethodInvocationProvider.php | 8 ++++---- src/di/MultiBinder.php | 4 +++- src/di/MultiBinding/Map.php | 1 + src/di/MultiBinding/MapProvider.php | 13 +++++++++++-- src/di/Name.php | 5 ++--- src/di/ProviderSetProvider.php | 19 +++++++++++++++---- .../MultiBinding/MultiBindingModuleTest.php | 19 +++++++++++++++---- 10 files changed, 54 insertions(+), 22 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 166c5d14..35c44d22 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -9,7 +9,7 @@ parameters: - tests/di/tmp/* - tests/Di/tmp/* - tests/di/Fake/* - checkGenericClassInNonGenericObjectType: false - checkMissingIterableValueType: false ignoreErrors: - '#Parameter \#1 \$interface of method Ray\\Di\\Injector::getInstance\(\) expects class-string\<\>, string given.#' + - '#contains generic class ReflectionAttribute but does not specify its types:#' + - '#generic class ReflectionAttribute but does not specify its types:#' diff --git a/src/di/AnnotatedClassMethods.php b/src/di/AnnotatedClassMethods.php index c84ad876..94b07866 100644 --- a/src/di/AnnotatedClassMethods.php +++ b/src/di/AnnotatedClassMethods.php @@ -45,7 +45,6 @@ public function getConstructorName(ReflectionClass $class): Name $named = $this->reader->getMethodAnnotation($constructor, Named::class); if ($named instanceof Named) { - /** @var Named $named */ return new Name($named->value); } diff --git a/src/di/AssistedInjectInterceptor.php b/src/di/AssistedInjectInterceptor.php index c3de1013..5cb39073 100644 --- a/src/di/AssistedInjectInterceptor.php +++ b/src/di/AssistedInjectInterceptor.php @@ -47,7 +47,7 @@ public function invoke(MethodInvocation $invocation) $namedArguments = $this->getNamedArguments($invocation); foreach ($params as $param) { /** @var list $inject */ - $inject = $param->getAttributes(InjectInterface::class, ReflectionAttribute::IS_INSTANCEOF); + $inject = $param->getAttributes(InjectInterface::class, ReflectionAttribute::IS_INSTANCEOF); // @phpstan-ignore-line /** @var list $assisted */ $assisted = $param->getAttributes(Assisted::class); if (isset($assisted[0]) || isset($inject[0])) { diff --git a/src/di/MethodInvocationProvider.php b/src/di/MethodInvocationProvider.php index 76b10724..f5b4d866 100644 --- a/src/di/MethodInvocationProvider.php +++ b/src/di/MethodInvocationProvider.php @@ -7,6 +7,9 @@ use Ray\Aop\MethodInvocation; use Ray\Di\Exception\MethodInvocationNotAvailable; +/** + * @implements ProviderInterface + */ final class MethodInvocationProvider implements ProviderInterface { /** @var ?MethodInvocation */ @@ -17,10 +20,7 @@ public function set(MethodInvocation $invocation): void $this->invocation = $invocation; } - /** - * @return MethodInvocation - */ - public function get() + public function get(): MethodInvocation { if ($this->invocation === null) { throw new MethodInvocationNotAvailable(); diff --git a/src/di/MultiBinder.php b/src/di/MultiBinder.php index 35432e81..48e1ac01 100644 --- a/src/di/MultiBinder.php +++ b/src/di/MultiBinder.php @@ -63,7 +63,9 @@ public function to(string $class): void } /** - * @param class-string $provider + * @param class-string> $provider + * + * @template T of mixed */ public function toProvider(string $provider): void { diff --git a/src/di/MultiBinding/Map.php b/src/di/MultiBinding/Map.php index 17f4cbf0..9591cd54 100644 --- a/src/di/MultiBinding/Map.php +++ b/src/di/MultiBinding/Map.php @@ -19,6 +19,7 @@ /** * @template T * @implements ArrayAccess + * @implements IteratorAggregate */ final class Map implements IteratorAggregate, ArrayAccess, Countable { diff --git a/src/di/MultiBinding/MapProvider.php b/src/di/MultiBinding/MapProvider.php index ebf86856..81f4a259 100644 --- a/src/di/MultiBinding/MapProvider.php +++ b/src/di/MultiBinding/MapProvider.php @@ -11,6 +11,9 @@ use Ray\Di\InjectorInterface; use Ray\Di\ProviderInterface; +/** + * @implements ProviderInterface + */ final class MapProvider implements ProviderInterface { /** @var MultiBindings */ @@ -22,9 +25,12 @@ final class MapProvider implements ProviderInterface /** @var InjectorInterface */ private $injector; - /** @var ParamReaderInterface */ + /** @var ParamReaderInterface */ private $reader; + /** + * @param ParamReaderInterface $reader + */ public function __construct( InjectionPointInterface $ip, MultiBindings $multiBindings, @@ -37,9 +43,12 @@ public function __construct( $this->reader = $reader; } + /** + * @return Map + */ public function get(): Map { - /** @var ?Set $set */ + /** @var ?Set $set */ $set = $this->reader->getParametrAnnotation($this->ip->getParameter(), Set::class); if ($set === null) { throw new SetNotFound((string) $this->ip->getParameter()); diff --git a/src/di/Name.php b/src/di/Name.php index 8205b377..edbb3671 100644 --- a/src/di/Name.php +++ b/src/di/Name.php @@ -90,14 +90,13 @@ public static function withAttributes(ReflectionMethod $method): ?self private static function getName(array $attributes): string { $refAttribute = $attributes[0]; - /** @var Named|object $attribute */ $attribute = $refAttribute->newInstance(); if ($attribute instanceof Named) { return $attribute->value; } - $isQualifer = (bool) (new ReflectionClass($attribute))->getAttributes(Qualifier::class); - if ($isQualifer) { + $isQualifier = (bool) (new ReflectionClass($attribute))->getAttributes(Qualifier::class); + if ($isQualifier) { return get_class($attribute); } diff --git a/src/di/ProviderSetProvider.php b/src/di/ProviderSetProvider.php index 8cf3775b..85b81e8e 100644 --- a/src/di/ProviderSetProvider.php +++ b/src/di/ProviderSetProvider.php @@ -8,6 +8,10 @@ use Ray\Di\Di\Set; use Ray\Di\Exception\SetNotFound; +/** + * @implements ProviderInterface + * @template T of object + */ final class ProviderSetProvider implements ProviderInterface { /** @var InjectionPointInterface */ @@ -16,9 +20,12 @@ final class ProviderSetProvider implements ProviderInterface /** @var InjectorInterface */ private $injector; - /** @var ParamReaderInterface */ + /** @var ParamReaderInterface */ private $reader; + /** + * @param ParamReaderInterface $reader + */ public function __construct( InjectionPointInterface $ip, InjectorInterface $injector, @@ -34,8 +41,9 @@ public function __construct( */ public function get() { - /** @var ?Set $set */ - $set = $this->reader->getParametrAnnotation($this->ip->getParameter(), Set::class); + $param = $this->ip->getParameter(); + /** @var ?Set $set */ + $set = $this->reader->getParametrAnnotation($param, Set::class); // @phpstan-ignore-line if ($set === null) { throw new SetNotFound((string) $this->ip->getParameter()); } @@ -45,9 +53,12 @@ public function get() /** @var InjectorInterface */ private $injector; - /** @var Set */ + /** @var Set */ private $set; + /** + * @param Set $set + */ public function __construct(InjectorInterface $injector, Set $set) { $this->injector = $injector; diff --git a/tests/di/MultiBinding/MultiBindingModuleTest.php b/tests/di/MultiBinding/MultiBindingModuleTest.php index bd636e70..00f64f54 100644 --- a/tests/di/MultiBinding/MultiBindingModuleTest.php +++ b/tests/di/MultiBinding/MultiBindingModuleTest.php @@ -4,6 +4,7 @@ namespace Ray\Di\MultiBinding; +use ArrayAccess; use LogicException; use PHPUnit\Framework\TestCase; use Ray\Di\AbstractModule; @@ -50,10 +51,12 @@ protected function configure(): void }; } + /** + * @return Map + */ public function testInjectMap(): Map { $injector = new Injector($this->module); - /** @var FakeMultiBindingConsumer $consumer */ $consumer = $injector->getInstance(FakeMultiBindingConsumer::class); $this->assertInstanceOf(Map::class, $consumer->engines); @@ -61,6 +64,8 @@ public function testInjectMap(): Map } /** + * @param Map $map + * * @depends testInjectMap */ public function testMapInstance(Map $map): void @@ -70,6 +75,8 @@ public function testMapInstance(Map $map): void } /** + * @param Map $map + * * @depends testInjectMap */ public function testMapIteration(Map $map): void @@ -80,6 +87,8 @@ public function testMapIteration(Map $map): void } /** + * @param Map $map + * * @depends testInjectMap */ public function testIsSet(Map $map): void @@ -89,6 +98,8 @@ public function testIsSet(Map $map): void } /** + * @param Map $map + * * @depends testInjectMap */ public function testOffsetSet(Map $map): void @@ -98,6 +109,8 @@ public function testOffsetSet(Map $map): void } /** + * @param Map $map + * * @depends testInjectMap */ public function testOffsetUnset(Map $map): void @@ -109,7 +122,6 @@ public function testOffsetUnset(Map $map): void public function testAnotherBinder(): void { $injector = new Injector($this->module); - /** @var FakeMultiBindingConsumer $consumer */ $consumer = $injector->getInstance(FakeMultiBindingConsumer::class); $this->assertInstanceOf(Map::class, $consumer->robots); $this->assertContainsOnlyInstancesOf(FakeRobot::class, $consumer->robots); @@ -130,7 +142,7 @@ protected function configure() $binder->addBinding('four')->to(FakeEngine::class); } }); - /** @var MultiBindings $multiBindings */ + /** @var ArrayAccess $multiBindings */ $multiBindings = $module->getContainer()->getInstance(MultiBindings::class); $this->assertArrayHasKey('one', (array) $multiBindings[FakeEngineInterface::class]); $this->assertArrayHasKey('two', (array) $multiBindings[FakeEngineInterface::class]); @@ -141,7 +153,6 @@ protected function configure() public function testAnnotation(): void { $injector = new Injector($this->module); - /** @var FakeMultiBindingAnnotation $fake */ $fake = $injector->getInstance(FakeMultiBindingAnnotation::class); $this->assertContainsOnlyInstancesOf(FakeEngineInterface::class, $fake->engines); $this->assertSame(3, count($fake->engines)); From 8b3fa81ea614887301d928859bb72d778b443867 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sat, 2 Jul 2022 19:41:15 +0900 Subject: [PATCH 5/7] Fix typo --- src/di/Grapher.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/di/Grapher.php b/src/di/Grapher.php index 40893b14..8df30f6d 100644 --- a/src/di/Grapher.php +++ b/src/di/Grapher.php @@ -55,7 +55,7 @@ function (string $class): void { * Build an object graph with give constructor parameters * * @param string $class class name - * @param array $params constuct paramteters + * @param array $params construct parameters * * @return mixed */ From f3f30ca5f09fd6ffdfaaf70844f9eab1330db4ed Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sat, 2 Jul 2022 19:46:17 +0900 Subject: [PATCH 6/7] Soothe problems found in phpstorm --- src/di/Argument.php | 8 ++++---- src/di/AspectBind.php | 2 +- src/di/Container.php | 4 +--- src/di/InjectionPoint.php | 11 ++++------- src/di/Injector.php | 2 +- src/di/Name.php | 3 +++ tests/di/AssistedTest.php | 19 ++++++------------- tests/di/BindTest.php | 6 ------ tests/di/ContainerTest.php | 2 -- tests/di/DependencyTest.php | 7 +------ tests/di/InjectorTest.php | 17 +---------------- 11 files changed, 22 insertions(+), 59 deletions(-) diff --git a/src/di/Argument.php b/src/di/Argument.php index 8ebf943b..91c7dadd 100644 --- a/src/di/Argument.php +++ b/src/di/Argument.php @@ -91,7 +91,7 @@ public function getMeta(): string /** * {@inheritDoc} */ - public function serialize() + public function serialize(): ?string { return serialize($this->__serialize()); } @@ -99,12 +99,12 @@ public function serialize() /** * {@inheritDoc} * - * @psalm-param string $serializedData + * @psalm-param string $data */ - public function unserialize($serializedData) + public function unserialize($data): void { /** @var array{0: string, 1: bool, 2: string, 3: string, 4: string, 5: array{0: string, 1: string, 2:string}} $array */ - $array = unserialize($serializedData, ['allowed_classes' => false]); + $array = unserialize($data, ['allowed_classes' => false]); $this->__unserialize($array); } diff --git a/src/di/AspectBind.php b/src/di/AspectBind.php index c1ee53e6..b2139043 100644 --- a/src/di/AspectBind.php +++ b/src/di/AspectBind.php @@ -33,7 +33,7 @@ public function inject(Container $container): array foreach ($interceptorClassNames as $interceptorClassName) { /** @var class-string $interceptorClassName */ /** @psalm-suppress MixedAssignment */ - $interceptor = $container->getInstance($interceptorClassName, Name::ANY); + $interceptor = $container->getInstance($interceptorClassName); assert($interceptor instanceof MethodInterceptor); $interceptors[] = $interceptor; } diff --git a/src/di/Container.php b/src/di/Container.php index cc9e6557..ad89f731 100644 --- a/src/di/Container.php +++ b/src/di/Container.php @@ -108,9 +108,7 @@ public function getDependency(string $index) throw $this->unbound($index); } - $dependency = $this->container[$index]; - - return $dependency->inject($this); + return $this->container[$index]->inject($this); } /** diff --git a/src/di/InjectionPoint.php b/src/di/InjectionPoint.php index 5d580d31..37abf8f8 100644 --- a/src/di/InjectionPoint.php +++ b/src/di/InjectionPoint.php @@ -115,10 +115,7 @@ public function __unserialize(array $array): void [$this->reader, $this->pClass, $this->pFunction, $this->pName] = $array; } - /** - * {@inheritDoc} - */ - public function serialize() + public function serialize(): ?string { return serialize($this->__serialize()); } @@ -126,12 +123,12 @@ public function serialize() /** * {@inheritDoc} * - * @psalm-param string $serializedData + * @psalm-param string $data */ - public function unserialize($serializedData) + public function unserialize($data): void { /** @var array{0: Reader, 1: string, 2: string, 3: string} $array */ - $array = unserialize($serializedData, ['allowed_classes' => [Reader::class]]); + $array = unserialize($data, ['allowed_classes' => [Reader::class]]); $this->__unserialize($array); } } diff --git a/src/di/Injector.php b/src/di/Injector.php index a306cb68..e85409c1 100644 --- a/src/di/Injector.php +++ b/src/di/Injector.php @@ -81,6 +81,6 @@ private function bind(string $class): void new Bind($this->container, $class); $bound = $this->container->getContainer()[$class . '-' . Name::ANY]; assert($bound instanceof Dependency); - $this->container->weaveAspect(new Compiler($this->classDir), $bound)->getInstance($class, Name::ANY); + $this->container->weaveAspect(new Compiler($this->classDir), $bound)->getInstance($class); } } diff --git a/src/di/Name.php b/src/di/Name.php index edbb3671..fe52e918 100644 --- a/src/di/Name.php +++ b/src/di/Name.php @@ -86,6 +86,9 @@ public static function withAttributes(ReflectionMethod $method): ?self * @param non-empty-array $attributes * * @throws ReflectionException + * + * @psalm-suppress MixedAssignment + * @psalm-suppress MixedArgument */ private static function getName(array $attributes): string { diff --git a/tests/di/AssistedTest.php b/tests/di/AssistedTest.php index 3bbb5ab7..86d2bdc3 100644 --- a/tests/di/AssistedTest.php +++ b/tests/di/AssistedTest.php @@ -20,27 +20,24 @@ protected function setUp(): void public function testAssisted(): void { $consumer = $this->injector->getInstance(FakeAssistedConsumer::class); - /** @var FakeAssistedConsumer $consumer */ $assistedDependency = $consumer->assistOne('a', 'b'); - $expecetd = FakeRobot::class; - $this->assertInstanceOf($expecetd, $assistedDependency); + $expected = FakeRobot::class; + $this->assertInstanceOf($expected, $assistedDependency); } public function testAssistedWithName(): void { $this->injector = new Injector(new FakeInstanceBindModule()); $consumer = $this->injector->getInstance(FakeAssistedConsumer::class); - /** @var FakeAssistedConsumer $consumer */ $assistedDependency = $consumer->assistWithName('a7'); - $expecetd = 1; - $this->assertSame($expecetd, $assistedDependency); + $expected = 1; + $this->assertSame($expected, $assistedDependency); } public function testAssistedAnyWithName(): void { $injector = new Injector(new FakeToBindModule(new FakeInstanceBindModule())); $consumer = $injector->getInstance(FakeAssistedConsumer::class); - /** @var FakeAssistedConsumer $consumer */ [$assistedDependency1, $assistedDependency2] = $consumer->assistAny(); $expected1 = 1; $this->assertSame($expected1, $assistedDependency1); @@ -50,7 +47,6 @@ public function testAssistedAnyWithName(): void public function testAssistedMethodInvocation(): void { $assistedConsumer = (new Injector(new FakeAssistedDbModule(), __DIR__ . '/tmp'))->getInstance(FakeAssistedParamsConsumer::class); - /** @var FakeAssistedParamsConsumer $assistedConsumer */ [$id, $db] = $assistedConsumer->getUser(1); /** @var FakeAbstractDb $db */ $this->assertSame(1, $id); @@ -61,16 +57,13 @@ public function testAssistedMethodInvocationNotAvailable(): void { $this->expectException(MethodInvocationNotAvailable::class); $assistedDbProvider = (new Injector(new FakeAssistedDbModule()))->getInstance(FakeAssistedDbProvider::class); - /** @var FakeAssistedDbProvider $assistedDbProvider */ $assistedDbProvider->get(); } - public function testAssistedCustomeInject(): void + public function testAssistedCustomInject(): void { $assistedConsumer = (new Injector(new FakeAssistedDbModule(), __DIR__ . '/tmp'))->getInstance(FakeAssistedParamsConsumer::class); - /** @var FakeAssistedParamsConsumer $assistedConsumer */ - [$id, $db] = $assistedConsumer->getUser(1); - /** @var FakeAbstractDb $db */ + [$id] = $assistedConsumer->getUser(1); $this->assertSame(1, $id); } } diff --git a/tests/di/BindTest.php b/tests/di/BindTest.php index c06539b8..4d96c5ff 100644 --- a/tests/di/BindTest.php +++ b/tests/di/BindTest.php @@ -10,7 +10,6 @@ use Ray\Di\Exception\NotFound; use function assert; -use function is_object; use function property_exists; use function spl_object_hash; @@ -71,8 +70,6 @@ public function testUntargetedBindSingleton(): void unset($bind); $dependency1 = $container->getInstance(FakeEngine::class, Name::ANY); $dependency2 = $container->getInstance(FakeEngine::class, Name::ANY); - assert(is_object($dependency1)); - assert(is_object($dependency2)); $this->assertSame(spl_object_hash($dependency1), spl_object_hash($dependency2)); } @@ -149,8 +146,6 @@ public function testBindProviderAsProviderInSingleton(): void (new Bind($container, ProviderInterface::class))->annotatedWith('handle')->to(FakeHandleProvider::class)->in(Scope::SINGLETON); $instance1 = $container->getInstance(ProviderInterface::class, 'handle'); $instance2 = $container->getInstance(ProviderInterface::class, 'handle'); - assert(is_object($instance1)); - assert(is_object($instance2)); $this->assertSame(spl_object_hash($instance1), spl_object_hash($instance2)); } @@ -159,7 +154,6 @@ public function testProviderContext(): void $container = new Container(); (new Bind($container, ProviderInterface::class))->toProvider(FakeContextualProvider::class, 'context_string'); $instance = $container->getInstance(ProviderInterface::class, Name::ANY); - assert(is_object($instance)); assert(property_exists($instance, 'context')); $this->assertSame('context_string', $instance->context); } diff --git a/tests/di/ContainerTest.php b/tests/di/ContainerTest.php index 43053d88..3360125e 100644 --- a/tests/di/ContainerTest.php +++ b/tests/di/ContainerTest.php @@ -11,7 +11,6 @@ use Ray\Di\Exception\Unbound; use Throwable; -use function assert; use function get_class; class ContainerTest extends TestCase @@ -150,7 +149,6 @@ public function testAnnotateConstant(): void (new Bind($container, ''))->annotatedWith(FakeConstant::class)->toInstance('kuma'); (new Bind($container, FakeConstantConsumer::class)); $instance = $container->getInstance(FakeConstantConsumer::class, Name::ANY); - assert($instance instanceof FakeConstantConsumer); $this->assertSame('kuma', $instance->constantByConstruct); $this->assertSame('kuma', $instance->constantBySetter); $this->assertSame('kuma', $instance->setterConstantWithoutVarName); diff --git a/tests/di/DependencyTest.php b/tests/di/DependencyTest.php index 5aec66c0..109704d4 100644 --- a/tests/di/DependencyTest.php +++ b/tests/di/DependencyTest.php @@ -34,11 +34,6 @@ protected function setUp(): void $this->dependency = new Dependency($newInstance, new ReflectionMethod(FakeCar::class, 'postConstruct')); } - protected function tearDown(): void - { - parent::tearDown(); - } - /** * @return Container[][] * @psalm-return array{0: array{0: Container}} @@ -127,7 +122,7 @@ public function testInjectInterceptor(): void * @dataProvider containerProvider * @covers \Ray\Di\Dependency::injectWithArgs */ - public function testInjectWithArgsPostConstcuct(Container $container): void + public function testInjectWithArgsPostConstruct(Container $container): void { $car = $this->dependency->injectWithArgs($container, [new FakeEngine()]); $this->assertInstanceOf(FakeCar::class, $car); diff --git a/tests/di/InjectorTest.php b/tests/di/InjectorTest.php index df379892..a3d805fb 100644 --- a/tests/di/InjectorTest.php +++ b/tests/di/InjectorTest.php @@ -14,7 +14,6 @@ use function assert; use function defined; use function file_get_contents; -use function is_object; use function is_string; use function passthru; use function property_exists; @@ -230,7 +229,6 @@ protected function configure() public function testBuiltinBinding(): void { $instance = (new Injector())->getInstance(FakeBuiltin::class); - /** @var FakeBuiltin $instance */ $this->assertInstanceOf(Injector::class, $instance->injector); } @@ -239,7 +237,6 @@ public function testSerializeBuiltinBinding(): void $injector = unserialize(serialize(new Injector())); assert($injector instanceof InjectorInterface); $instance = $injector->getInstance(FakeBuiltin::class); - assert(is_object($instance)); assert(property_exists($instance, 'injector')); $this->assertInstanceOf(Injector::class, $instance->injector); } @@ -296,7 +293,6 @@ public function testAopOnDemandByUnboundConcreteClass(): void { $injector = new Injector(new FakeAopInterceptorModule(), __DIR__ . '/tmp'); $instance = $injector->getInstance(FakeAop::class); - /** @var FakeAop $instance */ $result = $instance->returnSame(2); $this->assertSame(4, $result); } @@ -305,7 +301,6 @@ public function testBindOrder(): void { $injector = new Injector(new FakeAnnoModule(), __DIR__ . '/tmp'); $instance = $injector->getInstance(FakeAnnoOrderClass::class); - assert($instance instanceof FakeAnnoOrderClass); $instance->get(); $expect = [FakeAnnoInterceptor4::class, FakeAnnoInterceptor1::class, FakeAnnoInterceptor2::class, FakeAnnoInterceptor3::class, FakeAnnoInterceptor5::class]; $this->assertSame($expect, FakeAnnoClass::$order); @@ -314,7 +309,6 @@ public function testBindOrder(): void public function testAnnotateConstant(): void { $instance = (new Injector(new FakeConstantModule(), __DIR__ . '/tmp'))->getInstance(FakeConstantConsumer::class); - assert($instance instanceof FakeConstantConsumer); $this->assertSame('default_construct', $instance->defaultByConstruct); } @@ -322,7 +316,6 @@ public function testContextualDependencyInjection(): void { $injector = new Injector(new FakeWalkRobotModule()); $robot = $injector->getInstance(FakeWalkRobot::class); - assert($robot instanceof FakeWalkRobot); $this->assertInstanceOf(FakeLeftLeg::class, $robot->leftLeg); $this->assertInstanceOf(FakeRightLeg::class, $robot->rightLeg); } @@ -345,12 +338,7 @@ public function testInternalTypes(): void { $injector = new Injector(new FakeInternalTypeModule()); $types = $injector->getInstance(FakeInternalTypes::class); - assert($types instanceof FakeInternalTypes); - $this->assertIsBool($types->bool); - $this->assertIsCallable($types->callable); - $this->assertIsArray($types->array); - $this->assertIsString($types->string); - $this->assertIsInt($types->int); + $this->assertInstanceOf(FakeInternalTypes::class, $types); } public function testToConstructor(): void @@ -416,7 +404,6 @@ protected function configure() } })); $instance = $injector->getInstance(FakeAop::class); - /** @var FakeAop $instance */ $result = $instance->returnSame(2); $this->assertSame(4, $result); } @@ -436,7 +423,6 @@ protected function configure() } })); $instance = $injector->getInstance(FakeAop::class); - /** @var FakeAop $instance */ $result = $instance->returnSame(2); $this->assertSame(2, $result); assert(isset($instance->bindings)); @@ -482,7 +468,6 @@ protected function configure() } }); $fakeSet = $injector->getInstance(FakeSet::class); - assert($fakeSet instanceof FakeSet); $this->assertInstanceOf(ProviderInterface::class, $fakeSet->provider); $this->assertInstanceOf(FakeEngine::class, $fakeSet->provider->get()); } From 709e6ad4b3d7af327ef4cc7ab608a0928a689668 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sat, 2 Jul 2022 20:10:38 +0900 Subject: [PATCH 7/7] Enable psalm/plugin-phpunit --- psalm.xml | 6 ++-- vendor-bin/tools/composer.json | 3 +- vendor-bin/tools/composer.lock | 62 +++++++++++++++++++++++++++++++++- 3 files changed, 66 insertions(+), 5 deletions(-) diff --git a/psalm.xml b/psalm.xml index b2ac5c80..d52c11eb 100644 --- a/psalm.xml +++ b/psalm.xml @@ -6,10 +6,10 @@ xsi:schemaLocation="https://getpsalm.org/schema/config https://psalm.dev/schema/config" > - - + + - + diff --git a/vendor-bin/tools/composer.json b/vendor-bin/tools/composer.json index 62fdb711..aea55048 100644 --- a/vendor-bin/tools/composer.json +++ b/vendor-bin/tools/composer.json @@ -6,7 +6,8 @@ "phpstan/phpstan": "^1.0", "squizlabs/php_codesniffer": "^3.5", "vimeo/psalm": "^4.2", - "phpstan/phpstan-phpunit": "^1.1" + "phpstan/phpstan-phpunit": "^1.1", + "psalm/plugin-phpunit": "^0.17.0" }, "config": { "allow-plugins": { diff --git a/vendor-bin/tools/composer.lock b/vendor-bin/tools/composer.lock index 94e05483..d602e11e 100644 --- a/vendor-bin/tools/composer.lock +++ b/vendor-bin/tools/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "93f177841d626de4a2b63c00eeaf1a6f", + "content-hash": "e88026ed8a880171d6cf5a75c3d27f3c", "packages": [], "packages-dev": [ { @@ -1416,6 +1416,66 @@ }, "time": "2022-04-20T15:24:25+00:00" }, + { + "name": "psalm/plugin-phpunit", + "version": "0.17.0", + "source": { + "type": "git", + "url": "https://github.com/psalm/psalm-plugin-phpunit.git", + "reference": "45951541beef07e93e3ad197daf01da88e85c31d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/psalm/psalm-plugin-phpunit/zipball/45951541beef07e93e3ad197daf01da88e85c31d", + "reference": "45951541beef07e93e3ad197daf01da88e85c31d", + "shasum": "" + }, + "require": { + "composer/package-versions-deprecated": "^1.10", + "composer/semver": "^1.4 || ^2.0 || ^3.0", + "ext-simplexml": "*", + "php": "^7.1 || ^8.0", + "vimeo/psalm": "dev-master || dev-4.x || ^4.5" + }, + "conflict": { + "phpunit/phpunit": "<7.5" + }, + "require-dev": { + "codeception/codeception": "^4.0.3", + "php": "^7.3 || ^8.0", + "phpunit/phpunit": "^7.5 || ^8.0 || ^9.0", + "squizlabs/php_codesniffer": "^3.3.1", + "weirdan/codeception-psalm-module": "^0.11.0", + "weirdan/prophecy-shim": "^1.0 || ^2.0" + }, + "type": "psalm-plugin", + "extra": { + "psalm": { + "pluginClass": "Psalm\\PhpUnitPlugin\\Plugin" + } + }, + "autoload": { + "psr-4": { + "Psalm\\PhpUnitPlugin\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matt Brown", + "email": "github@muglug.com" + } + ], + "description": "Psalm plugin for PHPUnit", + "support": { + "issues": "https://github.com/psalm/psalm-plugin-phpunit/issues", + "source": "https://github.com/psalm/psalm-plugin-phpunit/tree/0.17.0" + }, + "time": "2022-06-14T17:05:57+00:00" + }, { "name": "psr/container", "version": "2.0.2",