diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 90608f0..8ebe78e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,7 +6,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php: [ '8.0', '8.1', '8.2', '8.3', '8.4' ] + php: [ '8.2', '8.3', '8.4', '8.5' ] steps: - uses: actions/checkout@master diff --git a/Makefile b/Makefile index 4f33df0..89d71d8 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,8 @@ .PHONY: tests tests: vendor - php -d zend.assertions=1 -d assert.exception=1 vendor/bin/peridot ./specs + php vendor/bin/pest -vendor: composer.json composer.phar - php composer.phar install +vendor: composer.json + composer install touch vendor - -composer.phar: - php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" - php -r "if (hash_file('sha384', 'composer-setup.php') === 'dac665fdc30fdd8ec78b38b9800061b4150413ff2e3b6f88543c636f7cd84f6db9189d43a81e5503cda447da73c7e5b6') { echo 'Installer verified'.PHP_EOL; } else { echo 'Installer corrupt'.PHP_EOL; unlink('composer-setup.php'); exit(1); }" - php composer-setup.php - php -r "unlink('composer-setup.php');" \ No newline at end of file diff --git a/composer.json b/composer.json index 00739ad..9eda63e 100644 --- a/composer.json +++ b/composer.json @@ -9,14 +9,14 @@ "auto-wiring" ], "require": { - "php": "^8.0", + "php": "^8.2", "psr/container": "^2.0", "technically/null-container": "^2.0", - "technically/callable-reflection": "^0.4.2" + "technically/callable-reflection": "^0.5.0" }, "require-dev": { - "peridot-php/peridot": "^1.19", - "technically/array-container": "^2.0" + "technically/array-container": "^2.0", + "pestphp/pest": "^2.0 || ^3.0 || ^4.0" }, "license": "MIT", "authors": [ @@ -33,7 +33,12 @@ }, "autoload-dev": { "psr-4": { - "Technically\\DependencyResolver\\Specs\\": "specs" + "Technically\\DependencyResolver\\Tests\\": "tests" + } + }, + "config": { + "allow-plugins": { + "pestphp/pest-plugin": true } } } diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..37c9c51 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,17 @@ + + + + + ./tests + + + + + src + + + diff --git a/specs/Fixtures/MyAbstractClass.php b/specs/Fixtures/MyAbstractClass.php deleted file mode 100644 index dccc695..0000000 --- a/specs/Fixtures/MyAbstractClass.php +++ /dev/null @@ -1,8 +0,0 @@ -parent = $parent; - } -} diff --git a/src/Contracts/DependencyResolver.php b/src/Contracts/DependencyResolver.php index 7582f3b..bcd369d 100644 --- a/src/Contracts/DependencyResolver.php +++ b/src/Contracts/DependencyResolver.php @@ -17,8 +17,9 @@ interface DependencyResolver * It can be either found in the container or constructed on the fly, * recursively auto-resolving the required parameters. * - * @param class-string $className - * @return mixed + * @template T + * @param class-string $className + * @return T * * @throws InvalidArgumentException If the class does not exist. * @throws ContainerExceptionInterface If an error occurs while retrieving the existing entry from the container. @@ -33,9 +34,10 @@ public function resolve(string $className): mixed; * Even if the container already has the instance bound, * it will still be instantiated. * - * @param class-string $className + * @template T + * @param class-string $className * @param array $bindings - * @return mixed + * @return T * * @throws ClassCannotBeInstantiated * @throws CannotAutowireDependencyArgument diff --git a/src/DependencyResolver.php b/src/DependencyResolver.php index 9ec9f8b..5b02801 100644 --- a/src/DependencyResolver.php +++ b/src/DependencyResolver.php @@ -25,8 +25,9 @@ public function __construct(?ContainerInterface $container = null) } /** - * @param class-string $className - * @return mixed + * @template T + * @param class-string $className + * @return T * * @throws InvalidArgumentException If class does not exist. * @throws ContainerExceptionInterface If error occurs while retrieving the existing entry from the container. @@ -47,9 +48,10 @@ public function resolve(string $className): mixed } /** - * @param class-string $className + * @template T + * @param class-string $className * @param array $bindings - * @return mixed + * @return T * * @throws ClassCannotBeInstantiated * @throws CannotAutowireDependencyArgument @@ -125,7 +127,6 @@ private function resolveParameters(array $parameters, array $bindings = []): arr } /** - * @param ParameterReflection $parameter * @return mixed|null * * @throws CannotAutowireArgument @@ -136,7 +137,7 @@ private function resolveParameter(ParameterReflection $parameter): mixed if ($type->isClassRequirement() && $this->container->has($class = $type->getClassRequirement())) { try { return $this->container->get($class); - } catch (ContainerExceptionInterface $exception) { + } catch (ContainerExceptionInterface) { throw new CannotAutowireArgument($parameter->getName()); } } @@ -155,7 +156,7 @@ private function resolveParameter(ParameterReflection $parameter): mixed $class = $type->getClassRequirement(); try { return $this->construct($class); - } catch (DependencyResolutionException $exception) { + } catch (DependencyResolutionException) { // try another one } } diff --git a/src/Exceptions/CannotAutowireArgument.php b/src/Exceptions/CannotAutowireArgument.php index 510c340..bf65c32 100644 --- a/src/Exceptions/CannotAutowireArgument.php +++ b/src/Exceptions/CannotAutowireArgument.php @@ -15,7 +15,7 @@ final class CannotAutowireArgument extends DependencyResolutionException * @param string $argumentName * @param Throwable|null $previous */ - public function __construct(string $argumentName, Throwable $previous = null) + public function __construct(string $argumentName, Throwable | null $previous = null) { $this->argumentName = $argumentName; diff --git a/src/Exceptions/CannotAutowireDependencyArgument.php b/src/Exceptions/CannotAutowireDependencyArgument.php index c1af508..ab5f139 100644 --- a/src/Exceptions/CannotAutowireDependencyArgument.php +++ b/src/Exceptions/CannotAutowireDependencyArgument.php @@ -21,12 +21,12 @@ final class CannotAutowireDependencyArgument extends DependencyResolutionExcepti * @param string $argumentName * @param Throwable|null $previous */ - public function __construct(string $dependencyName, string $argumentName, Throwable $previous = null) + public function __construct(string $dependencyName, string $argumentName, Throwable | null $previous = null) { $this->dependencyName = $dependencyName; $this->argumentName = $argumentName; - parent::__construct("Could not autowire argument `{$argumentName}` for `${dependencyName}`.", 0, $previous); + parent::__construct("Could not autowire argument `{$argumentName}` for `{$dependencyName}`.", 0, $previous); } /** diff --git a/src/Exceptions/ClassCannotBeInstantiated.php b/src/Exceptions/ClassCannotBeInstantiated.php index ab485cb..e291332 100644 --- a/src/Exceptions/ClassCannotBeInstantiated.php +++ b/src/Exceptions/ClassCannotBeInstantiated.php @@ -13,7 +13,7 @@ public function __construct(string $className) { $this->className = $className; - parent::__construct("Class (${className}) cannot be instantiated."); + parent::__construct("Class ({$className}) cannot be instantiated."); } /** diff --git a/specs/DependencyResolver.spec.php b/tests/DependencyResolver.spec.php similarity index 75% rename from specs/DependencyResolver.spec.php rename to tests/DependencyResolver.spec.php index 518b954..2edfb45 100644 --- a/specs/DependencyResolver.spec.php +++ b/tests/DependencyResolver.spec.php @@ -6,6 +6,6 @@ it('should instantiate', function () { $resolver = new DependencyResolver(); - assert($resolver instanceof DependencyResolver); + expect($resolver)->toBeInstanceOf(DependencyResolver::class); }); }); diff --git a/specs/DependencyResolver::call.spec.php b/tests/DependencyResolver::call.spec.php similarity index 76% rename from specs/DependencyResolver::call.spec.php rename to tests/DependencyResolver::call.spec.php index 77c61a7..50fc189 100644 --- a/specs/DependencyResolver::call.spec.php +++ b/tests/DependencyResolver::call.spec.php @@ -3,9 +3,9 @@ use Psr\Container\ContainerInterface; use Technically\ArrayContainer\ArrayContainer; use Technically\DependencyResolver\DependencyResolver; -use Technically\DependencyResolver\Specs\Fixtures\MyInstanceMethodService; -use Technically\DependencyResolver\Specs\Fixtures\MyInvokableService; -use Technically\DependencyResolver\Specs\Fixtures\MyStaticMethodService; +use Technically\DependencyResolver\Tests\Fixtures\MyInstanceMethodService; +use Technically\DependencyResolver\Tests\Fixtures\MyInvokableService; +use Technically\DependencyResolver\Tests\Fixtures\MyStaticMethodService; use Technically\NullContainer\NullContainer; describe('DependencyResolver::call()', function () { @@ -16,7 +16,7 @@ $value = $resolver->call('my_global_function', ['value' => $resolver]); - assert($value === true); + expect($value)->toBeTrue(); }); it('should call Closures resolving dependencies', function () { @@ -24,15 +24,15 @@ $resolver = new DependencyResolver($container); $container->set(DependencyResolver::class, $resolver); - [$r, $m, $n] = $resolver->call(function (DependencyResolver $resolver, string $message, int $else = null) { + [$r, $m, $n] = $resolver->call(function (DependencyResolver $resolver, string $message, ?int $else = null) { return [$resolver, $message, $else]; }, [ 'message' => 'Hello' ]); - assert($r === $resolver); - assert($m === 'Hello'); - assert($n === null); + expect($r)->toBe($resolver); + expect($m)->toBe('Hello'); + expect($n)->toBeNull(); }); it('should call static method array resolving dependencies', function () { @@ -44,9 +44,9 @@ 'message' => 'Hello' ]); - assert($r === $resolver); - assert($m === 'Hello'); - assert($n === null); + expect($r)->toBe($resolver); + expect($m)->toBe('Hello'); + expect($n)->toBeNull(); }); it('should call static method string resolving dependencies', function () { @@ -58,9 +58,9 @@ 'message' => 'Hello' ]); - assert($r === $resolver); - assert($m === 'Hello'); - assert($n === null); + expect($r)->toBe($resolver); + expect($m)->toBe('Hello'); + expect($n)->toBeNull(); }); it('should call instance methods resolving dependencies', function () { @@ -72,9 +72,9 @@ 'message' => 'Hello' ]); - assert($r === $resolver); - assert($m === 'Hello'); - assert($n === null); + expect($r)->toBe($resolver); + expect($m)->toBe('Hello'); + expect($n)->toBeNull(); }); it('should call invokable objects resolving dependencies', function () { @@ -86,9 +86,9 @@ 'message' => 'Hello' ]); - assert($r === $resolver); - assert($m === 'Hello'); - assert($n === null); + expect($r)->toBe($resolver); + expect($m)->toBe('Hello'); + expect($n)->toBeNull(); }); it('should resolve typed variadic parameters', function () { @@ -100,8 +100,8 @@ return $containers; }); - assert(is_array($ret)); - assert(count($ret) === 1); + expect($ret)->toBeArray(); + expect($ret)->toHaveCount(1); }); it('should call function by passing variadic parameter value explicitly', function () { @@ -117,8 +117,8 @@ 'containers' => [$null], ]); - assert(is_array($ret)); - assert(count($ret) === 1); - assert($ret === [$null]); + expect($ret)->toBeArray(); + expect($ret)->toHaveCount(1); + expect($ret)->toBe([$null]); }); }); diff --git a/specs/DependencyResolver::construct.spec.php b/tests/DependencyResolver::construct.spec.php similarity index 54% rename from specs/DependencyResolver::construct.spec.php rename to tests/DependencyResolver::construct.spec.php index 6733631..d837da0 100644 --- a/specs/DependencyResolver::construct.spec.php +++ b/tests/DependencyResolver::construct.spec.php @@ -5,16 +5,16 @@ use Technically\DependencyResolver\DependencyResolver; use Technically\DependencyResolver\Exceptions\CannotAutowireDependencyArgument; use Technically\DependencyResolver\Exceptions\ClassCannotBeInstantiated; -use Technically\DependencyResolver\Specs\Fixtures\MyAbstractClass; -use Technically\DependencyResolver\Specs\Fixtures\MyConcreteContainerService; -use Technically\DependencyResolver\Specs\Fixtures\MyAbstractContainerService; -use Technically\DependencyResolver\Specs\Fixtures\MyNullableArgumentService; -use Technically\DependencyResolver\Specs\Fixtures\MyOptionalArgumentService; -use Technically\DependencyResolver\Specs\Fixtures\MyParentDependencyService; -use Technically\DependencyResolver\Specs\Fixtures\MySelfDependencyService; -use Technically\DependencyResolver\Specs\Fixtures\MyUnionTypeDependencyService; -use Technically\DependencyResolver\Specs\Fixtures\MyUnresolvableScalarArgumentService; -use Technically\DependencyResolver\Specs\Fixtures\MyUntypedArgumentService; +use Technically\DependencyResolver\Tests\Fixtures\MyAbstractClass; +use Technically\DependencyResolver\Tests\Fixtures\MyConcreteContainerService; +use Technically\DependencyResolver\Tests\Fixtures\MyAbstractContainerService; +use Technically\DependencyResolver\Tests\Fixtures\MyNullableArgumentService; +use Technically\DependencyResolver\Tests\Fixtures\MyOptionalArgumentService; +use Technically\DependencyResolver\Tests\Fixtures\MyParentDependencyService; +use Technically\DependencyResolver\Tests\Fixtures\MySelfDependencyService; +use Technically\DependencyResolver\Tests\Fixtures\MyUnionTypeDependencyService; +use Technically\DependencyResolver\Tests\Fixtures\MyUnresolvableScalarArgumentService; +use Technically\DependencyResolver\Tests\Fixtures\MyUntypedArgumentService; describe('DependencyResolver::construct()', function () { it('should instantiate a class using the bindings passed', function () { @@ -25,8 +25,8 @@ 'container' => $container, ]); - assert($resolved instanceof MyAbstractContainerService); - assert($resolved->container === $container); + expect($resolved)->toBeInstanceOf(MyAbstractContainerService::class); + expect($resolved->container)->toBe($container); }); it('should instantiate a class resolving dependencies from container, if possible', function () { @@ -36,8 +36,8 @@ $resolved = $resolver->construct(MyAbstractContainerService::class); - assert($resolved instanceof MyAbstractContainerService); - assert($resolved->container === $container); + expect($resolved)->toBeInstanceOf(MyAbstractContainerService::class); + expect($resolved->container)->toBe($container); }); it('should instantiate a class resolving self type dependency', function () { @@ -50,10 +50,10 @@ $resolved = $resolver->construct(MySelfDependencyService::class); - assert($resolved instanceof MySelfDependencyService); - assert($resolved !== $service); - assert($resolved->parent instanceof MySelfDependencyService); - assert($resolved->parent === $service); + expect($resolved)->toBeInstanceOf(MySelfDependencyService::class); + expect($resolved)->not->toBe($service); + expect($resolved->parent)->toBeInstanceOf(MySelfDependencyService::class); + expect($resolved->parent)->toBe($service); }); it('should instantiate a class resolving parent type dependency', function () { @@ -66,9 +66,9 @@ $resolved = $resolver->construct(MyParentDependencyService::class); - assert($resolved instanceof MyParentDependencyService); - assert($resolved->parent instanceof MySelfDependencyService); - assert($resolved->parent === $service); + expect($resolved)->toBeInstanceOf(MyParentDependencyService::class); + expect($resolved->parent)->toBeInstanceOf(MySelfDependencyService::class); + expect($resolved->parent)->toBe($service); }); if (PHP_MAJOR_VERSION >= 8) { @@ -79,8 +79,8 @@ $resolved = $resolver->construct(MyUnionTypeDependencyService::class); - assert($resolved instanceof MyUnionTypeDependencyService); - assert($resolved->input === $container); + expect($resolved)->toBeInstanceOf(MyUnionTypeDependencyService::class); + expect($resolved->input)->toBe($container); }); } @@ -90,9 +90,9 @@ $resolved = $resolver->construct(MyConcreteContainerService::class); - assert($resolved instanceof MyConcreteContainerService); - assert($resolved->container instanceof ArrayContainer); - assert($resolved->container !== $container); + expect($resolved)->toBeInstanceOf(MyConcreteContainerService::class); + expect($resolved->container)->toBeInstanceOf(ArrayContainer::class); + expect($resolved->container)->not->toBe($container); }); it('should instantiate a class falling back to default values, if possible', function () { @@ -100,8 +100,8 @@ $resolved = $resolver->construct(MyOptionalArgumentService::class); - assert($resolved instanceof MyOptionalArgumentService); - assert($resolved->name === 'MyOptionalArgumentService'); + expect($resolved)->toBeInstanceOf(MyOptionalArgumentService::class); + expect($resolved->name)->toBe('MyOptionalArgumentService'); }); it('should instantiate a class falling back to null when there is no other choice', function () { @@ -109,8 +109,8 @@ $resolved = $resolver->construct(MyNullableArgumentService::class); - assert($resolved instanceof MyNullableArgumentService); - assert($resolved->container === null); + expect($resolved)->toBeInstanceOf(MyNullableArgumentService::class); + expect($resolved->container)->toBeNull(); }); it('should throw exception if class being resolved is abstract', function () { @@ -122,9 +122,9 @@ // passthru } - assert(isset($exception)); - assert($exception instanceof ClassCannotBeInstantiated); - assert($exception->getClassName() === MyAbstractClass::class); + expect(isset($exception))->toBeTrue(); + expect($exception)->toBeInstanceOf(ClassCannotBeInstantiated::class); + expect($exception->getClassName())->toBe(MyAbstractClass::class); }); it('should throw exception if dependency class cannot be autowired', function () { @@ -136,11 +136,11 @@ // passthru } - assert(isset($exception)); - assert($exception instanceof CannotAutowireDependencyArgument); - assert($exception->getDependencyName() === MyAbstractContainerService::class); - assert($exception->getArgumentName() === 'container'); - assert($exception->getMessage() === 'Could not autowire argument `container` for `Technically\DependencyResolver\Specs\Fixtures\MyAbstractContainerService`.'); + expect(isset($exception))->toBeTrue(); + expect($exception)->toBeInstanceOf(CannotAutowireDependencyArgument::class); + expect($exception->getDependencyName())->toBe(MyAbstractContainerService::class); + expect($exception->getArgumentName())->toBe('container'); + expect($exception->getMessage())->toBe('Could not autowire argument `container` for `Technically\DependencyResolver\Tests\Fixtures\MyAbstractContainerService`.'); }); it('should throw exception if scalar dependency cannot be autowired', function () { @@ -152,11 +152,11 @@ // passthru } - assert(isset($exception)); - assert($exception instanceof CannotAutowireDependencyArgument); - assert($exception->getDependencyName() === MyUnresolvableScalarArgumentService::class); - assert($exception->getArgumentName() === 'name'); - assert($exception->getMessage() === 'Could not autowire argument `name` for `Technically\DependencyResolver\Specs\Fixtures\MyUnresolvableScalarArgumentService`.'); + expect(isset($exception))->toBeTrue(); + expect($exception)->toBeInstanceOf(CannotAutowireDependencyArgument::class); + expect($exception->getDependencyName())->toBe(MyUnresolvableScalarArgumentService::class); + expect($exception->getArgumentName())->toBe('name'); + expect($exception->getMessage())->toBe('Could not autowire argument `name` for `Technically\DependencyResolver\Tests\Fixtures\MyUnresolvableScalarArgumentService`.'); }); it('should throw exception if untyped dependency cannot be autowired', function () { @@ -168,10 +168,10 @@ // passthru } - assert(isset($exception)); - assert($exception instanceof CannotAutowireDependencyArgument); - assert($exception->getDependencyName() === MyUntypedArgumentService::class); - assert($exception->getArgumentName() === 'input'); - assert($exception->getMessage() === 'Could not autowire argument `input` for `Technically\DependencyResolver\Specs\Fixtures\MyUntypedArgumentService`.'); + expect(isset($exception))->toBeTrue(); + expect($exception)->toBeInstanceOf(CannotAutowireDependencyArgument::class); + expect($exception->getDependencyName())->toBe(MyUntypedArgumentService::class); + expect($exception->getArgumentName())->toBe('input'); + expect($exception->getMessage())->toBe('Could not autowire argument `input` for `Technically\DependencyResolver\Tests\Fixtures\MyUntypedArgumentService`.'); }); }); diff --git a/specs/DependencyResolver::resolve.spec.php b/tests/DependencyResolver::resolve.spec.php similarity index 70% rename from specs/DependencyResolver::resolve.spec.php rename to tests/DependencyResolver::resolve.spec.php index dfa12d9..481d57b 100644 --- a/specs/DependencyResolver::resolve.spec.php +++ b/tests/DependencyResolver::resolve.spec.php @@ -12,8 +12,8 @@ $resolved = $resolver->resolve(DependencyResolver::class); - assert($resolved instanceof DependencyResolver); - assert($resolved === $resolver); + expect($resolved)->toBeInstanceOf(DependencyResolver::class); + expect($resolved)->toBe($resolver); }); it('should construct a new instance if container does not have it already', function () { @@ -22,8 +22,8 @@ $resolved = $resolver->resolve(DependencyResolver::class); - assert($resolved instanceof DependencyResolver); - assert($resolved !== $resolver); + expect($resolved)->toBeInstanceOf(DependencyResolver::class); + expect($resolved)->not->toBe($resolver); }); it('should throw InvalidArgumentException if the given class does not exist', function () { @@ -35,8 +35,8 @@ // passthru } - assert(isset($exception)); - assert($exception instanceof InvalidArgumentException); - assert($exception->getMessage() === '`Foo` is not a valid class name.'); + expect(isset($exception))->toBeTrue(); + expect($exception)->toBeInstanceOf(InvalidArgumentException::class); + expect($exception->getMessage())->toBe('`Foo` is not a valid class name.'); }); }); diff --git a/tests/Fixtures/MyAbstractClass.php b/tests/Fixtures/MyAbstractClass.php new file mode 100644 index 0000000..034b951 --- /dev/null +++ b/tests/Fixtures/MyAbstractClass.php @@ -0,0 +1,8 @@ +parent = $parent; + } +} diff --git a/specs/Fixtures/MyStaticMethodService.php b/tests/Fixtures/MyStaticMethodService.php similarity index 69% rename from specs/Fixtures/MyStaticMethodService.php rename to tests/Fixtures/MyStaticMethodService.php index 739caa3..c5f4b25 100644 --- a/specs/Fixtures/MyStaticMethodService.php +++ b/tests/Fixtures/MyStaticMethodService.php @@ -1,12 +1,12 @@