diff options
Diffstat (limited to 'phpBB/phpbb/composer')
-rw-r--r-- | phpBB/phpbb/composer/exception/managed_with_clean_error_exception_exception.php | 35 | ||||
-rw-r--r-- | phpBB/phpbb/composer/exception/managed_with_enable_error_exception.php | 35 | ||||
-rw-r--r-- | phpBB/phpbb/composer/exception/managed_with_error_exception.php | 35 | ||||
-rw-r--r-- | phpBB/phpbb/composer/exception/runtime_exception.php | 37 | ||||
-rw-r--r-- | phpBB/phpbb/composer/extension_manager.php | 316 | ||||
-rw-r--r-- | phpBB/phpbb/composer/installer.php | 716 | ||||
-rw-r--r-- | phpBB/phpbb/composer/io/console_io.php | 40 | ||||
-rw-r--r-- | phpBB/phpbb/composer/io/html_output_formatter.php | 86 | ||||
-rw-r--r-- | phpBB/phpbb/composer/io/io_interface.php | 26 | ||||
-rw-r--r-- | phpBB/phpbb/composer/io/null_io.php | 27 | ||||
-rw-r--r-- | phpBB/phpbb/composer/io/translate_composer_trait.php | 245 | ||||
-rw-r--r-- | phpBB/phpbb/composer/io/web_io.php | 37 | ||||
-rw-r--r-- | phpBB/phpbb/composer/manager.php | 332 | ||||
-rw-r--r-- | phpBB/phpbb/composer/manager_interface.php | 110 |
14 files changed, 2077 insertions, 0 deletions
diff --git a/phpBB/phpbb/composer/exception/managed_with_clean_error_exception_exception.php b/phpBB/phpbb/composer/exception/managed_with_clean_error_exception_exception.php new file mode 100644 index 0000000000..2339fa4096 --- /dev/null +++ b/phpBB/phpbb/composer/exception/managed_with_clean_error_exception_exception.php @@ -0,0 +1,35 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\composer\exception; + +/** + * Packaged managed with success but error occurred when cleaning the filesystem + */ +class managed_with_clean_error_exception extends managed_with_error_exception +{ + /** + * Constructor + * + * @param string $prefix The language string prefix + * @param string $message The Exception message to throw (must be a language variable). + * @param array $parameters The parameters to use with the language var. + * @param \Exception $previous The previous runtime_exception used for the runtime_exception chaining. + * @param integer $code The Exception code. + */ + public function __construct($prefix, $message = '', array $parameters = [], \Exception $previous = null, $code = 0) + { + parent::__construct($prefix . $message, $parameters, $previous, $code); + } + +} diff --git a/phpBB/phpbb/composer/exception/managed_with_enable_error_exception.php b/phpBB/phpbb/composer/exception/managed_with_enable_error_exception.php new file mode 100644 index 0000000000..7ef7a42df3 --- /dev/null +++ b/phpBB/phpbb/composer/exception/managed_with_enable_error_exception.php @@ -0,0 +1,35 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\composer\exception; + +/** + * Packaged managed with success but error occurred when re-enabling the extension + */ +class managed_with_enable_error_exception extends managed_with_error_exception +{ + /** + * Constructor + * + * @param string $prefix The language string prefix + * @param string $message The Exception message to throw (must be a language variable). + * @param array $parameters The parameters to use with the language var. + * @param \Exception $previous The previous runtime_exception used for the runtime_exception chaining. + * @param integer $code The Exception code. + */ + public function __construct($prefix, $message = '', array $parameters = [], \Exception $previous = null, $code = 0) + { + parent::__construct($prefix . $message, $parameters, $previous, $code); + } + +} diff --git a/phpBB/phpbb/composer/exception/managed_with_error_exception.php b/phpBB/phpbb/composer/exception/managed_with_error_exception.php new file mode 100644 index 0000000000..9e7c67580e --- /dev/null +++ b/phpBB/phpbb/composer/exception/managed_with_error_exception.php @@ -0,0 +1,35 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\composer\exception; + +/** + * Packaged managed with success but errored at some point + */ +class managed_with_error_exception extends runtime_exception +{ + /** + * Constructor + * + * @param string $prefix The language string prefix + * @param string $message The Exception message to throw (must be a language variable). + * @param array $parameters The parameters to use with the language var. + * @param \Exception $previous The previous runtime_exception used for the runtime_exception chaining. + * @param integer $code The Exception code. + */ + public function __construct($prefix, $message = '', array $parameters = [], \Exception $previous = null, $code = 0) + { + parent::__construct($prefix . $message, $parameters, $previous, $code); + } + +} diff --git a/phpBB/phpbb/composer/exception/runtime_exception.php b/phpBB/phpbb/composer/exception/runtime_exception.php new file mode 100644 index 0000000000..eb92759318 --- /dev/null +++ b/phpBB/phpbb/composer/exception/runtime_exception.php @@ -0,0 +1,37 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\composer\exception; + +use phpbb\exception\runtime_exception as base; + +/** + * Base class for exceptions thrown when managing packages through composer + */ +class runtime_exception extends base +{ + /** + * Constructor + * + * @param string $prefix The language string prefix + * @param string $message The Exception message to throw (must be a language variable). + * @param array $parameters The parameters to use with the language var. + * @param \Exception $previous The previous runtime_exception used for the runtime_exception chaining. + * @param integer $code The Exception code. + */ + public function __construct($prefix, $message = '', array $parameters = [], \Exception $previous = null, $code = 0) + { + parent::__construct($prefix . $message, $parameters, $previous, $code); + } + +} diff --git a/phpBB/phpbb/composer/extension_manager.php b/phpBB/phpbb/composer/extension_manager.php new file mode 100644 index 0000000000..96250e9dd3 --- /dev/null +++ b/phpBB/phpbb/composer/extension_manager.php @@ -0,0 +1,316 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\composer; + +use Composer\IO\IOInterface; +use phpbb\cache\driver\driver_interface; +use phpbb\composer\exception\managed_with_clean_error_exception; +use phpbb\composer\exception\managed_with_enable_error_exception; +use phpbb\composer\exception\runtime_exception; +use phpbb\config\config; +use phpbb\extension\manager as ext_manager; +use phpbb\filesystem\exception\filesystem_exception; +use phpbb\filesystem\filesystem; + +/** + * Class to safely manage extensions through composer. + */ +class extension_manager extends manager +{ + /** + * @var \phpbb\extension\manager + */ + protected $extension_manager; + + /** + * @var \phpbb\filesystem\filesystem + */ + protected $filesystem; + + /** + * @var array + */ + private $enabled_extensions; + + /** + * @var bool Enables extensions when installing them? + */ + private $enable_on_install = false; + + /** + * @var bool Purges extensions data when removing them? + */ + private $purge_on_remove = true; + + /** + * @param installer $installer Installer object + * @param driver_interface $cache Cache object + * @param ext_manager $extension_manager phpBB extension manager + * @param filesystem $filesystem Filesystem object + * @param string $package_type Composer type of managed packages + * @param string $exception_prefix Exception prefix to use + * @param string $root_path phpBB root path + * @param config $config Config object + */ + public function __construct(installer $installer, driver_interface $cache, ext_manager $extension_manager, filesystem $filesystem, $package_type, $exception_prefix, $root_path, config $config = null) + { + $this->extension_manager = $extension_manager; + $this->filesystem = $filesystem; + $this->root_path = $root_path; + + if ($config) + { + $this->enable_on_install = (bool) $config['exts_composer_enable_on_install']; + $this->purge_on_remove = (bool) $config['exts_composer_purge_on_remove']; + } + + parent::__construct($installer, $cache, $package_type, $exception_prefix); + } + + /** + * {@inheritdoc} + */ + public function pre_install(array $packages, IOInterface $io = null) + { + $installed_manually = array_intersect(array_keys($this->extension_manager->all_available()), array_keys($packages)); + if (count($installed_manually) !== 0) + { + throw new runtime_exception($this->exception_prefix, 'ALREADY_INSTALLED_MANUALLY', [implode('|', array_keys($installed_manually))]); + } + } + + /** + * {@inheritdoc} + */ + public function post_install(array $packages, IOInterface $io = null) + { + if ($this->enable_on_install) + { + $io->writeError([['ENABLING_EXTENSIONS', [], 1]], true); + foreach ($packages as $package => $version) + { + try + { + $this->extension_manager->enable($package); + } + catch (\phpbb\exception\runtime_exception $e) + { + $io->writeError([[$e->getMessage(), $e->get_parameters(), 4]], true); + } + catch (\Exception $e) + { + $io->writeError([[$e->getMessage(), [], 4]], true); + } + } + } + } + + /** + * {@inheritdoc} + */ + protected function pre_update(array $packages, IOInterface $io = null) + { + $io->writeError([['DISABLING_EXTENSIONS', [], 1]], true); + $this->enabled_extensions = []; + foreach ($packages as $package => $version) + { + try + { + if ($this->extension_manager->is_enabled($package)) + { + $this->enabled_extensions[] = $package; + $this->extension_manager->disable($package); + } + } + catch (\phpbb\exception\runtime_exception $e) + { + $io->writeError([[$e->getMessage(), $e->get_parameters(), 4]], true); + } + catch (\Exception $e) + { + $io->writeError([[$e->getMessage(), [], 4]], true); + } + } + } + + /** + * {@inheritdoc} + */ + protected function post_update(array $packages, IOInterface $io = null) + { + $io->writeError([['ENABLING_EXTENSIONS', [], 1]], true); + foreach ($this->enabled_extensions as $package) + { + try + { + $this->extension_manager->enable($package); + } + catch (\phpbb\exception\runtime_exception $e) + { + $io->writeError([[$e->getMessage(), $e->get_parameters(), 4]], true); + } + catch (\Exception $e) + { + $io->writeError([[$e->getMessage(), [], 4]], true); + } + } + } + + /** + * {@inheritdoc} + */ + public function remove(array $packages, IOInterface $io = null) + { + $packages = $this->normalize_version($packages); + + $not_installed = array_diff(array_keys($packages), array_keys($this->extension_manager->all_available())); + if (count($not_installed) !== 0) + { + throw new runtime_exception($this->exception_prefix, 'NOT_INSTALLED', [implode('|', array_keys($not_installed))]); + } + + parent::remove($packages, $io); + } + + /** + * {@inheritdoc} + */ + public function pre_remove(array $packages, IOInterface $io = null) + { + if ($this->purge_on_remove) + { + $io->writeError([['DISABLING_EXTENSIONS', [], 1]], true); + } + + foreach ($packages as $package => $version) + { + try + { + if ($this->extension_manager->is_enabled($package)) + { + if ($this->purge_on_remove) + { + $this->extension_manager->purge($package); + } + else + { + $this->extension_manager->disable($package); + } + } + } + catch (\phpbb\exception\runtime_exception $e) + { + $io->writeError([[$e->getMessage(), $e->get_parameters(), 4]], true); + } + catch (\Exception $e) + { + $io->writeError([[$e->getMessage(), [], 4]], true); + } + } + } + + /** + * {@inheritdoc} + */ + public function start_managing($package, $io) + { + if (!$this->extension_manager->is_available($package)) + { + throw new runtime_exception($this->exception_prefix, 'NOT_INSTALLED', [$package]); + } + + if ($this->is_managed($package)) + { + throw new runtime_exception($this->exception_prefix, 'ALREADY_MANAGED', [$package]); + } + + $enabled = false; + if ($this->extension_manager->is_enabled($package)) + { + $enabled = true; + $io->writeError([['DISABLING_EXTENSIONS', [], 1]], true); + $this->extension_manager->disable($package); + } + + $ext_path = $this->extension_manager->get_extension_path($package, true); + $backup_path = rtrim($ext_path, '/') . '__backup__'; + + try + { + $this->filesystem->rename($ext_path, $backup_path); + } + catch (filesystem_exception $e) + { + throw new runtime_exception($this->exception_prefix, 'CANNOT_MANAGE_FILESYSTEM_ERROR', [$package], $e); + } + + try + { + $this->install((array) $package, $io); + $this->filesystem->remove($backup_path); + } + catch (runtime_exception $e) + { + $this->filesystem->rename($backup_path, $ext_path); + throw new runtime_exception($this->exception_prefix, 'CANNOT_MANAGE_INSTALL_ERROR', [$package], $e); + } + catch (filesystem_exception $e) + { + throw new managed_with_clean_error_exception($this->exception_prefix, 'MANAGED_WITH_CLEAN_ERROR', [$package, $backup_path], $e); + } + + if ($enabled) + { + try + { + $io->writeError([['ENABLING_EXTENSIONS', [], 1]], true); + $this->extension_manager->enable($package); + } + catch (\Exception $e) + { + throw new managed_with_enable_error_exception($this->exception_prefix, 'MANAGED_WITH_ENABLE_ERROR', [$package], $e); + } + } + } + + /** + * {@inheritdoc} + */ + public function check_requirements() + { + return parent::check_requirements() && $this->filesystem->is_writable($this->root_path . 'ext/'); + } + + /** + * Enable the extensions when installing + * + * Warning: Only the explicitly required extensions will be enabled + * + * @param bool $enable + */ + public function set_enable_on_install($enable) + { + $this->enable_on_install = $enable; + } + + /** + * Purge the extension when disabling it + * + * @param bool $purge + */ + public function set_purge_on_remove($purge) + { + $this->purge_on_remove = $purge; + } +} diff --git a/phpBB/phpbb/composer/installer.php b/phpBB/phpbb/composer/installer.php new file mode 100644 index 0000000000..1bc2c8e6b4 --- /dev/null +++ b/phpBB/phpbb/composer/installer.php @@ -0,0 +1,716 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\composer; + +use Composer\Composer; +use Composer\Factory; +use Composer\IO\IOInterface; +use Composer\IO\NullIO; +use Composer\Json\JsonFile; +use Composer\Package\BasePackage; +use Composer\Package\CompletePackage; +use Composer\Repository\ComposerRepository; +use Composer\Semver\Constraint\ConstraintInterface; +use Composer\Util\RemoteFilesystem; +use phpbb\composer\io\null_io; +use phpbb\config\config; +use phpbb\exception\runtime_exception; +use phpbb\filesystem\filesystem; +use phpbb\request\request; +use Seld\JsonLint\ParsingException; +use phpbb\filesystem\helper as filesystem_helper; + +/** + * Class to install packages through composer while freezing core dependencies. + */ +class installer +{ + const PHPBB_TYPES = 'phpbb-extension,phpbb-style,phpbb-language'; + + /** + * @var array Repositories to look packages from + */ + protected $repositories = []; + + /** + * @var bool Indicates whether packagist usage is allowed or not + */ + protected $packagist = false; + + /** + * @var string Composer filename used to manage the packages + */ + protected $composer_filename = 'composer-ext.json'; + + /** + * @var string Directory where to install packages vendors + */ + protected $packages_vendor_dir = 'vendor-ext/'; + + /** + * @var string Minimum stability + */ + protected $minimum_stability = 'stable'; + + /** + * @var string phpBB root path + */ + protected $root_path; + + /** + * @var string Stores the original working directory in case it has been changed through move_to_root() + */ + private $original_cwd; + + /** + * @var array Stores the content of the ext json file before generate_ext_json_file() overrides it + */ + private $ext_json_file_backup; + + /** + * @var request phpBB request object + */ + private $request; + + /** + * @param string $root_path phpBB root path + * @param filesystem $filesystem Filesystem object + * @param request $request phpBB request object + * @param config $config Config object + */ + public function __construct($root_path, filesystem $filesystem, request $request, config $config = null) + { + if ($config) + { + $repositories = json_decode($config['exts_composer_repositories'], true); + + if (is_array($repositories) && !empty($repositories)) + { + $this->repositories = (array) $repositories; + } + + $this->packagist = (bool) $config['exts_composer_packagist']; + $this->composer_filename = $config['exts_composer_json_file']; + $this->packages_vendor_dir = $config['exts_composer_vendor_dir']; + $this->minimum_stability = $config['exts_composer_minimum_stability']; + } + + $this->root_path = $root_path; + $this->request = $request; + + putenv('COMPOSER_HOME=' . filesystem_helper::realpath($root_path) . '/store/composer'); + } + + /** + * Update the current installed set of packages + * + * @param array $packages Packages to install. + * Each entry may be a name or an array associating a version constraint to a name + * @param array $whitelist White-listed packages (packages that can be installed/updated/removed) + * @param IOInterface $io IO object used for the output + * + * @throws runtime_exception + */ + public function install(array $packages, $whitelist, IOInterface $io = null) + { + $this->wrap(function() use ($packages, $whitelist, $io) { + $this->do_install($packages, $whitelist, $io); + }); + } + + /** + * Update the current installed set of packages + * + * /!\ Doesn't change the current working directory + * + * @param array $packages Packages to install. + * Each entry may be a name or an array associating a version constraint to a name + * @param array $whitelist White-listed packages (packages that can be installed/updated/removed) + * @param IOInterface $io IO object used for the output + * + * @throws runtime_exception + */ + protected function do_install(array $packages, $whitelist, IOInterface $io = null) + { + if (!$io) + { + $io = new null_io(); + } + + $this->generate_ext_json_file($packages); + + $composer = Factory::create($io, $this->get_composer_ext_json_filename(), false); + $install = \Composer\Installer::create($io, $composer); + + $composer->getDownloadManager()->setOutputProgress(false); + + $install + ->setVerbose(true) + ->setPreferSource(false) + ->setPreferDist(true) + ->setDevMode(false) + ->setUpdate(true) + ->setUpdateWhitelist($whitelist) + ->setWhitelistDependencies(false) + ->setIgnorePlatformRequirements(false) + ->setOptimizeAutoloader(true) + ->setDumpAutoloader(true) + ->setPreferStable(true) + ->setRunScripts(false) + ->setDryRun(false); + + try + { + $result = $install->run(); + } + catch (\Exception $e) + { + $this->restore_ext_json_file(); + + throw new runtime_exception('COMPOSER_CANNOT_INSTALL', [], $e); + } + + if ($result !== 0) + { + $this->restore_ext_json_file(); + + throw new runtime_exception($io->get_composer_error(), []); + } + } + + /** + * Returns the list of currently installed packages + * + * @param string|array $types Returns only the packages with the given type(s) + * + * @return array The installed packages associated to their version. + * + * @throws runtime_exception + */ + public function get_installed_packages($types) + { + return $this->wrap(function() use ($types) { + return $this->do_get_installed_packages($types); + }); + } + + /** + * Returns the list of currently installed packages + * + * /!\ Doesn't change the current working directory + * + * @param string|array $types Returns only the packages with the given type(s) + * + * @return array The installed packages associated to their version. + */ + protected function do_get_installed_packages($types) + { + $types = (array) $types; + + try + { + $io = new NullIO(); + $composer = Factory::create($io, $this->get_composer_ext_json_filename(), false); + + $installed = []; + + /** @var \Composer\Package\Link[] $required_links */ + $required_links = $composer->getPackage()->getRequires(); + $installed_packages = $composer->getRepositoryManager()->getLocalRepository()->getCanonicalPackages(); + + foreach ($installed_packages as $package) + { + if (in_array($package->getType(), $types, true)) + { + $version = array_key_exists($package->getName(), $required_links) ? + $required_links[$package->getName()]->getPrettyConstraint() : '*'; + $installed[$package->getName()] = $version; + } + } + + return $installed; + } + catch (\Exception $e) + { + return []; + } + } + + /** + * Gets the list of the available packages of the configured type in the configured repositories + * + * /!\ Doesn't change the current working directory + * + * @param string $type Returns only the packages with the given type + * + * @return array The name of the available packages, associated to their definition. Ordered by name. + * + * @throws runtime_exception + */ + public function get_available_packages($type) + { + return $this->wrap(function() use ($type) { + return $this->do_get_available_packages($type); + }); + } + + /** + * Gets the list of the available packages of the configured type in the configured repositories + * + * @param string $type Returns only the packages with the given type + * + * @return array The name of the available packages, associated to their definition. Ordered by name. + */ + protected function do_get_available_packages($type) + { + try + { + $this->generate_ext_json_file($this->do_get_installed_packages(explode(',', self::PHPBB_TYPES))); + + $io = new NullIO(); + $composer = Factory::create($io, $this->get_composer_ext_json_filename(), false); + + /** @var ConstraintInterface $core_constraint */ + $core_constraint = $composer->getPackage()->getRequires()['phpbb/phpbb']->getConstraint(); + $core_stability = $composer->getPackage()->getMinimumStability(); + + $available = []; + + $compatible_packages = []; + $repositories = $composer->getRepositoryManager()->getRepositories(); + + /** @var \Composer\Repository\RepositoryInterface $repository */ + foreach ($repositories as $repository) + { + try + { + if ($repository instanceof ComposerRepository && $repository->hasProviders()) + { + // Special case for packagist which exposes an api to retrieve all packages of a given type. + // For the others composer repositories with providers we can't do anything. It would be too slow. + + $r = new \ReflectionObject($repository); + $repo_url = $r->getProperty('url'); + $repo_url->setAccessible(true); + + if ($repo_url->getValue($repository) === 'http://packagist.org') + { + $url = 'https://packagist.org/packages/list.json?type=' . $type; + $rfs = new RemoteFilesystem($io); + $hostname = parse_url($url, PHP_URL_HOST) ?: $url; + $json = $rfs->getContents($hostname, $url, false); + + /** @var \Composer\Package\PackageInterface $package */ + foreach (JsonFile::parseJson($json, $url)['packageNames'] as $package) + { + $versions = $repository->findPackages($package); + $compatible_packages = $this->get_compatible_versions($compatible_packages, $core_constraint, $core_stability, $package, $versions); + } + } + } + else + { + // Pre-filter repo packages by their type + $packages = []; + /** @var \Composer\Package\PackageInterface $package */ + foreach ($repository->getPackages() as $package) + { + if ($package->getType() === $type) + { + $packages[$package->getName()][] = $package; + } + } + + // Filter the compatibles versions + foreach ($packages as $package => $versions) + { + $compatible_packages = $this->get_compatible_versions($compatible_packages, $core_constraint, $core_stability, $package, $versions); + } + } + } + catch (\Exception $e) + { + // If a repo fails, just skip it. + continue; + } + } + + foreach ($compatible_packages as $name => $versions) + { + // Determine the highest version of the package + /** @var CompletePackage $highest_version */ + $highest_version = null; + + /** @var CompletePackage $version */ + foreach ($versions as $version) + { + if (!$highest_version || version_compare($version->getVersion(), $highest_version->getVersion(), '>')) + { + $highest_version = $version; + } + } + + // Generates the entry + $available[$name] = []; + $available[$name]['name'] = $highest_version->getPrettyName(); + $available[$name]['display_name'] = $highest_version->getExtra()['display-name']; + $available[$name]['composer_name'] = $highest_version->getName(); + $available[$name]['version'] = $highest_version->getPrettyVersion(); + + if ($version instanceof CompletePackage) + { + $available[$name]['description'] = $highest_version->getDescription(); + $available[$name]['url'] = $highest_version->getHomepage(); + $available[$name]['authors'] = $highest_version->getAuthors(); + } + else + { + $available[$name]['description'] = ''; + $available[$name]['url'] = ''; + $available[$name]['authors'] = []; + } + } + + usort($available, function($a, $b) + { + return strcasecmp($a['display_name'], $b['display_name']); + }); + + return $available; + } + catch (\Exception $e) + { + return []; + } + } + + /** + * Checks the requirements of the manager and returns true if it can be used. + * + * @return bool + */ + public function check_requirements() + { + $filesystem = new \phpbb\filesystem\filesystem(); + + return $filesystem->is_writable([ + $this->root_path . $this->composer_filename, + $this->root_path . $this->packages_vendor_dir, + $this->root_path . substr($this->composer_filename, 0, -5) . '.lock', + ]); + } + + /** + * Updates $compatible_packages with the versions of $versions compatibles with the $core_constraint + * + * @param array $compatible_packages List of compatibles versions + * @param ConstraintInterface $core_constraint Constraint against the phpBB version + * @param string $core_stability Core stability + * @param string $package_name Considered package + * @param array $versions List of available versions + * + * @return array + */ + private function get_compatible_versions(array $compatible_packages, ConstraintInterface $core_constraint, $core_stability, $package_name, array $versions) + { + $core_stability_value = BasePackage::$stabilities[$core_stability]; + + /** @var \Composer\Package\PackageInterface $version */ + foreach ($versions as $version) + { + try + { + if (BasePackage::$stabilities[$version->getStability()] > $core_stability_value) + { + continue; + } + + if (array_key_exists('phpbb/phpbb', $version->getRequires())) + { + /** @var ConstraintInterface $package_constraint */ + $package_constraint = $version->getRequires()['phpbb/phpbb']->getConstraint(); + + if (!$package_constraint->matches($core_constraint)) + { + continue; + } + } + + $compatible_packages[$package_name][] = $version; + } + catch (\Exception $e) + { + // Do nothing (to log when a true debug logger is available) + } + } + + return $compatible_packages; + } + + /** + * Generates and write the json file used to install the set of packages + * + * @param array $packages Packages to update. + * Each entry may be a name or an array associating a version constraint to a name + */ + protected function generate_ext_json_file(array $packages) + { + $io = new NullIO(); + + $composer = Factory::create($io, null, false); + + $core_packages = $this->get_core_packages($composer); + + // The composer/installers package must be installed on his own and not provided by the existing autoloader + $core_replace = $core_packages; + unset($core_replace['composer/installers']); + + $ext_json_data = [ + 'require' => array_merge( + ['php' => $this->get_core_php_requirement($composer)], + $core_packages, + $this->get_extra_dependencies(), + $packages), + 'replace' => $core_replace, + 'repositories' => $this->get_composer_repositories(), + 'config' => [ + 'vendor-dir'=> $this->packages_vendor_dir, + ], + 'minimum-stability' => $this->minimum_stability, + ]; + + $this->ext_json_file_backup = null; + $json_file = new JsonFile($this->get_composer_ext_json_filename()); + + try + { + $ext_json_file_backup = $json_file->read(); + } + catch (ParsingException $e) + { + $ext_json_file_backup = '{}'; + + $lockFile = new JsonFile(substr($this->get_composer_ext_json_filename(), 0, -5) . '.lock'); + $lockFile->write([]); + } + + $json_file->write($ext_json_data); + $this->ext_json_file_backup = $ext_json_file_backup; + } + + /** + * Restore the json file overridden by generate_ext_json_file() + */ + protected function restore_ext_json_file() + { + if ($this->ext_json_file_backup) + { + try + { + $json_file = new JsonFile($this->get_composer_ext_json_filename()); + $json_file->write($this->ext_json_file_backup); + } + catch (\Exception $e) + { + } + + $this->ext_json_file_backup = null; + } + } + + /** + * Get the core installed packages + * + * @param Composer $composer Composer object to load the dependencies + * @return array The core packages with their version + */ + protected function get_core_packages(Composer $composer) + { + $core_deps = []; + $packages = $composer->getRepositoryManager()->getLocalRepository()->getCanonicalPackages(); + + foreach ($packages as $package) + { + $core_deps[$package->getName()] = $package->getPrettyVersion(); + } + + $core_deps['phpbb/phpbb'] = PHPBB_VERSION; + + return $core_deps; + } + + /** + * Get the PHP version required by the core + * + * @param Composer $composer Composer object to load the dependencies + * @return string The PHP version required by the core + */ + protected function get_core_php_requirement(Composer $composer) + { + return $composer->getLocker()->getLockData()['platform']['php']; + } + + /** + * Generate the repositories entry of the packages json file + * + * @return array repositories entry + */ + protected function get_composer_repositories() + { + $repositories = []; + + if (!$this->packagist) + { + $repositories[]['packagist'] = false; + } + + foreach ($this->repositories as $repository) + { + if (preg_match('#^' . get_preg_expression('url') . '$#iu', $repository)) + { + $repositories[] = [ + 'type' => 'composer', + 'url' => $repository, + ]; + } + } + + return $repositories; + } + + /** + * Get the name of the json file used for the packages. + * + * @return string The json filename + */ + protected function get_composer_ext_json_filename() + { + return $this->composer_filename; + } + + /** + * Get extra dependencies required to install the packages + * + * @return array Array of composer dependencies + */ + protected function get_extra_dependencies() + { + return []; + } + + /** + * Sets the customs repositories + * + * @param array $repositories An array of composer repositories to use + */ + public function set_repositories(array $repositories) + { + $this->repositories = $repositories; + } + + /** + * Allow or disallow packagist + * + * @param boolean $packagist + */ + public function set_packagist($packagist) + { + $this->packagist = $packagist; + } + + /** + * Sets the name of the managed packages' json file + * + * @param string $composer_filename + */ + public function set_composer_filename($composer_filename) + { + $this->composer_filename = $composer_filename; + } + + /** + * Sets the location of the managed packages' vendors + * + * @param string $packages_vendor_dir + */ + public function set_packages_vendor_dir($packages_vendor_dir) + { + $this->packages_vendor_dir = $packages_vendor_dir; + } + + /** + * Sets the phpBB root path + * + * @param string $root_path + */ + public function set_root_path($root_path) + { + $this->root_path = $root_path; + } + + /** + * Change the current directory to phpBB root + */ + protected function move_to_root() + { + if ($this->original_cwd === null) + { + $this->original_cwd = getcwd(); + chdir($this->root_path); + } + } + + /** + * Restore the current working directory if move_to_root() have been called + */ + protected function restore_cwd() + { + if ($this->original_cwd) + { + chdir($this->original_cwd); + $this->original_cwd = null; + } + } + + /** + * Wraps a callable in order to adjust the context needed by composer + * + * @param callable $callable + * + * @return mixed + */ + protected function wrap(callable $callable) + { + // The composer installers works with a path relative to the current directory + $this->move_to_root(); + + // The composer installers uses some super globals + $super_globals = $this->request->super_globals_disabled(); + $this->request->enable_super_globals(); + + try + { + return $callable(); + } + finally + { + $this->restore_cwd(); + + if ($super_globals) + { + $this->request->disable_super_globals(); + } + } + } +} diff --git a/phpBB/phpbb/composer/io/console_io.php b/phpBB/phpbb/composer/io/console_io.php new file mode 100644 index 0000000000..5239b050bb --- /dev/null +++ b/phpBB/phpbb/composer/io/console_io.php @@ -0,0 +1,40 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\composer\io; + +use Composer\IO\ConsoleIO; +use phpbb\language\language; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class console_io extends ConsoleIO implements io_interface +{ + use translate_composer_trait; + + /** + * Constructor. + * + * @param InputInterface $input The input instance + * @param OutputInterface $output The output instance + * @param HelperSet $helperSet The helperSet instance + * @param language $language Language object + */ + public function __construct(InputInterface $input, OutputInterface $output, HelperSet $helperSet, language $language) + { + $this->language = $language; + + parent::__construct($input, $output, $helperSet); + } +} diff --git a/phpBB/phpbb/composer/io/html_output_formatter.php b/phpBB/phpbb/composer/io/html_output_formatter.php new file mode 100644 index 0000000000..588be30a21 --- /dev/null +++ b/phpBB/phpbb/composer/io/html_output_formatter.php @@ -0,0 +1,86 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\composer\io; + +class html_output_formatter extends \Composer\Console\HtmlOutputFormatter +{ + protected static $availableForegroundColors = [ + 30 => 'black', + 31 => 'red', + 32 => 'green', + 33 => 'orange', + 34 => 'blue', + 35 => 'magenta', + 36 => 'cyan', + 37 => 'white', + ]; + + protected static $availableBackgroundColors = [ + 40 => 'black', + 41 => 'red', + 42 => 'green', + 43 => 'yellow', + 44 => 'blue', + 45 => 'magenta', + 46 => 'cyan', + 47 => 'white', + ]; + + protected static $availableOptions + = [ + 1 => 'bold', + 4 => 'underscore', + ]; + + /** + * {@inheritdoc} + */ + public function format($message) + { + $formatted = parent::format($message); + + return preg_replace_callback("{[\033\e]\[([0-9;]+)m(.*?)[\033\e]\[[0-9;]+m}s", [$this, 'formatHtml'], $formatted); + } + + protected function formatHtml($matches) + { + $out = '<span style="'; + foreach (explode(';', $matches[1]) as $code) + { + if (isset(self::$availableForegroundColors[$code])) + { + $out .= 'color:' . self::$availableForegroundColors[$code] . ';'; + } + else if (isset(self::$availableBackgroundColors[$code])) + { + $out .= 'background-color:' . self::$availableBackgroundColors[$code] . ';'; + } + else if (isset(self::$availableOptions[$code])) + { + switch (self::$availableOptions[$code]) + { + case 'bold': + $out .= 'font-weight:bold;'; + break; + + case 'underscore': + $out .= 'text-decoration:underline;'; + break; + } + } + } + + return $out . '">' . $matches[2] . '</span>'; + } +} diff --git a/phpBB/phpbb/composer/io/io_interface.php b/phpBB/phpbb/composer/io/io_interface.php new file mode 100644 index 0000000000..a1d41122cb --- /dev/null +++ b/phpBB/phpbb/composer/io/io_interface.php @@ -0,0 +1,26 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\composer\io; + +use Composer\IO\IOInterface; + +interface io_interface extends IOInterface +{ + /** + * Returns the composer errors that occurred since the last tcall of the method. + * + * @return string + */ + public function get_composer_error(); +} diff --git a/phpBB/phpbb/composer/io/null_io.php b/phpBB/phpbb/composer/io/null_io.php new file mode 100644 index 0000000000..9a667ca097 --- /dev/null +++ b/phpBB/phpbb/composer/io/null_io.php @@ -0,0 +1,27 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\composer\io; + +use Composer\IO\NullIO; + +class null_io extends NullIO implements io_interface +{ + /** + * {@inheritdoc} + */ + public function get_composer_error() + { + return ''; + } +} diff --git a/phpBB/phpbb/composer/io/translate_composer_trait.php b/phpBB/phpbb/composer/io/translate_composer_trait.php new file mode 100644 index 0000000000..444cf38e6b --- /dev/null +++ b/phpBB/phpbb/composer/io/translate_composer_trait.php @@ -0,0 +1,245 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\composer\io; + +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Trait to translate the composer Output + */ +trait translate_composer_trait +{ + /** + * @var \phpbb\language\language + */ + protected $language; + + /** + * @var array + */ + protected $composer_error = []; + + /** + * {@inheritdoc} + */ + public function write($messages, $newline = true, $verbosity = self::NORMAL) + { + $messages = (array) $messages; + $translated_messages = []; + + foreach ($messages as $message) + { + $level = 0; + if (is_array($message)) + { + $lang_key = $message[0]; + $parameters = $message[1]; + if (count($message) > 2) + { + $level = $message[2]; + } + } + else + { + $lang_key = $message; + $parameters = []; + } + + $message = trim($this->strip_format($lang_key), "\n\r"); + + if ($this->output->getVerbosity() === OutputInterface::VERBOSITY_DEBUG) + { + // Do nothing + } + else if (strpos($message, 'Deleting ') === 0) + { + $elements = explode(' ', $message); + $lang_key = 'COMPOSER_DELETING'; + $parameters = [$elements[1]]; + } + + $translated_message = $this->language->lang_array($lang_key, $parameters); + + switch ($level) + { + case 1: + $translated_message = '<info>' . $translated_message . '</info>'; + break; + case 2: + $translated_message = '<comment>' . $translated_message . '</comment>'; + break; + case 3: + $translated_message = '<warning>' . $translated_message . '</warning>'; + break; + case 4: + $translated_message = '<error>' . $translated_message . '</error>'; + break; + } + + $translated_messages[] = $translated_message; + } + + parent::write($translated_messages, $newline); + } + + /** + * {@inheritdoc} + */ + public function writeError($messages, $newline = true, $verbosity = self::NORMAL) + { + $messages = (array) $messages; + $translated_messages = []; + + foreach ($messages as $message) + { + $level = 0; + if (is_array($message)) + { + $lang_key = $message[0]; + $parameters = $message[1]; + if (count($message) > 2) + { + $level = $message[2]; + } + } + else + { + $lang_key = $message; + $parameters = []; + } + + $message = trim($this->strip_format($lang_key), "\n\r"); + + if ($message === 'Your requirements could not be resolved to an installable set of packages.') + { + $this->composer_error[] = ['COMPOSER_ERROR_CONFLICT', []]; + + if ($this->output->getVerbosity() < OutputInterface::VERBOSITY_DEBUG) + { + continue; + } + } + else if (strpos($message, ' Problem ') === 0) + { + if ($this->output->getVerbosity() < OutputInterface::VERBOSITY_VERBOSE) + { + continue; + } + + $lang_key = "\n" . htmlentities($message) . "\n"; + $level = 4; + } + else if ($message === 'Updating dependencies') + { + $lang_key = 'COMPOSER_UPDATING_DEPENDENCIES'; + $level = 1; + } + else if ($message === 'Loading composer repositories with package information') + { + $lang_key = 'COMPOSER_LOADING_REPOSITORIES'; + $level = 1; + } + else if (strpos($message, 'could not be fully loaded, package information was loaded from the local cache and may be out of date') !== false) + { + $end_repo = strpos($message, 'could not be fully loaded, package information was loaded from the local cache and may be out of date'); + $repo = substr($message, 0, $end_repo - 1); + + $lang_key = 'COMPOSER_REPOSITORY_UNAVAILABLE'; + $parameters = [$repo]; + $level = 3; + } + else if (strpos($message, 'file could not be downloaded') !== false) + { + continue; + } + else if (strpos($message, ' - Installing ') === 0) + { + $elements = explode(' ', $message); + $lang_key = 'COMPOSER_INSTALLING_PACKAGE'; + $parameters = [$elements[4], trim($elements[5], '()')]; + } + else if ($message === 'Nothing to install or update') + { + $lang_key = 'COMPOSER_UPDATE_NOTHING'; + $level = 3; + } + else if ($message === ' Downloading') + { + continue; + } + else if ($message === ' Loading from cache') + { + continue; + } + else if ($message === 'Writing lock file') + { + continue; + } + else if ($message === ' Extracting archive') + { + continue; + } + else if (empty($message)) + { + continue; + } + + $translated_message = $this->language->lang_array($lang_key, $parameters); + + switch ($level) + { + case 1: + $translated_message = '<info>' . $translated_message . '</info>'; + break; + case 2: + $translated_message = '<comment>' . $translated_message . '</comment>'; + break; + case 3: + $translated_message = '<warning>' . $translated_message . '</warning>'; + break; + case 4: + $translated_message = '<error>' . $translated_message . '</error>'; + break; + } + + $translated_messages[] = $translated_message; + } + + parent::writeError($translated_messages, $newline); + } + + public function get_composer_error() + { + $error = ''; + foreach ($this->composer_error as $error_line) + { + $error .= $this->language->lang_array($error_line[0], $error_line[1]); + $error .= "\n"; + } + + $this->composer_error = []; + + return $error; + } + + protected function strip_format($message) + { + return str_replace([ + '<info>', '</info>', + '<warning>', '</warning>', + '<comment>', '</comment>', + '<error>', '</error>', + ], '', $message); + } +} diff --git a/phpBB/phpbb/composer/io/web_io.php b/phpBB/phpbb/composer/io/web_io.php new file mode 100644 index 0000000000..4eab3d099a --- /dev/null +++ b/phpBB/phpbb/composer/io/web_io.php @@ -0,0 +1,37 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\composer\io; + +use Composer\IO\BufferIO; +use phpbb\language\language; +use Symfony\Component\Console\Formatter\OutputFormatterInterface; +use Symfony\Component\Console\Output\StreamOutput; + +class web_io extends BufferIO implements io_interface +{ + use translate_composer_trait; + + /** + * @param language $language Language object + * @param string $input Input string + * @param int $verbosity Verbosity level + * @param OutputFormatterInterface $formatter Output formatter + */ + public function __construct(language $language, $input = '', $verbosity = StreamOutput::VERBOSITY_NORMAL, OutputFormatterInterface $formatter = null) + { + $this->language = $language; + + parent::__construct($input, $verbosity, $formatter); + } +} diff --git a/phpBB/phpbb/composer/manager.php b/phpBB/phpbb/composer/manager.php new file mode 100644 index 0000000000..43ea8a2503 --- /dev/null +++ b/phpBB/phpbb/composer/manager.php @@ -0,0 +1,332 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\composer; + +use Composer\IO\IOInterface; +use phpbb\cache\driver\driver_interface; +use phpbb\composer\exception\runtime_exception; + +/** + * Class to manage packages through composer. + */ +class manager implements manager_interface +{ + /** + * @var installer Composer packages installer + */ + protected $installer; + + /** + * @var driver_interface Cache instance + */ + protected $cache; + + /** + * @var string Type of packages (phpbb-packages per example) + */ + protected $package_type; + + /** + * @var string Prefix used for the exception's language string + */ + protected $exception_prefix; + + /** + * @var array Caches the managed packages list (for the current type) + */ + private $managed_packages; + + /** + * @var array Caches the managed packages list (for all phpBB types) + */ + private $all_managed_packages; + + /** + * @var array Caches the available packages list + */ + private $available_packages; + + /** + * @param installer $installer Installer object + * @param driver_interface $cache Cache object + * @param string $package_type Composer type of managed packages + * @param string $exception_prefix Exception prefix to use + */ + public function __construct(installer $installer, driver_interface $cache, $package_type, $exception_prefix) + { + $this->installer = $installer; + $this->cache = $cache; + $this->package_type = $package_type; + $this->exception_prefix = $exception_prefix; + } + + /** + * {@inheritdoc} + */ + public function install(array $packages, IOInterface $io = null) + { + $packages = $this->normalize_version($packages); + + $already_managed = array_intersect(array_keys($this->get_managed_packages()), array_keys($packages)); + if (count($already_managed) !== 0) + { + throw new runtime_exception($this->exception_prefix, 'ALREADY_INSTALLED', [implode('|', $already_managed)]); + } + + $this->pre_install($packages, $io); + + $managed_packages = array_merge($this->get_all_managed_packages(), $packages); + ksort($managed_packages); + + $this->installer->install($managed_packages, array_keys($packages), $io); + + $this->post_install($packages, $io); + + $this->managed_packages = null; + } + + /** + * Hook called before installing the packages + * + * @param array $packages Packages to update. + * Each entry may be a name or an array associating a version constraint to a name + * @param IOInterface $io IO object used for the output + */ + protected function pre_install(array $packages, IOInterface $io = null) + { + } + + /** + * Hook called after installing the packages + * + * @param array $packages Packages to update. + * Each entry may be a name or an array associating a version constraint to a name + * @param IOInterface $io IO object used for the output + */ + protected function post_install(array $packages, IOInterface $io = null) + { + } + + /** + * {@inheritdoc} + */ + public function update(array $packages, IOInterface $io = null) + { + $packages = $this->normalize_version($packages); + + $not_managed = array_diff_key($packages, $this->get_managed_packages()); + if (count($not_managed) !== 0) + { + throw new runtime_exception($this->exception_prefix, 'NOT_MANAGED', [implode('|', array_keys($not_managed))]); + } + + $this->pre_update($packages, $io); + + $managed_packages = array_merge($this->get_all_managed_packages(), $packages); + ksort($managed_packages); + + $this->installer->install($managed_packages, array_keys($packages), $io); + + $this->post_update($packages, $io); + } + + /** + * Hook called before updating the packages + * + * @param array $packages Packages to update. + * Each entry may be a name or an array associating a version constraint to a name + * @param IOInterface $io IO object used for the output + */ + protected function pre_update(array $packages, IOInterface $io = null) + { + } + + /** + * Hook called after updating the packages + * + * @param array $packages Packages to update. + * Each entry may be a name or an array associating a version constraint to a name + * @param IOInterface $io IO object used for the output + */ + protected function post_update(array $packages, IOInterface $io = null) + { + } + + /** + * {@inheritdoc} + */ + public function remove(array $packages, IOInterface $io = null) + { + $packages = $this->normalize_version($packages); + + $not_managed = array_diff_key($packages, $this->get_managed_packages()); + if (count($not_managed) !== 0) + { + throw new runtime_exception($this->exception_prefix, 'NOT_MANAGED', [implode('|', array_keys($not_managed))]); + } + + $this->pre_remove($packages, $io); + + $managed_packages = array_diff_key($this->get_all_managed_packages(), $packages); + ksort($managed_packages); + + $this->installer->install($managed_packages, array_keys($packages), $io); + + $this->post_remove($packages, $io); + + $this->managed_packages = null; + } + + /** + * Hook called before removing the packages + * + * @param array $packages Packages to update. + * Each entry may be a name or an array associating a version constraint to a name + * @param IOInterface $io IO object used for the output + */ + protected function pre_remove(array $packages, IOInterface $io = null) + { + } + + /** + * Hook called after removing the packages + * + * @param array $packages Packages to update. + * Each entry may be a name or an array associating a version constraint to a name + * @param IOInterface $io IO object used for the output + */ + protected function post_remove(array $packages, IOInterface $io = null) + { + } + + /** + * {@inheritdoc} + */ + public function is_managed($package) + { + return array_key_exists($package, $this->get_managed_packages()); + } + + /** + * {@inheritdoc} + */ + public function get_managed_packages() + { + if ($this->managed_packages === null) + { + $this->managed_packages = $this->installer->get_installed_packages($this->package_type); + } + + return $this->managed_packages; + } + + /** + * {@inheritdoc} + */ + public function get_all_managed_packages() + { + if ($this->all_managed_packages === null) + { + $this->all_managed_packages = $this->installer->get_installed_packages(explode(',', installer::PHPBB_TYPES)); + } + + return $this->all_managed_packages; + } + + /** + * {@inheritdoc} + */ + public function get_available_packages() + { + if ($this->available_packages === null) + { + $this->available_packages = $this->cache->get('_composer_' . $this->package_type . '_available'); + + if (!$this->available_packages) + { + $this->available_packages = $this->installer->get_available_packages($this->package_type); + $this->cache->put('_composer_' . $this->package_type . '_available', $this->available_packages, 24*60*60); + } + } + + return $this->available_packages; + } + + /** + * {@inheritdoc} + */ + public function reset_cache() + { + $this->cache->destroy('_composer_' . $this->package_type . '_available'); + + $this->available_packages = null; + $this->managed_packages = null; + $this->all_managed_packages = null; + } + + /** + * {@inheritdoc} + */ + public function start_managing($package, $io) + { + throw new \phpbb\exception\runtime_exception('COMPOSER_UNSUPPORTED_OPERATION', (array) $this->package_type); + } + + /** + * {@inheritdoc} + */ + public function check_requirements() + { + return $this->installer->check_requirements(); + } + + /** + * Normalize a packages/version array. Every entry can have 3 different forms: + * - $package => $version + * - $indice => $package:$version + * - $indice => $package + * They are converted to he form: + * - $package => $version ($version is set to '*' for the third form) + * + * @param array $packages + * + * @return array + */ + protected function normalize_version(array $packages) + { + $normalized_packages = []; + + foreach ($packages as $package => $version) + { + if (is_numeric($package)) + { + if (strpos($version, ':') !== false) + { + $parts = explode(':', $version); + $normalized_packages[$parts[0]] = $parts[1]; + } + else + { + $normalized_packages[$version] = '*'; + } + } + else + { + $normalized_packages[$package] = $version; + } + } + + return $normalized_packages; + } +} diff --git a/phpBB/phpbb/composer/manager_interface.php b/phpBB/phpbb/composer/manager_interface.php new file mode 100644 index 0000000000..3cb401f2b6 --- /dev/null +++ b/phpBB/phpbb/composer/manager_interface.php @@ -0,0 +1,110 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\composer; + +use Composer\IO\IOInterface; +use phpbb\composer\exception\runtime_exception; + +/** + * Class to manage packages through composer. + */ +interface manager_interface +{ + /** + * Installs (if necessary) a set of packages + * + * @param array $packages Packages to install. + * Each entry may be a name or an array associating a version constraint to a name + * @param IOInterface $io IO object used for the output + * + * @throws runtime_exception + */ + public function install(array $packages, IOInterface $io = null); + + /** + * Updates or installs a set of packages + * + * @param array $packages Packages to update. + * Each entry may be a name or an array associating a version constraint to a name + * @param IOInterface $io IO object used for the output + * + * @throws runtime_exception + */ + public function update(array $packages, IOInterface $io = null); + + /** + * Removes a set of packages + * + * @param array $packages Packages to remove. + * Each entry may be a name or an array associating a version constraint to a name + * @param IOInterface $io IO object used for the output + * + * @throws runtime_exception + */ + public function remove(array $packages, IOInterface $io = null); + + /** + * Tells whether or not a package is managed by Composer. + * + * @param string $packages Package name + * + * @return bool + */ + public function is_managed($packages); + + /** + * Returns the list of managed packages for the current type + * + * @return array The managed packages associated to their version. + */ + public function get_managed_packages(); + + /** + * Returns the list of managed packages for all phpBB types + * + * @return array The managed packages associated to their version. + */ + public function get_all_managed_packages(); + + /** + * Returns the list of available packages + * + * @return array The name of the available packages, associated to their definition. Ordered by name. + */ + public function get_available_packages(); + + /** + * Reset the cache + */ + public function reset_cache(); + + /** + * Start managing a manually installed package + * + * Remove a package installed manually and reinstall it using composer. + * + * @param string $package Package to manage + * @param IOInterface $io IO object used for the output + * + * @throws runtime_exception + */ + public function start_managing($package, $io); + + /** + * Checks the requirements of the manager and returns true if it can be used. + * + * @return bool + */ + public function check_requirements(); +} |