Skip to content

Commit

Permalink
Multiple/Parallel PHP Version Support (#1)
Browse files Browse the repository at this point in the history
Multiple/Parallel PHP Version Support for Valet
  • Loading branch information
NasirNobin authored Feb 6, 2022
1 parent cf5d765 commit 5c865d3
Show file tree
Hide file tree
Showing 5 changed files with 315 additions and 54 deletions.
19 changes: 1 addition & 18 deletions cli/Valet/Nginx.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public function installServer()
str_replace(
['VALET_HOME_PATH', 'VALET_SERVER_PATH', 'VALET_STATIC_PREFIX'],
[VALET_HOME_PATH, VALET_SERVER_PATH, VALET_STATIC_PREFIX],
$this->replaceLoopback($this->files->get(__DIR__.'/../stubs/valet.conf'))
$this->site->replaceLoopback($this->files->get(__DIR__.'/../stubs/valet.conf'))
)
);

Expand All @@ -90,23 +90,6 @@ public function installServer()
);
}

public function replaceLoopback($siteConf)
{
$loopback = $this->configuration->read()['loopback'];

if ($loopback === VALET_LOOPBACK) {
return $siteConf;
}

$str = '#listen VALET_LOOPBACK:80; # valet loopback';

return str_replace(
$str,
substr(str_replace('VALET_LOOPBACK', $loopback, $str), 1),
$siteConf
);
}

/**
* Install the Nginx configuration directory to the ~/.config/valet directory.
*
Expand Down
160 changes: 132 additions & 28 deletions cli/Valet/PhpFpm.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,20 @@ public function __construct(Brew $brew, CommandLine $cli, Filesystem $files)
/**
* Install and configure PhpFpm.
*
* @param null $phpVersion
* @return void
*/
public function install()
public function install($phpVersion = null)
{
if (! $this->brew->hasInstalledPhp()) {
$this->brew->ensureInstalled('php', [], $this->taps);
}

$this->files->ensureDirExists(VALET_HOME_PATH.'/Log', user());

$this->updateConfiguration();
$this->updateConfiguration($phpVersion);

$this->restart();
$this->restart($phpVersion);
}

/**
Expand All @@ -63,13 +64,14 @@ public function uninstall()
/**
* Update the PHP FPM configuration.
*
* @param 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());

Expand All @@ -93,6 +95,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');
Expand All @@ -118,9 +125,9 @@ public function updateConfiguration()
*
* @return void
*/
public function restart()
public function restart($phpVersion = null)
{
$this->brew->restartLinkedPhp();
$this->brew->restartService($phpVersion ?: $this->getPhpVersionsToPerformRestart());
}

/**
Expand All @@ -141,11 +148,13 @@ public function stop()
*
* @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'
Expand All @@ -171,15 +180,16 @@ public function stopRunning()
* Use a specific version of php.
*
* @param $version
* @param $force
* @param bool $force
* @param null $site
* @return string
*/
public function useVersion($version, $force = false)
public function useVersion($version, $force = false, $site = null)
{
$version = $this->validateRequestedVersion($version);

try {
if ($this->brew->linkedPhp() === $version && ! $force) {
if ($this->brew->linkedPhp() === $version && !$force) {
output(sprintf('<info>Valet is already using version: <comment>%s</comment>.</info> To re-link and re-configure use the --force parameter.'.PHP_EOL,
$version));
exit();
Expand All @@ -192,24 +202,40 @@ public function useVersion($version, $force = false)
$this->brew->ensureInstalled($version, [], $this->taps);
}

// Unlink the current php if there is one
if ($this->brew->hasLinkedPhp()) {
$currentVersion = $this->brew->getLinkedPhpFormula();
info(sprintf('Unlinking current version: %s', $currentVersion));
$this->brew->unlink($currentVersion);
}
// we need to unlink and link only for global php version change
if ($site) {
$this->cli->quietly('sudo rm '.VALET_HOME_PATH."/". $this->fpmSockName($version));
$this->install($version);
} else {
// Unlink the current php if there is one
if ($this->brew->hasLinkedPhp()) {
$linkedPhp = $this->brew->linkedPhp();

// updating old fpm to use a custom sock
// so exising lokced php versioned sites doesn't mess up
$this->updateConfiguration($linkedPhp);

// check all custom nginx config files
// look for the php version and maybe delete that custom config
$this->swapConfigForGlobalPhpUpdate($version, $linkedPhp);

info(sprintf('Linking new version: %s', $version));
$this->brew->link($version, true);
$currentVersion = $this->brew->getLinkedPhpFormula();
info(sprintf('Unlinking current version: %s', $currentVersion));
$this->brew->unlink($currentVersion);
}

info(sprintf('Linking new version: %s', $version));
$this->brew->link($version, true);

$this->stopRunning();
$this->stopRunning();

// remove any orphaned valet.sock files that PHP didn't clean up due to version conflicts
$this->files->unlink(VALET_HOME_PATH.'/valet.sock');
$this->cli->quietly('sudo rm '.VALET_HOME_PATH.'/valet.sock');
// remove any orphaned valet.sock files that PHP didn't clean up due to version conflicts
$this->files->unlink(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();
// ensure configuration is correct and start the linked version
$this->install();
}

return $version === 'php' ? $this->brew->determineAliasedVersion($version) : $version;
}
Expand Down Expand Up @@ -257,4 +283,82 @@ public function validateRequestedVersion($version)

return $version;
}

/**
* Get fpm sock file name from a given php version
*
* @param string|null $phpVersion
* @return string
*/
function fpmSockName($phpVersion = null)
{
$versionInteger = preg_replace('~[^\d]~', '', $phpVersion);

return "valet{$versionInteger}.sock";
}

/**
* Preseve exising isolated PHP versioned sites, when running a gloabl php version update
*
* Look for the php version and will adjust that custom nginx config
*
* @param $newPhpVersion
* @param $oldPhpVersion
*/
private function swapConfigForGlobalPhpUpdate($newPhpVersion, $oldPhpVersion)
{
collect($this->files->scandir(VALET_HOME_PATH.'/Nginx'))
->filter(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 the PHP versions to perform restart
*
* @return array
*/
public function getPhpVersionsToPerformRestart()
{
// scan through custom nginx files
// look for config file, that is using custom .sock files (example: php74.sock)
// restart all those PHP FPM though valet
// to make sure all the custom php versioned sites keep running

$fpmSockFiles = $this->brew->supportedPhpVersions()->map(function ($version) {
return $this->fpmSockName($this->normalizePhpVersion($version));
})->unique();

return collect($this->files->scandir(VALET_HOME_PATH.'/Nginx'))
->filter(function ($file) {
return !starts_with($file, '.');
})
->map(function ($file) use ($fpmSockFiles) {
$content = $this->files->get(VALET_HOME_PATH.'/Nginx/'.$file);

foreach ($fpmSockFiles as $sock) {
if (strpos($content, $sock) !== false) {
// find the PHP version number from .sock path
// valet74.sock will outout php74
$phpVersion = 'php'.str_replace(['valet', '.sock'], '', $sock);
return $this->normalizePhpVersion($phpVersion); // example output [email protected]
}
}
})->merge([$this->brew->getLinkedPhpFormula()])->filter()->unique()->toArray();
}
}
Loading

0 comments on commit 5c865d3

Please sign in to comment.