123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490 |
- <?php
- /*
- * This file is part of the symfony package.
- * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
- /**
- * sfPluginManager allows you to manage plugins installation and uninstallation.
- *
- * @package symfony
- * @subpackage plugin
- * @author Fabien Potencier <fabien.potencier@symfony-project.com>
- * @version SVN: $Id: sfPluginManager.class.php 16445 2009-03-19 18:03:10Z FabianLange $
- */
- class sfPluginManager
- {
- protected
- $dispatcher = null,
- $environment = null,
- $installing = array();
- /**
- * Constructs a new sfPluginManager.
- *
- * @param sfEventDispatcher $dispatcher An event dispatcher instance
- * @param sfPearEnvironment $environment A sfPearEnvironment instance
- */
- public function __construct(sfEventDispatcher $dispatcher, sfPearEnvironment $environment)
- {
- $this->initialize($dispatcher, $environment);
- }
- /**
- * Initializes this sfPluginManager instance.
- *
- * see sfPearEnvironment for available options.
- *
- * @param sfEventDispatcher $dispatcher An event dispatcher instance
- * @param sfPearEnvironment $environment A sfPearEnvironment instance
- */
- public function initialize(sfEventDispatcher $dispatcher, sfPearEnvironment $environment)
- {
- $this->dispatcher = $dispatcher;
- $this->environment = $environment;
- // configure this plugin manager
- $this->configure();
- }
- /**
- * Configures this plugin manager.
- */
- public function configure()
- {
- }
- /**
- * Returns the sfPearEnvironment instance.
- *
- * @return sfPearEnvironment The sfPearEnvironment instance
- */
- public function getEnvironment()
- {
- return $this->environment;
- }
- /**
- * Returns a list of installed plugin.
- *
- * @return array An array of installed plugins
- */
- public function getInstalledPlugins()
- {
- $installed = array();
- foreach ($this->environment->getRegistry()->packageInfo(null, null, null) as $channel => $packages)
- {
- foreach ($packages as $package)
- {
- $installed[] = $this->environment->getRegistry()->getPackage(isset($package['package']) ? $package['package'] : $package['name'], $channel);
- }
- }
- return $installed;
- }
- /**
- * Installs a plugin.
- *
- * If you don't pass a version, it will install the latest version available
- * for the current project symfony version.
- *
- * Available options:
- *
- * * channel: The plugin channel name
- * * version: The version to install
- * * stability: The stability preference
- * * install_deps: Whether to automatically install dependencies (default to false)
- *
- * @param string $plugin The plugin name
- * @param array $options An array of options
- */
- public function installPlugin($plugin, $options = array())
- {
- $this->installing = array();
- $this->doInstallPlugin($plugin, $options);
- }
- /**
- * Installs a plugin
- *
- * @see installPlugin()
- */
- protected function doInstallPlugin($plugin, $options = array())
- {
- $channel = isset($options['channel']) ? $options['channel'] : $this->environment->getConfig()->get('default_channel');
- $stability = isset($options['stability']) ? $options['stability'] : $this->environment->getConfig()->get('preferred_state', null, $channel);
- $version = isset($options['version']) ? $options['version'] : null;
- $isPackage = true;
- if (0 === strpos($plugin, 'http://') || file_exists($plugin))
- {
- if (0 === strpos($plugin, 'http://plugins.symfony-project.'))
- {
- throw new sfPluginException("You try to install a symfony 1.0 plugin.\nPlease read the help message of this task to know how to install a plugin for the current version of symfony.");
- }
- $download = $plugin;
- $isPackage = false;
- }
- else if (false !== strpos($plugin, '/'))
- {
- list($channel, $plugin) = explode('/', $plugin);
- }
- $this->dispatcher->notify(new sfEvent($this, 'plugin.pre_install', array('channel' => $channel, 'plugin' => $plugin, 'is_package' => $isPackage)));
- if ($isPackage)
- {
- $this->environment->getRest()->setChannel($channel);
- if (!preg_match(PEAR_COMMON_PACKAGE_NAME_PREG, $plugin))
- {
- throw new sfPluginException(sprintf('Plugin name "%s" is not a valid package name', $plugin));
- }
- if (!$version)
- {
- $version = $this->getPluginVersion($plugin, $stability);
- }
- else
- {
- if (!$this->isPluginCompatible($plugin, $version))
- {
- throw new sfPluginDependencyException(sprintf('Plugin "%s" in version "%s" is not compatible with the current application', $plugin, $version));
- }
- }
- if (!preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $version))
- {
- throw new sfPluginException(sprintf('Plugin version "%s" is not a valid version', $version));
- }
- $existing = $this->environment->getRegistry()->packageInfo($plugin, 'version', $channel);
- if (version_compare($existing, $version) === 0)
- {
- $this->dispatcher->notify(new sfEvent($this, 'application.log', array('Plugin is already installed')));
- return true;
- }
- // skip if the plugin is already installing and we are here through a dependency)
- if (isset($this->installing[$channel.'/'.$plugin]))
- {
- return true;
- }
- // convert the plugin package into a discrete download URL
- $download = $this->environment->getRest()->getPluginDownloadURL($plugin, $version, $stability);
- if (PEAR::isError($download))
- {
- throw new sfPluginException(sprintf('Problem downloading the plugin "%s": %s', $plugin, $download->getMessage()));
- }
- }
- // download the plugin and install
- $class = $this->environment->getOption('downloader_base_class');
- $downloader = new $class($this, array('upgrade' => true), $this->environment->getConfig());
- $this->installing[$channel.'/'.$plugin] = true;
- if ($isPackage)
- {
- $this->checkPluginDependencies($plugin, $version, isset($options['install_deps']) ? (bool) $options['install_deps'] : false);
- }
- // download the actual URL to the plugin
- $downloaded = $downloader->download(array($download));
- if (PEAR::isError($downloaded))
- {
- throw new sfPluginException(sprintf('Problem when downloading "%s": %s', $download, $downloaded->getMessage()));
- }
- $errors = $downloader->getErrorMsgs();
- if (count($errors))
- {
- $err = array();
- foreach ($errors as $error)
- {
- $err[] = $error;
- }
- if (!count($downloaded))
- {
- throw new sfPluginException(sprintf('Plugin "%s" installation failed: %s', $plugin, implode("\n", $err)));
- }
- }
- $pluginPackage = $downloaded[0];
- $installer = new PEAR_Installer($this);
- $installer->setOptions(array('upgrade' => true));
- $packages = array($pluginPackage);
- $installer->sortPackagesForInstall($packages);
- PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
- $err = $installer->setDownloadedPackages($packages);
- if (PEAR::isError($err))
- {
- PEAR::staticPopErrorHandling();
- throw new sfPluginException($err->getMessage());
- }
- $info = $installer->install($pluginPackage, array('upgrade' => true));
- PEAR::staticPopErrorHandling();
- if (PEAR::isError($info))
- {
- throw new sfPluginException(sprintf('Installation of "%s" plugin failed: %s', $plugin, $info->getMessage()));
- }
- if (is_array($info))
- {
- $this->dispatcher->notify(new sfEvent($this, 'application.log', array(sprintf('Installation successful for plugin "%s"', $plugin))));
- $this->dispatcher->notify(new sfEvent($this, 'plugin.post_install', array('channel' => $channel, 'plugin' => $plugin)));
- unset($this->installing[$channel.'/'.$plugin]);
- return true;
- }
- else
- {
- throw new sfPluginException(sprintf('Installation of "%s" plugin failed', $plugin));
- }
- }
- /**
- * Uninstalls a plugin.
- *
- * @param string $plugin The plugin name
- * @param string $channel The channel name
- */
- public function uninstallPlugin($plugin, $channel = null)
- {
- if (false !== strpos($plugin, '/'))
- {
- list($channel, $plugin) = explode('/', $plugin);
- }
- $channel = is_null($channel) ? $this->environment->getConfig()->get('default_channel') : $channel;
- $existing = $this->environment->getRegistry()->packageInfo($plugin, 'version', $channel);
- if (is_null($existing))
- {
- $this->dispatcher->notify(new sfEvent($this, 'application.log', array(sprintf('Plugin "%s" is not installed', $plugin))));
- return false;
- }
- $this->dispatcher->notify(new sfEvent($this, 'plugin.pre_uninstall', array('channel' => $channel, 'plugin' => $plugin)));
- $package = $this->environment->getRegistry()->parsePackageName($plugin, $channel);
- $installer = new PEAR_Installer($this);
- $packages = array($this->environment->getRegistry()->getPackage($plugin, $channel));
- $installer->setUninstallPackages($packages);
- $ret = $installer->uninstall($package);
- if (PEAR::isError($ret))
- {
- throw new sfPluginException(sprintf('Problem uninstalling plugin "%s": %s', $plugin, $ret->getMessage()));
- }
- if ($ret)
- {
- $this->dispatcher->notify(new sfEvent($this, 'application.log', array(sprintf('Uninstallation successful for plugin "%s"', $plugin))));
- $this->dispatcher->notify(new sfEvent($this, 'plugin.post_uninstall', array('channel' => $channel, 'plugin' => $plugin)));
- }
- else
- {
- throw new sfPluginException(sprintf('Uninstallation of "%s" plugin failed', $plugin));
- }
- return $ret;
- }
- /**
- * Checks all plugin dependencies.
- *
- * @param string $plugin The plugin name
- * @param string $version The plugin version
- * @param Boolean $install true if dependencies must be installed, false otherwise
- */
- public function checkPluginDependencies($plugin, $version, $install = false)
- {
- $dependencies = $this->environment->getRest()->getPluginDependencies($plugin, $version);
- if (!isset($dependencies['required']) || !isset($dependencies['required']['package']))
- {
- return;
- }
- $deps = $dependencies['required']['package'];
- if (!isset($deps[0]))
- {
- $deps = array($deps);
- }
- foreach ($deps as $dependency)
- {
- if (!$this->checkDependency($dependency))
- {
- $version = (isset($dependency['min']) ? ' >= '.$dependency['min'] : '').(isset($dependency['max']) ? ' <= '.$dependency['max'] : '').(isset($dependency['exclude']) ? ' exclude '.$dependency['exclude'] : '');
- if ($install)
- {
- try
- {
- $this->doInstallPlugin($dependency['name'], array('channel' => $dependency['channel'], 'install_deps' => true));
- }
- catch (sfException $e)
- {
- throw new sfPluginRecursiveDependencyException(sprintf('Unable to install plugin "%s" (version %s) because it depends on plugin "%s" which cannot be installed automatically: %s', $plugin, $version, $dependency['name'], $e->getMessage()));
- }
- continue;
- }
- throw new sfPluginDependencyException(sprintf('Unable to install plugin "%s" (version %s) because it depends on plugin "%s" which is not installed.', $plugin, $version, $dependency['name']));
- }
- }
- }
- /**
- * Gets the "best" version available for a given plugin.
- *
- * @param string $plugin The plugin name
- * @param string $stability The stability name
- *
- * @return string The version
- */
- public function getPluginVersion($plugin, $stability = null)
- {
- $versions = $this->environment->getRest()->getPluginVersions($plugin, $stability);
- foreach ($versions as $version)
- {
- if (!$this->isPluginCompatible($plugin, $version))
- {
- continue;
- }
- return $version;
- }
- throw new sfPluginDependencyException(sprintf('No release available for plugin "%s" in state "%s" that satisfies the application requirements.', $plugin, $stability));
- }
- /**
- * Returns true if the plugin is comptatible with your environment.
- *
- * @param string $plugin The plugin name
- * @param string $version The plugin version
- *
- * @return Boolean true if the plugin is compatible, false otherwise
- */
- public function isPluginCompatible($plugin, $version)
- {
- $dependencies = $this->environment->getRest()->getPluginDependencies($plugin, $version);
- if (!isset($dependencies['required']) || !isset($dependencies['required']['package']))
- {
- return true;
- }
- $deps = $dependencies['required']['package'];
- if (!isset($deps[0]))
- {
- $deps = array($deps);
- }
- foreach ($deps as $dependency)
- {
- if (!$this->isPluginCompatibleWithDependency($dependency))
- {
- return false;
- }
- }
- return true;
- }
- /**
- * Returns the license for a given plugin.
- *
- * @param string $plugin The plugin name
- * @param array $options An array of options
- *
- * @return string The license
- *
- * @see installPlugin() for available options
- */
- public function getPluginLicense($plugin, $options = array())
- {
- $channel = isset($options['channel']) ? $options['channel'] : $this->environment->getConfig()->get('default_channel');
- $stability = isset($options['stability']) ? $options['stability'] : $this->environment->getConfig()->get('preferred_state', null, $channel);
- $version = isset($options['version']) ? $options['version'] : null;
- $rest = $this->environment->getRest();
- $rest->setChannel(is_null($channel) ? $this->environment->getConfig()->get('default_channel') : $channel);
- if (is_null($version))
- {
- try
- {
- $version = $this->getPluginVersion($plugin, $stability);
- }
- catch (Exception $e)
- {
- // no release available
- return false;
- }
- }
- else
- {
- if (!$this->isPluginCompatible($plugin, $version))
- {
- throw new sfPluginDependencyException(sprintf('Plugin "%s" in version "%s" is not compatible with the current application', $plugin, $version));
- }
- }
- return $rest->getPluginLicense($plugin, $version);
- }
- /**
- * Returns true if the plugin is comptatible with the dependency.
- *
- * @param array $dependency An dependency array
- *
- * @return Boolean true if the plugin is compatible, false otherwise
- */
- protected function isPluginCompatibleWithDependency($dependency)
- {
- return true;
- }
- /**
- * Checks that the dependency is valid.
- *
- * @param array $dependency A dependency array
- *
- * @return Boolean true if the dependency is valid, false otherwise
- */
- protected function checkDependency($dependency)
- {
- $dependencyChecker = new PEAR_Dependency2($this->environment->getConfig(), array(), array('package' => '', 'channel' => ''));
- PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
- $e = $dependencyChecker->validatePackageDependency($dependency, true, array());
- PEAR::staticPopErrorHandling();
- if (PEAR::isError($e))
- {
- return false;
- }
- return true;
- }
- }
|