From 74e4012feea704b2a151e395fd80830f0e432215 Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Thu, 18 Jun 2020 20:24:26 +0300 Subject: [PATCH] di improvements --- .../Core/AppParams/TemplateEntityTypeList.php | 24 ++++-- application/Espo/Core/Container.php | 26 +++--- application/Espo/Core/InjectableFactory.php | 85 ++++++++++++++----- application/Espo/Core/InjectableService.php | 58 ------------- .../Core/Interfaces/InjectableService.php | 36 -------- ...torFactory.php => NotificatorFactory1.php} | 3 +- application/Espo/Core/NotificatorFactory.php | 18 +++- application/Espo/Core/Portal/Container.php | 2 +- .../metadata/app/containerServices.json | 3 + 9 files changed, 115 insertions(+), 140 deletions(-) delete mode 100644 application/Espo/Core/InjectableService.php delete mode 100644 application/Espo/Core/Interfaces/InjectableService.php rename application/Espo/Core/Loaders/{NotificatorFactory.php => NotificatorFactory1.php} (93%) diff --git a/application/Espo/Core/AppParams/TemplateEntityTypeList.php b/application/Espo/Core/AppParams/TemplateEntityTypeList.php index c901090a47..db8867c206 100644 --- a/application/Espo/Core/AppParams/TemplateEntityTypeList.php +++ b/application/Espo/Core/AppParams/TemplateEntityTypeList.php @@ -29,29 +29,37 @@ namespace Espo\Core\AppParams; -class TemplateEntityTypeList extends \Espo\Core\Injectable +use Espo\Core\Acl; +use Espo\Core\SelectManagerFactory; +use Espo\ORM\EntityManager; + +class TemplateEntityTypeList { - protected function init() + protected $acl; + protected $selectManagerFactory; + protected $entityManager; + + public function __construct(Acl $acl, SelectManagerFactory $selectManagerFactory, EntityManager $entityManager) { - $this->addDependency('acl'); - $this->addDependency('selectManagerFactory'); - $this->addDependency('entityManager'); + $this->acl = $acl; + $this->selectManagerFactory = $selectManagerFactory; + $this->entityManager = $entityManager; } public function get() { - if (!$this->getInjection('acl')->checkScope('Template')) { + if (!$this->acl->checkScope('Template')) { return []; } $list = []; - $selectManager = $this->getInjection('selectManagerFactory')->create('Template'); + $selectManager = $this->selectManagerFactory->create('Template'); $selectParams = $selectManager->getEmptySelectParams(); $selectManager->applyAccess($selectParams); - $templateList = $this->getInjection('entityManager')->getRepository('Template') + $templateList = $this->entityManager->getRepository('Template') ->select(['entityType']) ->groupBy(['entityType']) ->find($selectParams); diff --git a/application/Espo/Core/Container.php b/application/Espo/Core/Container.php index 60c198c54b..5f710ed85e 100644 --- a/application/Espo/Core/Container.php +++ b/application/Espo/Core/Container.php @@ -71,8 +71,10 @@ class Container $metadata = $this->get('metadata'); try { + // deprecated $className = $metadata->get(['app', 'containerServices', $name, 'loaderClassName']); if (!$className) { + // deprecated $className = $metadata->get(['app', 'loaders', ucfirst($name)]); } } catch (\Exception $e) {} @@ -84,6 +86,7 @@ class Container } } + $object = null; if (class_exists($className)) { @@ -94,21 +97,18 @@ class Container $className = $this->getServiceClassName($name); if ($className && class_exists($className)) { - $dependencyList = $this->getServiceDependencyList($name); - $dependencyObjectList = []; - foreach ($dependencyList as $item) { - $dependencyObjectList[] = $this->get($item); - } - $reflector = new \ReflectionClass($className); - if ($reflector->isSubclassOf('\\Espo\\Core\\Interfaces\\InjectableService')) { - $object = $reflector->newInstance(); - foreach ($dependencyObjectList as $i => $item) { - $object->inject($dependencyList[$i], $item); + if (!is_null($dependencyList)) { + $dependencyObjectList = []; + foreach ($dependencyList as $item) { + $dependencyObjectList[] = $this->get($item); } - } else { + $reflector = new \ReflectionClass($className); $object = $reflector->newInstanceArgs($dependencyObjectList); + } else { + $object = $this->get('injectableFactory')->create($className); } + $this->data[$name] = $object; } } @@ -117,9 +117,9 @@ class Container return null; } - protected function getServiceDependencyList(string $name) : array + protected function getServiceDependencyList(string $name) : ?array { - return $this->get('metadata')->get(['app', 'containerServices', $name, 'dependencyList']) ?? []; + return $this->get('metadata')->get(['app', 'containerServices', $name, 'dependencyList']) ?? null; } protected function getServiceClassName(string $name, ?string $default = null) diff --git a/application/Espo/Core/InjectableFactory.php b/application/Espo/Core/InjectableFactory.php index 90581187b2..5303a8124c 100644 --- a/application/Espo/Core/InjectableFactory.php +++ b/application/Espo/Core/InjectableFactory.php @@ -29,8 +29,12 @@ namespace Espo\Core; -use \Espo\Core\Exceptions\Error; +use Espo\Core\Exceptions\Error; +/** + * Creates instance by class name. Uses either Injectable interface or constructor param names to detect + * which dependencies are needed. Only container services supported as dependencies. + */ class InjectableFactory { private $container; @@ -40,28 +44,71 @@ class InjectableFactory $this->container = $container; } - public function createByClassName($className) + public function create(string $className) : object { - if (class_exists($className)) { - $service = new $className(); - if (!($service instanceof \Espo\Core\Interfaces\Injectable)) { - throw new Error("Class '$className' is not instance of Injectable interface"); - } - $dependencyList = $service->getDependencyList(); - foreach ($dependencyList as $name) { - $service->inject($name, $this->container->get($name)); - } - if (method_exists($service, 'prepare')) { - $service->prepare(); - } - return $service; - } - throw new Error("Class '$className' does not exist"); + return $this->createByClassName($className); } - protected function getMetadata() + public function createByClassName(string $className) : object { - return $this->getContainer()->get('metadata'); + if (!class_exists($className)) { + throw new Error("Class '{$className}' does not exist."); + } + + $class = new \ReflectionClass($className); + if ($class->implementsInterface('\\Espo\\Core\\Interfaces\\Injectable')) { + return $this->createByClassNameInjectable($className); + } + + return $this->createByClassNameByConstructorParams($className); + } + + protected function createByClassNameByConstructorParams(string $className) + { + $class = new \ReflectionClass($className); + + $dependencyList = []; + + $constructor = $class->getConstructor(); + if (!is_null($constructor)) { + $params = $constructor->getParameters(); + + foreach ($params as $param) { + $dependencyClassName = $param->getClass(); + if (is_null($dependencyClassName)) { + if ($param->isDefaultValueAvailable()) { + $dependencyList[] = $param->getDefaultValue(); + continue; + } + } + + $name = $param->getName(); + $dependency = $this->getContainer()->get($name); + + if (!$dependency) { + throw new Error("InjectableFactory: Could not create {$className}, dependency {$name} not found."); + } + + $dependencyList[] = $dependency; + } + } + + return $class->newInstanceArgs($dependencyList); + } + + protected function createByClassNameInjectable(string $className) + { + $obj = new $className(); + + $dependencyList = $obj->getDependencyList(); + foreach ($dependencyList as $name) { + $obj->inject($name, $this->container->get($name)); + } + if (method_exists($obj, 'prepare')) { + $obj->prepare(); + } + + return $obj; } protected function getContainer() diff --git a/application/Espo/Core/InjectableService.php b/application/Espo/Core/InjectableService.php deleted file mode 100644 index c678dc6ef2..0000000000 --- a/application/Espo/Core/InjectableService.php +++ /dev/null @@ -1,58 +0,0 @@ -injections[$name] = $object; - } - - public function __call($methodName, $args) - { - if (strpos($methodName, 'get') === 0) { - $injectionName = lcfirst(substr($methodName, 3)); - if (isset($this->injections[$injectionName])) { - return $this->getInjection($injectionName); - } - } - throw new \BadMethodCallException('Method ' . $methodName . ' does not exist'); - } - - protected function getInjection(string $name) : object - { - $object = $this->injections[$name] ?? null; - if (!$object) throw new \Exception("Injection {$name} is not set."); - return $object; - } -} diff --git a/application/Espo/Core/Interfaces/InjectableService.php b/application/Espo/Core/Interfaces/InjectableService.php deleted file mode 100644 index ce8947771a..0000000000 --- a/application/Espo/Core/Interfaces/InjectableService.php +++ /dev/null @@ -1,36 +0,0 @@ -getContainer() + $this->getContainer()->get('injectableFactory'), + $this->getContainer()->get('classFinder') ); } } diff --git a/application/Espo/Core/NotificatorFactory.php b/application/Espo/Core/NotificatorFactory.php index 34faeb6935..07f082a4bb 100644 --- a/application/Espo/Core/NotificatorFactory.php +++ b/application/Espo/Core/NotificatorFactory.php @@ -29,23 +29,33 @@ namespace Espo\Core; -use Espo\Core\Utils\Util; use Espo\Core\InjectableFactory; +use Espo\Core\Utils\ClassFinder; use Espo\Core\Notificators\Base as BaseNotificator; -class NotificatorFactory extends InjectableFactory +class NotificatorFactory { protected $baseClassName = '\\Espo\\Core\\Notificators\\Base'; + protected $injectableFactory; + protected $classFinder; + + public function __construct(InjectableFactory $injectableFactory, ClassFinder $classFinder) + { + $this->injectableFactory = $injectableFactory; + $this->classFinder = $classFinder; + } + public function create(string $entityType) : BaseNotificator { - $className = $this->getContainer()->get('classFinder')->find('Notificators', $entityType); + $className = $this->classFinder->find('Notificators', $entityType); if (!$className || !class_exists($className)) { $className = $this->baseClassName; } - $obj = $this->createByClassName($className); + $obj = $this->injectableFactory->create($className); + $obj->setEntityType($entityType); return $obj; diff --git a/application/Espo/Core/Portal/Container.php b/application/Espo/Core/Portal/Container.php index 89e0568109..19e0f7e5cc 100644 --- a/application/Espo/Core/Portal/Container.php +++ b/application/Espo/Core/Portal/Container.php @@ -43,7 +43,7 @@ class Container extends \Espo\Core\Container return $metadata->get(['app', 'portalContainerServices', $name, 'className'], $default); } - protected function getServiceDependencyList(string $name) : array + protected function getServiceDependencyList(string $name) : ?array { return $this->get('metadata')->get(['app', 'portalContainerServices', $name, 'dependencyList']) ?? parent::getServiceDependencyList($name); diff --git a/application/Espo/Resources/metadata/app/containerServices.json b/application/Espo/Resources/metadata/app/containerServices.json index 22faeb6208..24836055f0 100644 --- a/application/Espo/Resources/metadata/app/containerServices.json +++ b/application/Espo/Resources/metadata/app/containerServices.json @@ -1,4 +1,7 @@ { + "notificatorFactory": { + "className": "\\Espo\\Core\\NotificatorFactory" + }, "clientManager": { "className": "\\Espo\\Core\\Utils\\ClientManager", "dependencyList": ["config", "themeManager", "metadata"]