From a03e2e09ceaae9abce12f77f20328d1ee700bc84 Mon Sep 17 00:00:00 2001 From: Chris Brown Date: Sat, 28 Nov 2020 23:38:30 -0500 Subject: [PATCH] Improvements to "valet use" command - properly detects if the requested version is already installed, and skips re-installing/re-starting/re-configuring - allows --force to re-configure anyway - smarter treatment of 'php' when it's aliased to another specific installed version --- cli/Valet/Brew.php | 51 ++++++++++++++++++++++++++++++---------- cli/Valet/Diagnose.php | 1 + cli/Valet/PhpFpm.php | 47 +++++++++++++++++++++++++----------- cli/includes/helpers.php | 20 ++++++++++++++++ cli/valet.php | 5 ++-- 5 files changed, 94 insertions(+), 30 deletions(-) diff --git a/cli/Valet/Brew.php b/cli/Valet/Brew.php index b36aee0..150aec2 100644 --- a/cli/Valet/Brew.php +++ b/cli/Valet/Brew.php @@ -41,14 +41,23 @@ function __construct(CommandLine $cli, Filesystem $files) } /** - * Determine if the given formula is installed. + * Ensure the formula exists in the current Homebrew configuration * * @param string $formula * @return bool */ function installed($formula) { - return in_array($formula, explode(PHP_EOL, $this->cli->runAsUser('brew list --formula | grep '.$formula))); + $result = $this->cli->runAsUser("brew info $formula --json"); + + // should be a json response, but if not installed then "Error: No available formula ..." + if (starts_with($result, 'Error: No')) { + return false; + } + + $details = json_decode($result); + + return !empty($details[0]->installed); } /** @@ -58,9 +67,11 @@ function installed($formula) */ function hasInstalledPhp() { - return $this->supportedPhpVersions()->contains(function ($version) { - return $this->installed($version); + $installed = $this->installedPhpFormulae()->first(function ($formula) { + return $this->supportedPhpVersions()->contains($formula); }); + + return !empty($installed); } /** @@ -73,13 +84,25 @@ function supportedPhpVersions() return collect(static::SUPPORTED_PHP_VERSIONS); } + function installedPhpFormulae() + { + return collect( + explode(PHP_EOL, $this->cli->runAsUser('brew list --formula | grep php')) + ); + } + /** * Get the aliased formula version from Homebrew */ function determineAliasedVersion($formula) { $details = json_decode($this->cli->runAsUser("brew info $formula --json")); - return $details[0]->aliases[0] ?: 'ERROR - NO BREW ALIAS FOUND'; + + if (!empty($details[0]->aliases[0])) { + return $details[0]->aliases[0]; + } + + return 'ERROR - NO BREW ALIAS FOUND'; } /** @@ -135,6 +158,9 @@ function installOrFail($formula, $options = [], $taps = []) } output('['.$formula.'] is not installed, installing it now via Brew... 🍻'); + if ($formula !== 'php' && starts_with($formula, 'php') && preg_replace('/[^\d]/', '', $formula) < '73') { + warning('Note: older PHP versions may take 10+ minutes to compile from source. Please wait ...'); + } $this->cli->runAsUser(trim('brew install '.$formula.' '.implode(' ', $options)), function ($exitCode, $errorOutput) use ($formula) { output($errorOutput); @@ -213,16 +239,16 @@ function hasLinkedPhp() function getParsedLinkedPhp() { if (! $this->hasLinkedPhp()) { - throw new DomainException("Homebrew PHP appears not to be linked."); + throw new DomainException("Homebrew PHP appears not to be linked. Please run [valet use php@X.Y]"); } $resolvedPath = $this->files->readLink(BREW_PREFIX.'/bin/php'); /** * Typical homebrew path resolutions are like: - * "../Cellar/php@7.2/7.2.13/bin/php" + * "../Cellar/php@7.4/7.4.13/bin/php" * or older styles: - * "../Cellar/php/7.2.9_2/bin/php + * "../Cellar/php/7.4.9_2/bin/php * "../Cellar/php55/bin/php */ preg_match('~\w{3,}/(php)(@?\d\.?\d)?/(\d\.\d)?([_\d\.]*)?/?\w{3,}~', $resolvedPath, $matches); @@ -231,12 +257,11 @@ function getParsedLinkedPhp() } /** - * Gets the currently linked formula - * E.g if under php, will be php, if under php@7.3 will be that - * Different to ->linkedPhp() in that this will just get the linked directory name (whether that is php, php55 or - * php@7.2) + * Gets the currently linked formula by identifying the symlink in the hombrew bin directory. + * Different to ->linkedPhp() in that this will just get the linked directory name, + * whether that is php, php74 or php@7.4 * - * @return mixed + * @return string */ function getLinkedPhpFormula() { diff --git a/cli/Valet/Diagnose.php b/cli/Valet/Diagnose.php index 966bc42..3277aaf 100644 --- a/cli/Valet/Diagnose.php +++ b/cli/Valet/Diagnose.php @@ -20,6 +20,7 @@ class Diagnose 'brew services list', 'brew list --formula --versions | grep -E "(php|nginx|dnsmasq|mariadb|mysql|mailhog|openssl)(@\d\..*)?\s"', 'brew outdated', + 'brew tap', 'php -v', 'which -a php', 'php --ini', diff --git a/cli/Valet/PhpFpm.php b/cli/Valet/PhpFpm.php index e2d59f3..e75eb7c 100644 --- a/cli/Valet/PhpFpm.php +++ b/cli/Valet/PhpFpm.php @@ -9,7 +9,7 @@ class PhpFpm var $brew, $cli, $files; var $taps = [ - 'homebrew/homebrew-core' + 'homebrew/homebrew-core', ]; /** @@ -168,14 +168,26 @@ function stopRunning() * Use a specific version of php * * @param $version + * @param $force * @return string */ - function useVersion($version) + function useVersion($version, $force = false) { $version = $this->validateRequestedVersion($version); - // Install the relevant formula if not already installed - $this->brew->ensureInstalled($version); + try { + if ($this->brew->linkedPhp() === $version && !$force) { + output(sprintf('Valet is already using version: %s. To re-link and re-configure use the --force parameter.' . PHP_EOL, + $version)); + exit(); + } + } catch (DomainException $e) + { /* ignore thrown exception when no linked php is found */ } + + if (!$this->brew->installed($version)) { + // Install the relevant formula if not already installed + $this->brew->ensureInstalled($version, [], $this->taps); + } // Unlink the current php if there is one if ($this->brew->hasLinkedPhp()) { @@ -187,6 +199,9 @@ function useVersion($version) info(sprintf('Linking new version: %s', $version)); $this->brew->link($version, true); + $this->stopRunning(); + + // ensure configuration is correct and start the linked version $this->install(); return $version === 'php' ? $this->brew->determineAliasedVersion($version) : $version; @@ -211,16 +226,6 @@ function validateRequestedVersion($version) { $version = $this->normalizePhpVersion($version); - if ($version === 'php') { - if (strpos($this->brew->determineAliasedVersion($version), '@')) { - return $version; - } - - if ($this->brew->hasInstalledPhp()) { - throw new DomainException('Brew is already using PHP '.PHP_VERSION.' as \'php\' in Homebrew. To use another version, please specify. eg: php@7.3'); - } - } - if (!$this->brew->supportedPhpVersions()->contains($version)) { throw new DomainException( sprintf( @@ -230,6 +235,20 @@ function validateRequestedVersion($version) ); } + if (strpos($aliasedVersion = $this->brew->determineAliasedVersion($version), '@')) { + return $aliasedVersion; + } + + if ($version === 'php') { + if (strpos($aliasedVersion = $this->brew->determineAliasedVersion($version), '@')) { + return $aliasedVersion; + } + + if ($this->brew->hasInstalledPhp()) { + throw new DomainException('Brew is already using PHP '.PHP_VERSION.' as \'php\' in Homebrew. To use another version, please specify. eg: php@7.3'); + } + } + return $version; } } diff --git a/cli/includes/helpers.php b/cli/includes/helpers.php index 7e19248..50ec48d 100644 --- a/cli/includes/helpers.php +++ b/cli/includes/helpers.php @@ -172,6 +172,26 @@ function ends_with($haystack, $needles) { } } +if (! function_exists('starts_with')) { + /** + * Determine if a given string starts with a given substring. + * + * @param string $haystack + * @param string|string[] $needles + * @return bool + */ + function starts_with($haystack, $needles) + { + foreach ((array) $needles as $needle) { + if ((string) $needle !== '' && strncmp($haystack, $needle, strlen($needle)) === 0) { + return true; + } + } + + return false; + } +} + /** * Get the user */ diff --git a/cli/valet.php b/cli/valet.php index 4381376..43cf10b 100755 --- a/cli/valet.php +++ b/cli/valet.php @@ -462,15 +462,14 @@ /** * Allow the user to change the version of php valet uses */ - $app->command('use [phpVersion]', function ($phpVersion) { + $app->command('use [phpVersion] [--force]', function ($phpVersion, $force) { if (!$phpVersion) { return info('Valet is using ' . Brew::linkedPhp()); } PhpFpm::validateRequestedVersion($phpVersion); - PhpFpm::stopRunning(); - $newVersion = PhpFpm::useVersion($phpVersion); + $newVersion = PhpFpm::useVersion($phpVersion, $force); Nginx::restart(); info(sprintf('Valet is now using %s.', $newVersion) . PHP_EOL);