From db5e96bdc1a913dee18f4c275e45f638bfc877c5 Mon Sep 17 00:00:00 2001 From: Tom Klingenberg Date: Sat, 11 Feb 2017 18:59:10 +0100 Subject: [PATCH] Forward port Magento installer invocation, fixes #267 It came to the attention in #267 that some of the improvements in Magerun 1 when installing Magento were not yet forward ported to Magerun 2. Fixes in detail: - Use the same php binary as Magerun is running with (solves the issue with aliasing the php binary as reported in #267) - Using Exec::run() utility (solves the issue of not showing the error output if the install command fails as reported in #267) - When invoking the command, PHP is executed with full error reporting so that errors during the install can be more easily trouble-shooted Refs: - #267 - Command: install --- CHANGELOG.md | 1 + .../Installer/SubCommand/InstallMagento.php | 91 +++++++++++-------- src/N98/Util/Exec.php | 26 ++++-- src/N98/Util/OperatingSystem.php | 23 ++++- 4 files changed, 94 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0290c953..fcf5c801 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ RECENT CHANGES 1.3.1 ----- +* Fix: Install command using wrong php binary and eating installer errors (report by David Lambauer, fix by Tom Klingenberg, #267) * Fix: Minor PHP version for Magento 2 extensions (by Alexander Turiak, #269) * Fix: Magento object manager usage in production mode (by Tom Klingenberg, #241) * Fix: Support for db-setting arrays (e.g. driver_options) (by Tom Klingenberg) diff --git a/src/N98/Magento/Command/Installer/SubCommand/InstallMagento.php b/src/N98/Magento/Command/Installer/SubCommand/InstallMagento.php index 63d1adf5..72bbb522 100644 --- a/src/N98/Magento/Command/Installer/SubCommand/InstallMagento.php +++ b/src/N98/Magento/Command/Installer/SubCommand/InstallMagento.php @@ -4,15 +4,21 @@ use Exception; use N98\Magento\Command\SubCommand\AbstractSubCommand; +use N98\Util\Exec; use N98\Util\OperatingSystem; +use RuntimeException; +use Symfony\Component\Console\Output\OutputInterface; class InstallMagento extends AbstractSubCommand { /** - * @var int + * @deprecated since since 1.3.1; Use constant from Exec-Utility instead + * @see Exec::CODE_CLEAN_EXIT */ const EXEC_STATUS_OK = 0; + const MAGENTO_INSTALL_SCRIPT_PATH = 'bin/magento'; + /** * @var \Closure */ @@ -196,35 +202,19 @@ public function execute() $this->config->setArray('installation_args', $argv); - $installArgs = ''; - foreach ($argv as $argName => $argValue) { - if (is_null($argValue)) { - $installArgs .= '--' . $argName . ' '; - } elseif (is_bool($argValue)) { - if ($argValue) { - $argValue = '1'; - } else { - $argValue = '0'; - } - $installArgs .= '--' . $argName . '=' . $argValue . ' '; - } else { - $installArgs .= '--' . $argName . '=' . escapeshellarg($argValue) . ' '; - } - } - - $this->output->writeln('Start installation process.'); - $this->_runInstaller($installArgs); + $this->runInstallScriptCommand($this->output, $this->config->getString('installationFolder'), $argv); } /** + * @deprecated since 1.3.1 (obsolete) * @return string * @throws Exception */ protected function getInstallScriptPath() { - $installerScript = $this->config->getString('installationFolder') . '/bin/magento'; + $installerScript = $this->config->getString('installationFolder') . '/' . self::MAGENTO_INSTALL_SCRIPT_PATH; if (!file_exists($installerScript)) { - throw new \RuntimeException('Installation script was not found.', 1); + throw new RuntimeException('Installation script was not found.', 1); } return $installerScript; @@ -261,30 +251,55 @@ protected function _prepareDbHost() } /** - * @param string $installArgs + * Invoke Magento PHP install script bin/magento setup:install * - * @throws Exception + * @param OutputInterface $output + * @param string $installationFolder folder where magento is installed in, must exists setup script in + * @param array $argv + * @return void */ - protected function _runInstaller($installArgs) + private function runInstallScriptCommand(OutputInterface $output, $installationFolder, array $argv) { + $installArgs = ''; + foreach ($argv as $argName => $argValue) { + if (is_null($argValue)) { + $installArgs .= '--' . $argName . ' '; + } elseif (is_bool($argValue)) { + $installArgs .= '--' . $argName . '=' . (int) $argValue . ' '; + } else { + $installArgs .= '--' . $argName . '=' . escapeshellarg($argValue) . ' '; + } + } + + $output->writeln('Start installation process.'); + + $installCommand = sprintf( + '%s -ddisplay_startup_errors=1 -ddisplay_errors=1 -derror_reporting=-1 -f %s -- setup:install %s', + OperatingSystem::getPhpBinary(), + escapeshellarg($installationFolder . '/' . self::MAGENTO_INSTALL_SCRIPT_PATH), + $installArgs + ); + + $output->writeln('' . $installCommand . ''); + $installException = null; $installationOutput = null; $returnStatus = null; - - if (OperatingSystem::isWindows()) { - $installCommand = 'php ' . $this->getInstallScriptPath() . ' setup:install ' . $installArgs; - } else { - $installCommand = '/usr/bin/env php ' . $this->getInstallScriptPath() . ' setup:install ' . $installArgs; + try { + Exec::run($installCommand, $installationOutput, $returnStatus); + } catch (Exception $installException) { + /* fall-through intended */ } - $this->output->writeln('' . $installCommand . ''); - exec($installCommand, $installationOutput, $returnStatus); - $installationOutput = implode(PHP_EOL, $installationOutput); - if ($returnStatus !== self::EXEC_STATUS_OK) { - throw new \RuntimeException('Installation failed.' . $installationOutput, 1); - } else { - $this->output->writeln('Successfully installed Magento'); - $encryptionKey = trim(substr($installationOutput, strpos($installationOutput, ':') + 1)); - $this->output->writeln('Encryption Key: ' . $encryptionKey . ''); + if (isset($installException) || $returnStatus !== Exec::CODE_CLEAN_EXIT) { + $this->getCommand()->getApplication()->setAutoExit(true); + throw new RuntimeException( + sprintf('Installation failed (Exit code %s). %s', $returnStatus, $installationOutput), + 1, + $installException + ); } + $output->writeln('Successfully installed Magento'); + $encryptionKey = trim(substr(strstr($installationOutput, ':'), 1)); + $output->writeln('Encryption Key: ' . $encryptionKey . ''); } } diff --git a/src/N98/Util/Exec.php b/src/N98/Util/Exec.php index 18915073..169adf66 100644 --- a/src/N98/Util/Exec.php +++ b/src/N98/Util/Exec.php @@ -6,6 +6,7 @@ /** * Class Exec + * * @package N98\Util */ class Exec @@ -22,18 +23,25 @@ class Exec /** * @param string $command - * @param string $commandOutput + * @param string|null $output * @param int $returnCode */ - public static function run($command, &$commandOutput = null, &$returnCode = null) + public static function run($command, &$output = null, &$returnCode = null) { + if (!self::allowed()) { + $message = sprintf("No PHP exec(), can not execute command '%s'.", $command); + throw new RuntimeException($message); + } + $command = $command . self::REDIRECT_STDERR_TO_STDOUT; - exec($command, $commandOutput, $returnCode); - $commandOutput = self::parseCommandOutput($commandOutput); + exec($command, $outputArray, $returnCode); + $output = self::parseCommandOutput((array) $outputArray); if ($returnCode !== self::CODE_CLEAN_EXIT) { - throw new RuntimeException($commandOutput); + throw new RuntimeException( + sprintf("Exit status %d for command %s. Output was: %s", $returnCode, $command, $output) + ); } } @@ -48,11 +56,13 @@ public static function allowed() } /** - * @param $commandOutput + * string from array of strings representing one line per entry + * + * @param array $commandOutput * @return string */ - private static function parseCommandOutput($commandOutput) + private static function parseCommandOutput(array $commandOutput) { - return implode(PHP_EOL, $commandOutput); + return implode(PHP_EOL, $commandOutput) . PHP_EOL; } } diff --git a/src/N98/Util/OperatingSystem.php b/src/N98/Util/OperatingSystem.php index 90eeffd6..d787b678 100644 --- a/src/N98/Util/OperatingSystem.php +++ b/src/N98/Util/OperatingSystem.php @@ -81,7 +81,9 @@ public static function isProgramInstalled($program) } /** - * @return string + * Home directory of the current user + * + * @return string|false false in case there is no environment variable related to the home directory */ public static function getHomeDir() { @@ -113,4 +115,23 @@ public static function getCwd() { return getcwd(); } + + /** + * Retrieve path to php binary + * + * @return string + */ + public static function getPhpBinary() + { + // PHP_BINARY (>= php 5.4) + if (defined('PHP_BINARY')) { + return PHP_BINARY; + } + + if (self::isWindows()) { + return 'php'; + } + + return '/usr/bin/env php'; + } }