diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..7706dfe
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,10 @@
+# Path-based git attributes
+# https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html
+
+# Ignore all test and documentation with "export-ignore".
+/.github export-ignore
+/.gitattributes export-ignore
+/.gitignore export-ignore
+/phpcs.xml export-ignore
+/phpmd.xml export-ignore
+/phpunit.xml export-ignore
diff --git a/.github/workflows/.editorconfig b/.github/workflows/.editorconfig
new file mode 100644
index 0000000..dd7e381
--- /dev/null
+++ b/.github/workflows/.editorconfig
@@ -0,0 +1,2 @@
+[{*.yaml,*.yml}]
+indent_size = 2
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
new file mode 100644
index 0000000..08a0740
--- /dev/null
+++ b/.github/workflows/ci.yaml
@@ -0,0 +1,44 @@
+name: Plugin CI
+on:
+ push:
+ branches: [ 'master' ]
+ pull_request:
+
+jobs:
+ tests:
+ name: "Tests ${{ matrix.php-version }} deps ${{ matrix.dependency-versions }}"
+ runs-on: ubuntu-22.04
+
+ strategy:
+ fail-fast: false
+ matrix:
+ # normal, highest, non-dev installs
+ php-version: [ '8.2' ]
+ dependency-versions: [ 'highest' ]
+ include:
+ # testing lowest PHP version with the lowest dependencies
+ - php-version: '8.2'
+ dependency-versions: 'lowest'
+
+ # testing dev versions with the highest PHP
+ - php-version: '8.2'
+ dependency-versions: 'highest'
+
+ steps:
+ - name: "Checkout code"
+ uses: "actions/checkout@v2"
+
+ - name: "Install PHP"
+ uses: "shivammathur/setup-php@v2"
+ with:
+ coverage: "none"
+ php-version: "${{ matrix.php-version }}"
+
+ - name: "Composer install"
+ uses: "ramsey/composer-install@v2"
+ with:
+ dependency-versions: "${{ matrix.dependency-versions }}"
+ composer-options: "--prefer-dist --no-progress"
+
+ - name: "Run tests"
+ run: "./vendor/bin/phpunit"
diff --git a/.github/workflows/static.yaml b/.github/workflows/static.yaml
new file mode 100644
index 0000000..a17c349
--- /dev/null
+++ b/.github/workflows/static.yaml
@@ -0,0 +1,65 @@
+on: [pull_request]
+name: Static analysis
+
+jobs:
+ composer-validate:
+ name: Composer validate & normalize
+ runs-on: ubuntu-22.04
+
+ steps:
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: 8.2
+ coverage: none
+ tools: composer-normalize
+
+ - name: Checkout code
+ uses: actions/checkout@v2
+
+ - name: Validate
+ run: composer validate --strict
+
+ - name: Normalize
+ run: composer-normalize --dry-run
+
+ phpcs:
+ name: PHP_CodeSniffer
+ 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: squizlabs/php_codesniffer:^3.7
+
+ - 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
diff --git a/README.md b/README.md
index ded1663..56ec097 100755
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
### Requirements
-PHP >= 8.0.0
+PHP >= 8.2
### How to use the library
@@ -34,7 +34,7 @@ include 'vendor/autoload.php';
```php
use \Micro\Component\DependencyInjection\Container;
-class Logger {
+class Logger implements LoggerInterface {
}
class Mailer {
@@ -43,10 +43,14 @@ class Mailer {
$container = new Container();
-$container->register(Logger::class, function(Container $container) {
+$container->register(LoggerInterface::class, function(Container $container) {
return new Logger();
});
+$container->register('logger.doctrine', function(Container $container) {
+ return new Logger('doctrine-channel');
+});
+
$container->register(Mailer::class, function(Container $container) {
return new Mailer($container->get(Logger::class));
});
@@ -70,13 +74,11 @@ class HelloWorldFacade implements HelloWorldFacadeInterface
}
}
-
-class HelloWorldDecorator implements HelloWorldFacadeInterface
+class NiceHelloWorldDecorator implements HelloWorldFacadeInterface
{
public function __construct(
- private readonly HelloWorldFacadeInterface $decoratedService,
- )
- {
+ private readonly HelloWorldFacadeInterface $decoratedService
+ ) {
}
public function hello(string $name): string
@@ -87,25 +89,51 @@ class HelloWorldDecorator implements HelloWorldFacadeInterface
}
}
+class HelloWorldLoggerAwareDecorator implements HelloWorldFacadeInterface
+{
+ public function __construct(
+ private readonly HelloWorldFacadeInterface $decoratedService,
+ private readonly LoggerInterface $logger
+ ) {
+ }
+
+ public function hello(string $name): string
+ {
+ $result = $this->decoratedService->hello($name);
+
+ $this->logger->info->info($result);
+
+ return $result;
+ }
+}
+
$container = new Container();
$container->register(HelloWorldFacadeInterface::class, function () {
return new HelloWorldFacade();
});
+$container->register(HelloWorldFacadeInterface::class, function (
+ HelloWorldFacadeInterface $decorated
+) {
+ return new NiceHelloWorldDecorator($decorated);
+});
+
$container->decorate(HelloWorldFacadeInterface::class, function(
- HelloWorldFacadeInterface $serviceForDecoration
+ HelloWorldFacadeInterface $decorated,
+ Container $container
) {
- return new HelloWorldLoggerAwareDecorator($serviceForDecoration);
+ return new HelloWorldLoggerAwareDecorator(
+ $decorated,
+ $container->get(LoggerInterface::class)
+ );
});
echo $container->get(HelloWorldFacadeInterface::class)->hello('Stas');
// Output: Hello, Stas. I'm glad to see you
-
```
-
### Sample code for:
- [PSR-11](https://www.php-fig.org/psr/psr-11/)
diff --git a/composer.json b/composer.json
index 5178b06..a6274b8 100755
--- a/composer.json
+++ b/composer.json
@@ -1,14 +1,8 @@
{
"name": "micro/dependency-injection",
"description": "Dependency injection service container",
- "type": "library",
"license": "MIT",
- "version": "1.1",
- "autoload": {
- "psr-4": {
- "Micro\\Component\\DependencyInjection\\": "src/"
- }
- },
+ "type": "library",
"authors": [
{
"name": "Stanislau.Komar",
@@ -16,8 +10,34 @@
}
],
"require": {
- "php": ">=8.0",
- "psr/container": "^2.0",
- "micro/autowire": "^1"
+ "php": "^8.2",
+ "psr/container": "^2.0"
+ },
+ "require-dev": {
+ "ergebnis/composer-normalize": "^2.29",
+ "phpmd/phpmd": "^2.13",
+ "phpunit/phpunit": "^9.5",
+ "squizlabs/php_codesniffer": "^3.7"
+ },
+ "conflict": {
+ "micro/autowire": "<1.1"
+ },
+ "suggest": {
+ "micro/autowire": "Autowire helper for dependency injection"
+ },
+ "autoload": {
+ "psr-4": {
+ "Micro\\Component\\DependencyInjection\\": "src/"
+ }
+ },
+ "config": {
+ "allow-plugins": {
+ "ergebnis/composer-normalize": true
+ }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
}
}
diff --git a/phpcs.xml b/phpcs.xml
index 727ba01..6581b2d 100755
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -14,7 +14,12 @@
-
+
+
+
+
+
+
diff --git a/src/Container.php b/src/Container.php
index 5d12fb7..6133f6c 100755
--- a/src/Container.php
+++ b/src/Container.php
@@ -4,7 +4,6 @@
use Micro\Component\DependencyInjection\Exception\ServiceNotRegisteredException;
use Micro\Component\DependencyInjection\Exception\ServiceRegistrationException;
-use \Closure;
use Psr\Container\ContainerInterface;
class Container implements ContainerInterface, ContainerRegistryInterface, ContainerDecoratorInterface
@@ -12,25 +11,18 @@ class Container implements ContainerInterface, ContainerRegistryInterface, Conta
/**
* @var array
*/
- private array $services;
+ private array $services = [];
/**
- * @var array
+ * @var array
*/
- private array $servicesRaw;
+ private array $servicesRaw = [];
/**
- * @var array>
+ * @var array>
*/
private array $decorators = [];
- public function __construct(
- )
- {
- $this->services = [];
- $this->servicesRaw = [];
- }
-
/**
* @template T
*
@@ -54,7 +46,7 @@ public function has(string $id): bool
/**
* {@inheritDoc}
*/
- public function register(string $id, Closure $service): void
+ public function register(string $id, \Closure $service): void
{
if($this->has($id)) {
throw new ServiceRegistrationException(sprintf('Service "%s" already registered', $id));
@@ -66,13 +58,13 @@ public function register(string $id, Closure $service): void
/**
* {@inheritDoc}
*/
- public function decorate(string $id, Closure $service, int $priority = 0): void
+ public function decorate(string $id, \Closure $service, int $priority = 0): void
{
if(!array_key_exists($id, $this->decorators)) {
$this->decorators[$id] = [];
}
- $this->decorators[$id][] = [$service, $priority];
+ $this->decorators[$id][$priority] = $service;
}
/**
@@ -93,7 +85,6 @@ private function lookup(string $id): object
/**
* @param string $serviceId
- * @return object
*/
protected function initializeService(string $serviceId): void
{
@@ -101,8 +92,8 @@ protected function initializeService(string $serviceId): void
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)) {
@@ -111,19 +102,10 @@ protected function initializeService(string $serviceId): void
$decorators = $this->decorators[$serviceId];
- usort($decorators, function(array $left, array $right): int {
- $l = $left[1];
- $r = $right[1];
- if($l === $r) {
- return 0;
- }
-
- return $left[1] > $right[1] ? 1 : -1;
- });
+ ksort($decorators);
- /** @var array $decorator */
foreach ($decorators as $decorator) {
- $this->services[$serviceId] = $decorator[0]();
+ $this->services[$serviceId] = $decorator($this->services[$serviceId], $this);
}
}
}
diff --git a/src/ContainerDecoratorInterface.php b/src/ContainerDecoratorInterface.php
index 65767ea..e95089c 100644
--- a/src/ContainerDecoratorInterface.php
+++ b/src/ContainerDecoratorInterface.php
@@ -12,4 +12,4 @@ interface ContainerDecoratorInterface
* @return void
*/
public function decorate(string $id, \Closure $service, int $priority = 0): void;
-}
\ No newline at end of file
+}
diff --git a/src/ContainerRegistryInterface.php b/src/ContainerRegistryInterface.php
index 33e4dac..44d76c8 100755
--- a/src/ContainerRegistryInterface.php
+++ b/src/ContainerRegistryInterface.php
@@ -2,7 +2,6 @@
namespace Micro\Component\DependencyInjection;
-
interface ContainerRegistryInterface
{
/**
diff --git a/src/Exception/ServiceNotRegisteredException.php b/src/Exception/ServiceNotRegisteredException.php
index aa22fb0..b6dca6f 100755
--- a/src/Exception/ServiceNotRegisteredException.php
+++ b/src/Exception/ServiceNotRegisteredException.php
@@ -4,7 +4,19 @@
use Psr\Container\NotFoundExceptionInterface;
-class ServiceNotRegisteredException extends \RuntimeException
- implements NotFoundExceptionInterface
+class ServiceNotRegisteredException extends \RuntimeException implements NotFoundExceptionInterface
{
+ private string $serviceId;
+
+ public function __construct(string $serviceId, int $code = 0, ?\Throwable $previous = null)
+ {
+ $this->serviceId = $serviceId;
+
+ parent::__construct(sprintf('Service "%s" not registered.', $this->serviceId), $code, $previous);
+ }
+
+ public function getServiceId(): string
+ {
+ return $this->serviceId;
+ }
}
diff --git a/src/Exception/ServiceRegistrationException.php b/src/Exception/ServiceRegistrationException.php
index 73c2a8f..79a2f04 100755
--- a/src/Exception/ServiceRegistrationException.php
+++ b/src/Exception/ServiceRegistrationException.php
@@ -4,8 +4,6 @@
use Psr\Container\ContainerExceptionInterface;
-class ServiceRegistrationException extends \RuntimeException
- implements ContainerExceptionInterface
+class ServiceRegistrationException extends \RuntimeException implements ContainerExceptionInterface
{
-
}
diff --git a/tests/unit/ContainerTest.php b/tests/unit/ContainerTest.php
index c35223b..dacca27 100755
--- a/tests/unit/ContainerTest.php
+++ b/tests/unit/ContainerTest.php
@@ -14,17 +14,16 @@ public function testContainerResolveDependencies(): void
{
$container = new Container();
- $container->register(
- 'test', function ( Container $container ) {
- return new class {
- public string $name = 'success';
- };
- }
- );
+ $container->register('test', function () {
+ return new NamedService('success');
+ });
+ /** @var NamedInterface $service */
$service = $container->get('test');
$this->assertIsObject($service);
- $this->assertEquals('success', $service->name);
+ $this->assertInstanceOf(NamedInterface::class, $service);
+ $this->assertInstanceOf(NamedService::class, $service);
+ $this->assertEquals('success', $service->getName());
}
public function testRegisterTwoServicesWithEqualAliasesException(): void
@@ -32,18 +31,8 @@ public function testRegisterTwoServicesWithEqualAliasesException(): void
$this->expectException(ServiceRegistrationException::class);
$container = new Container();
- $container->register(
- 'test', function ( Container $container ) {
- return new class {
- };
- }
- );
- $container->register(
- 'test', function ( Container $container ) {
- return new class {
- };
- }
- );
+ $container->register('test', function () { return new class {}; });
+ $container->register('test', function () { return new class {}; });
}
public function testContainerUnresolvedException(): void
@@ -51,57 +40,68 @@ public function testContainerUnresolvedException(): void
$this->expectException(ServiceNotRegisteredException::class);
$container = new Container();
- $container->register(
- 'test', function ( Container $container ) {
- return new class {
- public string $name = 'success';
- };
- }
- );
+ $container->register('test', function () {
+ return new NamedService('success');
+ });
$container->get('test2');
}
public function testDecorateService(): void
{
- $container = new ContainerAutowire(new Container());
+ $container = new Container();
- $container->register('test', function ($container) {
- return new class {
- public function getA(): string { return 'D'; }
- };
+ $container->register('test', function () {
+ return new NamedService('D');
});
-
- $container->decorate('test', function (Container $container) {
- return new class($container->get('test')) {
- public function __construct(private readonly object $decorated) {}
- public function getA(): string {
- return 'C' . $this->decorated->getA();
- }
- };
+ $container->decorate('test', function (NamedInterface $decorated) {
+ return new NamedServiceDecorator($decorated, 'C');
});
- $container->decorate('test', function (Container $container) {
- return new class($container->get('test')) {
- public function __construct(private readonly object $decorated) {}
- public function getA(): string {
- return 'A' . $this->decorated->getA();
- }
- };
+ $container->decorate('test', function (NamedInterface $decorated) {
+ return new NamedServiceDecorator($decorated, 'A');
}, 10);
- $container->decorate('test', function (Container $container) {
- return new class($container->get('test')) {
- public function __construct(private readonly object $decorated) {}
- public function getA(): string {
- return 'B' . $this->decorated->getA();
- }
- };
+ $container->decorate('test', function (NamedInterface $decorated) {
+ return new NamedServiceDecorator($decorated, 'B');
}, 5);
-
+ /** @var NamedInterface $result */
$result = $container->get('test');
- $this->assertEquals('ABCD', $result->getA());
+ $this->assertInstanceOf(NamedServiceDecorator::class, $result);
+ $this->assertInstanceOf(NamedInterface::class, $result);
+ $this->assertEquals('ABCD', $result->getName());
+ }
+}
+
+interface NamedInterface
+{
+ public function getName(): string;
+}
+
+readonly class NamedService implements NamedInterface
+{
+ public function __construct(private string $name)
+ {
+ }
+
+ public function getName(): string
+ {
+ return $this->name;
+ }
+}
+
+readonly class NamedServiceDecorator implements NamedInterface
+{
+ public function __construct(
+ private object $decorated,
+ private string $name
+ ) {
+ }
+
+ public function getName(): string
+ {
+ return $this->name.$this->decorated->getName();
}
}