diff --git a/application/Espo/Core/Application.php b/application/Espo/Core/Application.php index eeb34cd6ec..427c601f61 100644 --- a/application/Espo/Core/Application.php +++ b/application/Espo/Core/Application.php @@ -48,6 +48,7 @@ class Application $GLOBALS['log'] = $this->getContainer()->get('log'); $this->initAutoloads(); + $this->initPreloads(); } protected function initContainer() @@ -346,6 +347,15 @@ class Application $autoload->register(); } + protected function initPreloads() + { + foreach ($this->getMetadata()->get(['app', 'containerServices']) ?? [] as $name => $defs) { + if ($defs['preload'] ?? false) { + $this->getContainer()->get($name); + } + } + } + public function setBasePath($basePath) { $this->getContainer()->get('clientManager')->setBasePath($basePath); diff --git a/application/Espo/Core/Container.php b/application/Espo/Core/Container.php index 304818aad4..062e76a125 100644 --- a/application/Espo/Core/Container.php +++ b/application/Espo/Core/Container.php @@ -61,9 +61,10 @@ class Container $obj = $this->$loadMethod(); $this->data[$name] = $obj; } else { + $metadata = $this->get('metadata'); try { - $className = $this->get('metadata')->get(['app', 'loaders', ucfirst($name)]); + $className = $metadata->get(['app', 'loaders', ucfirst($name)]); } catch (\Exception $e) {} if (!isset($className) || !class_exists($className)) { @@ -73,19 +74,46 @@ class Container } } + $object = null; + if (class_exists($className)) { - $loadClass = new $className($this); - $this->data[$name] = $loadClass->load(); + $loadClass = new $className($this); + $object = $loadClass->load(); + $this->data[$name] = $object; + } else { + $className = $this->getServiceClassName($name); + + if ($className && class_exists($className)) { + + $dependencyList = $metadata->get(['app', 'containerServices', $name, 'dependencyList']) ?? []; + $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); + } + } else { + $object = $reflector->newInstanceArgs($dependencyObjectList); + } + $this->data[$name] = $object; + } } } return null; } - public function getServiceClassName(string $name, string $default) + public function getServiceClassName(string $name, ?string $default = null) { $metadata = $this->get('metadata'); - $className = $metadata->get(['app', 'serviceContainer', 'classNames', $name], $default); + + $className = $metadata->get(['app', 'containerServices', $name, 'className']) ?? + $metadata->get(['app', 'serviceContainer', 'classNames', $name], $default); + return $className; } diff --git a/application/Espo/Core/InjectableService.php b/application/Espo/Core/InjectableService.php new file mode 100644 index 0000000000..799db48fd1 --- /dev/null +++ b/application/Espo/Core/InjectableService.php @@ -0,0 +1,58 @@ +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/Loaders/ClientManager.php b/application/Espo/Core/Interfaces/InjectableService.php similarity index 81% rename from application/Espo/Core/Loaders/ClientManager.php rename to application/Espo/Core/Interfaces/InjectableService.php index c0b446ccb1..63f285dd5a 100644 --- a/application/Espo/Core/Loaders/ClientManager.php +++ b/application/Espo/Core/Interfaces/InjectableService.php @@ -27,16 +27,10 @@ * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ -namespace Espo\Core\Loaders; +namespace Espo\Core\Interfaces; -class ClientManager extends Base +interface InjectableService { - public function load() - { - return new \Espo\Core\Utils\ClientManager( - $this->getContainer()->get('config'), - $this->getContainer()->get('themeManager'), - $this->getContainer()->get('metadata') - ); - } + public function inject(string $name, object $object); } + diff --git a/application/Espo/Core/Portal/Container.php b/application/Espo/Core/Portal/Container.php index 57948a55a0..d32c9b557b 100644 --- a/application/Espo/Core/Portal/Container.php +++ b/application/Espo/Core/Portal/Container.php @@ -31,23 +31,20 @@ namespace Espo\Core\Portal; class Container extends \Espo\Core\Container { - public function getServiceClassName(string $name, string $default) + public function getServicePortalClassName(string $name, ?string $default = null) { $metadata = $this->get('metadata'); - $className = $metadata->get(['app', 'serviceContainerPortal', 'classNames', $name], $default); - return $className; + return $metadata->get(['app', 'portalContainerServices', $name, 'className'], $default); } - protected function getServiceMainClassName(string $name, string $default) + protected function getServiceMainClassName(string $name, ?string $default = null) { - $metadata = $this->get('metadata'); - $className = $metadata->get(['app', 'serviceContainer', 'classNames', $name], $default); - return $className; + return parent::getServiceClassName($name, $default); } protected function loadAclManager() { - $className = $this->getServiceClassName('aclManager', '\\Espo\\Core\\Portal\\AclManager'); + $className = $this->getServicePortalClassName('aclManager', '\\Espo\\Core\\Portal\\AclManager'); $mainClassName = $this->getServiceMainClassName('aclManager', '\\Espo\\Core\\AclManager'); $obj = new $className( @@ -63,7 +60,7 @@ class Container extends \Espo\Core\Container protected function loadAcl() { - $className = $this->getServiceClassName('acl', '\\Espo\\Core\\Portal\\Acl'); + $className = $this->getServicePortalClassName('acl', '\\Espo\\Core\\Portal\\Acl'); return new $className( $this->get('aclManager'), $this->get('user') diff --git a/application/Espo/Resources/metadata/app/containerServices.json b/application/Espo/Resources/metadata/app/containerServices.json new file mode 100644 index 0000000000..7499f843c2 --- /dev/null +++ b/application/Espo/Resources/metadata/app/containerServices.json @@ -0,0 +1,6 @@ +{ + "clientManager": { + "className": "\\Espo\\Core\\Utils\\ClientManager", + "dependencyList": ["config", "themeManager", "metadata"] + } +} \ No newline at end of file diff --git a/application/Espo/Resources/metadata/app/metadata.json b/application/Espo/Resources/metadata/app/metadata.json index 840afc945f..cc90a9943b 100644 --- a/application/Espo/Resources/metadata/app/metadata.json +++ b/application/Espo/Resources/metadata/app/metadata.json @@ -2,6 +2,9 @@ "frontendHiddenPathList": [ ["app", "metadata"], ["app", "loaders"], + ["app", "containerServices"], + ["app", "portalContainerServices"], + ["app", "serviceContainer"], ["app", "consoleCommands"], ["app", "formula", "functionClassNameMap"], ["app", "fileStorage", "implementationClassNameMap"],