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
This commit is contained in:
Samuel Georges 2015-06-27 13:37:34 +10:00
parent 19e648cab8
commit 6c6c7b9e1f
3 changed files with 129 additions and 2 deletions

View File

@ -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.

View File

@ -0,0 +1,126 @@
<?php namespace System\Classes;
use Composer\Autoload\ClassLoader as ComposerLoader;
/**
* Composer manager
*
* This class manages composer packages introduced by plugins. Each loaded
* package is added to a global pool to ensure a package is not loaded
* twice by the composer instance introduced by a plugin. This class
* is used as a substitute for the vendor/autoload.php file.
*
* @package october\system
* @author Alexey Bobkov, Samuel Georges
*/
class ComposerManager
{
use \October\Rain\Support\Traits\Singleton;
protected $namespacePool = [];
protected $psr4Pool = [];
protected $classMapPool = [];
protected $includeFilesPool = [];
/**
* @var Composer\Autoload\ClassLoader The primary composer instance.
*/
protected $loader;
public function init()
{
$this->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;
}
}

View File

@ -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) {