* * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Behat\Testwork\ServiceContainer; use Behat\Testwork\ServiceContainer\Exception\ExtensionInitializationException; /** * Manages both default and 3rd-party extensions. * * @author Konstantin Kudryashov */ final class ExtensionManager { /** * @var string */ private $extensionsPath; /** * @var Extension[] */ private $extensions = array(); /** * @var Extension[string] */ private $locatedExtensions = array(); private $debugInformation = array( 'extensions_list' => array() ); /** * Initializes manager. * * @param Extension[] $extensions List of default extensions * @param null|string $extensionsPath Base path where to search custom extension files */ public function __construct(array $extensions, $extensionsPath = null) { foreach ($extensions as $extension) { $this->extensions[$extension->getConfigKey()] = $extension; } $this->extensionsPath = $extensionsPath; } /** * Sets path to directory in which manager will try to find extension files. * * @param null|string $path */ public function setExtensionsPath($path) { $this->extensionsPath = $path; } /** * Activate extension by its locator. * * @param string $locator phar file name, php file name, class name * * @return Extension */ public function activateExtension($locator) { $extension = $this->initialize($locator); $this->debugInformation['extensions_list'][] = $extension->getConfigKey(); return $this->extensions[$extension->getConfigKey()] = $extension; } /** * Returns specific extension by its name. * * @param string $key * * @return Extension */ public function getExtension($key) { return isset($this->extensions[$key]) ? $this->extensions[$key] : null; } /** * Returns all available extensions. * * @return Extension[] */ public function getExtensions() { return $this->extensions; } /** * Returns activated extension names. * * @return array */ public function getExtensionClasses() { return array_map('get_class', array_values($this->extensions)); } /** * Initializes all activated and predefined extensions. */ public function initializeExtensions() { foreach ($this->extensions as $extension) { $extension->initialize($this); } } /** * Returns array with extensions debug information. * * @return array */ public function debugInformation() { return $this->debugInformation; } /** * Attempts to guess full extension class from relative. * * @param string $locator * * @return string */ private function getFullExtensionClass($locator) { $parts = explode('\\', $locator); $name = preg_replace('/Extension$/', '', end($parts)) . 'Extension'; return $locator . '\\ServiceContainer\\' . $name; } /** * Initializes extension by id. * * @param string $locator * * @return Extension * * @throws ExtensionInitializationException */ private function initialize($locator) { if (isset($this->locatedExtensions[$locator])) { return $this->locatedExtensions[$locator]; } $extension = $this->instantiateExtension($locator); $this->validateExtensionInstance($extension, $locator); return $this->locatedExtensions[$locator] = $extension; } /** * Instantiates extension from its locator. * * @param string $locator * * @return Extension * * @throws ExtensionInitializationException */ private function instantiateExtension($locator) { if (class_exists($class = $locator)) { return new $class; } if (class_exists($class = $this->getFullExtensionClass($locator))) { return new $class; } if (file_exists($locator)) { return require($locator); } if (file_exists($path = $this->extensionsPath . DIRECTORY_SEPARATOR . $locator)) { return require($path); } throw new ExtensionInitializationException(sprintf( '`%s` extension file or class could not be located.', $locator ), $locator); } /** * Validates extension instance. * * @param Extension $extension * @param string $locator * * @throws ExtensionInitializationException */ private function validateExtensionInstance($extension, $locator) { if (null === $extension) { throw new ExtensionInitializationException(sprintf( '`%s` extension could not be found.', $locator ), $locator); } if (!is_object($extension)) { throw new ExtensionInitializationException(sprintf( '`%s` extension could not be initialized.', $locator ), $locator); } if (!$extension instanceof Extension) { throw new ExtensionInitializationException(sprintf( '`%s` extension class should implement Testwork Extension interface.', get_class($extension) ), $locator); } } }