diff --git a/phpstan.neon.dist b/phpstan.neon.dist index f27b06b..3812e72 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -16,4 +16,4 @@ parameters: - message: '~^Constant RCMAIL_VERSION not found\.$~' path: 'src/ExtensionInstaller.php' - count: 3 + count: 1 diff --git a/src/ExtensionInstaller.php b/src/ExtensionInstaller.php index 33fff66..8cfbd61 100644 --- a/src/ExtensionInstaller.php +++ b/src/ExtensionInstaller.php @@ -5,56 +5,81 @@ use Composer\Installer\LibraryInstaller; use Composer\Package\PackageInterface; use Composer\Package\Version\VersionParser; +use Composer\Repository\InstalledRepository; use Composer\Repository\InstalledRepositoryInterface; +use Composer\Repository\RootPackageRepository; use Composer\Util\Filesystem; use Composer\Util\ProcessExecutor; use React\Promise\PromiseInterface; abstract class ExtensionInstaller extends LibraryInstaller { + /** @var string|null */ + private $roundcubemailInstallPath; + + /** @var string */ protected $composer_type; - protected function getRoundcubemailInstallPath(): string + protected function setRoundcubemailInstallPath(InstalledRepositoryInterface $installedRepo): void { - $rootPackage = $this->composer->getPackage(); - if ($rootPackage->getName() === 'roundcube/roundcubemail') { + // https://github.com/composer/composer/discussions/11927#discussioncomment-9116893 + $rootPackage = clone $this->composer->getPackage(); + $installedRepo = new InstalledRepository([ + $installedRepo, + new RootPackageRepository($rootPackage), + ]); + + $roundcubemailPackages = $installedRepo->findPackagesWithReplacersAndProviders('roundcube/roundcubemail'); + assert(count($roundcubemailPackages) === 1); + $roundcubemailPackage = $roundcubemailPackages[0]; + + if ($roundcubemailPackage === $rootPackage) { // $this->getInstallPath($package) does not work for root package $this->initializeVendorDir(); - - return dirname($this->vendorDir); + $this->roundcubemailInstallPath = dirname($this->vendorDir); + } else { + $this->roundcubemailInstallPath = $this->getInstallPath($roundcubemailPackage); } + } - $roundcubemailPackage = $this->composer - ->getRepositoryManager() - ->findPackage('roundcube/roundcubemail', '*'); - - return $this->getInstallPath($roundcubemailPackage); + protected function getRoundcubemailInstallPath(): string + { + return $this->roundcubemailInstallPath; } public function getInstallPath(PackageInterface $package) { - if (!$this->supports($package->getType())) { + if ( + !$this->supports($package->getType()) + || $this->roundcubemailInstallPath === null // install path is not known at download phase + ) { return parent::getInstallPath($package); } $vendorDir = $this->getVendorDir(); - return sprintf('%s/%s', $vendorDir, $this->getPackageName($package)); + return $vendorDir . \DIRECTORY_SEPARATOR + . str_replace('/', \DIRECTORY_SEPARATOR, $this->getPackageName($package)); } - public function install(InstalledRepositoryInterface $repo, PackageInterface $package) + private function initializeRoundcubemailEnvironment(): void { // initialize Roundcube environment if (!defined('INSTALL_PATH')) { define('INSTALL_PATH', $this->getRoundcubemailInstallPath() . '/'); } require_once INSTALL_PATH . 'program/include/iniset.php'; + } + public function install(InstalledRepositoryInterface $repo, PackageInterface $package) + { + $this->setRoundcubemailInstallPath($repo); + $this->initializeRoundcubemailEnvironment(); $this->rcubeVersionCheck($package); $postInstall = function () use ($package) { $config_file = $this->rcubeConfigFile(); $package_name = $this->getPackageName($package); - $package_dir = $this->getVendorDir() . \DIRECTORY_SEPARATOR . $package_name; + $package_dir = $this->getInstallPath($package); $extra = $package->getExtra(); if (is_writable($config_file) && \PHP_SAPI === 'cli' && $this->confirmInstall($package_name)) { @@ -83,12 +108,7 @@ public function install(InstalledRepositoryInterface $repo, PackageInterface $pa if ($sqldir = realpath($package_dir . \DIRECTORY_SEPARATOR . $extra['roundcube']['sql-dir'])) { $this->io->write("Running database initialization script for {$package_name}"); - $roundcube_version = self::versionNormalize(RCMAIL_VERSION); - if (self::versionCompare($roundcube_version, '1.2.0', '>=')) { - \rcmail_utils::db_init($sqldir); - } else { - throw new \Exception('Database initialization failed. Roundcube 1.2.0 or above required.'); - } + \rcmail_utils::db_init($sqldir); } } @@ -113,20 +133,15 @@ public function install(InstalledRepositoryInterface $repo, PackageInterface $pa public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target) { - // initialize Roundcube environment - if (!defined('INSTALL_PATH')) { - define('INSTALL_PATH', $this->getRoundcubemailInstallPath() . '/'); - } - require_once INSTALL_PATH . 'program/include/iniset.php'; - + $this->setRoundcubemailInstallPath($repo); + $this->initializeRoundcubemailEnvironment(); $this->rcubeVersionCheck($target); $extra = $target->getExtra(); $fs = new Filesystem(); // backup persistent files e.g. config.inc.php - $package_name = $this->getPackageName($initial); - $package_dir = $this->getVendorDir() . \DIRECTORY_SEPARATOR . $package_name; + $package_dir = $this->getInstallPath($initial); $temp_dir = $package_dir . '-' . sprintf('%010d%010d', mt_rand(), mt_rand()); // make a backup of existing files (for restoring persistent files) @@ -134,10 +149,12 @@ public function update(InstalledRepositoryInterface $repo, PackageInterface $ini $postUpdate = function () use ($target, $extra, $fs, $temp_dir) { $package_name = $this->getPackageName($target); - $package_dir = $this->getVendorDir() . \DIRECTORY_SEPARATOR . $package_name; + $package_dir = $this->getInstallPath($target); // restore persistent files - $persistent_files = !empty($extra['roundcube']['persistent-files']) ? $extra['roundcube']['persistent-files'] : ['config.inc.php']; + $persistent_files = !empty($extra['roundcube']['persistent-files']) + ? $extra['roundcube']['persistent-files'] + : ['config.inc.php']; foreach ($persistent_files as $file) { $path = $temp_dir . \DIRECTORY_SEPARATOR . $file; if (is_readable($path)) { @@ -156,12 +173,7 @@ public function update(InstalledRepositoryInterface $repo, PackageInterface $ini if ($sqldir = realpath($package_dir . \DIRECTORY_SEPARATOR . $extra['roundcube']['sql-dir'])) { $this->io->write("Updating database schema for {$package_name}"); - $roundcube_version = self::versionNormalize(RCMAIL_VERSION); - if (self::versionCompare($roundcube_version, '1.2.0', '>=')) { - \rcmail_utils::db_update($sqldir, $package_name); - } else { - throw new \Exception('Database update failed. Roundcube 1.2.0 or above required.'); - } + \rcmail_utils::db_update($sqldir, $package_name); } } @@ -186,18 +198,15 @@ public function update(InstalledRepositoryInterface $repo, PackageInterface $ini public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package) { - // initialize Roundcube environment - if (!defined('INSTALL_PATH')) { - define('INSTALL_PATH', $this->getRoundcubemailInstallPath() . '/'); - } - require_once INSTALL_PATH . 'program/include/iniset.php'; + $this->setRoundcubemailInstallPath($repo); + $this->initializeRoundcubemailEnvironment(); $config = $this->composer->getConfig()->get('roundcube'); $postUninstall = function () use ($package, $config) { // post-uninstall: deactivate package $package_name = $this->getPackageName($package); - $package_dir = $this->getVendorDir() . \DIRECTORY_SEPARATOR . $package_name; + $package_dir = $this->getInstallPath($package); $this->rcubeAlterConfig($package_name, false); @@ -380,7 +389,7 @@ private function rcubeRunScript($script, PackageInterface $package) { $package_name = $this->getPackageName($package); $package_type = $package->getType(); - $package_dir = $this->getVendorDir() . \DIRECTORY_SEPARATOR . $package_name; + $package_dir = $this->getInstallPath($package); // check for executable shell script if (($scriptfile = realpath($package_dir . \DIRECTORY_SEPARATOR . $script)) && is_executable($scriptfile)) { @@ -404,7 +413,7 @@ private function rcubeRunScript($script, PackageInterface $package) } /** - * normalize Roundcube version string. + * Normalize Roundcube version string. */ private static function versionNormalize(string $version): string { diff --git a/src/PluginInstaller.php b/src/PluginInstaller.php index 6bd2c9f..e09fbe4 100644 --- a/src/PluginInstaller.php +++ b/src/PluginInstaller.php @@ -26,7 +26,9 @@ protected function confirmInstall($package_name) protected function getConfig($package_name, $config, $add) { - $cur_config = !empty($config['plugins']) ? ((array) $config['plugins']) : []; + $cur_config = !empty($config['plugins']) + ? ((array) $config['plugins']) + : []; $new_config = $cur_config; if ($add && !in_array($package_name, $new_config, true)) { @@ -36,7 +38,9 @@ protected function getConfig($package_name, $config, $add) } if ($new_config !== $cur_config) { - $config_val = count($new_config) > 0 ? "[\n\t'" . implode("',\n\t'", $new_config) . "',\n];" : '[];'; + $config_val = count($new_config) > 0 + ? "[\n\t'" . implode("',\n\t'", $new_config) . "',\n];" + : '[];'; $result = ['plugins', $config_val]; } else { $result = false; diff --git a/src/SkinInstaller.php b/src/SkinInstaller.php index 5854bf9..4d9cee5 100644 --- a/src/SkinInstaller.php +++ b/src/SkinInstaller.php @@ -26,7 +26,9 @@ protected function confirmInstall($package_name) protected function getConfig($package_name, $config, $add) { - $cur_config = !empty($config['skin']) ? $config['skin'] : null; + $cur_config = !empty($config['skin']) + ? $config['skin'] + : null; $new_config = $cur_config; if ($add && $new_config !== $package_name) { @@ -36,7 +38,9 @@ protected function getConfig($package_name, $config, $add) } if ($new_config !== $cur_config) { - $config_val = !empty($new_config) ? "'{$new_config}';" : null; + $config_val = !empty($new_config) + ? "'{$new_config}';" + : null; $result = ['skin', $config_val]; } else { $result = false;