-
Notifications
You must be signed in to change notification settings - Fork 718
Multiple/Parallel PHP Version Support for Valet #1192
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
37b4af4
Multiple/Parallel PHP Version Support for Valet
NasirNobin acf190c
Apply suggestions from code review
NasirNobin c247dc9
Add Valet PHP isolation remover option & apply more suggestions from …
NasirNobin 8de8d9b
Stop unused PHP versions
NasirNobin 794e366
StyleCI Patch
NasirNobin 5c10437
Apply refactor & cleanup from the code review
NasirNobin 49ca4ae
Apply suggestions from code review
NasirNobin 3c83367
Apply more suggestions from code review
NasirNobin cc41e3c
Update cli/Valet/PhpFpm.php
NasirNobin 9a5d30b
Tests for Parallel PHP Version Support
NasirNobin 7a80a2e
Apply suggestions from code review
NasirNobin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,9 @@ class PhpFpm | |
| public $brew; | ||
| public $cli; | ||
| public $files; | ||
| public $config; | ||
| public $site; | ||
| public $nginx; | ||
|
|
||
| public $taps = [ | ||
| 'homebrew/homebrew-core', | ||
|
|
@@ -21,13 +24,19 @@ class PhpFpm | |
| * @param Brew $brew | ||
| * @param CommandLine $cli | ||
| * @param Filesystem $files | ||
| * @param Configuration $config | ||
| * @param Site $site | ||
| * @param Nginx $nginx | ||
| * @return void | ||
| */ | ||
| public function __construct(Brew $brew, CommandLine $cli, Filesystem $files) | ||
| public function __construct(Brew $brew, CommandLine $cli, Filesystem $files, Configuration $config, Site $site, Nginx $nginx) | ||
| { | ||
| $this->cli = $cli; | ||
| $this->brew = $brew; | ||
| $this->files = $files; | ||
| $this->config = $config; | ||
| $this->site = $site; | ||
| $this->nginx = $nginx; | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -63,13 +72,14 @@ public function uninstall() | |
| /** | ||
| * Update the PHP FPM configuration. | ||
| * | ||
| * @param string|null $phpVersion | ||
| * @return void | ||
| */ | ||
| public function updateConfiguration() | ||
| public function updateConfiguration($phpVersion = null) | ||
| { | ||
| info('Updating PHP configuration...'); | ||
| info(sprintf('Updating PHP configuration%s...', $phpVersion ? ' for '.$phpVersion : '')); | ||
|
|
||
| $fpmConfigFile = $this->fpmConfigPath(); | ||
| $fpmConfigFile = $this->fpmConfigPath($phpVersion); | ||
|
|
||
| $this->files->ensureDirExists(dirname($fpmConfigFile), user()); | ||
|
|
||
|
|
@@ -93,6 +103,11 @@ public function updateConfiguration() | |
| $contents = preg_replace('/^;?listen\.group = .+$/m', 'listen.group = staff', $contents); | ||
| $contents = preg_replace('/^;?listen\.mode = .+$/m', 'listen.mode = 0777', $contents); | ||
| } | ||
|
|
||
| if ($phpVersion) { | ||
| $contents = str_replace('valet.sock', $this->fpmSockName($phpVersion), $contents); | ||
| } | ||
|
|
||
| $this->files->put($fpmConfigFile, $contents); | ||
|
|
||
| $contents = $this->files->get(__DIR__.'/../stubs/php-memory-limits.ini'); | ||
|
|
@@ -116,11 +131,12 @@ public function updateConfiguration() | |
| /** | ||
| * Restart the PHP FPM process. | ||
| * | ||
NasirNobin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| * @param string|null $phpVersion | ||
| * @return void | ||
| */ | ||
| public function restart() | ||
| public function restart($phpVersion = null) | ||
| { | ||
| $this->brew->restartLinkedPhp(); | ||
| $this->brew->restartService($phpVersion ?: $this->utilizedPhpVersions()); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -139,13 +155,16 @@ public function stop() | |
| /** | ||
| * Get the path to the FPM configuration file for the current PHP version. | ||
| * | ||
NasirNobin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| * @param string|null $phpVersion | ||
| * @return string | ||
| */ | ||
| public function fpmConfigPath() | ||
| public function fpmConfigPath($phpVersion = null) | ||
| { | ||
| $version = $this->brew->linkedPhp(); | ||
| if (! $phpVersion) { | ||
| $phpVersion = $this->brew->linkedPhp(); | ||
| } | ||
|
|
||
| $versionNormalized = $this->normalizePhpVersion($version === 'php' ? Brew::LATEST_PHP_VERSION : $version); | ||
| $versionNormalized = $this->normalizePhpVersion($phpVersion === 'php' ? Brew::LATEST_PHP_VERSION : $phpVersion); | ||
| $versionNormalized = preg_replace('~[^\d\.]~', '', $versionNormalized); | ||
|
|
||
| return $versionNormalized === '5.6' | ||
|
|
@@ -167,15 +186,62 @@ public function stopRunning() | |
| ); | ||
| } | ||
|
|
||
| /** | ||
| * Stop PHP, if a specific version isn't being used globally or by any sites. | ||
| * | ||
| * @param string|null $phpVersion | ||
| * @return void | ||
| */ | ||
| public function stopIfUnused($phpVersion) | ||
| { | ||
| if (! $phpVersion) { | ||
| return; | ||
| } | ||
|
|
||
| if (strpos($phpVersion, 'php') === false) { | ||
| $phpVersion = 'php'.$phpVersion; | ||
| } | ||
|
|
||
| $phpVersion = $this->normalizePhpVersion($phpVersion); | ||
|
|
||
| if (! in_array($phpVersion, $this->utilizedPhpVersions())) { | ||
| $this->brew->stopService($phpVersion); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Use a specific version of php. | ||
| * | ||
| * @param $version | ||
| * @param $force | ||
| * @return string | ||
| * @param string $version | ||
| * @param bool $force | ||
| * @param string|null $directory | ||
| * @return string|void | ||
| */ | ||
| public function useVersion($version, $force = false) | ||
| public function useVersion($version, $force = false, $directory = null) | ||
| { | ||
| if ($directory) { | ||
| $site = $this->site->getSiteUrl($directory); | ||
|
|
||
| if (! $site) { | ||
| throw new DomainException( | ||
| sprintf( | ||
| "The [%s] site could not be found in Valet's site list.", | ||
| $directory | ||
| ) | ||
| ); | ||
| } | ||
|
|
||
| if ($version == 'default') { // Remove isolation for this site | ||
| $oldCustomPhpVersion = $this->site->customPhpVersion($site); // Example output: "74" | ||
| $this->site->removeIsolation($site); | ||
| $this->stopIfUnused($oldCustomPhpVersion); | ||
| $this->nginx->restart(); | ||
| info(sprintf('The site [%s] is now using the default PHP version.', $site)); | ||
|
|
||
| return; | ||
| } | ||
| } | ||
|
|
||
| $version = $this->validateRequestedVersion($version); | ||
|
|
||
| try { | ||
|
|
@@ -192,8 +258,31 @@ public function useVersion($version, $force = false) | |
| $this->brew->ensureInstalled($version, [], $this->taps); | ||
| } | ||
|
|
||
| // Unlink the current php if there is one | ||
| if ($directory) { | ||
| $oldCustomPhpVersion = $this->site->customPhpVersion($site); // Example output: "74" | ||
| $this->cli->quietly('sudo rm '.VALET_HOME_PATH.'/'.$this->fpmSockName($version)); | ||
mattstauffer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| $this->updateConfiguration($version); | ||
| $this->site->installSiteConfig($site, $this->fpmSockName($version), $version); | ||
|
|
||
| $this->stopIfUnused($oldCustomPhpVersion); | ||
| $this->restart($version); | ||
| $this->nginx->restart(); | ||
| info(sprintf('The site [%s] is now using %s.', $site, $version)); | ||
|
|
||
| return; | ||
| } | ||
|
|
||
| // Unlink the current global PHP if there is one installed | ||
| if ($this->brew->hasLinkedPhp()) { | ||
| $linkedPhp = $this->brew->linkedPhp(); | ||
|
|
||
| // Update the old FPM to keep running, using a custom sock file, so existing isolated sites aren't broken | ||
| $this->updateConfiguration($linkedPhp); | ||
|
|
||
| // Update existing custom Nginx config files; if they're using the old or new PHP version, | ||
| // update them to the new correct sock file location | ||
| $this->updateConfigurationForGlobalUpdate($version, $linkedPhp); | ||
mattstauffer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| $currentVersion = $this->brew->getLinkedPhpFormula(); | ||
| info(sprintf('Unlinking current version: %s', $currentVersion)); | ||
| $this->brew->unlink($currentVersion); | ||
|
|
@@ -206,12 +295,19 @@ public function useVersion($version, $force = false) | |
|
|
||
| // remove any orphaned valet.sock files that PHP didn't clean up due to version conflicts | ||
| $this->files->unlink(VALET_HOME_PATH.'/valet.sock'); | ||
mattstauffer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| $this->cli->quietly('sudo rm '.VALET_HOME_PATH.'/valet.sock'); | ||
| $this->cli->quietly('sudo rm '.VALET_HOME_PATH.'/valet*.sock'); | ||
|
|
||
| // ensure configuration is correct and start the linked version | ||
| $this->install(); | ||
|
|
||
| return $version === 'php' ? $this->brew->determineAliasedVersion($version) : $version; | ||
| $newVersion = $version === 'php' ? $this->brew->determineAliasedVersion($version) : $version; | ||
|
|
||
| $this->nginx->restart(); | ||
|
|
||
| info(sprintf('Valet is now using %s.', $newVersion).PHP_EOL); | ||
| info('Note that you might need to run <comment>composer global update</comment> if your PHP version change affects the dependencies of global packages required by Composer.'); | ||
|
|
||
| return $newVersion; | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -257,4 +353,83 @@ public function validateRequestedVersion($version) | |
|
|
||
| return $version; | ||
| } | ||
|
|
||
| /** | ||
| * Get FPM sock file name for a given PHP version. | ||
| * | ||
| * @param string|null $phpVersion | ||
| * @return string | ||
| */ | ||
| public function fpmSockName($phpVersion = null) | ||
| { | ||
| $versionInteger = preg_replace('~[^\d]~', '', $phpVersion); | ||
|
|
||
| return "valet{$versionInteger}.sock"; | ||
| } | ||
|
|
||
| /** | ||
| * Update all existing Nginx files when running a global PHP version update. | ||
| * If a given file is pointing to `valet.sock`, it's targeting the old global PHP version; | ||
| * update it to point to the new custom sock file for that version. | ||
| * If a given file is pointing the custom sock file for the new global version, that new | ||
| * version will now be hosted at `valet.sock`, so update the config file to point to that instead. | ||
| * | ||
| * @param string $newPhpVersion | ||
| * @param string $oldPhpVersion | ||
| * @return void | ||
| */ | ||
| public function updateConfigurationForGlobalUpdate($newPhpVersion, $oldPhpVersion) | ||
| { | ||
| collect($this->files->scandir(VALET_HOME_PATH.'/Nginx')) | ||
| ->reject(function ($file) { | ||
| return starts_with($file, '.'); | ||
| }) | ||
| ->each(function ($file) use ($newPhpVersion, $oldPhpVersion) { | ||
| $content = $this->files->get(VALET_HOME_PATH.'/Nginx/'.$file); | ||
|
|
||
| if (! starts_with($content, '# Valet isolated PHP version')) { | ||
| return; | ||
| } | ||
|
|
||
| if (strpos($content, $this->fpmSockName($newPhpVersion)) !== false) { | ||
| info(sprintf('Updating site %s to keep using version: %s', $file, $newPhpVersion)); | ||
| $this->files->put(VALET_HOME_PATH.'/Nginx/'.$file, str_replace($this->fpmSockName($newPhpVersion), 'valet.sock', $content)); | ||
| } elseif (strpos($content, 'valet.sock') !== false) { | ||
| info(sprintf('Updating site %s to keep using version: %s', $file, $oldPhpVersion)); | ||
| $this->files->put(VALET_HOME_PATH.'/Nginx/'.$file, str_replace('valet.sock', $this->fpmSockName($oldPhpVersion), $content)); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Get a list including the global PHP version and allPHP versions currently serving "isolated sites" (sites with | ||
| * custom Nginx configs pointing them to a specific PHP version). | ||
| * | ||
| * @return array | ||
| */ | ||
| public function utilizedPhpVersions() | ||
| { | ||
| $fpmSockFiles = $this->brew->supportedPhpVersions()->map(function ($version) { | ||
| return $this->fpmSockName($this->normalizePhpVersion($version)); | ||
| })->unique(); | ||
|
|
||
| return collect($this->files->scandir(VALET_HOME_PATH.'/Nginx')) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential future refactor here to:
|
||
| ->reject(function ($file) { | ||
| return starts_with($file, '.'); | ||
| }) | ||
| ->map(function ($file) use ($fpmSockFiles) { | ||
| $content = $this->files->get(VALET_HOME_PATH.'/Nginx/'.$file); | ||
|
|
||
| // Get the normalized PHP version for this config file, if it's defined | ||
| foreach ($fpmSockFiles as $sock) { | ||
NasirNobin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if (strpos($content, $sock) !== false) { | ||
| // Extract the PHP version number from a custom .sock path; | ||
| // for example, "valet74.sock" will output "php74" | ||
| $phpVersion = 'php'.str_replace(['valet', '.sock'], '', $sock); | ||
|
|
||
| return $this->normalizePhpVersion($phpVersion); // Example output [email protected] | ||
| } | ||
| } | ||
| })->merge([$this->brew->getLinkedPhpFormula()])->filter()->unique()->values()->toArray(); | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.