From ac9f80312d0b7b03407f46603b2d5ea2e88f082c Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Wed, 22 Feb 2023 12:37:30 +0200 Subject: [PATCH] container get by class --- application/Espo/Core/Container.php | 45 +++++++++++++------ application/Espo/Core/Container/Container.php | 13 +++++- .../Espo/Core/Container/ContainerBuilder.php | 5 +-- tests/integration/Espo/Core/ContainerTest.php | 42 +++++++++++++++++ 4 files changed, 87 insertions(+), 18 deletions(-) create mode 100644 tests/integration/Espo/Core/ContainerTest.php diff --git a/application/Espo/Core/Container.php b/application/Espo/Core/Container.php index f54001d460..0bb4ff6455 100644 --- a/application/Espo/Core/Container.php +++ b/application/Espo/Core/Container.php @@ -29,6 +29,7 @@ namespace Espo\Core; +use Espo\Core\Binding\Binding; use Espo\Core\Container\Exceptions\NotFoundException; use Espo\Core\Container\Exceptions\NotSettableException; use Espo\Core\Container\Loader; @@ -45,7 +46,7 @@ use RuntimeException; /** * DI container for services. Lazy initialization is used. Services are instantiated only once. - * See https://docs.espocrm.com/development/di/. + * @see https://docs.espocrm.com/development/di/. */ class Container implements ContainerInterface { @@ -60,7 +61,6 @@ class Container implements ContainerInterface private array $loaderClassNames; private ?Configuration $configuration = null; - private ?BindingContainer $bindingContainer = null; private InjectableFactory $injectableFactory; /** @@ -71,9 +71,9 @@ class Container implements ContainerInterface */ public function __construct( string $configurationClassName, + private BindingContainer $bindingContainer, array $loaderClassNames = [], - array $services = [], - ?BindingContainer $bindingContainer = null + array $services = [] ) { $this->loaderClassNames = $loaderClassNames; @@ -85,8 +85,6 @@ class Container implements ContainerInterface $this->setForced($name, $service); } - $this->bindingContainer = $bindingContainer; - /** @var InjectableFactory $injectableFactory */ $injectableFactory = $this->get(self::ID_INJECTABLE_FACTORY); $this->injectableFactory = $injectableFactory; @@ -95,9 +93,7 @@ class Container implements ContainerInterface } /** - * Obtain a service object. - * - * @throws NotFoundExceptionInterface If not gettable. + * @inheritDoc */ public function get(string $id): object { @@ -113,7 +109,7 @@ class Container implements ContainerInterface } /** - * Check whether a service can be obtained. + * @inheritDoc */ public function has(string $id): bool { @@ -146,6 +142,31 @@ class Container implements ContainerInterface return false; } + /** + * @inheritDoc + * @template T of object + * @param class-string $className A class name or interface name. + * @return T A service instance. + * @throws NotFoundExceptionInterface If not gettable. + */ + public function getByClass(string $className): object + { + $binding = $this->bindingContainer->getByInterface($className); + + if ($binding->getType() !== Binding::CONTAINER_SERVICE) { + throw new NotFoundException("No service bound to `{$className}`."); + } + + $id = $binding->getValue(); + + if (!is_string($id)) { + throw new LogicException(); + } + + /** @var T */ + return $this->get($id); + } + private function isSet(string $id): bool { return isset($this->data[$id]); @@ -243,9 +264,7 @@ class Container implements ContainerInterface } /** - * Set a service object. Must be configured as settable. - * - * @throws NotSettableException Is not settable or already set. + * @inheritDoc */ public function set(string $id, object $object): void { diff --git a/application/Espo/Core/Container/Container.php b/application/Espo/Core/Container/Container.php index 25eacbebbb..2f557e65fb 100644 --- a/application/Espo/Core/Container/Container.php +++ b/application/Espo/Core/Container/Container.php @@ -36,8 +36,7 @@ use ReflectionClass; /** * DI container for services. Lazy initialization is used. Services are instantiated only once. - * - * See https://docs.espocrm.com/development/di/. + * @see https://docs.espocrm.com/development/di/. */ interface Container extends ContainerInterface { @@ -67,4 +66,14 @@ interface Container extends ContainerInterface * @throws NotFoundExceptionInterface If not gettable. */ public function getClass(string $id): ReflectionClass; + + /** + * Get a service by a class name. A service should be bound to a class or interface. + * + * @template T of object + * @param class-string $className A class name or interface name. + * @return T A service instance. + * @throws NotFoundExceptionInterface If not gettable. + */ + public function getByClass(string $className): object; } diff --git a/application/Espo/Core/Container/ContainerBuilder.php b/application/Espo/Core/Container/ContainerBuilder.php index f5ccbd8b26..871281554d 100644 --- a/application/Espo/Core/Container/ContainerBuilder.php +++ b/application/Espo/Core/Container/ContainerBuilder.php @@ -30,7 +30,6 @@ namespace Espo\Core\Container; use Espo\Core\Container; -use Espo\Core\Container\ContainerConfiguration; use Espo\Core\Container\Container as ContainerInterface; use Espo\Core\Binding\BindingContainer; @@ -194,9 +193,9 @@ class ContainerBuilder return new $this->containerClassName( $this->containerConfigurationClassName, + $bindingContainer, $this->loaderClassNames, - $this->services, - $bindingContainer + $this->services ); } } diff --git a/tests/integration/Espo/Core/ContainerTest.php b/tests/integration/Espo/Core/ContainerTest.php new file mode 100644 index 0000000000..c67b094332 --- /dev/null +++ b/tests/integration/Espo/Core/ContainerTest.php @@ -0,0 +1,42 @@ +getContainer()->getByClass(EntityManager::class); + + $this->assertInstanceOf(EntityManager::class, $em); + } +}