From 4025c77366c521e0e12748d456a432a00e1184f0 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Tue, 2 Feb 2016 20:16:54 -0700 Subject: [PATCH] CRM-17957 - Allow extensions to register with PHP classloader (PSR-4) For example, in info.xml, add: ``` ``` --- CRM/Extension/ClassLoader.php | 132 ++++++++++++++++++++++++++++++++++ CRM/Extension/Info.php | 17 +++++ CRM/Extension/System.php | 15 ++++ Civi/Core/Container.php | 1 + 4 files changed, 165 insertions(+) create mode 100644 CRM/Extension/ClassLoader.php diff --git a/CRM/Extension/ClassLoader.php b/CRM/Extension/ClassLoader.php new file mode 100644 index 0000000000..7097d8d371 --- /dev/null +++ b/CRM/Extension/ClassLoader.php @@ -0,0 +1,132 @@ +mapper = $mapper; + $this->container = $container; + $this->manager = $manager; + } + + public function __destruct() { + if ($this->loader) { + $this->loader->unregister(); + $this->loader = NULL; + } + } + + /** + * Registers this instance as an autoloader. + * @return $this + */ + public function register() { + // In pre-installation environments, don't bother with caching. + if (!defined('CIVICRM_TEMPLATE_COMPILEDIR') || !defined('CIVICRM_DSN') || \CRM_Utils_System::isInUpgradeMode()) { + return $this->buildClassLoader()->register(); + } + + $envId = \CRM_Core_Config_Runtime::getId(); + $file = CIVICRM_TEMPLATE_COMPILEDIR . "/CachedExtLoader.{$envId}.php"; + if (file_exists($file)) { + $loader = require $file; + } + else { + $loader = $this->buildClassLoader(); + $ser = serialize($loader); + file_put_contents($file, + sprintf("register(); + } + + /** + * @return \Composer\Autoload\ClassLoader + * @throws \CRM_Extension_Exception + * @throws \Exception + */ + public function buildClassLoader() { + $loader = new \Composer\Autoload\ClassLoader(); + + $statuses = $this->manager->getStatuses(); + foreach ($statuses as $key => $status) { + if ($status !== CRM_Extension_Manager::STATUS_INSTALLED) { + continue; + } + $path = $this->mapper->keyToBasePath($key); + $info = $this->mapper->keyToInfo($key); + if (!empty($info->classloader)) { + foreach ($info->classloader as $mapping) { + switch ($mapping['type']) { + case 'psr4': + $loader->setPsr4($mapping['prefix'], $path . '/' . $mapping['path']); + break; + } + $result[] = $mapping; + } + } + } + + return $loader; + } + +} diff --git a/CRM/Extension/Info.php b/CRM/Extension/Info.php index b5bf1049de..d0d8c94c05 100644 --- a/CRM/Extension/Info.php +++ b/CRM/Extension/Info.php @@ -44,6 +44,13 @@ class CRM_Extension_Info { public $label = NULL; public $file = NULL; + /** + * @var array + * Each item is a specification like: + * array('type'=>'psr4', 'namespace'=>'Foo\Bar', 'path'=>'/foo/bar'). + */ + public $classloader = array(); + /** * Load extension info an XML file. * @@ -124,6 +131,16 @@ class CRM_Extension_Info { } ksort($this->urls); } + elseif ($attr === 'classloader') { + $this->classloader = array(); + foreach ($val->psr4 as $psr4) { + $this->classloader[] = array( + 'type' => 'psr4', + 'prefix' => (string) $psr4->attributes()->prefix, + 'path' => (string) $psr4->attributes()->path, + ); + } + } else { $this->$attr = CRM_Utils_XML::xmlObjToArray($val); } diff --git a/CRM/Extension/System.php b/CRM/Extension/System.php index 9ffccd35e6..92e5c36e7c 100644 --- a/CRM/Extension/System.php +++ b/CRM/Extension/System.php @@ -43,6 +43,11 @@ class CRM_Extension_System { private $browser = NULL; private $downloader = NULL; + /** + * @var CRM_Extension_ClassLoader + * */ + private $classLoader; + /** * The URL of the remote extensions repository. * @@ -182,6 +187,16 @@ class CRM_Extension_System { return $this->mapper; } + /** + * @return \CRM_Extension_ClassLoader + */ + public function getClassLoader() { + if ($this->classLoader === NULL) { + $this->classLoader = new CRM_Extension_ClassLoader($this->getMapper(), $this->getFullContainer(), $this->getManager()); + } + return $this->classLoader; + } + /** * Get the service for enabling and disabling extensions. * diff --git a/Civi/Core/Container.php b/Civi/Core/Container.php index 68404a6028..3a443f3759 100644 --- a/Civi/Core/Container.php +++ b/Civi/Core/Container.php @@ -379,6 +379,7 @@ class Container { \CRM_Core_DAO::init($runtime->dsn); \CRM_Utils_Hook::singleton(TRUE); \CRM_Extension_System::singleton(TRUE); + \CRM_Extension_System::singleton(TRUE)->getClassLoader()->register(); $c = new self(); \Civi::$statics[__CLASS__]['container'] = $c->loadContainer(); -- 2.25.1