From 6c6c7b9e1fd93007701976c35c47ec444ea612bc Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Sat, 27 Jun 2015 13:37:34 +1000 Subject: [PATCH] Implement ComposerManager::autoload for plugins that use composer. All packages are now added to a global pool to prevent double loading and the load order is respected. Refs #1227 --- CHANGELOG.md | 3 +- modules/system/classes/ComposerManager.php | 126 +++++++++++++++++++++ modules/system/classes/PluginManager.php | 2 +- 3 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 modules/system/classes/ComposerManager.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 9791cf245..4f2e15ca2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ * **Build 27x** (2015-06-xx) - - List columns now support specifying a `default` option used when the value would otherwise be null. + - List columns now support specifying a `default` option used when the value would otherwise be null. + - Implement a custom autoloader for plugins that use composer. Now only one instance of composer is used, all packages are now added to a global pool to prevent double loading and the load order is respected. * **Build 272** (2015-06-27) - Protected images and their thumbnails are now supported in the back-end. diff --git a/modules/system/classes/ComposerManager.php b/modules/system/classes/ComposerManager.php new file mode 100644 index 000000000..741eba2ea --- /dev/null +++ b/modules/system/classes/ComposerManager.php @@ -0,0 +1,126 @@ +loader = include base_path() .'/vendor/autoload.php'; + $this->preloadPools(); + } + + protected function preloadPools() + { + $this->classMapPool = array_fill_keys(array_keys($this->loader->getClassMap()), true); + $this->namespacePool = array_fill_keys(array_keys($this->loader->getPrefixes()), true); + $this->psr4Pool = array_fill_keys(array_keys($this->loader->getPrefixesPsr4()), true); + $this->includeFilesPool = $this->preloadIncludeFilesPool(); + } + + protected function preloadIncludeFilesPool() + { + $result = []; + $vendorPath = base_path() .'/vendor'; + + if (file_exists($file = $vendorPath . '/composer/autoload_files.php')) { + $includeFiles = require $file; + foreach ($includeFiles as $includeFile) { + $relativeFile = $this->stripVendorDir($includeFile, $vendorPath); + $result[$relativeFile] = true; + } + } + + return $result; + } + + /** + * Similar function to including vendor/autoload.php. + * @param string $vendorPath Absoulte path to the vendor directory. + * @return void + */ + public function autoload($vendorPath) + { + $dir = $vendorPath . '/composer'; + + if (file_exists($file = $dir . '/autoload_namespaces.php')) { + $map = require $file; + foreach ($map as $namespace => $path) { + if (isset($this->namespacePool[$namespace])) continue; + $this->loader->set($namespace, $path); + $this->namespacePool[$namespace] = true; + } + } + + if (file_exists($file = $dir . '/autoload_psr4.php')) { + $map = require $file; + foreach ($map as $namespace => $path) { + if (isset($this->psr4Pool[$namespace])) continue; + $this->loader->setPsr4($namespace, $path); + $this->psr4Pool[$namespace] = true; + } + } + + if (file_exists($file = $dir . '/autoload_classmap.php')) { + $classMap = require $file; + if ($classMap) { + $classMapDiff = array_diff_key($classMap, $this->classMapPool); + $this->loader->addClassMap($classMapDiff); + $this->classMapPool += array_fill_keys(array_keys($classMapDiff), true); + } + } + + if (file_exists($file = $dir . '/autoload_files.php')) { + $includeFiles = require $file; + foreach ($includeFiles as $includeFile) { + $relativeFile = $this->stripVendorDir($includeFile, $vendorPath); + if (isset($this->includeFilesPool[$relativeFile])) continue; + require $includeFile; + $this->includeFilesPool[$relativeFile] = true; + } + } + } + + /** + * Removes the vendor directory from a path. + * @param string $path + * @return string + */ + protected function stripVendorDir($path, $vendorDir) + { + $path = realpath($path); + $vendorDir = realpath($vendorDir); + + if (strpos($path, $vendorDir) === 0) { + $path = substr($path, strlen($vendorDir)); + } + + return $path; + } +} \ No newline at end of file diff --git a/modules/system/classes/PluginManager.php b/modules/system/classes/PluginManager.php index d0cda58c4..fe53d7eda 100644 --- a/modules/system/classes/PluginManager.php +++ b/modules/system/classes/PluginManager.php @@ -178,7 +178,7 @@ class PluginManager */ $autoloadPath = $pluginPath . '/vendor/autoload.php'; if (File::isFile($autoloadPath)) { - require_once $autoloadPath; + ComposerManager::instance()->autoload($pluginPath . '/vendor'); } if (!self::$noInit || $plugin->elevated) {