diff --git a/.github/workflows/analyse.yml b/.github/workflows/analyse.yml new file mode 100644 index 0000000..e69cb57 --- /dev/null +++ b/.github/workflows/analyse.yml @@ -0,0 +1,35 @@ +name: Static Analysis +on: + pull_request: + push: + branches: + - master + workflow_dispatch: +jobs: + analyse: + name: Static Analysis + runs-on: ubuntu-latest + strategy: + matrix: + php-version: [ '7.4', '8.0', '8.1' ] + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Cache Composer dependencies + uses: actions/cache@v3 + with: + path: /tmp/composer-cache + key: ${{ runner.os }}-${{ matrix.php-version }}-{{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-${{ matrix.php-version }} + + - name: Install dependencies + uses: php-actions/composer@v6 + with: + php_version: ${{ matrix.php-version }} + + - name: Lint PHP Files + run: composer run-script lint + + - name: PHPStan + run: composer run-script analyse diff --git a/.github/workflows/code-style.yml b/.github/workflows/code-style.yml new file mode 100644 index 0000000..c34f2b9 --- /dev/null +++ b/.github/workflows/code-style.yml @@ -0,0 +1,34 @@ +name: PHP Code Style +on: + pull_request: + push: + workflow_dispatch: +jobs: + code-style: + name: PHP Code Style Check + strategy: + matrix: + operating-system: [ubuntu-latest] + php-version: ['7.4'] + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Cache Composer dependencies + uses: actions/cache@v3 + with: + path: /tmp/composer-cache + key: ${{ runner.os }}-${{ matrix.php-version }}-{{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-${{ matrix.php-version }} + + - name: Install dependencies + uses: php-actions/composer@v6 + with: + php_version: ${{ matrix.php-version }} + + - name: PHP Code Style Check + uses: php-actions/composer@v6 + with: + php_version: ${{ matrix.php-version }} + command: cs-check diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml deleted file mode 100644 index 476ad89..0000000 --- a/.github/workflows/php.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: PHP -on: [push, pull_request] -jobs: - tests: - strategy: - matrix: - operating-system: [ubuntu-16.04] - php-versions: ['5.3'] - runs-on: ${{ matrix.operating-system }} - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php-versions }} - coverage: xdebug - ini-values: memory_limit=-1 - - - name: Validate composer.json and composer.lock - run: composer validate - - - name: Get composer cache directory - id: composercache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" - - - name: Cache composer dependencies - id: composer-cache - uses: actions/cache@v2 - with: - path: ${{ steps.composercache.outputs.dir }} - key: ${{ matrix.php-versions }}-composer-${{ hashFiles('**/composer.json') }} - restore-keys: ${{ matrix.php-versions }}-composer- - - - name: Install dependencies - run: composer install --prefer-dist --no-progress --no-suggest - - - name: Run test suite - run: composer run-script test diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..eca912f --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,37 @@ +name: Tests +on: + pull_request: + push: + branches: + - master + workflow_dispatch: +jobs: + tests: + name: Unit Tests + runs-on: ubuntu-latest + strategy: + matrix: + php-version: [ '7.4', '8.0', '8.1' ] + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Cache Composer dependencies + uses: actions/cache@v3 + with: + path: /tmp/composer-cache + key: ${{ runner.os }}-${{ matrix.php-version }}-{{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-${{ matrix.php-version }} + + - name: Install dependencies + uses: php-actions/composer@v6 + with: + php_version: ${{ matrix.php-version }} + + - name: PHPUnit Unit Test Suite + uses: php-actions/composer@v6 + with: + php_version: ${{ matrix.php-version }} + memory_limit: -1 + command: test + args: -- --testsuite=Unit diff --git a/.php_cs.dist b/.php-cs-fixer.dist.php similarity index 71% rename from .php_cs.dist rename to .php-cs-fixer.dist.php index 40aed3d..ef877e3 100644 --- a/.php_cs.dist +++ b/.php-cs-fixer.dist.php @@ -1,5 +1,7 @@ + * @copyright Copyright (c) 2019-2022 Felipe Sayão Lobato Abreu * @license https://opensource.org/licenses/MIT MIT License */ -$paths = array( + +$paths = [ __FILE__, __DIR__, -); +]; -$header = \file_get_contents(__DIR__ . '/.docheader'); +$header = file_get_contents(__DIR__ . '/.docheader'); return CoiSA\PhpCsFixer\PhpCsFixer::create($paths, $header); diff --git a/composer.json b/composer.json index 8496c19..314d600 100644 --- a/composer.json +++ b/composer.json @@ -10,23 +10,23 @@ } ], "require": { - "php": ">=5.3", - "coisa/exceptions": "~1.0", - "coisa/factory": "~1.6", - "coisa/service-provider": "~1.1", + "php": "^7.4 || ^8.0", + "coisa/exceptions": "^1.0", + "coisa/factory": "^1.7", + "coisa/service-provider": "^2.0", "container-interop/service-provider": "^0.4.0", - "psr/container": "^1.0" + "psr/container": "^1.1 || ^2.0" }, "provide": { - "psr/container-implementation": "^1.0" + "psr/container-implementation": "^1.1 || ^2.0" }, "require-dev": { - "coisa/php-cs-fixer": "^1.0", - "jakub-onderka/php-console-highlighter": "^0.3.2 || ^0.4", - "jakub-onderka/php-parallel-lint": "^1.0", - "phpunit/phpunit": "^4.8", - "sensiolabs/security-checker": "^4.1", - "squizlabs/php_codesniffer": "^2.9 || ^3.0" + "coisa/php-cs-fixer": "^2.1", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.3", + "phpunit/phpunit": "^9.5", + "sensiolabs/security-checker": "^6.0", + "squizlabs/php_codesniffer": "^3.6" }, "config": { "prefer-stable": true, @@ -34,7 +34,7 @@ }, "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { diff --git a/config/autoload.php b/config/autoload.php index 85fe094..378efc7 100644 --- a/config/autoload.php +++ b/config/autoload.php @@ -1,5 +1,7 @@ + * @copyright Copyright (c) 2019-2022 Felipe Sayão Lobato Abreu * @license https://opensource.org/licenses/MIT MIT License */ +namespace CoiSA\Container; + +use CoiSA\Container\Factory\AggregateContainerAbstractFactory; +use CoiSA\Container\Factory\ContainerAbstractFactory; use CoiSA\Factory\AbstractFactory; use CoiSA\Factory\AbstractFactoryFactory; use CoiSA\Factory\AliasFactory; +use Psr\Container\ContainerInterface as PsrContainer; \call_user_func( - function($aggregateContainerFactory, $containerFactory, $aliasFactory) { - AbstractFactory::setFactory('CoiSA\\Container\\AggregateContainer', $aggregateContainerFactory); - AbstractFactory::setFactory('CoiSA\\Container\\Container', $containerFactory); - AbstractFactory::setFactory('CoiSA\\Container\\ContainerInterface', $aliasFactory); - AbstractFactory::setFactory('Psr\\Container\\ContainerInterface', $aliasFactory); + function ( + AbstractFactoryFactory $aggregateContainerFactory, + AbstractFactoryFactory $containerFactory, + AliasFactory $aliasFactory + ): void { + AbstractFactory::setFactory(AggregateContainer::class, $aggregateContainerFactory); + AbstractFactory::setFactory(Container::class, $containerFactory); + AbstractFactory::setFactory(ContainerInterface::class, $aliasFactory); + AbstractFactory::setFactory(PsrContainer::class, $aliasFactory); }, - 'CoiSA\\Container\\Factory\\AggregateContainerFactory', - new AbstractFactoryFactory('CoiSA\\Container\\Factory\\ContainerAbstractFactory'), - new AliasFactory('CoiSA\\Container\\Container') + new AbstractFactoryFactory(AggregateContainerAbstractFactory::class), + new AbstractFactoryFactory(ContainerAbstractFactory::class), + new AliasFactory(Container::class) ); diff --git a/examples/autowire.php b/examples/autowire.php new file mode 100644 index 0000000..e7e74b9 --- /dev/null +++ b/examples/autowire.php @@ -0,0 +1,30 @@ + + * @license https://opensource.org/licenses/MIT MIT License + */ + +use CoiSA\Container\AggregateContainer; +use CoiSA\Container\Factory\ContainerAbstractFactory; +use Laminas\Di\Injector; + +require_once 'vendor/autoload.php'; + +/** @var \Laminas\ConfigAggregator\ConfigAggregator $config */ +// $config = require __DIR__ . '/config.php'; + +$coisaContainer = ContainerAbstractFactory::create(/* $config */); + +return new AggregateContainer([ + $coisaContainer, + (new Injector())->getContainer(), +]); diff --git a/examples/coisa-factory.php b/examples/coisa-factory.php new file mode 100644 index 0000000..e2f5c03 --- /dev/null +++ b/examples/coisa-factory.php @@ -0,0 +1,55 @@ + + * @license https://opensource.org/licenses/MIT MIT License + */ + +use CoiSA\Container\Test\Stub\ServiceProvider\ExampleOtherServiceProvider; +use CoiSA\Container\Test\Stub\ServiceProvider\ExampleServiceProvider; +use CoiSA\Factory\AbstractFactory; +use Psr\Container\ContainerInterface; + +require_once __DIR__ . '/../vendor/autoload.php'; + +$serviceProvider = new ExampleOtherServiceProvider(); + +// Create a container instance +$container = AbstractFactory::create( + ContainerInterface::class, + + // Register service-provider using full-qualified class name + ExampleServiceProvider::class, + + // Register service-provider using an object instance + $serviceProvider, + + // You can also register service-provider using Laminas ConfigProvider pattern dependencies array maping + // @see examples/laminas-config-provider-pattern.php for more details + + // Or use a full-qualified ConfigProvider class name + // ConfiProvider::class, + + // ...keep adding the service-providers of your application +); + +// You would be able to access the service-provider instance using the container +var_dump($container->has(ExampleServiceProvider::class), $container->get(ExampleServiceProvider::class)); +var_dump($container->has(ExampleOtherServiceProvider::class), $container->get(ExampleOtherServiceProvider::class)); + +// If you register a service-provider using an object instance, you can access it using the container +var_dump($serviceProvider === $container->get(ExampleOtherServiceProvider::class)); + +// You can also get the factory instance for a service-provider +var_dump(AbstractFactory::getFactory(ExampleOtherServiceProvider::class)); + +// Memory usage +var_dump(memory_get_peak_usage(true) / 1024 / 1024); diff --git a/examples/example.php b/examples/example.php deleted file mode 100644 index 3006593..0000000 --- a/examples/example.php +++ /dev/null @@ -1,52 +0,0 @@ - - * @license https://opensource.org/licenses/MIT MIT License - */ - -use CoiSA\Container\Test\Stub\ServiceProvider\ExampleOtherServiceProvider; -use CoiSA\Factory\AbstractFactory; - -require_once __DIR__ . '/../vendor/autoload.php'; - -$exampleServiceProvider = 'CoiSA\\Container\\Test\\Stub\\ServiceProvider\\ExampleServiceProvider'; -$otherServiceProvider = new ExampleOtherServiceProvider(); - -$container = AbstractFactory::create( - 'Psr\\Container\\ContainerInterface', - $exampleServiceProvider, - $otherServiceProvider -); -// or -//$container = CoiSA\Container\Factory\ContainerAbstractFactory::create( -// $exampleServiceProvider, -// $otherServiceProvider -//); - -\var_dump( - // ExampleServiceProvider - $container->has($exampleServiceProvider), - $container->get($exampleServiceProvider), - - // ExampleOtherServiceProvider - $container->has(\get_class($otherServiceProvider)), - $otherServiceProvider === $container->get(\get_class($otherServiceProvider)), - $container->get(\get_class($otherServiceProvider)) -); - -\var_dump( - AbstractFactory::getFactory($exampleServiceProvider) instanceof CoiSA\Factory\ContainerFactory, - AbstractFactory::getFactory($exampleServiceProvider) -); - -\var_dump( - \memory_get_peak_usage(true) / 1024 / 1024 -); diff --git a/examples/laminas-config-provider-pattern.php b/examples/laminas-config-provider-pattern.php new file mode 100644 index 0000000..62c99af --- /dev/null +++ b/examples/laminas-config-provider-pattern.php @@ -0,0 +1,38 @@ + + * @license https://opensource.org/licenses/MIT MIT License + */ + +use CoiSA\Factory\AbstractFactory; +use Psr\Container\ContainerInterface; + +require_once __DIR__ . '/../vendor/autoload.php'; + +$config = [ + 'dependencies' => [ + 'factories' => [ + stdClass::class => fn (ContainerInterface $container) => new stdClass(), + ], + 'aliases' => [ + 'db' => stdClass::class, + ], + ], +]; + +$container = AbstractFactory::create(ContainerInterface::class, $config); + +// If you don't have coisa/factory installed in your project use: +// $container = CoiSA\Container\Factory\ContainerAbstractFactory::create($config); + +var_dump($container->get('config')); +var_dump($container->get('db')); diff --git a/examples/standalone.php b/examples/standalone.php new file mode 100644 index 0000000..5765cfa --- /dev/null +++ b/examples/standalone.php @@ -0,0 +1,48 @@ + + * @license https://opensource.org/licenses/MIT MIT License + */ + +use CoiSA\Container\Factory\ContainerAbstractFactory; +use CoiSA\Container\Test\Stub\ServiceProvider\ExampleOtherServiceProvider; +use CoiSA\Container\Test\Stub\ServiceProvider\ExampleServiceProvider; + +require_once __DIR__ . '/../vendor/autoload.php'; + +$serviceProvider = new ExampleOtherServiceProvider(); + +$container = ContainerAbstractFactory::create( + // Register service-provider using full-qualified class name + ExampleServiceProvider::class, + + // Register service-provider using an object instance + $serviceProvider + + // You can also register service-provider using Laminas ConfigProvider pattern dependencies array maping + // @see examples/laminas-config-provider-pattern.php for more details + + // Or a callable + // fn (ContainerInterface $container) => new ExampleServiceProvicer($container->get('options')), + + // ...keep adding the service-providers of your application +); + +// You would be able to access the service-provider instance using the container +var_dump($container->has(ExampleServiceProvider::class), $container->get(ExampleServiceProvider::class)); +var_dump($container->has(ExampleOtherServiceProvider::class), $container->get(ExampleOtherServiceProvider::class)); + +// If you register a service-provider using an object instance, you can access it using the container +var_dump($serviceProvider === $container->get(ExampleOtherServiceProvider::class)); + +// Memory usage +var_dump(memory_get_peak_usage(true) / 1024 / 1024); diff --git a/src/AggregateContainer.php b/src/AggregateContainer.php index ca6aed6..f33b2bb 100644 --- a/src/AggregateContainer.php +++ b/src/AggregateContainer.php @@ -1,5 +1,7 @@ + * @copyright Copyright (c) 2019-2022 Felipe Sayão Lobato Abreu * @license https://opensource.org/licenses/MIT MIT License */ + namespace CoiSA\Container; use CoiSA\Exception\Container\ContainerException; @@ -28,14 +30,9 @@ final class AggregateContainer implements ContainerInterface, \IteratorAggregate /** * @var array */ - private $containers = array(); + private $containers = []; - /** - * AggregateContainer constructor. - * - * @param array $containers - */ - public function __construct(array $containers = array()) + public function __construct(ContainerInterface ...$containers) { foreach ($containers as $container) { $this->append($container); @@ -53,64 +50,61 @@ public function getContainers() /** * {@inheritdoc} */ + #[\ReturnTypeWillChange] public function getIterator() { return new \ArrayIterator($this->containers); } /** - * @param ContainerInterface $container - * * @return self */ public function prepend(ContainerInterface $container) { - \array_unshift($this->containers, $container); + $found = array_search($container, $this->containers, true); + + if ($found) { + unset($this->containers[$found]); + } + + array_unshift($this->containers, $container); return $this; } /** - * @param ContainerInterface $container - * * @return self */ public function append(ContainerInterface $container) { - $this->containers[] = $container; + if (!\in_array($container, $this->containers, true)) { + $this->containers[] = $container; + } return $this; } - /** - * @param string $id - * - * @return bool - */ - public function has($id) + public function has(string $id): bool { - return \array_reduce($this->containers, function($has, $container) use ($id) { - return $has || $container->has($id); - }, false); + return array_reduce($this->containers, fn ($has, $container) => $has || $container->has($id), false); } /** - * @param string $id - * * @return mixed */ - public function get($id) + public function get(string $id) { if (!$this->has($id)) { throw NotFoundException::forNotFoundIdentifierFactory($id); } - return \array_reduce($this->containers, function($object, $container) use ($id) { + foreach ($this->containers as $container) { try { - return $object || ($container->has($id) ? $container->get($id) : false); + return $container->get($id); } catch (ContainerExceptionInterface $containerException) { - throw ContainerException::forExceptionResolvingIdentifier($containerException, $id); } - }); + } + + throw ContainerException::forExceptionResolvingIdentifier($containerException, $id); } } diff --git a/src/ContainerAwareInterface.php b/src/ContainerAwareInterface.php index 674c25f..9b6ba2c 100644 --- a/src/ContainerAwareInterface.php +++ b/src/ContainerAwareInterface.php @@ -1,5 +1,7 @@ + * @copyright Copyright (c) 2019-2022 Felipe Sayão Lobato Abreu * @license https://opensource.org/licenses/MIT MIT License */ + namespace CoiSA\Container; use Psr\Container\ContainerInterface as PsrContainer; @@ -25,9 +27,7 @@ interface ContainerAwareInterface /** * Sets a container instance on object. * - * @param PsrContainer $container - * * @return void */ - public function setContainer(PsrContainer $container); + public function setContainer(PsrContainer $container): void; } diff --git a/src/ContainerAwareTrait.php b/src/ContainerAwareTrait.php index 0ae5089..933bf25 100644 --- a/src/ContainerAwareTrait.php +++ b/src/ContainerAwareTrait.php @@ -1,5 +1,7 @@ + * @copyright Copyright (c) 2019-2022 Felipe Sayão Lobato Abreu * @license https://opensource.org/licenses/MIT MIT License */ @@ -16,7 +18,7 @@ use Psr\Container\ContainerInterface as PsrContainer; /** - * Trait ContainerAwareTrait + * Trait ContainerAwareTrait. * * @package CoiSA\Container */ @@ -24,17 +26,13 @@ trait ContainerAwareTrait { /** * The container instance. - * - * @var PsrContainer */ - protected $container; + protected PsrContainer $container; /** * Sets a container. - * - * @param PsrContainer $container */ - public function setContainer(PsrContainer $container) + public function setContainer(PsrContainer $container): void { $this->container = $container; } diff --git a/src/ContainerInterface.php b/src/ContainerInterface.php index 4444d7e..8626365 100644 --- a/src/ContainerInterface.php +++ b/src/ContainerInterface.php @@ -1,5 +1,7 @@ + * @copyright Copyright (c) 2019-2022 Felipe Sayão Lobato Abreu * @license https://opensource.org/licenses/MIT MIT License */ + namespace CoiSA\Container; use Interop\Container\ServiceProviderInterface; @@ -25,10 +27,6 @@ interface ContainerInterface extends PsrContainer { /** * Register a new service provider to the container. - * - * @param ServiceProviderInterface $serviceProvider - * - * @return ContainerInterface */ - public function register(ServiceProviderInterface $serviceProvider); + public function register(ServiceProviderInterface $serviceProvider): self; } diff --git a/src/Factory/AggregateContainerAbstractFactory.php b/src/Factory/AggregateContainerAbstractFactory.php new file mode 100644 index 0000000..540c386 --- /dev/null +++ b/src/Factory/AggregateContainerAbstractFactory.php @@ -0,0 +1,37 @@ + + * @license https://opensource.org/licenses/MIT MIT License + */ + +namespace CoiSA\Container\Factory; + +use CoiSA\Container\AggregateContainer; +use CoiSA\Container\ContainerInterface; +use CoiSA\Factory\AbstractFactory; + +/** + * Class AggregateContainerAbstractFactory. + * + * @package CoiSA\Container\Factory + */ +final class AggregateContainerAbstractFactory implements ContainerAbstractFactoryInterface +{ + public static function create(): AggregateContainer + { + $containers = \func_get_args(); + $container = AbstractFactory::create(ContainerInterface::class); + $aggregateContainerFactory = new AggregateContainerFactory($container); + + return $aggregateContainerFactory->create(...$containers); + } +} diff --git a/src/Factory/AggregateContainerFactory.php b/src/Factory/AggregateContainerFactory.php index 4f504fd..6930089 100644 --- a/src/Factory/AggregateContainerFactory.php +++ b/src/Factory/AggregateContainerFactory.php @@ -1,5 +1,7 @@ + * @copyright Copyright (c) 2019-2022 Felipe Sayão Lobato Abreu * @license https://opensource.org/licenses/MIT MIT License */ + namespace CoiSA\Container\Factory; use CoiSA\Container\AggregateContainer; +use CoiSA\Container\ContainerInterface; use CoiSA\Factory\AbstractFactory; -use Psr\Container\ContainerInterface; +use Psr\Container\ContainerInterface as PsrContainerInterface; /** * Class AggregateContainerFactory. @@ -24,13 +27,25 @@ */ final class AggregateContainerFactory implements ContainerFactoryInterface { + private ContainerInterface $container; + /** - * @return ContainerInterface + * AggregateContainerFactory constructor. */ - public function create() + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + public function create(): AggregateContainer { $containers = \func_get_args(); - $aggregateContainer = new AggregateContainer($containers); + $aggregateContainer = new AggregateContainer(...$containers); + + $this->container->setService(AggregateContainer::class, $aggregateContainer); + $this->container->setAlias(PsrContainerInterface::class, AggregateContainer::class); + + $aggregateContainer->prepend($this->container); AbstractFactory::setContainer($aggregateContainer); diff --git a/src/Factory/ContainerAbstractFactory.php b/src/Factory/ContainerAbstractFactory.php index 809b1dc..37aed9e 100644 --- a/src/Factory/ContainerAbstractFactory.php +++ b/src/Factory/ContainerAbstractFactory.php @@ -1,5 +1,7 @@ + * @copyright Copyright (c) 2019-2022 Felipe Sayão Lobato Abreu * @license https://opensource.org/licenses/MIT MIT License */ + namespace CoiSA\Container\Factory; +use CoiSA\Container\ContainerInterface; use CoiSA\Factory\AbstractFactory; -use Psr\Container\ContainerInterface; +use CoiSA\ServiceProvider\AggregateServiceProvider; /** * Class ContainerAbstractFactory. @@ -23,13 +26,10 @@ */ final class ContainerAbstractFactory implements ContainerAbstractFactoryInterface { - /** - * @return ContainerInterface - */ - public static function create() + public static function create(): ContainerInterface { $serviceProviders = \func_get_args(); - $aggregateServiceProvider = AbstractFactory::create('CoiSA\\ServiceProvider\\AggregateServiceProvider'); + $aggregateServiceProvider = AbstractFactory::create(AggregateServiceProvider::class); $containerFactory = new ContainerFactory($aggregateServiceProvider); return $containerFactory->create($serviceProviders); diff --git a/src/Factory/ContainerAbstractFactoryInterface.php b/src/Factory/ContainerAbstractFactoryInterface.php index 3cd85aa..e02a72f 100644 --- a/src/Factory/ContainerAbstractFactoryInterface.php +++ b/src/Factory/ContainerAbstractFactoryInterface.php @@ -1,5 +1,7 @@ + * @copyright Copyright (c) 2019-2022 Felipe Sayão Lobato Abreu * @license https://opensource.org/licenses/MIT MIT License */ + namespace CoiSA\Container\Factory; use CoiSA\Factory\AbstractFactoryInterface; /** - * Interface ContainerAbstractFactoryInterface. + * Interface ContainerFactoryInterface. * * @package CoiSA\Container\Factory */ diff --git a/src/Factory/ContainerFactory.php b/src/Factory/ContainerFactory.php index 5857af3..43ef119 100644 --- a/src/Factory/ContainerFactory.php +++ b/src/Factory/ContainerFactory.php @@ -1,5 +1,7 @@ + * @copyright Copyright (c) 2019-2022 Felipe Sayão Lobato Abreu * @license https://opensource.org/licenses/MIT MIT License */ + namespace CoiSA\Container\Factory; use CoiSA\Container\Container; +use CoiSA\Container\ContainerInterface; use CoiSA\Factory\AbstractFactory; use CoiSA\Factory\Exception\InvalidArgumentException; use CoiSA\ServiceProvider\AggregateServiceProvider; +use CoiSA\ServiceProvider\Exception\ReflectionException; use CoiSA\ServiceProvider\LaminasConfigServiceProvider; use Interop\Container\ServiceProviderInterface; -use Psr\Container\ContainerInterface; +use Psr\Container\ContainerInterface as PsrContainerInterface; /** * Class ContainerFactory. @@ -33,10 +37,8 @@ final class ContainerFactory implements ContainerFactoryInterface */ private $aggregateServiceProvider; - /**, + /** * ContainerFactory constructor. - * - * @param AggregateServiceProvider $aggregateServiceProvider */ public function __construct(AggregateServiceProvider $aggregateServiceProvider) { @@ -44,41 +46,72 @@ public function __construct(AggregateServiceProvider $aggregateServiceProvider) } /** - * @throws \CoiSA\Factory\Exception\ReflectionException - * @throws \CoiSA\Factory\Exception\InvalidArgumentException - * - * @return ContainerInterface + * @throws InvalidArgumentException + * @throws ReflectionException */ - public function create() + public function create(): ContainerInterface { $serviceProviders = \func_get_args(); $container = new Container($this->aggregateServiceProvider); - foreach ($serviceProviders as $serviceProvider) { - if (\is_string($serviceProvider)) { - $serviceProvider = AbstractFactory::create($serviceProvider); - } + $this->registerServiceProviders($serviceProviders, $container); - if (\is_callable($serviceProvider)) { - $serviceProvider = \call_user_func($serviceProvider); - } + $container->setService(ContainerInterface::class, $container); + $container->setAlias(PsrContainerInterface::class, ContainerInterface::class); - if (\is_array($serviceProvider)) { - $serviceProvider = new LaminasConfigServiceProvider($serviceProvider); - } + if (class_exists(AbstractFactory::class)) { + AbstractFactory::setContainer($container); + } - if (!$serviceProvider instanceof ServiceProviderInterface) { - throw InvalidArgumentException::forInvalidArgumentType( - 'serviceProviders', - 'array' - ); + return $container; + } + + /** + * @param array|callable|ServiceProviderInterface|string> $serviceProviders + */ + private function registerServiceProviders(array $serviceProviders, Container $container): void + { + try { + foreach ($serviceProviders as $serviceProvider) { + $serviceProvider = $this->resolveServiceProvider($serviceProvider, $container); + $container->register($serviceProvider); } + } catch (\Throwable $throwable) { + throw InvalidArgumentException::forInvalidArgumentType( + 'serviceProviders', + 'array>', + 0, + $throwable + ); + } + } + + /** + * @param array|callable|ServiceProviderInterface|string $serviceProvider + * + * @throws ReflectionException + */ + private function resolveServiceProvider($serviceProvider, Container $container): ServiceProviderInterface + { + if (\is_string($serviceProvider)) { + $serviceProvider = AbstractFactory::create($serviceProvider); + } - $container->register($serviceProvider); + if (\is_callable($serviceProvider)) { + $serviceProvider = \call_user_func($serviceProvider, $container); } - AbstractFactory::setContainer($container); + if (\is_array($serviceProvider)) { + $serviceProvider = new LaminasConfigServiceProvider($serviceProvider); + } - return $container; + if (!$serviceProvider instanceof ServiceProviderInterface) { + throw InvalidArgumentException::forInvalidArgumentType( + 'serviceProvider', + 'string|callable|' . ServiceProviderInterface::class . '|array' + ); + } + + return $serviceProvider; } } diff --git a/src/Factory/ContainerFactoryInterface.php b/src/Factory/ContainerFactoryInterface.php index e442eb0..3f13992 100644 --- a/src/Factory/ContainerFactoryInterface.php +++ b/src/Factory/ContainerFactoryInterface.php @@ -1,5 +1,7 @@ + * @copyright Copyright (c) 2019-2022 Felipe Sayão Lobato Abreu * @license https://opensource.org/licenses/MIT MIT License */ + namespace CoiSA\Container\Factory; use CoiSA\Factory\FactoryInterface; diff --git a/tests/Stub/ServiceProvider/ExampleOtherServiceProvider.php b/tests/Stub/ServiceProvider/ExampleOtherServiceProvider.php index c1ddc31..1572461 100644 --- a/tests/Stub/ServiceProvider/ExampleOtherServiceProvider.php +++ b/tests/Stub/ServiceProvider/ExampleOtherServiceProvider.php @@ -1,5 +1,7 @@ + * @copyright Copyright (c) 2019-2022 Felipe Sayão Lobato Abreu * @license https://opensource.org/licenses/MIT MIT License */ + namespace CoiSA\Container\Test\Stub\ServiceProvider; use CoiSA\ServiceProvider\Factory\ServiceFactory; @@ -28,6 +30,6 @@ final class ExampleOtherServiceProvider extends ServiceProvider */ public function __construct() { - $this->setFactory(\get_called_class(), new ServiceFactory($this)); + $this->setFactory(static::class, new ServiceFactory($this)); } } diff --git a/tests/Stub/ServiceProvider/ExampleServiceProvider.php b/tests/Stub/ServiceProvider/ExampleServiceProvider.php index 38380c4..c0e37fd 100644 --- a/tests/Stub/ServiceProvider/ExampleServiceProvider.php +++ b/tests/Stub/ServiceProvider/ExampleServiceProvider.php @@ -1,5 +1,7 @@ + * @copyright Copyright (c) 2019-2022 Felipe Sayão Lobato Abreu * @license https://opensource.org/licenses/MIT MIT License */ + namespace CoiSA\Container\Test\Stub\ServiceProvider; use CoiSA\ServiceProvider\Factory\ServiceFactory; @@ -26,8 +28,9 @@ final class ExampleServiceProvider extends ServiceProvider /** * ExampleServiceProvider constructor. */ - public function __construct() + public function __construct(array $options = []) { - $this->setFactory(\get_called_class(), new ServiceFactory($this)); + $this->setFactory(static::class, new ServiceFactory($this)); + $this->setFactory('options', new ServiceFactory($options)); } } diff --git a/tests/Unit/Factory/ContainerFactoryTest.php b/tests/Unit/Factory/ContainerFactoryTest.php index 8e44918..949c2c7 100644 --- a/tests/Unit/Factory/ContainerFactoryTest.php +++ b/tests/Unit/Factory/ContainerFactoryTest.php @@ -1,5 +1,7 @@ + * @copyright Copyright (c) 2019-2022 Felipe Sayão Lobato Abreu * @license https://opensource.org/licenses/MIT MIT License */ + namespace CoiSA\Container\Test\Unit\Factory; use CoiSA\Container\Factory\ContainerFactory; @@ -24,11 +26,14 @@ * Class ContainerFactoryTest. * * @package CoiSA\Container\Test\Unit\Factory + * + * @internal + * @coversNothing */ final class ContainerFactoryTest extends TestCase { /** - * @var ObjectProphecy|AggregateServiceProvider + * @var AggregateServiceProvider|ObjectProphecy */ private $aggregateServiceProvider; @@ -37,7 +42,7 @@ final class ContainerFactoryTest extends TestCase */ private $containerFactory; - public function setUp() + protected function setUp(): void { $this->aggregateServiceProvider = $this->prophesize('CoiSA\\ServiceProvider\\AggregateServiceProvider'); $this->containerFactory = new ContainerFactory($this->aggregateServiceProvider->reveal()); @@ -45,17 +50,17 @@ public function setUp() public function provideServiceProviders() { - return array( - array(), - array(new ExampleServiceProvider()), - array(new ExampleServiceProvider(), new ExampleOtherServiceProvider()), - ); + return [ + [], + [new ExampleServiceProvider()], + [new ExampleServiceProvider(), new ExampleOtherServiceProvider()], + ]; } /** * @dataProvider provideServiceProviders */ - public function testCreateWillCallConstructorFactoryCreateWithGivenServiceProviders() + public function testCreateWillCallConstructorFactoryCreateWithGivenServiceProviders(): void { $serviceProviders = \func_get_args(); @@ -63,38 +68,38 @@ public function testCreateWillCallConstructorFactoryCreateWithGivenServiceProvid $this->aggregateServiceProvider->append($serviceProvider)->shouldBeCalledOnce(); } - \call_user_func_array(array($this->containerFactory, 'create'), $serviceProviders); + \call_user_func_array([$this->containerFactory, 'create'], $serviceProviders); } /** * @dataProvider provideServiceProviders */ - public function testCreateWillReturnContainerWithGivenServiceProviders() + public function testCreateWillReturnContainerWithGivenServiceProviders(): void { $serviceProviders = \func_get_args(); - $container = \call_user_func_array(array($this->containerFactory, 'create'), $serviceProviders); + $container = \call_user_func_array([$this->containerFactory, 'create'], $serviceProviders); - self::assertInstanceOf('CoiSA\\Container\\Container', $container); + static::assertInstanceOf('CoiSA\\Container\\Container', $container); $reflectionProperty = new \ReflectionProperty('CoiSA\\Container\\Container', 'aggregateServiceProvider'); $reflectionProperty->setAccessible(true); - self::assertSame($this->aggregateServiceProvider->reveal(), $reflectionProperty->getValue($container)); + static::assertSame($this->aggregateServiceProvider->reveal(), $reflectionProperty->getValue($container)); } /** * @dataProvider provideServiceProviders */ - public function testCreateWillSetContainerForAbstractFactory() + public function testCreateWillSetContainerForAbstractFactory(): void { $serviceProviders = \func_get_args(); - $container = \call_user_func_array(array($this->containerFactory, 'create'), $serviceProviders); + $container = \call_user_func_array([$this->containerFactory, 'create'], $serviceProviders); $reflectionProperty = new \ReflectionProperty('CoiSA\\Factory\\FactoryAbstractFactory', 'container'); $reflectionProperty->setAccessible(true); - self::assertSame($container, $reflectionProperty->getValue()); + static::assertSame($container, $reflectionProperty->getValue()); } }