diff --git a/cli/Valet/Site.php b/cli/Valet/Site.php index 7922c8593..5277ffacf 100644 --- a/cli/Valet/Site.php +++ b/cli/Valet/Site.php @@ -7,6 +7,7 @@ class Site { + public $brew; public $config; public $cli; public $files; @@ -14,12 +15,14 @@ class Site /** * Create a new Site instance. * + * @param Brew $brew * @param Configuration $config * @param CommandLine $cli * @param Filesystem $files */ - public function __construct(Configuration $config, CommandLine $cli, Filesystem $files) + public function __construct(Brew $brew, Configuration $config, CommandLine $cli, Filesystem $files) { + $this->brew = $brew; $this->cli = $cli; $this->files = $files; $this->config = $config; @@ -317,12 +320,14 @@ public function getSites($path, $certs) })->map(function ($path, $site) use ($certs, $config) { $secured = $certs->has($site); $url = ($secured ? 'https' : 'http').'://'.$site.'.'.$config['tld']; + $phpVersion = $this->getPhpVersion($site.'.'.$config['tld']); return [ 'site' => $site, 'secured' => $secured ? ' X' : '', 'url' => $url, 'path' => $path, + 'phpVersion' => $phpVersion, ]; }); } @@ -356,6 +361,23 @@ public function pruneLinks() $this->files->removeBrokenLinksAt($this->sitesPath()); } + /** + * Get the PHP version for the given site. + * + * @param string $url Site URL including the TLD + * @return string + */ + public function getPhpVersion($url) + { + $defaultPhpVersion = $this->brew->linkedPhp(); + $phpVersion = PhpFpm::normalizePhpVersion($this->customPhpVersion($url)); + if (empty($phpVersion)) { + $phpVersion = PhpFpm::normalizePhpVersion($defaultPhpVersion); + } + + return $phpVersion; + } + /** * Resecure all currently secured sites with a fresh configuration. * diff --git a/cli/valet.php b/cli/valet.php index 58deaa882..aed7a7c45 100755 --- a/cli/valet.php +++ b/cli/valet.php @@ -166,7 +166,7 @@ $app->command('links', function () { $links = Site::links(); - table(['Site', 'SSL', 'URL', 'Path'], $links->all()); + table(['Site', 'SSL', 'URL', 'Path', 'PHP Version'], $links->all()); })->descriptions('Display all of the registered Valet links'); /** diff --git a/tests/PhpFpmTest.php b/tests/PhpFpmTest.php index 3b0bf1196..025886118 100644 --- a/tests/PhpFpmTest.php +++ b/tests/PhpFpmTest.php @@ -425,6 +425,8 @@ public function test_un_isolate_will_remove_isolation_for_a_site() public function test_isolate_will_throw_if_site_is_not_parked_or_linked() { $brewMock = Mockery::mock(Brew::class); + $brewMock->shouldReceive('linkedPhp')->andReturn('php@7.4'); + $configMock = Mockery::mock(Configuration::class); $configMock->shouldReceive('read')->andReturn(['tld' => 'jamble', 'paths' => []]); diff --git a/tests/SiteTest.php b/tests/SiteTest.php index 7899b0707..9c884aa1f 100644 --- a/tests/SiteTest.php +++ b/tests/SiteTest.php @@ -1,6 +1,7 @@ shouldReceive('ensureDirExists') ->once() ->with($dirPath, user()); + $files->shouldReceive('exists')->andReturn(false); $config = Mockery::mock(Configuration::class); $config->shouldReceive('read') ->once() ->andReturn(['tld' => 'local']); + $brew = Mockery::mock(Brew::class); + $brew->shouldReceive('linkedPhp')->andReturn('php@8.1'); + swap(Filesystem::class, $files); swap(Configuration::class, $config); + swap(Brew::class, $brew); /** @var Site $site */ $site = resolve(Site::class); + $phpVersion = $site->brew->linkedPhp(); + $certs = Mockery::mock(\Illuminate\Support\Collection::class); $certs->shouldReceive('has') ->twice() @@ -96,12 +104,14 @@ public function test_get_sites_will_return_if_secured() 'secured' => '', 'url' => 'http://sitetwo.local', 'path' => $dirPath.'/sitetwo', + 'phpVersion' => $phpVersion, ], $sites->first()); $this->assertSame([ 'site' => 'sitethree', 'secured' => ' X', 'url' => 'https://sitethree.local', 'path' => $dirPath.'/sitethree', + 'phpVersion' => $phpVersion, ], $sites->last()); } @@ -125,18 +135,25 @@ public function test_get_sites_will_work_with_non_symlinked_path() $files->shouldReceive('ensureDirExists') ->once() ->with($dirPath, user()); + $files->shouldReceive('exists')->andReturn(false); $config = Mockery::mock(Configuration::class); $config->shouldReceive('read') ->once() ->andReturn(['tld' => 'local']); + $brew = Mockery::mock(Brew::class); + $brew->shouldReceive('linkedPhp')->andReturn('php@8.1'); + swap(Filesystem::class, $files); swap(Configuration::class, $config); + swap(Brew::class, $brew); /** @var Site $site */ $site = resolve(Site::class); + $phpVersion = $site->brew->linkedPhp(); + $sites = $site->getSites($dirPath, collect()); $this->assertCount(1, $sites); $this->assertSame([ @@ -144,6 +161,7 @@ public function test_get_sites_will_work_with_non_symlinked_path() 'secured' => '', 'url' => 'http://sitetwo.local', 'path' => $dirPath.'/sitetwo', + 'phpVersion' => $phpVersion, ], $sites->first()); } @@ -162,18 +180,25 @@ public function test_get_sites_will_not_return_if_path_is_not_directory() $files->shouldReceive('ensureDirExists') ->once() ->with($dirPath, user()); + $files->shouldReceive('exists')->andReturn(false); $config = Mockery::mock(Configuration::class); $config->shouldReceive('read') ->once() ->andReturn(['tld' => 'local']); + $brew = Mockery::mock(Brew::class); + $brew->shouldReceive('linkedPhp')->andReturn('php@8.1'); + swap(Filesystem::class, $files); swap(Configuration::class, $config); + swap(Brew::class, $brew); /** @var Site $site */ $site = resolve(Site::class); + $phpVersion = $site->brew->linkedPhp(); + $sites = $site->getSites($dirPath, collect()); $this->assertCount(1, $sites); $this->assertSame([ @@ -181,6 +206,7 @@ public function test_get_sites_will_not_return_if_path_is_not_directory() 'secured' => '', 'url' => 'http://siteone.local', 'path' => $dirPath.'/siteone', + 'phpVersion' => $phpVersion, ], $sites->first()); } @@ -204,18 +230,25 @@ public function test_get_sites_will_work_with_symlinked_path() $files->shouldReceive('ensureDirExists') ->once() ->with($dirPath, user()); + $files->shouldReceive('exists')->andReturn(false); $config = Mockery::mock(Configuration::class); $config->shouldReceive('read') ->once() ->andReturn(['tld' => 'local']); + $brew = Mockery::mock(Brew::class); + $brew->shouldReceive('linkedPhp')->andReturn('php@8.1'); + swap(Filesystem::class, $files); swap(Configuration::class, $config); + swap(Brew::class, $brew); /** @var Site $site */ $site = resolve(Site::class); + $phpVersion = $site->brew->linkedPhp(); + $sites = $site->getSites($dirPath, collect()); $this->assertCount(1, $sites); $this->assertSame([ @@ -223,6 +256,7 @@ public function test_get_sites_will_work_with_symlinked_path() 'secured' => '', 'url' => 'http://siteone.local', 'path' => $linkedPath, + 'phpVersion' => $phpVersion, ], $sites->first()); } @@ -534,6 +568,7 @@ public function test_gets_site_url_from_directory() swap(Configuration::class, $config); $siteMock = Mockery::mock(Site::class, [ + resolve(Brew::class), resolve(Configuration::class), resolve(CommandLine::class), resolve(Filesystem::class), @@ -585,6 +620,7 @@ public function test_it_throws_getting_nonexistent_site() swap(Configuration::class, $config); $siteMock = Mockery::mock(Site::class, [ + resolve(Brew::class), resolve(Configuration::class), resolve(CommandLine::class), resolve(Filesystem::class), @@ -609,6 +645,7 @@ public function test_isolation_will_persist_when_adding_ssl_certificate() $config = Mockery::mock(Configuration::class); $siteMock = Mockery::mock(Site::class, [ + resolve(Brew::class), $config, Mockery::mock(CommandLine::class), $files, @@ -641,6 +678,7 @@ public function test_isolation_will_persist_when_removing_ssl_certificate() $cli = Mockery::mock(CommandLine::class); $siteMock = Mockery::mock(Site::class, [ + resolve(Brew::class), $config, $cli, $files, @@ -662,12 +700,40 @@ public function test_isolation_will_persist_when_removing_ssl_certificate() resolve(Site::class)->unsecure('site2.test'); } + public function test_php_version_returns_correct_version_for_site() + { + $files = Mockery::mock(Filesystem::class); + $files->shouldReceive('exists')->andReturn(false); + + $brew = Mockery::mock(Brew::class); + $brew->shouldReceive('linkedPhp')->andReturn('php@8.1'); + + swap(Brew::class, $brew); + + $site = Mockery::mock(Site::class, [ + resolve(Brew::class), + Mockery::mock(Configuration::class), + Mockery::mock(CommandLine::class), + $files, + ])->makePartial(); + $site->shouldReceive('customPhpVersion')->with('site1.test')->andReturn('73')->once(); + $site->shouldReceive('customPhpVersion')->with('site2.test')->andReturn(null)->once(); + + swap(Site::class, $site); + + $phpVersion = $site->brew->linkedPhp(); + + $this->assertEquals('php@7.3', $site->getPhpVersion('site1.test')); + $this->assertEquals($phpVersion, $site->getPhpVersion('site2.test')); + } + public function test_can_install_nginx_site_config_for_specific_php_version() { $files = Mockery::mock(Filesystem::class); $config = Mockery::mock(Configuration::class); $siteMock = Mockery::mock(Site::class, [ + resolve(Brew::class), $config, resolve(CommandLine::class), $files, @@ -719,6 +785,7 @@ public function test_it_removes_isolation() $files = Mockery::mock(Filesystem::class); $siteMock = Mockery::mock(Site::class, [ + resolve(Brew::class), resolve(Configuration::class), resolve(CommandLine::class), $files, @@ -744,6 +811,7 @@ public function test_retrieves_custom_php_version_from_nginx_config() $files = Mockery::mock(Filesystem::class); $siteMock = Mockery::mock(Site::class, [ + resolve(Brew::class), resolve(Configuration::class), resolve(CommandLine::class), $files, @@ -829,6 +897,7 @@ public function test_it_can_read_php_rc_version() swap(Filesystem::class, $files); $siteMock = Mockery::mock(Site::class, [ + resolve(Brew::class), resolve(Configuration::class), resolve(CommandLine::class), resolve(Filesystem::class),