From 6c6d1a7f5361b8179045cc7585d0eb0725a51b3e Mon Sep 17 00:00:00 2001 From: Taras Machyshyn Date: Mon, 18 Feb 2019 11:44:38 +0200 Subject: [PATCH] Autoload: added psr-0, psr-4, classmap, autoloadFileList, files --- application/Espo/Core/Application.php | 27 +-- application/Espo/Core/Utils/Autoload.php | 53 +++- .../Espo/Core/Utils/Autoload/Loader.php | 99 ++++++++ .../Core/Utils/Autoload/NamespaceLoader.php | 229 ++++++++++++++++++ 4 files changed, 377 insertions(+), 31 deletions(-) create mode 100644 application/Espo/Core/Utils/Autoload/Loader.php create mode 100644 application/Espo/Core/Utils/Autoload/NamespaceLoader.php diff --git a/application/Espo/Core/Application.php b/application/Espo/Core/Application.php index 1ccf026277..b2de4c0d96 100644 --- a/application/Espo/Core/Application.php +++ b/application/Espo/Core/Application.php @@ -310,8 +310,6 @@ class Application protected function getRouteList() { $routes = new \Espo\Core\Utils\Route($this->getConfig(), $this->getMetadata(), $this->getContainer()->get('fileManager')); - - return $routes->getAll(); } @@ -339,30 +337,7 @@ class Application protected function initAutoloads() { $autoload = new \Espo\Core\Utils\Autoload($this->getConfig(), $this->getMetadata(), $this->getContainer()->get('fileManager')); - - try { - $autoloadList = $autoload->getAll(); - } catch (\Exception $e) {} //bad permissions - - if (empty($autoloadList)) { - return; - } - - $namespacesPath = 'vendor/composer/autoload_namespaces.php'; - $existingNamespaces = file_exists($namespacesPath) ? include($namespacesPath) : array(); - if (!empty($existingNamespaces) && is_array($existingNamespaces)) { - $existingNamespaces = array_keys($existingNamespaces); - } - - $classLoader = new \Composer\Autoload\ClassLoader(); - - foreach ($autoloadList as $prefix => $path) { - if (!in_array($prefix, $existingNamespaces)) { - $classLoader->add($prefix, $path); - } - } - - $classLoader->register(true); + $autoload->register(); } public function setBasePath($basePath) diff --git a/application/Espo/Core/Utils/Autoload.php b/application/Espo/Core/Utils/Autoload.php index bcd1c7ddec..8af271a80b 100644 --- a/application/Espo/Core/Utils/Autoload.php +++ b/application/Espo/Core/Utils/Autoload.php @@ -33,10 +33,14 @@ class Autoload { protected $data = null; - private $fileManager; private $config; + private $metadata; + private $fileManager; + + private $loader; + protected $cacheFile = 'data/cache/application/autoload.php'; protected $paths = array( @@ -50,6 +54,7 @@ class Autoload $this->config = $config; $this->metadata = $metadata; $this->fileManager = $fileManager; + $this->loader = new \Espo\Core\Utils\Autoload\Loader($config, $fileManager); } protected function getConfig() @@ -67,6 +72,11 @@ class Autoload return $this->metadata; } + protected function getLoader() + { + return $this->loader; + } + public function get($key = null, $returns = null) { if (!isset($this->data)) { @@ -80,7 +90,6 @@ class Autoload return Utill::getValueByKey($this->data, $key, $returns); } - public function getAll() { return $this->get(); @@ -120,10 +129,10 @@ class Autoload protected function loadData($autoloadFile, $returns = array()) { if (file_exists($autoloadFile)) { - $content= $this->getFileManager()->getContents($autoloadFile); + $content = $this->getFileManager()->getContents($autoloadFile); $arrayContent = Json::getArrayData($content); if (!empty($arrayContent)) { - return $arrayContent; + return $this->normalizeData($arrayContent); } $GLOBALS['log']->error('Autoload::unify() - Empty file or syntax error - ['.$autoloadFile.']'); @@ -131,4 +140,38 @@ class Autoload return $returns; } -} \ No newline at end of file + + protected function normalizeData(array $data) + { + $normalizedData = []; + + foreach ($data as $key => $value) { + switch ($key) { + case 'psr-4': + case 'psr-0': + case 'classmap': + case 'files': + case 'autoloadFileList': + $normalizedData[$key] = $value; + break; + + default: + $normalizedData['psr-0'][$key] = $value; + break; + } + } + + return $normalizedData; + } + + public function register() + { + try { + $autoloadList = $this->getAll(); + } catch (\Exception $e) {} //bad permissions + + if (!empty($autoloadList)) { + $this->getLoader()->register($autoloadList); + } + } +} diff --git a/application/Espo/Core/Utils/Autoload/Loader.php b/application/Espo/Core/Utils/Autoload/Loader.php new file mode 100644 index 0000000000..5222d93507 --- /dev/null +++ b/application/Espo/Core/Utils/Autoload/Loader.php @@ -0,0 +1,99 @@ +config = $config; + $this->fileManager = $fileManager; + $this->namespaceLoader = new NamespaceLoader($config, $fileManager); + } + + public function getConfig() + { + return $this->config; + } + + public function getFileManager() + { + return $this->fileManager; + } + + public function getNamespaceLoader() + { + return $this->namespaceLoader; + } + + public function register(array $autoloadList) + { + /* load "psr-4", "psr-0", "classmap" */ + $this->getNamespaceLoader()->register($autoloadList); + + /* load "autoloadFileList" */ + $this->registerAutoloadFileList($autoloadList); + + /* load "files" */ + $this->registerFiles($autoloadList); + } + + protected function registerAutoloadFileList(array $autoloadList) + { + $keyName = 'autoloadFileList'; + + if (!isset($autoloadList[$keyName])) return; + + foreach ($autoloadList[$keyName] as $filePath) { + if (file_exists($filePath)) { + require_once($filePath); + } + } + } + + protected function registerFiles(array $autoloadList) + { + $keyName = 'files'; + + if (!isset($autoloadList[$keyName])) return; + + foreach ($autoloadList[$keyName] as $id => $filePath) { + if (file_exists($filePath)) { + require_once($filePath); + } + } + } +} diff --git a/application/Espo/Core/Utils/Autoload/NamespaceLoader.php b/application/Espo/Core/Utils/Autoload/NamespaceLoader.php new file mode 100644 index 0000000000..c3a94ac2a0 --- /dev/null +++ b/application/Espo/Core/Utils/Autoload/NamespaceLoader.php @@ -0,0 +1,229 @@ + 'vendor/composer/autoload_psr4.php', + 'psr-0' => 'vendor/composer/autoload_namespaces.php', + 'classmap' => 'vendor/composer/autoload_classmap.php', + ]; + + /** + * Method names in ClassLoader + * @var array + */ + protected $methodNameMap = [ + 'psr-4' => 'addPsr4', + 'psr-0' => 'add', + 'classmap' => 'addClassMap', + ]; + + protected $vendorNamespaces; + + protected $vendorNamespacesCacheFile = 'data/cache/application/autoload-vendor-namespaces.php'; + + public function __construct(\Espo\Core\Utils\Config $config, \Espo\Core\Utils\File\Manager $fileManager) + { + $this->config = $config; + $this->fileManager = $fileManager; + $this->classLoader = new \Composer\Autoload\ClassLoader(); + } + + protected function getConfig() + { + return $this->config; + } + + protected function getFileManager() + { + return $this->fileManager; + } + + protected function getClassLoader() + { + return $this->classLoader; + } + + public function register(array $autoloadList) + { + $classLoader = $this->getClassLoader(); + $this->addListToClassLoader($classLoader, $autoloadList); + $classLoader->register(true); + } + + protected function loadNamespaces($basePath = '') + { + $namespaces = []; + + foreach ($this->namespacesPaths as $type => $path) { + $mapFile = Util::concatPath($basePath, $path); + if (file_exists($mapFile)) { + $map = require($mapFile); + if (!empty($map) && is_array($map)) { + $namespaces[$type] = $map; + } + } + } + + return $namespaces; + } + + protected function getNamespaces() + { + if (!$this->namespaces) { + $this->namespaces = $this->loadNamespaces(); + } + + return $this->namespaces; + } + + protected function getNamespaceList($type, $default = []) + { + $namespaces = $this->getNamespaces(); + + if (isset($namespaces[$type])) { + return array_keys($namespaces[$type]); + } + + return $default; + } + + protected function addNamespace($type, $name, $path) + { + if (!$this->namespaces) { + $this->getNamespaces(); + } + + $this->namespaces[$type][$name] = (array) $path; + } + + protected function hasNamespace($type, $name) + { + if (in_array($name, $this->getNamespaceList($type))) { + return true; + } + + if (!preg_match('/\\\$/', $name)) { + $name = $name . '\\'; + if (in_array($name, $this->getNamespaceList($type))) { + return true; + } + } + + return false; + } + + protected function addListToClassLoader($classLoader, array $list, $skipVendorNamespaces = false) + { + foreach ($this->methodNameMap as $type => $methodName) { + if (!isset($list[$type])) continue; + + if (!method_exists($classLoader, $methodName)) { + $GLOBALS['log']->warning('Autoload: ClassLoader method ['. $methodName .'] is not found.'); + continue; + } + + foreach ($list[$type] as $prefix => $path) { + if (!$skipVendorNamespaces) { + $vendorNamespaces = $this->getVendorNamespaces($path); + if (!empty($vendorNamespaces)) { + $this->addListToClassLoader($classLoader, $vendorNamespaces, true); + } + } + + if (!$this->hasNamespace($type, $prefix)) { + try { + $classLoader->$methodName($prefix, $path); + $this->addNamespace($type, $prefix, $path); + } catch (\Exception $e) { + $GLOBALS['log']->warning('Autoload: error adding the namespace ['. $prefix .']'); + } + } + } + } + } + + protected function getVendorNamespaces($path) + { + if (!isset($this->vendorNamespaces)) { + $this->vendorNamespaces = []; + + if (file_exists($this->vendorNamespacesCacheFile) && $this->getConfig()->get('useCache')) { + $this->vendorNamespaces = $this->getFileManager()->getPhpContents($this->vendorNamespacesCacheFile); + } + } + + if (!array_key_exists($path, $this->vendorNamespaces)) { + $vendorPath = $this->findVendorPath($path); + if ($vendorPath) { + $this->vendorNamespaces[$path] = $this->loadNamespaces($vendorPath); + + if ($this->getConfig()->get('useCache')) { + $this->getFileManager()->putPhpContents($this->vendorNamespacesCacheFile, $this->vendorNamespaces); + } + } + } + + if (isset($this->vendorNamespaces[$path])) { + return $this->vendorNamespaces[$path]; + } + } + + protected function findVendorPath($path) + { + $vendor = Util::concatPath($path, $this->autoloadFilePath); + if (file_exists($vendor)) { + return $path; + } + + $parentDir = dirname($path); + if (!empty($parentDir) && $parentDir != '.') { + return $this->findVendorPath($parentDir); + } + } +}