1
0
mirror of https://github.com/laravel/valet.git synced 2026-02-04 08:10:07 +01:00

Merge branch 'master' into feature/support-parked-domains

This commit is contained in:
Matt Stauffer
2022-03-31 10:52:18 -04:00
committed by GitHub
56 changed files with 2279 additions and 774 deletions

View File

@@ -11,5 +11,5 @@ trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.yml]
[*.{yml,yaml}]
indent_size = 2

14
.gitattributes vendored
View File

@@ -1,10 +1,22 @@
* text=auto
*.blade.php diff=html
*.css diff=css
*.html diff=html
*.md diff=markdown
*.php diff=php
/.github export-ignore
/art export-ignore
/tests export-ignore
.editorconfig export-ignore
.gitattributes export-ignore
.gitignore export-ignore
.travis.yml export-ignore
.styleci.yml export-ignore
phpunit.xml export-ignore
CHANGELOG.md export-ignore
phpunit.xml.dist export-ignore
UPGRADE.md export-ignore
/bin/ngrok -diff
/bin/ngrok-arm -diff

3
.github/CODE_OF_CONDUCT.md vendored Normal file
View File

@@ -0,0 +1,3 @@
# Code of Conduct
The Laravel Code of Conduct can be found in the [Laravel documentation](https://laravel.com/docs/contributions#code-of-conduct).

3
.github/CONTRIBUTING.md vendored Normal file
View File

@@ -0,0 +1,3 @@
# Contribution Guide
The Laravel contributing guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).

24
.github/ISSUE_TEMPLATE/1_Bug_report.md vendored Normal file
View File

@@ -0,0 +1,24 @@
---
name: "Bug report"
about: 'Report a general library issue. Please ensure your version is still supported: https://laravel.com/docs/releases#support-policy'
---
<!-- DID YOU KNOW? MOST PROBLEMS CAN BE RESOLVED BY RUNNING 3 COMMANDS:
`composer self-update`
`composer global update`
`brew upgrade`
... in fact it's good to run these commands at least once a month! -->
- Valet Version: #.#.#
- PHP Version: #.#.#
### Description:
### Steps To Reproduce:
### Diagnosis
<!-- Please upgrade to Valet 2.10 or greater, then run "valet diagnose", and then
paste the output here. -->

11
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,11 @@
blank_issues_enabled: false
contact_links:
- name: Feature request
url: https://github.com/laravel/valet/discussions
about: 'For ideas or feature requests, start a new discussion'
- name: Support question
url: https://github.com/laravel/valet/discussions
about: 'This repository is only for reporting bugs. For support, start a new discussion'
- name: Documentation issue
url: https://github.com/laravel/docs
about: For documentation issues, open a pull request at the laravel/docs repository

9
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,9 @@
<!--
Please only send a pull request to branches which are currently supported: https://laravel.com/docs/releases#support-policy
If you are unsure which branch your pull request should be sent to, please read: https://laravel.com/docs/contributions#which-branch
Pull requests without a descriptive title, thorough description, or tests will be closed.
In addition, please describe the benefit to end users; the reasons it does not break any existing features; how it makes building web applications easier, etc.
-->

92
.github/SECURITY.md vendored Normal file
View File

@@ -0,0 +1,92 @@
# Security Policy
**PLEASE DON'T DISCLOSE SECURITY-RELATED ISSUES PUBLICLY, [SEE BELOW](#reporting-a-vulnerability).**
## Supported Versions
Only the latest major version receives security fixes.
## Reporting a Vulnerability
If you discover a security vulnerability within Laravel, please send an email to Taylor Otwell at taylor@laravel.com. All security vulnerabilities will be promptly addressed.
### Public PGP Key
```
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: OpenPGP v2.0.8
Comment: https://sela.io/pgp/
xsFNBFugFSQBEACxEKhIY9IoJzcouVTIYKJfWFGvwFgbRjQWBiH3QdHId5vCrbWo
s2l+4Rv03gMG+yHLJ3rWElnNdRaNdQv59+lShrZF7Bvu7Zvc0mMNmFOM/mQ/K2Lt
OK/8bh6iwNNbEuyOhNQlarEy/w8hF8Yf55hBeu/rajGtcyURJDloQ/vNzcx4RWGK
G3CLr8ka7zPYIjIFUvHLt27mcYFF9F4/G7b4HKpn75ICKC4vPoQSaYNAHlHQBLFb
Jg/WPl93SySHLugU5F58sICs+fBZadXYQG5dWmbaF5OWB1K2XgRs45BQaBzf/8oS
qq0scN8wVhAdBeYlVFf0ImDOxGlZ2suLK1BKJboR6zCIkBAwufKss4NV1R9KSUMv
YGn3mq13PGme0QoIkvQkua5VjTwWfQx7wFDxZ3VQSsjIlbVyRL/Ac/hq71eAmiIR
t6ZMNMPFpuSwBfYimrXqqb4EOffrfsTzRenG1Cxm4jNZRzX/6P4an7F/euoqXeXZ
h37TiC7df+eHKcBI4mL+qOW4ibBqg8WwWPJ+jvuhANyQpRVzf3NNEOwJYCNbQPM/
PbqYvMruAH+gg7uyu9u0jX3o/yLSxJMV7kF4x/SCDuBKIxSSUI4cgbjIlnxWLXZC
wl7KW4xAKkerO3wgIPnxNfxQoiYiEKA1c3PShWRA0wHIMt3rVRJxwGM4CwARAQAB
zRJ0YXlsb3JAbGFyYXZlbC5jb23CwXAEEwEKABoFAlugFSQCGy8DCwkHAxUKCAIe
AQIXgAIZAQAKCRDKAI7r/Ml7Zo0SD/9zwu9K87rbqXbvZ3TVu7TnN+z7mPvVBzl+
SFEK360TYq8a4GosghZuGm4aNEyZ90CeUjPQwc5fHwa26tIwqgLRppsG21B/mZwu
0m8c5RaBFRFX/mCTEjlpvBkOwMJZ8f05nNdaktq6W98DbMN03neUwnpWlNSLeoNI
u4KYZmJopNFLEax5WGaaDpmqD1J+WDr/aPHx39MUAg2ZVuC3Gj/IjYZbD1nCh0xD
a09uDODje8a9uG33cKRBcKKPRLZjWEt5SWReLx0vsTuqJSWhCybHRBl9BQTc/JJR
gJu5V4X3f1IYMTNRm9GggxcXrlOAiDCjE2J8ZTUt0cSxedQFnNyGfKxe/l94oTFP
wwFHbdKhsSDZ1OyxPNIY5OHlMfMvvJaNbOw0xPPAEutPwr1aqX9sbgPeeiJwAdyw
mPw2x/wNQvKJITRv6atw56TtLxSevQIZGPHCYTSlsIoi9jqh9/6vfq2ruMDYItCq
+8uzei6TyH6w+fUpp/uFmcwZdrDwgNVqW+Ptu+pD2WmthqESF8UEQVoOv7OPgA5E
ofOMaeH2ND74r2UgcXjPxZuUp1RkhHE2jJeiuLtbvOgrWwv3KOaatyEbVl+zHA1e
1RHdJRJRPK+S7YThxxduqfOBX7E03arbbhHdS1HKhPwMc2e0hNnQDoNxQcv0GQp4
2Y6UyCRaus7ATQRboBUkAQgA0h5j3EO2HNvp8YuT1t/VF00uUwbQaz2LIoZogqgC
14Eb77diuIPM9MnuG7bEOnNtPVMFXxI5UYBIlzhLMxf7pfbrsoR4lq7Ld+7KMzdm
eREqJRgUNfjZhtRZ9Z+jiFPr8AGpYxwmJk4v387uQGh1GC9JCc3CCLJoI62I9t/1
K2b25KiOzW/FVZ/vYFj1WbISRd5GqS8SEFh4ifU79LUlJ/nEsFv4JxAXN9RqjU0e
H4S/m1Nb24UCtYAv1JKymcf5O0G7kOzvI0w06uKxk0hNwspjDcOebD8Vv9IdYtGl
0bn7PpBlVO1Az3s8s6Xoyyw+9Us+VLNtVka3fcrdaV/n0wARAQABwsKEBBgBCgAP
BQJboBUkBQkPCZwAAhsuASkJEMoAjuv8yXtmwF0gBBkBCgAGBQJboBUkAAoJEA1I
8aTLtYHmjpIH/A1ZKwTGetHFokJxsd2omvbqv+VtpAjnUbvZEi5g3yZXn+dHJV+K
UR/DNlfGxLWEcY6datJ3ziNzzD5u8zcPp2CqeTlCxOky8F74FjEL9tN/EqUbvvmR
td2LXsSFjHnLJRK5lYfZ3rnjKA5AjqC9MttILBovY2rI7lyVt67kbS3hMHi8AZl8
EgihnHRJxGZjEUxyTxcB13nhfjAvxQq58LOj5754Rpe9ePSKbT8DNMjHbGpLrESz
cmyn0VzDMLfxg8AA9uQFMwdlKqve7yRZXzeqvy08AatUpJaL7DsS4LKOItwvBub6
tHbCE3mqrUw5lSNyUahO3vOcMAHnF7fd4W++eA//WIQKnPX5t3CwCedKn8Qkb3Ow
oj8xUNl2T6kEtQJnO85lKBFXaMOUykopu6uB9EEXEr0ShdunOKX/UdDbkv46F2AB
7TtltDSLB6s/QeHExSb8Jo3qra86JkDUutWdJxV7DYFUttBga8I0GqdPu4yRRoc/
0irVXsdDY9q7jz6l7fw8mSeJR96C0Puhk70t4M1Vg/tu/ONRarXQW7fJ8kl21PcD
UKNWWa242gji/+GLRI8AIpGMsBiX7pHhqmMMth3u7+ne5BZGGJz0uX+CzWboOHyq
kWgfY4a62t3hM0vwnUkl/D7VgSGy4LiKQrapd3LvU2uuEfFsMu3CDicZBRXPqoXj
PBjkkPKhwUTNlwEQrGF3QsZhNe0M9ptM2fC34qtxZtMIMB2NLvE4S621rmQ05oQv
sT0B9WgUL3GYRKdx700+ojHEuwZ79bcLgo1dezvkfPtu/++2CXtieFthDlWHy8x5
XJJjI1pDfGO+BgX0rS3QrQEYlF/uPQynKwxe6cGI62eZ0ug0hNrPvKEcfMLVqBQv
w4VH6iGp9yNKMUOgAECLCs4YCxK+Eka9Prq/Gh4wuqjWiX8m66z8YvKf27sFL3fR
OwGaz3LsnRSxbk/8oSiZuOVLfn44XRcxsHebteZat23lwD93oq54rtKnlJgmZHJY
4vMgk1jpS4laGnvhZj7OwE0EW6AVJAEIAKJSrUvXRyK3XQnLp3Kfj82uj0St8Dt2
h8BMeVbrAbg38wCN8XQZzVR9+bRZRR+aCzpKSqwhEQVtH7gdKgfdNdGNhG2DFAVk
SihMhQz190FKttUZgwY00enzD7uaaA5VwNAZzRIr8skwiASB7UoO+lIhrAYgcQCA
LpwCSMrUNB3gY1IVa2xi9FljEbS2uMABfOsTfl7z4L4T4DRv/ovDf+ihyZOXsXiH
RVoUTIpN8ZILCZiiKubE1sMj4fSQwCs06UyDy17HbOG5/dO9awR/LHW53O3nZCxE
JbCqr5iHa2MdHMC12+luxWJKD9DbVB01LiiPZCTkuKUDswCyi7otpVEAEQEAAcLC
hAQYAQoADwUCW6AVJAUJDwmcAAIbLgEpCRDKAI7r/Ml7ZsBdIAQZAQoABgUCW6AV
JAAKCRDxrCjKN7eORjt2B/9EnKVJ9lwB1JwXcQp6bZgJ21r6ghyXBssv24N9UF+v
5QDz/tuSkTsKK1UoBrBDEinF/xTP2z+xXIeyP4c3mthMHsYdMl7AaGpcCwVJiL62
fZvd+AiYNX3C+Bepwnwoziyhx4uPaqoezSEMD8G2WQftt6Gqttmm0Di5RVysCECF
EyhkHwvCcbpXb5Qq+4XFzCUyaIZuGpe+oeO7U8B1CzOC16hEUu0Uhbk09Xt6dSbS
ZERoxFjrGU+6bk424MkZkKvNS8FdTN2s3kQuHoNmhbMY+fRzKX5JNrcQ4dQQufiB
zFcc2Ba0JVU0nYAMftTeT5ALakhwSqr3AcdD2avJZp3EYfYP/3smPGTeg1cDJV3E
WIlCtSlhbwviUjvWEWJUE+n9MjhoUNU0TJtHIliUYUajKMG/At5wJZTXJaKVUx32
UCWr4ioKfSzlbp1ngBuFlvU7LgZRcKbBZWvKj/KRYpxpfvPyPElmegCjAk6oiZYV
LOV+jFfnMkk9PnR91ZZfTNx/bK+BwjOnO+g7oE8V2g2bA90vHdeSUHR52SnaVN/b
9ytt07R0f+YtyKojuPmlNsbyAaUYUtJ1o+XNCwdVxzarYEuUabhAfDiVTu9n8wTr
YVvnriSFOjNvOY9wdLAa56n7/qM8bzuGpoBS5SilXgJvITvQfWPvg7I9C3QhwK1S
F6B1uquQGbBSze2wlnMbKXmhyGLlv9XpOqpkkejQo3o58B+Sqj4B8DuYixSjoknr
pRbj8gqgqBKlcpf1wD5X9qCrl9vq19asVOHaKhiFZGxZIVbBpBOdvAKaMj4p/uln
yklN3YFIfgmGPYbL0elvXVn7XfvwSV1mCQV5LtMbLHsFf0VsA16UsG8A/tLWtwgt
0antzftRHXb+DI4qr+qEYKFkv9F3oCOXyH4QBhPA42EzKqhMXByEkEK9bu6skioL
mHhDQ7yHjTWcxstqQjkUQ0T/IF9ls+Sm5u7rVXEifpyI7MCb+76kSCDawesvInKt
WBGOG/qJGDlNiqBYYt2xNqzHCJoC
=zXOv
-----END PGP PUBLIC KEY BLOCK-----
```

3
.github/SUPPORT.md vendored Normal file
View File

@@ -0,0 +1,3 @@
# Support Questions
This repository is only for reporting bugs. For support, [start a new discussion](https://github.com/laravel/framework/discussions).

View File

@@ -1,36 +0,0 @@
<!-- Delete this note after reading -->
NOTE: If your issue is a feature request, please feel to delete this template and write your own.
------
<!-- End note -->
<!-- DID YOU KNOW? MOST PROBLEMS CAN BE RESOLVED BY RUNNING 3 COMMANDS:
`composer self-update`
`composer global update`
`brew upgrade`
... in fact it's good to run these commands at least once a month! -->
## Clear description of your problem
<!-- Please provide a general summary of the issue here -->
## Expected behavior
<!-- Tell us what you think should happen -->
## Current behavior
<!-- Tell us what is happening instead -->
## Steps to Reproduce
<!-- Please make it as easy as possible for us to reproduce this bug. -->
1.
2.
3.
### Output of these steps
<!-- Please, for any steps that are helpful, let us know what happened in response to them -->
## Possible solution
<!-- This is not required, but if you have a suggestion for the fix, we're open -->
## Diagnosis
<!-- Please upgrade to Valet 2.10 or greater, then run "valet diagnose", and then
paste the output here. -->

View File

@@ -3,15 +3,17 @@ name: Tests
on:
push:
pull_request:
schedule:
- cron: '0 0 * * *'
jobs:
mac_tests:
tests:
runs-on: macos-latest
strategy:
fail-fast: true
matrix:
php: [5.6, 7.0, 7.1, 7.2, 7.3, 7.4, 8.0]
php: ['7.0', 7.1, 7.2, 7.3, 7.4, '8.0', 8.1]
name: PHP ${{ matrix.php }}
@@ -31,6 +33,6 @@ jobs:
run: composer install --no-interaction --prefer-dist
- name: Execute tests
run: vendor/bin/phpunit --verbose
env:
APP_ENV: testing
run: vendor/bin/phpunit --verbose

1
.gitignore vendored
View File

@@ -4,3 +4,4 @@ composer.lock
error.log
.idea
.phpunit.result.cache
tests/conf.d

4
.styleci.yml Normal file
View File

@@ -0,0 +1,4 @@
php:
preset: laravel
js: true
css: true

View File

@@ -1,4 +1,4 @@
<p align="center"><img src="https://laravel.com/assets/img/components/logo-valet.svg"></p>
<p align="center"><img src="/art/logo.svg"></p>
<p align="center">
<a href="https://github.com/laravel/valet/actions?query=workflow%3ATests"><img src="https://github.com/laravel/valet/workflows/Tests/badge.svg?branch=master" alt="Build Status"></a>
@@ -19,6 +19,18 @@ ## Official Documentation
Documentation for Valet can be found on the [Laravel website](https://laravel.com/docs/valet).
## Contributing
Thank you for considering contributing to Valet! You can read the contribution guide [here](.github/CONTRIBUTING.md).
## Code of Conduct
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
## Security Vulnerabilities
Please review [our security policy](https://github.com/laravel/valet/security/policy) on how to report security vulnerabilities.
## License
Laravel Valet is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).

1
art/logo.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.5 KiB

BIN
bin/ngrok-arm Executable file

Binary file not shown.

View File

@@ -3,6 +3,7 @@
namespace Valet;
use DomainException;
use PhpFpm;
class Brew
{
@@ -15,17 +16,16 @@ class Brew
'php@7.2',
'php@7.1',
'php@7.0',
'php@5.6',
'php73',
'php72',
'php71',
'php70',
'php56'
];
const LATEST_PHP_VERSION = 'php@8.0';
const LATEST_PHP_VERSION = 'php@8.1';
var $cli, $files;
public $cli;
public $files;
/**
* Create a new Brew instance.
@@ -34,19 +34,19 @@ class Brew
* @param Filesystem $files
* @return void
*/
function __construct(CommandLine $cli, Filesystem $files)
public function __construct(CommandLine $cli, Filesystem $files)
{
$this->cli = $cli;
$this->files = $files;
}
/**
* Ensure the formula exists in the current Homebrew configuration
* Ensure the formula exists in the current Homebrew configuration.
*
* @param string $formula
* @return bool
*/
function installed($formula)
public function installed($formula)
{
$result = $this->cli->runAsUser("brew info $formula --json");
@@ -57,7 +57,7 @@ function installed($formula)
$details = json_decode($result);
return !empty($details[0]->installed);
return ! empty($details[0]->installed);
}
/**
@@ -65,13 +65,13 @@ function installed($formula)
*
* @return bool
*/
function hasInstalledPhp()
public function hasInstalledPhp()
{
$installed = $this->installedPhpFormulae()->first(function ($formula) {
return $this->supportedPhpVersions()->contains($formula);
});
return !empty($installed);
return ! empty($installed);
}
/**
@@ -79,12 +79,12 @@ function hasInstalledPhp()
*
* @return \Illuminate\Support\Collection
*/
function supportedPhpVersions()
public function supportedPhpVersions()
{
return collect(static::SUPPORTED_PHP_VERSIONS);
}
function installedPhpFormulae()
public function installedPhpFormulae()
{
return collect(
explode(PHP_EOL, $this->cli->runAsUser('brew list --formula | grep php'))
@@ -92,13 +92,13 @@ function installedPhpFormulae()
}
/**
* Get the aliased formula version from Homebrew
* Get the aliased formula version from Homebrew.
*/
function determineAliasedVersion($formula)
public function determineAliasedVersion($formula)
{
$details = json_decode($this->cli->runAsUser("brew info $formula --json"));
if (!empty($details[0]->aliases[0])) {
if (! empty($details[0]->aliases[0])) {
return $details[0]->aliases[0];
}
@@ -110,7 +110,7 @@ function determineAliasedVersion($formula)
*
* @return bool
*/
function hasInstalledNginx()
public function hasInstalledNginx()
{
return $this->installed('nginx')
|| $this->installed('nginx-full');
@@ -121,7 +121,7 @@ function hasInstalledNginx()
*
* @return string
*/
function nginxServiceName()
public function nginxServiceName()
{
return $this->installed('nginx-full') ? 'nginx-full' : 'nginx';
}
@@ -134,7 +134,7 @@ function nginxServiceName()
* @param array $taps
* @return void
*/
function ensureInstalled($formula, $options = [], $taps = [])
public function ensureInstalled($formula, $options = [], $taps = [])
{
if (! $this->installed($formula)) {
$this->installOrFail($formula, $options, $taps);
@@ -149,7 +149,7 @@ function ensureInstalled($formula, $options = [], $taps = [])
* @param array $taps
* @return void
*/
function installOrFail($formula, $options = [], $taps = [])
public function installOrFail($formula, $options = [], $taps = [])
{
info("Installing {$formula}...");
@@ -175,7 +175,7 @@ function installOrFail($formula, $options = [], $taps = [])
* @param dynamic[string] $formula
* @return void
*/
function tap($formulas)
public function tap($formulas)
{
$formulas = is_array($formulas) ? $formulas : func_get_args();
@@ -189,7 +189,7 @@ function tap($formulas)
*
* @param
*/
function restartService($services)
public function restartService($services)
{
$services = is_array($services) ? $services : func_get_args();
@@ -212,7 +212,7 @@ function restartService($services)
*
* @param
*/
function stopService($services)
public function stopService($services)
{
$services = is_array($services) ? $services : func_get_args();
@@ -225,6 +225,19 @@ function stopService($services)
// stop the sudo version
$this->cli->quietly('sudo brew services stop '.$service);
// restore folder permissions: for each brew formula, these directories are owned by root:admin
$directories = [
BREW_PREFIX."/Cellar/$service",
BREW_PREFIX."/opt/$service",
BREW_PREFIX."/var/homebrew/linked/$service",
];
$whoami = get_current_user();
foreach ($directories as $directory) {
$this->cli->quietly("sudo chown -R {$whoami}:admin '$directory'");
}
}
}
}
@@ -234,7 +247,7 @@ function stopService($services)
*
* @return bool
*/
function hasLinkedPhp()
public function hasLinkedPhp()
{
return $this->files->isLink(BREW_PREFIX.'/bin/php');
}
@@ -244,37 +257,29 @@ function hasLinkedPhp()
*
* @return mixed
*/
function getParsedLinkedPhp()
public function getParsedLinkedPhp()
{
if (! $this->hasLinkedPhp()) {
throw new DomainException("Homebrew PHP appears not to be linked. Please run [valet use php@X.Y]");
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.4/7.4.13/bin/php"
* or older styles:
* "../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);
return $matches;
return $this->parsePhpPath($resolvedPath);
}
/**
* 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
* whether that is php, php74 or php@7.4.
*
* @return string
*/
function getLinkedPhpFormula()
public function getLinkedPhpFormula()
{
$matches = $this->getParsedLinkedPhp();
return $matches[1] . $matches[2];
return $matches[1].$matches[2];
}
/**
@@ -282,19 +287,56 @@ function getLinkedPhpFormula()
*
* @return string
*/
function linkedPhp()
public function linkedPhp()
{
$matches = $this->getParsedLinkedPhp();
$resolvedPhpVersion = $matches[3] ?: $matches[2];
return $this->supportedPhpVersions()->first(
function ($version) use ($resolvedPhpVersion) {
$resolvedVersionNormalized = preg_replace('/[^\d]/', '', $resolvedPhpVersion);
$versionNormalized = preg_replace('/[^\d]/', '', $version);
return $resolvedVersionNormalized === $versionNormalized;
}, function () use ($resolvedPhpVersion) {
throw new DomainException("Unable to determine linked PHP when parsing '$resolvedPhpVersion'");
});
return $this->arePhpVersionsEqual($resolvedPhpVersion, $version);
}, function () use ($resolvedPhpVersion) {
throw new DomainException("Unable to determine linked PHP when parsing '$resolvedPhpVersion'");
});
}
/**
* Extract PHP executable path from PHP Version.
*
* @param string $phpVersion For example, "php@8.1"
* @return string
*/
public function getPhpExecutablePath($phpVersion = null)
{
if (! $phpVersion) {
return BREW_PREFIX.'/bin/php';
}
$phpVersion = PhpFpm::normalizePhpVersion($phpVersion);
// Check the default `/opt/homebrew/opt/php@8.1/bin/php` location first
if ($this->files->exists(BREW_PREFIX."/opt/{$phpVersion}/bin/php")) {
return BREW_PREFIX."/opt/{$phpVersion}/bin/php";
}
// Check the `/opt/homebrew/opt/php71/bin/php` location for older installations
$phpVersion = str_replace(['@', '.'], '', $phpVersion); // php@8.1 to php81
if ($this->files->exists(BREW_PREFIX."/opt/{$phpVersion}/bin/php")) {
return BREW_PREFIX."/opt/{$phpVersion}/bin/php";
}
// Check if the default PHP is the version we are looking for
if ($this->files->isLink(BREW_PREFIX.'/opt/php')) {
$resolvedPath = $this->files->readLink(BREW_PREFIX.'/opt/php');
$matches = $this->parsePhpPath($resolvedPath);
$resolvedPhpVersion = $matches[3] ?: $matches[2];
if ($this->arePhpVersionsEqual($resolvedPhpVersion, $phpVersion)) {
return BREW_PREFIX.'/opt/php/bin/php';
}
}
return BREW_PREFIX.'/bin/php';
}
/**
@@ -302,7 +344,7 @@ function ($version) use ($resolvedPhpVersion) {
*
* @return void
*/
function restartLinkedPhp()
public function restartLinkedPhp()
{
$this->restartService($this->getLinkedPhpFormula());
}
@@ -312,7 +354,7 @@ function restartLinkedPhp()
*
* @return void
*/
function createSudoersEntry()
public function createSudoersEntry()
{
$this->files->ensureDirExists('/etc/sudoers.d');
@@ -325,7 +367,7 @@ function createSudoersEntry()
*
* @return void
*/
function removeSudoersEntry()
public function removeSudoersEntry()
{
$this->cli->quietly('rm /etc/sudoers.d/brew');
}
@@ -334,55 +376,92 @@ function removeSudoersEntry()
* Link passed formula.
*
* @param $formula
* @param bool $force
*
* @param bool $force
* @return string
*/
function link($formula, $force = false)
public function link($formula, $force = false)
{
return $this->cli->runAsUser(
sprintf('brew link %s%s', $formula, $force ? ' --force': ''),
sprintf('brew link %s%s', $formula, $force ? ' --force' : ''),
function ($exitCode, $errorOutput) use ($formula) {
output($errorOutput);
throw new DomainException('Brew was unable to link [' . $formula . '].');
throw new DomainException('Brew was unable to link ['.$formula.'].');
}
);
}
/**
* Unlink passed formula.
* @param $formula
*
* @param $formula
* @return string
*/
function unlink($formula)
public function unlink($formula)
{
return $this->cli->runAsUser(
sprintf('brew unlink %s', $formula),
function ($exitCode, $errorOutput) use ($formula) {
output($errorOutput);
throw new DomainException('Brew was unable to unlink [' . $formula . '].');
throw new DomainException('Brew was unable to unlink ['.$formula.'].');
}
);
}
/**
* Get the currently running brew services.
* Get all the currently running brew services.
*
* @return \Illuminate\Support\Collection
*/
function getRunningServices()
public function getAllRunningServices()
{
return collect(array_filter(explode(PHP_EOL, $this->cli->runAsUser(
'brew services list | grep started | awk \'{ print $1; }\'',
function ($exitCode, $errorOutput) {
output($errorOutput);
return $this->getRunningServicesAsRoot()
->concat($this->getRunningServicesAsUser())
->unique();
}
throw new DomainException('Brew was unable to check which services are running.');
}
))));
/**
* Get the currently running brew services as root.
* i.e. /Library/LaunchDaemons (started at boot).
*
* @return \Illuminate\Support\Collection
*/
public function getRunningServicesAsRoot()
{
return $this->getRunningServices();
}
/**
* Get the currently running brew services.
* i.e. ~/Library/LaunchAgents (started at login).
*
* @return \Illuminate\Support\Collection
*/
public function getRunningServicesAsUser()
{
return $this->getRunningServices(true);
}
/**
* Get the currently running brew services.
*
* @param bool $asUser
* @return \Illuminate\Support\Collection
*/
public function getRunningServices($asUser = false)
{
$command = 'brew services list | grep started | awk \'{ print $1; }\'';
$onError = function ($exitCode, $errorOutput) {
output($errorOutput);
throw new DomainException('Brew was unable to check which services are running.');
};
return collect(array_filter(explode(PHP_EOL, $asUser
? $this->cli->runAsUser($command, $onError)
: $this->cli->run('sudo '.$command, $onError)
)));
}
/**
@@ -390,7 +469,7 @@ function ($exitCode, $errorOutput) {
*
* @return string
*/
function uninstallAllPhpVersions()
public function uninstallAllPhpVersions()
{
$this->supportedPhpVersions()->each(function ($formula) {
$this->uninstallFormula($formula);
@@ -401,11 +480,11 @@ function uninstallAllPhpVersions()
/**
* Uninstall a Homebrew app by formula name.
* @param string $formula
*
* @param string $formula
* @return void
*/
function uninstallFormula($formula)
public function uninstallFormula($formula)
{
$this->cli->runAsUser('brew uninstall --force '.$formula);
$this->cli->run('rm -rf '.BREW_PREFIX.'/Cellar/'.$formula);
@@ -416,7 +495,7 @@ function uninstallFormula($formula)
*
* @return string
*/
function cleanupBrew()
public function cleanupBrew()
{
return $this->cli->runAsUser(
'brew cleanup && brew services cleanup',
@@ -425,4 +504,39 @@ function ($exitCode, $errorOutput) {
}
);
}
/**
* Parse homebrew PHP Path.
*
* @param string $resolvedPath
* @return array
*/
public function parsePhpPath($resolvedPath)
{
/**
* Typical homebrew path resolutions are like:
* "../Cellar/php@7.4/7.4.13/bin/php"
* or older styles:
* "../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);
return $matches;
}
/**
* Check if two PHP versions are equal.
*
* @param string $versionA
* @param string $versionB
* @return bool
*/
public function arePhpVersionsEqual($versionA, $versionB)
{
$versionANormalized = preg_replace('/[^\d]/', '', $versionA);
$versionBNormalized = preg_replace('/[^\d]/', '', $versionB);
return $versionANormalized === $versionBNormalized;
}
}

View File

@@ -12,7 +12,7 @@ class CommandLine
* @param string $command
* @return void
*/
function quietly($command)
public function quietly($command)
{
$this->runCommand($command.' > /dev/null 2>&1');
}
@@ -23,7 +23,7 @@ function quietly($command)
* @param string $command
* @return void
*/
function quietlyAsUser($command)
public function quietlyAsUser($command)
{
$this->quietly('sudo -u "'.user().'" '.$command.' > /dev/null 2>&1');
}
@@ -34,7 +34,7 @@ function quietlyAsUser($command)
* @param string $command
* @return void
*/
function passthru($command)
public function passthru($command)
{
passthru($command);
}
@@ -43,10 +43,10 @@ function passthru($command)
* Run the given command as the non-root user.
*
* @param string $command
* @param callable $onError
* @param callable $onError
* @return string
*/
function run($command, callable $onError = null)
public function run($command, callable $onError = null)
{
return $this->runCommand($command, $onError);
}
@@ -55,10 +55,10 @@ function run($command, callable $onError = null)
* Run the given command.
*
* @param string $command
* @param callable $onError
* @param callable $onError
* @return string
*/
function runAsUser($command, callable $onError = null)
public function runAsUser($command, callable $onError = null)
{
return $this->runCommand('sudo -u "'.user().'" '.$command, $onError);
}
@@ -67,12 +67,13 @@ function runAsUser($command, callable $onError = null)
* Run the given command.
*
* @param string $command
* @param callable $onError
* @param callable $onError
* @return string
*/
function runCommand($command, callable $onError = null)
public function runCommand($command, callable $onError = null)
{
$onError = $onError ?: function () {};
$onError = $onError ?: function () {
};
// Symfony's 4.x Process component has deprecated passing a command string
// to the constructor, but older versions (which Valet's Composer

View File

@@ -4,14 +4,14 @@
class Configuration
{
var $files;
public $files;
/**
* Create a new Valet configuration class instance.
*
* @param Filesystem $files
* @param Filesystem $files
*/
function __construct(Filesystem $files)
public function __construct(Filesystem $files)
{
$this->files = $files;
}
@@ -21,7 +21,7 @@ function __construct(Filesystem $files)
*
* @return void
*/
function install()
public function install()
{
$this->createConfigurationDirectory();
$this->createDriversDirectory();
@@ -39,7 +39,7 @@ function install()
*
* @return void
*/
function uninstall()
public function uninstall()
{
$this->files->unlink(VALET_HOME_PATH);
}
@@ -49,7 +49,7 @@ function uninstall()
*
* @return void
*/
function createConfigurationDirectory()
public function createConfigurationDirectory()
{
$this->files->ensureDirExists(preg_replace('~/valet$~', '', VALET_HOME_PATH), user());
@@ -68,7 +68,7 @@ function createConfigurationDirectory()
*
* @return void
*/
function createDriversDirectory()
public function createDriversDirectory()
{
if ($this->files->isDir($driversDirectory = VALET_HOME_PATH.'/Drivers')) {
return;
@@ -87,7 +87,7 @@ function createDriversDirectory()
*
* @return void
*/
function createSitesDirectory()
public function createSitesDirectory()
{
$this->files->ensureDirExists(VALET_HOME_PATH.'/Sites', user());
}
@@ -97,7 +97,7 @@ function createSitesDirectory()
*
* @return void
*/
function createExtensionsDirectory()
public function createExtensionsDirectory()
{
$this->files->ensureDirExists(VALET_HOME_PATH.'/Extensions', user());
}
@@ -107,7 +107,7 @@ function createExtensionsDirectory()
*
* @return void
*/
function createLogDirectory()
public function createLogDirectory()
{
$this->files->ensureDirExists(VALET_HOME_PATH.'/Log', user());
@@ -119,7 +119,7 @@ function createLogDirectory()
*
* @return void
*/
function createCertificatesDirectory()
public function createCertificatesDirectory()
{
$this->files->ensureDirExists(VALET_HOME_PATH.'/Certificates', user());
}
@@ -127,19 +127,19 @@ function createCertificatesDirectory()
/**
* Write the base, initial configuration for Valet.
*/
function writeBaseConfiguration()
public function writeBaseConfiguration()
{
if (! $this->files->exists($this->path())) {
$this->write(['tld' => 'test', 'loopback' => VALET_LOOPBACK, 'paths' => []]);
}
/**
* Migrate old configurations from 'domain' to 'tld'
* Migrate old configurations from 'domain' to 'tld'.
*/
$config = $this->read();
if (! isset($config['tld'])) {
$this->updateKey('tld', !empty($config['domain']) ? $config['domain'] : 'test');
$this->updateKey('tld', ! empty($config['domain']) ? $config['domain'] : 'test');
}
if (! isset($config['loopback'])) {
@@ -154,7 +154,7 @@ function writeBaseConfiguration()
* @param bool $prepend
* @return void
*/
function addPath($path, $prepend = false)
public function addPath($path, $prepend = false)
{
$this->write(tap($this->read(), function (&$config) use ($path, $prepend) {
$method = $prepend ? 'prepend' : 'push';
@@ -169,7 +169,7 @@ function addPath($path, $prepend = false)
* @param string $path
* @return void
*/
function prependPath($path)
public function prependPath($path)
{
$this->addPath($path, true);
}
@@ -180,11 +180,11 @@ function prependPath($path)
* @param string $path
* @return void
*/
function removePath($path)
public function removePath($path)
{
if ($path == VALET_HOME_PATH.'/Sites') {
info("Cannot remove this directory because this is where Valet stores its site definitions.\nRun [valet paths] for a list of parked paths.");
die();
exit();
}
$this->write(tap($this->read(), function (&$config) use ($path) {
@@ -199,7 +199,7 @@ function removePath($path)
*
* @return void
*/
function prune()
public function prune()
{
if (! $this->files->exists($this->path())) {
return;
@@ -217,7 +217,7 @@ function prune()
*
* @return array
*/
function read()
public function read()
{
return json_decode($this->files->get($this->path()), true);
}
@@ -229,7 +229,7 @@ function read()
* @param mixed $value
* @return array
*/
function updateKey($key, $value)
public function updateKey($key, $value)
{
return tap($this->read(), function (&$config) use ($key, $value) {
$config[$key] = $value;
@@ -244,7 +244,7 @@ function updateKey($key, $value)
* @param array $config
* @return void
*/
function write($config)
public function write($config)
{
$this->files->putAsUser($this->path(), json_encode(
$config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES
@@ -256,7 +256,7 @@ function write($config)
*
* @return string
*/
function path()
public function path()
{
return VALET_HOME_PATH.'/config.json';
}

View File

@@ -7,7 +7,7 @@
class Diagnose
{
var $commands = [
public $commands = [
'sw_vers',
'valet --version',
'cat ~/.config/valet/config.json',
@@ -28,6 +28,7 @@ class Diagnose
'curl --version',
'php --ri curl',
'~/.composer/vendor/laravel/valet/bin/ngrok version',
'~/.composer/vendor/laravel/valet/bin/ngrok-arm version',
'ls -al ~/.ngrok2',
'brew info nginx',
'brew info php',
@@ -50,7 +51,10 @@ class Diagnose
'sh -c \'for file in ~/.config/valet/nginx/*; do echo "------\n~/.config/valet/nginx/$(basename $file)\n---\n"; cat $file | grep -n "# valet loopback"; echo "\n------\n"; done\'',
];
var $cli, $files, $print, $progressBar;
public $cli;
public $files;
public $print;
public $progressBar;
/**
* Create a new Diagnose instance.
@@ -59,7 +63,7 @@ class Diagnose
* @param Filesystem $files
* @return void
*/
function __construct(CommandLine $cli, Filesystem $files)
public function __construct(CommandLine $cli, Filesystem $files)
{
$this->cli = $cli;
$this->files = $files;
@@ -68,7 +72,7 @@ function __construct(CommandLine $cli, Filesystem $files)
/**
* Run diagnostics.
*/
function run($print, $plainText)
public function run($print, $plainText)
{
$this->print = $print;
@@ -79,7 +83,9 @@ function run($print, $plainText)
$output = $this->runCommand($command);
if ($this->ignoreOutput($command)) return;
if ($this->ignoreOutput($command)) {
return;
}
$this->afterCommand($command, $output);
@@ -97,7 +103,7 @@ function run($print, $plainText)
$this->afterRun();
}
function beforeRun()
public function beforeRun()
{
if ($this->print) {
return;
@@ -108,7 +114,7 @@ function beforeRun()
$this->progressBar->start();
}
function afterRun()
public function afterRun()
{
if ($this->progressBar) {
$this->progressBar->finish();
@@ -117,21 +123,21 @@ function afterRun()
output('');
}
function runCommand($command)
public function runCommand($command)
{
return strpos($command, 'sudo ') === 0
? $this->cli->run($command)
: $this->cli->runAsUser($command);
}
function beforeCommand($command)
public function beforeCommand($command)
{
if ($this->print) {
info(PHP_EOL."$ $command");
}
}
function afterCommand($command, $output)
public function afterCommand($command, $output)
{
if ($this->print) {
output(trim($output));
@@ -140,12 +146,12 @@ function afterCommand($command, $output)
}
}
function ignoreOutput($command)
public function ignoreOutput($command)
{
return strpos($command, '> /dev/null 2>&1') !== false;
}
function format($results, $plainText)
public function format($results, $plainText)
{
return $results->map(function ($result) use ($plainText) {
$command = $result['command'];

View File

@@ -4,16 +4,19 @@
class DnsMasq
{
var $brew, $cli, $files, $configuration;
public $brew;
public $cli;
public $files;
public $configuration;
var $dnsmasqMasterConfigFile = BREW_PREFIX.'/etc/dnsmasq.conf';
var $dnsmasqSystemConfDir = BREW_PREFIX.'/etc/dnsmasq.d';
var $resolverPath = '/etc/resolver';
public $dnsmasqMasterConfigFile = BREW_PREFIX.'/etc/dnsmasq.conf';
public $dnsmasqSystemConfDir = BREW_PREFIX.'/etc/dnsmasq.d';
public $resolverPath = '/etc/resolver';
/**
* Create a new DnsMasq instance.
*/
function __construct(Brew $brew, CommandLine $cli, Filesystem $files, Configuration $configuration)
public function __construct(Brew $brew, CommandLine $cli, Filesystem $files, Configuration $configuration)
{
$this->cli = $cli;
$this->brew = $brew;
@@ -26,7 +29,7 @@ function __construct(Brew $brew, CommandLine $cli, Filesystem $files, Configurat
*
* @return void
*/
function install($tld = 'test')
public function install($tld = 'test')
{
$this->brew->ensureInstalled('dnsmasq');
@@ -49,7 +52,7 @@ function install($tld = 'test')
*
* @return void
*/
function uninstall()
public function uninstall()
{
$this->brew->stopService('dnsmasq');
$this->brew->uninstallFormula('dnsmasq');
@@ -59,21 +62,21 @@ function uninstall()
}
/**
* Tell Homebrew to restart dnsmasq
* Tell Homebrew to restart dnsmasq.
*
* @return void
*/
function restart()
public function restart()
{
$this->brew->restartService('dnsmasq');
}
/**
* Ensure the DnsMasq configuration primary config is set to read custom configs
* Ensure the DnsMasq configuration primary config is set to read custom configs.
*
* @return void
*/
function ensureUsingDnsmasqDForConfigs()
public function ensureUsingDnsmasqDForConfigs()
{
info('Updating Dnsmasq configuration...');
@@ -81,7 +84,7 @@ function ensureUsingDnsmasqDForConfigs()
$contents = $this->files->get($this->dnsmasqMasterConfigFile);
// ensure the line we need to use is present, and uncomment it if needed
if (false === strpos($contents, 'conf-dir='.BREW_PREFIX.'/etc/dnsmasq.d/,*.conf')) {
$contents .= PHP_EOL . 'conf-dir='.BREW_PREFIX.'/etc/dnsmasq.d/,*.conf' . PHP_EOL;
$contents .= PHP_EOL.'conf-dir='.BREW_PREFIX.'/etc/dnsmasq.d/,*.conf'.PHP_EOL;
}
$contents = str_replace('#conf-dir='.BREW_PREFIX.'/etc/dnsmasq.d/,*.conf', 'conf-dir='.BREW_PREFIX.'/etc/dnsmasq.d/,*.conf', $contents);
@@ -92,7 +95,7 @@ function ensureUsingDnsmasqDForConfigs()
$this->files->put($this->dnsmasqMasterConfigFile, $contents);
// remove old ~/.config/valet/dnsmasq.conf file because things are moved to the ~/.config/valet/dnsmasq.d/ folder now
if (file_exists($file = dirname($this->dnsmasqUserConfigDir()) . '/dnsmasq.conf')) {
if (file_exists($file = dirname($this->dnsmasqUserConfigDir()).'/dnsmasq.conf')) {
unlink($file);
}
@@ -100,19 +103,20 @@ function ensureUsingDnsmasqDForConfigs()
$contents = $this->files->get(__DIR__.'/../stubs/etc-dnsmasq-valet.conf');
$contents = str_replace('VALET_HOME_PATH', VALET_HOME_PATH, $contents);
$this->files->ensureDirExists($this->dnsmasqSystemConfDir, user());
$this->files->putAsUser($this->dnsmasqSystemConfDir . '/dnsmasq-valet.conf', $contents);
$this->files->putAsUser($this->dnsmasqSystemConfDir.'/dnsmasq-valet.conf', $contents);
$this->files->ensureDirExists(VALET_HOME_PATH . '/dnsmasq.d', user());
$this->files->ensureDirExists(VALET_HOME_PATH.'/dnsmasq.d', user());
}
/**
* Create the TLD-specific dnsmasq config file
* Create the TLD-specific dnsmasq config file.
*
* @param string $tld
* @return void
*/
function createDnsmasqTldConfigFile($tld)
public function createDnsmasqTldConfigFile($tld)
{
$tldConfigFile = $this->dnsmasqUserConfigDir() . 'tld-' . $tld . '.conf';
$tldConfigFile = $this->dnsmasqUserConfigDir().'tld-'.$tld.'.conf';
$loopback = $this->configuration->read()['loopback'];
$this->files->putAsUser($tldConfigFile, 'address=/.'.$tld.'/'.$loopback.PHP_EOL.'listen-address='.$loopback.PHP_EOL);
@@ -124,7 +128,7 @@ function createDnsmasqTldConfigFile($tld)
* @param string $tld
* @return void
*/
function createTldResolver($tld)
public function createTldResolver($tld)
{
$this->files->ensureDirExists($this->resolverPath);
$loopback = $this->configuration->read()['loopback'];
@@ -139,10 +143,10 @@ function createTldResolver($tld)
* @param string $newTld
* @return void
*/
function updateTld($oldTld, $newTld)
public function updateTld($oldTld, $newTld)
{
$this->files->unlink($this->resolverPath.'/'.$oldTld);
$this->files->unlink($this->dnsmasqUserConfigDir() . 'tld-' . $oldTld . '.conf');
$this->files->unlink($this->dnsmasqUserConfigDir().'tld-'.$oldTld.'.conf');
$this->install($newTld);
}
@@ -152,7 +156,7 @@ function updateTld($oldTld, $newTld)
*
* @return void
*/
function refreshConfiguration()
public function refreshConfiguration()
{
$tld = $this->configuration->read()['tld'];
@@ -164,7 +168,7 @@ function refreshConfiguration()
*
* @return string
*/
function dnsmasqUserConfigDir()
public function dnsmasqUserConfigDir()
{
return $_SERVER['HOME'].'/.config/valet/dnsmasq.d/';
}

View File

@@ -12,7 +12,7 @@ class Filesystem
* @param string $path
* @return bool
*/
function isDir($path)
public function isDir($path)
{
return is_dir($path);
}
@@ -25,7 +25,7 @@ function isDir($path)
* @param int $mode
* @return void
*/
function mkdir($path, $owner = null, $mode = 0755)
public function mkdir($path, $owner = null, $mode = 0755)
{
mkdir($path, $mode, true);
@@ -42,7 +42,7 @@ function mkdir($path, $owner = null, $mode = 0755)
* @param int $mode
* @return void
*/
function ensureDirExists($path, $owner = null, $mode = 0755)
public function ensureDirExists($path, $owner = null, $mode = 0755)
{
if (! $this->isDir($path)) {
$this->mkdir($path, $owner, $mode);
@@ -56,7 +56,7 @@ function ensureDirExists($path, $owner = null, $mode = 0755)
* @param int $mode
* @return void
*/
function mkdirAsUser($path, $mode = 0755)
public function mkdirAsUser($path, $mode = 0755)
{
$this->mkdir($path, user(), $mode);
}
@@ -68,7 +68,7 @@ function mkdirAsUser($path, $mode = 0755)
* @param string|null $owner
* @return string
*/
function touch($path, $owner = null)
public function touch($path, $owner = null)
{
touch($path);
@@ -85,7 +85,7 @@ function touch($path, $owner = null)
* @param string $path
* @return void
*/
function touchAsUser($path)
public function touchAsUser($path)
{
return $this->touch($path, user());
}
@@ -96,7 +96,7 @@ function touchAsUser($path)
* @param string $path
* @return bool
*/
function exists($path)
public function exists($path)
{
return file_exists($path);
}
@@ -107,7 +107,7 @@ function exists($path)
* @param string $path
* @return string
*/
function get($path)
public function get($path)
{
return file_get_contents($path);
}
@@ -120,7 +120,7 @@ function get($path)
* @param string|null $owner
* @return void
*/
function put($path, $contents, $owner = null)
public function put($path, $contents, $owner = null)
{
file_put_contents($path, $contents);
@@ -136,7 +136,7 @@ function put($path, $contents, $owner = null)
* @param string $contents
* @return void
*/
function putAsUser($path, $contents)
public function putAsUser($path, $contents)
{
$this->put($path, $contents, user());
}
@@ -149,7 +149,7 @@ function putAsUser($path, $contents)
* @param string|null $owner
* @return void
*/
function append($path, $contents, $owner = null)
public function append($path, $contents, $owner = null)
{
file_put_contents($path, $contents, FILE_APPEND);
@@ -165,7 +165,7 @@ function append($path, $contents, $owner = null)
* @param string $contents
* @return void
*/
function appendAsUser($path, $contents)
public function appendAsUser($path, $contents)
{
$this->append($path, $contents, user());
}
@@ -177,7 +177,7 @@ function appendAsUser($path, $contents)
* @param string $to
* @return void
*/
function copy($from, $to)
public function copy($from, $to)
{
copy($from, $to);
}
@@ -189,7 +189,7 @@ function copy($from, $to)
* @param string $to
* @return void
*/
function copyAsUser($from, $to)
public function copyAsUser($from, $to)
{
copy($from, $to);
@@ -203,7 +203,7 @@ function copyAsUser($from, $to)
* @param string $link
* @return void
*/
function symlink($target, $link)
public function symlink($target, $link)
{
if ($this->exists($link)) {
$this->unlink($link);
@@ -221,7 +221,7 @@ function symlink($target, $link)
* @param string $link
* @return void
*/
function symlinkAsUser($target, $link)
public function symlinkAsUser($target, $link)
{
if ($this->exists($link)) {
$this->unlink($link);
@@ -236,7 +236,7 @@ function symlinkAsUser($target, $link)
* @param string $path
* @return void
*/
function unlink($path)
public function unlink($path)
{
if (file_exists($path) || is_link($path)) {
@unlink($path);
@@ -249,7 +249,7 @@ function unlink($path)
* @param string $path
* @param string $user
*/
function chown($path, $user)
public function chown($path, $user)
{
chown($path, $user);
}
@@ -260,7 +260,7 @@ function chown($path, $user)
* @param string $path
* @param string $group
*/
function chgrp($path, $group)
public function chgrp($path, $group)
{
chgrp($path, $group);
}
@@ -271,7 +271,7 @@ function chgrp($path, $group)
* @param string $path
* @return string
*/
function realpath($path)
public function realpath($path)
{
return realpath($path);
}
@@ -282,7 +282,7 @@ function realpath($path)
* @param string $path
* @return bool
*/
function isLink($path)
public function isLink($path)
{
return is_link($path);
}
@@ -293,7 +293,7 @@ function isLink($path)
* @param string $path
* @return string
*/
function readLink($path)
public function readLink($path)
{
return readlink($path);
}
@@ -304,7 +304,7 @@ function readLink($path)
* @param string $path
* @return void
*/
function removeBrokenLinksAt($path)
public function removeBrokenLinksAt($path)
{
collect($this->scandir($path))
->filter(function ($file) use ($path) {
@@ -321,7 +321,7 @@ function removeBrokenLinksAt($path)
* @param string $path
* @return bool
*/
function isBrokenLink($path)
public function isBrokenLink($path)
{
return is_link($path) && ! file_exists($path);
}
@@ -332,7 +332,7 @@ function isBrokenLink($path)
* @param string $path
* @return array
*/
function scandir($path)
public function scandir($path)
{
return collect(scandir($path))
->reject(function ($file) {

View File

@@ -6,11 +6,11 @@
class Nginx
{
var $brew;
var $cli;
var $files;
var $configuration;
var $site;
public $brew;
public $cli;
public $files;
public $configuration;
public $site;
const NGINX_CONF = BREW_PREFIX.'/etc/nginx/nginx.conf';
/**
@@ -23,7 +23,7 @@ class Nginx
* @param Site $site
* @return void
*/
function __construct(Brew $brew, CommandLine $cli, Filesystem $files,
public function __construct(Brew $brew, CommandLine $cli, Filesystem $files,
Configuration $configuration, Site $site)
{
$this->cli = $cli;
@@ -38,9 +38,9 @@ function __construct(Brew $brew, CommandLine $cli, Filesystem $files,
*
* @return void
*/
function install()
public function install()
{
if (!$this->brew->hasInstalledNginx()) {
if (! $this->brew->hasInstalledNginx()) {
$this->brew->installOrFail('nginx', []);
}
@@ -54,7 +54,7 @@ function install()
*
* @return void
*/
function installConfiguration()
public function installConfiguration()
{
info('Installing nginx configuration...');
@@ -71,7 +71,7 @@ function installConfiguration()
*
* @return void
*/
function installServer()
public function installServer()
{
$this->files->ensureDirExists(BREW_PREFIX.'/etc/nginx/valet');
@@ -80,7 +80,7 @@ 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'))
)
);
@@ -90,23 +90,6 @@ function installServer()
);
}
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.
*
@@ -114,7 +97,7 @@ function replaceLoopback($siteConf)
*
* @return void
*/
function installNginxDirectory()
public function installNginxDirectory()
{
info('Installing nginx directory...');
@@ -145,7 +128,7 @@ function ($exitCode, $outputMessage) {
*
* @return void
*/
function rewriteSecureNginxFiles()
public function rewriteSecureNginxFiles()
{
$tld = $this->configuration->read()['tld'];
$loopback = $this->configuration->read()['loopback'];
@@ -164,7 +147,7 @@ function rewriteSecureNginxFiles()
*
* @return void
*/
function restart()
public function restart()
{
$this->lint();
@@ -176,11 +159,9 @@ function restart()
*
* @return void
*/
function stop()
public function stop()
{
info('Stopping nginx...');
$this->cli->quietly('sudo brew services stop '. $this->brew->nginxServiceName());
$this->brew->stopService(['nginx']);
}
/**
@@ -188,10 +169,23 @@ function stop()
*
* @return void
*/
function uninstall()
public function uninstall()
{
$this->brew->stopService(['nginx', 'nginx-full']);
$this->brew->uninstallFormula('nginx nginx-full');
$this->cli->quietly('rm -rf '.BREW_PREFIX.'/etc/nginx '.BREW_PREFIX.'/var/log/nginx');
}
/**
* Return a list of all sites with explicit Nginx configurations.
*
* @return \Illuminate\Support\Collection
*/
public function configuredSites()
{
return collect($this->files->scandir(VALET_HOME_PATH.'/Nginx'))
->reject(function ($file) {
return starts_with($file, '.');
});
}
}

View File

@@ -2,12 +2,12 @@
namespace Valet;
use Httpful\Request;
use DomainException;
use GuzzleHttp\Client;
class Ngrok
{
var $tunnelsEndpoints = [
public $tunnelsEndpoints = [
'http://127.0.0.1:4040/api/tunnels',
'http://127.0.0.1:4041/api/tunnels',
];
@@ -17,26 +17,26 @@ class Ngrok
*
* @return string
*/
function currentTunnelUrl($domain = null)
public function currentTunnelUrl($domain = null)
{
// wait a second for ngrok to start before attempting to find available tunnels
sleep(1);
foreach ($this->tunnelsEndpoints as $endpoint) {
$response = retry(20, function () use ($endpoint, $domain) {
$body = Request::get($endpoint)->send()->body;
$body = json_decode((new Client())->get($endpoint)->getBody());
if (isset($body->tunnels) && count($body->tunnels) > 0) {
return $this->findHttpTunnelUrl($body->tunnels, $domain);
}
}, 250);
if (!empty($response)) {
if (! empty($response)) {
return $response;
}
}
throw new DomainException("Tunnel not established.");
throw new DomainException('Tunnel not established.');
}
/**
@@ -45,13 +45,13 @@ function currentTunnelUrl($domain = null)
* @param array $tunnels
* @return string|null
*/
function findHttpTunnelUrl($tunnels, $domain)
public function findHttpTunnelUrl($tunnels, $domain)
{
// If there are active tunnels on the Ngrok instance we will spin through them and
// find the one responding on HTTP. Each tunnel has an HTTP and a HTTPS address
// but for local dev purposes we just desire the plain HTTP URL endpoint.
foreach ($tunnels as $tunnel) {
if ($tunnel->proto === 'http' && strpos($tunnel->config->addr, $domain) ) {
if ($tunnel->proto === 'http' && strpos($tunnel->config->addr, $domain)) {
return $tunnel->public_url;
}
}

View File

@@ -6,9 +6,14 @@
class PhpFpm
{
var $brew, $cli, $files;
public $brew;
public $cli;
public $files;
public $config;
public $site;
public $nginx;
var $taps = [
public $taps = [
'homebrew/homebrew-core',
'shivammathur/php',
];
@@ -19,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
*/
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;
}
/**
@@ -33,25 +44,32 @@ function __construct(Brew $brew, CommandLine $cli, Filesystem $files)
*
* @return void
*/
function install()
public function install()
{
if (! $this->brew->hasInstalledPhp()) {
$this->brew->ensureInstalled('php', [], $this->taps);
}
$this->files->ensureDirExists(VALET_HOME_PATH . '/Log', user());
$this->files->ensureDirExists(VALET_HOME_PATH.'/Log', user());
$this->updateConfiguration();
$phpVersion = $this->brew->linkedPhp();
$this->createConfigurationFiles($phpVersion);
// Remove old valet.sock
$this->files->unlink(VALET_HOME_PATH.'/valet.sock');
$this->cli->quietly('sudo rm '.VALET_HOME_PATH.'/valet.sock');
$this->restart();
$this->symlinkPrimaryValetSock($phpVersion);
}
/**
* Forcefully uninstall all of Valet's supported PHP versions and configurations
* Forcefully uninstall all of Valet's supported PHP versions and configurations.
*
* @return void
*/
function uninstall()
public function uninstall()
{
$this->brew->uninstallAllPhpVersions();
rename(BREW_PREFIX.'/etc/php', BREW_PREFIX.'/etc/php-valet-bak'.time());
@@ -59,66 +77,65 @@ function uninstall()
}
/**
* Update the PHP FPM configuration.
* Create (or re-create) the PHP FPM configuration files.
*
* Writes FPM config file, pointing to the correct .sock file, and log and ini files.
*
* @param string $phpVersion
* @return void
*/
function updateConfiguration()
public function createConfigurationFiles($phpVersion)
{
info('Updating PHP configuration...');
info("Updating PHP configuration for {$phpVersion}...");
$fpmConfigFile = $this->fpmConfigPath();
$fpmConfigFile = $this->fpmConfigPath($phpVersion);
$this->files->ensureDirExists(dirname($fpmConfigFile), user());
// rename (to disable) old FPM Pool configuration, regardless of whether it's a default config or one customized by an older Valet version
$oldFile = dirname($fpmConfigFile) . '/www.conf';
$oldFile = dirname($fpmConfigFile).'/www.conf';
if (file_exists($oldFile)) {
rename($oldFile, $oldFile . '-backup');
rename($oldFile, $oldFile.'-backup');
}
if (false === strpos($fpmConfigFile, '5.6')) {
// since PHP 7 we can simply drop in a valet-specific fpm pool config, and not touch the default config
$contents = $this->files->get(__DIR__.'/../stubs/etc-phpfpm-valet.conf');
$contents = str_replace(['VALET_USER', 'VALET_HOME_PATH'], [user(), VALET_HOME_PATH], $contents);
} else {
// for PHP 5 we must do a direct edit of the fpm pool config to switch it to Valet's needs
$contents = $this->files->get($fpmConfigFile);
$contents = preg_replace('/^user = .+$/m', 'user = '.user(), $contents);
$contents = preg_replace('/^group = .+$/m', 'group = staff', $contents);
$contents = preg_replace('/^listen = .+$/m', 'listen = '.VALET_HOME_PATH.'/valet.sock', $contents);
$contents = preg_replace('/^;?listen\.owner = .+$/m', 'listen.owner = '.user(), $contents);
$contents = preg_replace('/^;?listen\.group = .+$/m', 'listen.group = staff', $contents);
$contents = preg_replace('/^;?listen\.mode = .+$/m', 'listen.mode = 0777', $contents);
}
// Create FPM Config File from stub
$contents = str_replace(
['VALET_USER', 'VALET_HOME_PATH', 'valet.sock'],
[user(), VALET_HOME_PATH, self::fpmSockName($phpVersion)],
$this->files->get(__DIR__.'/../stubs/etc-phpfpm-valet.conf')
);
$this->files->put($fpmConfigFile, $contents);
$contents = $this->files->get(__DIR__.'/../stubs/php-memory-limits.ini');
$destFile = dirname($fpmConfigFile);
$destFile = str_replace('/php-fpm.d', '', $destFile);
$destFile .= '/conf.d/php-memory-limits.ini';
$this->files->ensureDirExists(dirname($destFile), user());
$this->files->putAsUser($destFile, $contents);
// Create other config files from stubs
$destDir = dirname(dirname($fpmConfigFile)).'/conf.d';
$this->files->ensureDirExists($destDir, user());
$contents = $this->files->get(__DIR__.'/../stubs/etc-phpfpm-error_log.ini');
$contents = str_replace(['VALET_USER', 'VALET_HOME_PATH'], [user(), VALET_HOME_PATH], $contents);
$destFile = dirname($fpmConfigFile);
$destFile = str_replace('/php-fpm.d', '', $destFile);
$destFile .= '/conf.d/error_log.ini';
$this->files->ensureDirExists(dirname($destFile), user());
$this->files->putAsUser($destFile, $contents);
$this->files->ensureDirExists(VALET_HOME_PATH . '/Log', user());
$this->files->touch(VALET_HOME_PATH . '/Log/php-fpm.log', user());
$this->files->putAsUser(
$destDir.'/php-memory-limits.ini',
$this->files->get(__DIR__.'/../stubs/php-memory-limits.ini')
);
$contents = str_replace(
['VALET_USER', 'VALET_HOME_PATH'],
[user(), VALET_HOME_PATH],
$this->files->get(__DIR__.'/../stubs/etc-phpfpm-error_log.ini')
);
$this->files->putAsUser($destDir.'/error_log.ini', $contents);
// Create log directory and file
$this->files->ensureDirExists(VALET_HOME_PATH.'/Log', user());
$this->files->touch(VALET_HOME_PATH.'/Log/php-fpm.log', user());
}
/**
* Restart the PHP FPM process.
* Restart the PHP FPM process (if one specified) or processes (if none specified).
*
* @param string|null $phpVersion
* @return void
*/
function restart()
public function restart($phpVersion = null)
{
$this->brew->restartLinkedPhp();
$this->brew->restartService($phpVersion ?: $this->utilizedPhpVersions());
}
/**
@@ -126,7 +143,7 @@ function restart()
*
* @return void
*/
function stop()
public function stop()
{
call_user_func_array(
[$this->brew, 'stopService'],
@@ -137,27 +154,28 @@ function stop()
/**
* Get the path to the FPM configuration file for the current PHP version.
*
* @param string|null $phpVersion
* @return string
*/
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'
? BREW_PREFIX.'/etc/php/5.6/php-fpm.conf'
: BREW_PREFIX."/etc/php/${versionNormalized}/php-fpm.d/valet-fpm.conf";
return BREW_PREFIX."/etc/php/${versionNormalized}/php-fpm.d/valet-fpm.conf";
}
/**
* Only stop running php services
* Stop only the running php services.
*/
function stopRunning()
public function stopRunning()
{
$this->brew->stopService(
$this->brew->getRunningServices()
$this->brew->getAllRunningServices()
->filter(function ($service) {
return substr($service, 0, 3) === 'php';
})
@@ -166,31 +184,107 @@ function stopRunning()
}
/**
* Use a specific version of php
* Stop a given PHP version, if that specific version isn't being used globally or by any sites.
*
* @param $version
* @param $force
* @return string
* @param string|null $phpVersion
* @return void
*/
function useVersion($version, $force = false)
public function stopIfUnused($phpVersion = null)
{
if (! $phpVersion) {
return;
}
$phpVersion = $this->normalizePhpVersion($phpVersion);
if (! in_array($phpVersion, $this->utilizedPhpVersions())) {
$this->brew->stopService($phpVersion);
}
}
/**
* Isolate a given directory to use a specific version of PHP.
*
* @param string $directory
* @param string $version
* @return void
*/
public function isolateDirectory($directory, $version)
{
$site = $this->site->getSiteUrl($directory);
$version = $this->validateRequestedVersion($version);
$this->brew->ensureInstalled($version, [], $this->taps);
$oldCustomPhpVersion = $this->site->customPhpVersion($site); // Example output: "74"
$this->createConfigurationFiles($version);
$this->site->isolate($site, $version);
$this->stopIfUnused($oldCustomPhpVersion);
$this->restart($version);
$this->nginx->restart();
info(sprintf('The site [%s] is now using %s.', $site, $version));
}
/**
* Remove PHP version isolation for a given directory.
*
* @param string $directory
* @return void
*/
public function unIsolateDirectory($directory)
{
$site = $this->site->getSiteUrl($directory);
$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));
}
/**
* List all directories with PHP isolation configured.
*
* @return \Illuminate\Support\Collection
*/
public function isolatedDirectories()
{
return $this->nginx->configuredSites()->filter(function ($item) {
return strpos($this->files->get(VALET_HOME_PATH.'/Nginx/'.$item), ISOLATED_PHP_VERSION) !== false;
})->map(function ($item) {
return ['url' => $item, 'version' => $this->normalizePhpVersion($this->site->customPhpVersion($item))];
});
}
/**
* Use a specific version of PHP globally.
*
* @param string $version
* @param bool $force
* @return string|void
*/
public function useVersion($version, $force = false)
{
$version = $this->validateRequestedVersion($version);
try {
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,
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();
}
} 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);
} catch (DomainException $e) { /* ignore thrown exception when no linked php is found */
}
// Unlink the current php if there is one
$this->brew->ensureInstalled($version, [], $this->taps);
// Unlink the current global PHP if there is one installed
if ($this->brew->hasLinkedPhp()) {
$currentVersion = $this->brew->getLinkedPhpFormula();
info(sprintf('Unlinking current version: %s', $currentVersion));
@@ -202,42 +296,53 @@ function useVersion($version, $force = false)
$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');
// 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;
}
/**
* Symlink (Capistrano-style) a given Valet.sock file to be the primary valet.sock.
*
* @param string $phpVersion
* @return void
*/
public function symlinkPrimaryValetSock($phpVersion)
{
$this->files->symlinkAsUser(VALET_HOME_PATH.'/'.$this->fpmSockName($phpVersion), VALET_HOME_PATH.'/valet.sock');
}
/**
* If passed php7.4 or php74 formats, normalize to php@7.4 format.
* If passed php7.4, or php74, 7.4, or 74 formats, normalize to php@7.4 format.
*/
function normalizePhpVersion($version)
public function normalizePhpVersion($version)
{
return preg_replace('/(php)([0-9+])(?:.)?([0-9+])/i', '$1@$2.$3', $version);
return preg_replace('/(?:php@?)?([0-9+])(?:.)?([0-9+])/i', 'php@$1.$2', $version);
}
/**
* Validate the requested version to be sure we can support it.
*
* @param $version
* @param string $version
* @return string
*/
function validateRequestedVersion($version)
public function validateRequestedVersion($version)
{
if (is_null($version)) {
throw new DomainException("Please specify a PHP version (try something like 'php@8.1')");
}
$version = $this->normalizePhpVersion($version);
if (!$this->brew->supportedPhpVersions()->contains($version)) {
throw new DomainException(
sprintf(
'Valet doesn\'t support PHP version: %s (try something like \'php@7.3\' instead)',
$version
)
);
if (! $this->brew->supportedPhpVersions()->contains($version)) {
throw new DomainException("Valet doesn't support PHP version: {$version} (try something like 'php@8.1' instead)");
}
if (strpos($aliasedVersion = $this->brew->determineAliasedVersion($version), '@')) {
@@ -245,15 +350,49 @@ function validateRequestedVersion($version)
}
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');
throw new DomainException('Brew is already using PHP '.PHP_VERSION.' as \'php\' in Homebrew. To use another version, please specify. eg: php@8.1');
}
}
return $version;
}
/**
* Get FPM sock file name for a given PHP version.
*
* @param string|null $phpVersion
* @return string
*/
public static function fpmSockName($phpVersion = null)
{
$versionInteger = preg_replace('~[^\d]~', '', $phpVersion);
return "valet{$versionInteger}.sock";
}
/**
* 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 self::fpmSockName($this->normalizePhpVersion($version));
})->unique();
return $this->nginx->configuredSites()->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) {
if (strpos($content, $sock) !== false) {
// Extract the PHP version number from a custom .sock path and normalize it to, e.g., "php@7.4"
return $this->normalizePhpVersion(str_replace(['valet', '.sock'], '', $sock));
}
}
})->merge([$this->brew->getLinkedPhpFormula()])->filter()->unique()->values()->toArray();
}
}

View File

@@ -3,10 +3,13 @@
namespace Valet;
use DomainException;
use PhpFpm;
class Site
{
var $config, $cli, $files;
public $config;
public $cli;
public $files;
/**
* Create a new Site instance.
@@ -15,7 +18,7 @@ class Site
* @param CommandLine $cli
* @param Filesystem $files
*/
function __construct(Configuration $config, CommandLine $cli, Filesystem $files)
public function __construct(Configuration $config, CommandLine $cli, Filesystem $files)
{
$this->cli = $cli;
$this->files = $files;
@@ -25,7 +28,7 @@ function __construct(Configuration $config, CommandLine $cli, Filesystem $files)
/**
* Get the name of the site.
*
* @param string|null $name
* @param string|null $name
* @return string
*/
private function getRealSiteName($name)
@@ -65,7 +68,7 @@ private function getLinkNameByCurrentDir()
* @param string $path
* @return string|null
*/
function host($path)
public function host($path)
{
foreach ($this->files->scandir($this->sitesPath()) as $link) {
if ($resolved = realpath($this->sitesPath($link)) === $path) {
@@ -83,7 +86,7 @@ function host($path)
* @param string $link
* @return string
*/
function link($target, $link)
public function link($target, $link)
{
$this->files->ensureDirExists(
$linkPath = $this->sitesPath(), user()
@@ -101,7 +104,7 @@ function link($target, $link)
*
* @return \Illuminate\Support\Collection
*/
function links()
public function links()
{
$certsPath = $this->certificatesPath();
@@ -113,11 +116,11 @@ function links()
}
/**
* Pretty print out all parked links in Valet
* Pretty print out all parked links in Valet.
*
* @return \Illuminate\Support\Collection
*/
function parked()
public function parked()
{
$certs = $this->getCertificates();
@@ -132,7 +135,7 @@ function parked()
// Only merge on the parked sites that don't interfere with the linked sites
$sites = $this->getSites($path, $certs)->filter(function ($site, $key) use ($links) {
return !$links->has($key);
return ! $links->has($key);
});
$parkedLinks = $parkedLinks->merge($sites);
@@ -142,11 +145,11 @@ function parked()
}
/**
* Get all sites which are proxies (not Links, and contain proxy_pass directive)
* Get all sites which are proxies (not Links, and contain proxy_pass directive).
*
* @return \Illuminate\Support\Collection
*/
function proxies()
public function proxies()
{
$dir = $this->nginxPath();
$tld = $this->config->read()['tld'];
@@ -168,17 +171,18 @@ function proxies()
return $links->has($site);
})->mapWithKeys(function ($site) {
$host = $this->getProxyHostForSite($site) ?: '(other)';
return [$site => $host];
})->reject(function ($host, $site) {
// If proxy host is null, it may be just a normal SSL stub, or something else; either way we exclude it from the list
return $host === '(other)';
})->map(function ($host, $site) use ($certs, $tld) {
$secured = $certs->has($site);
$url = ($secured ? 'https': 'http').'://'.$site.'.'.$tld;
$url = ($secured ? 'https' : 'http').'://'.$site.'.'.$tld;
return [
'site' => $site,
'secured' => $secured ? ' X': '',
'secured' => $secured ? ' X' : '',
'url' => $url,
'path' => $host,
];
@@ -187,14 +191,37 @@ function proxies()
return $proxies;
}
/**
* Get the site URL from a directory if it's a valid Valet site.
*
* @param string $directory
* @return string
*/
public function getSiteUrl($directory)
{
$tld = $this->config->read()['tld'];
if ($directory == '.' || $directory == './') { // Allow user to use dot as current dir's site `--site=.`
$directory = $this->host(getcwd());
}
$directory = str_replace('.'.$tld, '', $directory); // Remove .tld from sitename if it was provided
if (! $this->parked()->merge($this->links())->where('site', $directory)->count() > 0) {
throw new DomainException("The [{$directory}] site could not be found in Valet's site list.");
}
return $directory.'.'.$tld;
}
/**
* Identify whether a site is for a proxy by reading the host name from its config file.
*
* @param string $site Site name without TLD
* @param string $configContents Config file contents
* @param string $site Site name without TLD
* @param string $configContents Config file contents
* @return string|null
*/
function getProxyHostForSite($site, $configContents = null)
public function getProxyHostForSite($site, $configContents = null)
{
$siteConf = $configContents ?: $this->getSiteConfigFileContents($site);
@@ -206,24 +233,26 @@ function getProxyHostForSite($site, $configContents = null)
if (preg_match('~proxy_pass\s+(?<host>https?://.*)\s*;~', $siteConf, $patterns)) {
$host = trim($patterns['host']);
}
return $host;
}
function getSiteConfigFileContents($site, $suffix = null)
public function getSiteConfigFileContents($site, $suffix = null)
{
$config = $this->config->read();
$suffix = $suffix ?: '.'.$config['tld'];
$file = str_replace($suffix,'',$site).$suffix;
$file = str_replace($suffix, '', $site).$suffix;
return $this->files->exists($this->nginxPath($file)) ? $this->files->get($this->nginxPath($file)) : null;
}
/**
* Get all certificates from config folder.
*
* @param string $path
* @param string $path
* @return \Illuminate\Support\Collection
*/
function getCertificates($path = null)
public function getCertificates($path = null)
{
$path = $path ?: $this->certificatesPath();
@@ -250,25 +279,24 @@ function getCertificates($path = null)
/**
* @deprecated Use getSites instead which works for both normal and symlinked paths.
*
* @param string $path
* @param \Illuminate\Support\Collection $certs
* @param string $path
* @param \Illuminate\Support\Collection $certs
* @return \Illuminate\Support\Collection
*/
function getLinks($path, $certs)
public function getLinks($path, $certs)
{
return $this->getSites($path, $certs);
}
/**
* Get list of sites and return them formatted
* Will work for symlink and normal site paths
* Will work for symlink and normal site paths.
*
* @param $path
* @param $certs
*
* @return \Illuminate\Support\Collection
*/
function getSites($path, $certs)
public function getSites($path, $certs)
{
$config = $this->config->read();
@@ -282,16 +310,17 @@ function getSites($path, $certs)
} else {
$realPath = $this->files->realpath($sitePath);
}
return [$site => $realPath];
})->filter(function ($path) {
return $this->files->isDir($path);
})->map(function ($path, $site) use ($certs, $config) {
$secured = $certs->has($site);
$url = ($secured ? 'https': 'http').'://'.$site.'.'.$config['tld'];
$url = ($secured ? 'https' : 'http').'://'.$site.'.'.$config['tld'];
return [
'site' => $site,
'secured' => $secured ? ' X': '',
'secured' => $secured ? ' X' : '',
'url' => $url,
'path' => $path,
];
@@ -304,7 +333,7 @@ function getSites($path, $certs)
* @param string $name
* @return void
*/
function unlink($name)
public function unlink($name)
{
$name = $this->getRealSiteName($name);
@@ -320,7 +349,7 @@ function unlink($name)
*
* @return void
*/
function pruneLinks()
public function pruneLinks()
{
$this->files->ensureDirExists($this->sitesPath(), user());
@@ -338,7 +367,7 @@ function pruneLinks()
* @param array $new
* @return void
*/
function resecureForNewConfiguration(array $old, array $new)
public function resecureForNewConfiguration(array $old, array $new)
{
if (! $this->files->exists($this->certificatesPath())) {
return;
@@ -347,18 +376,18 @@ function resecureForNewConfiguration(array $old, array $new)
$secured = $this->secured();
$defaultTld = $this->config->read()['tld'];
$oldTld = !empty($old['tld']) ? $old['tld'] : $defaultTld;
$tld = !empty($new['tld']) ? $new['tld'] : $defaultTld;
$oldTld = ! empty($old['tld']) ? $old['tld'] : $defaultTld;
$tld = ! empty($new['tld']) ? $new['tld'] : $defaultTld;
$defaultLoopback = $this->config->read()['loopback'];
$oldLoopback = !empty($old['loopback']) ? $old['loopback'] : $defaultLoopback;
$loopback = !empty($new['loopback']) ? $new['loopback'] : $defaultLoopback;
$oldLoopback = ! empty($old['loopback']) ? $old['loopback'] : $defaultLoopback;
$loopback = ! empty($new['loopback']) ? $new['loopback'] : $defaultLoopback;
foreach ($secured as $url) {
$newUrl = str_replace('.'.$oldTld, '.'.$tld, $url);
$siteConf = $this->getSiteConfigFileContents($url, '.'.$oldTld);
if (!empty($siteConf) && strpos($siteConf, '# valet stub: proxy.valet.conf') === 0) {
if (! empty($siteConf) && strpos($siteConf, '# valet stub: proxy.valet.conf') === 0) {
// proxy config
$this->unsecure($url);
$this->secure(
@@ -380,12 +409,12 @@ function resecureForNewConfiguration(array $old, array $new)
/**
* Parse Nginx site config file contents to swap old domain to new.
*
* @param string $siteConf Nginx site config content
* @param string $old Old domain
* @param string $new New domain
* @param string $siteConf Nginx site config content
* @param string $old Old domain
* @param string $new New domain
* @return string
*/
function replaceOldDomainWithNew($siteConf, $old, $new)
public function replaceOldDomainWithNew($siteConf, $old, $new)
{
$lookups = [];
$lookups[] = '~server_name .*;~';
@@ -400,18 +429,19 @@ function replaceOldDomainWithNew($siteConf, $old, $new)
$siteConf = str_replace($match, $replaced, $siteConf);
}
}
return $siteConf;
}
/**
* Parse Nginx site config file contents to swap old loopback address to new.
*
* @param string $siteConf Nginx site config content
* @param string $old Old loopback address
* @param string $new New loopback address
* @param string $siteConf Nginx site config content
* @param string $old Old loopback address
* @param string $new New loopback address
* @return string
*/
function replaceOldLoopbackWithNew($siteConf, $old, $new)
public function replaceOldLoopbackWithNew($siteConf, $old, $new)
{
$shouldComment = $new === VALET_LOOPBACK;
@@ -436,6 +466,7 @@ function replaceOldLoopbackWithNew($siteConf, $old, $new)
$siteConf = str_replace($match, $replaced, $siteConf);
}
}
return $siteConf;
}
@@ -444,10 +475,12 @@ function replaceOldLoopbackWithNew($siteConf, $old, $new)
*
* @return array
*/
function secured()
public function secured()
{
return collect($this->files->scandir($this->certificatesPath()))
->map(function ($file) {
->filter(function ($file) {
return ends_with($file, ['.key', '.csr', '.crt', '.conf']);
})->map(function ($file) {
return str_replace(['.key', '.csr', '.crt', '.conf'], '', $file);
})->unique()->values()->all();
}
@@ -457,10 +490,19 @@ function secured()
*
* @param string $url
* @param string $siteConf pregenerated Nginx config file contents
* @param int $certificateExpireInDays The number of days the self signed certificate is valid.
* Certificates SHOULD NOT have a validity period greater than 397 days.
* @param int $caExpireInYears The number of years the self signed certificate authority is valid.
*
* @see https://github.com/cabforum/servercert/blob/main/docs/BR.md
*
* @return void
*/
function secure($url, $siteConf = null)
public function secure($url, $siteConf = null, $certificateExpireInDays = 396, $caExpireInYears = 20)
{
// Extract in order to later preserve custom PHP version config when securing
$phpVersion = $this->customPhpVersion($url);
$this->unsecure($url);
$this->files->ensureDirExists($this->caPath(), user());
@@ -469,21 +511,28 @@ function secure($url, $siteConf = null)
$this->files->ensureDirExists($this->nginxPath(), user());
$this->createCa();
$caExpireInDate = (new \DateTime())->diff(new \DateTime("+{$caExpireInYears} years"));
$this->createCertificate($url);
$this->createCa($caExpireInDate->format('%a'));
$this->createCertificate($url, $certificateExpireInDays);
$this->files->putAsUser(
$this->nginxPath($url), $this->buildSecureNginxServer($url, $siteConf)
);
$siteConf = $this->buildSecureNginxServer($url, $siteConf);
// If the user had isolated the PHP version for this site, swap out .sock file
if ($phpVersion) {
$siteConf = $this->replaceSockFile($siteConf, $phpVersion);
}
$this->files->putAsUser($this->nginxPath($url), $siteConf);
}
/**
* If CA and root certificates are nonexistent, create them and trust the root cert.
*
* @param int $caExpireInDays The number of days the self signed certificate authority is valid.
* @return void
*/
function createCa()
public function createCa($caExpireInDays)
{
$caPemPath = $this->caPath('LaravelValetCASelfSigned.pem');
$caKeyPath = $this->caPath('LaravelValetCASelfSigned.key');
@@ -508,8 +557,8 @@ function createCa()
));
$this->cli->runAsUser(sprintf(
'openssl req -new -newkey rsa:2048 -days 730 -nodes -x509 -subj "/C=/ST=/O=%s/localityName=/commonName=%s/organizationalUnitName=Developers/emailAddress=%s/" -keyout "%s" -out "%s"',
$oName, $cName, 'rootcertificate@laravel.valet', $caKeyPath, $caPemPath
'openssl req -new -newkey rsa:2048 -days %s -nodes -x509 -subj "/C=/ST=/O=%s/localityName=/commonName=%s/organizationalUnitName=Developers/emailAddress=%s/" -keyout "%s" -out "%s"',
$caExpireInDays, $oName, $cName, 'rootcertificate@laravel.valet', $caKeyPath, $caPemPath
));
$this->trustCa($caPemPath);
}
@@ -518,9 +567,10 @@ function createCa()
* Create and trust a certificate for the given URL.
*
* @param string $url
* @param int $caExpireInDays The number of days the self signed certificate is valid.
* @return void
*/
function createCertificate($url)
public function createCertificate($url, $caExpireInDays)
{
$caPemPath = $this->caPath('LaravelValetCASelfSigned.pem');
$caKeyPath = $this->caPath('LaravelValetCASelfSigned.key');
@@ -534,21 +584,21 @@ function createCertificate($url)
$this->createPrivateKey($keyPath);
$this->createSigningRequest($url, $keyPath, $csrPath, $confPath);
$caSrlParam = '-CAserial "' . $caSrlPath . '"';
$caSrlParam = '-CAserial "'.$caSrlPath.'"';
if (! $this->files->exists($caSrlPath)) {
$caSrlParam .= ' -CAcreateserial';
}
$result = $this->cli->runAsUser(sprintf(
'openssl x509 -req -sha256 -days 730 -CA "%s" -CAkey "%s" %s -in "%s" -out "%s" -extensions v3_req -extfile "%s"',
$caPemPath, $caKeyPath, $caSrlParam, $csrPath, $crtPath, $confPath
'openssl x509 -req -sha256 -days %s -CA "%s" -CAkey "%s" %s -in "%s" -out "%s" -extensions v3_req -extfile "%s"',
$caExpireInDays, $caPemPath, $caKeyPath, $caSrlParam, $csrPath, $crtPath, $confPath
));
// If cert could not be created using runAsUser(), use run().
if (strpos($result, 'Permission denied')) {
$this->cli->run(sprintf(
'openssl x509 -req -sha256 -days 730 -CA "%s" -CAkey "%s" %s -in "%s" -out "%s" -extensions v3_req -extfile "%s"',
$caPemPath, $caKeyPath, $caSrlParam, $csrPath, $crtPath, $confPath
'openssl x509 -req -sha256 -days %s -CA "%s" -CAkey "%s" %s -in "%s" -out "%s" -extensions v3_req -extfile "%s"',
$caExpireInDays, $caPemPath, $caKeyPath, $caSrlParam, $csrPath, $crtPath, $confPath
));
}
@@ -561,7 +611,7 @@ function createCertificate($url)
* @param string $keyPath
* @return void
*/
function createPrivateKey($keyPath)
public function createPrivateKey($keyPath)
{
$this->cli->runAsUser(sprintf('openssl genrsa -out "%s" 2048', $keyPath));
}
@@ -572,7 +622,7 @@ function createPrivateKey($keyPath)
* @param string $keyPath
* @return void
*/
function createSigningRequest($url, $keyPath, $csrPath, $confPath)
public function createSigningRequest($url, $keyPath, $csrPath, $confPath)
{
$this->cli->runAsUser(sprintf(
'openssl req -new -key "%s" -out "%s" -subj "/C=/ST=/O=/localityName=/commonName=%s/organizationalUnitName=/emailAddress=%s%s/" -config "%s"',
@@ -586,7 +636,7 @@ function createSigningRequest($url, $keyPath, $csrPath, $confPath)
* @param string $pemPath
* @return void
*/
function trustCa($caPemPath)
public function trustCa($caPemPath)
{
$this->cli->run(sprintf(
'sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "%s"', $caPemPath
@@ -599,7 +649,7 @@ function trustCa($caPemPath)
* @param string $crtPath
* @return void
*/
function trustCertificate($crtPath)
public function trustCertificate($crtPath)
{
$this->cli->run(sprintf(
'sudo security add-trusted-cert -d -r trustAsRoot -k /Library/Keychains/System.keychain "%s"', $crtPath
@@ -612,7 +662,7 @@ function trustCertificate($crtPath)
* @param string $url
* @return string
*/
function buildCertificateConf($path, $url)
public function buildCertificateConf($path, $url)
{
$config = str_replace('VALET_DOMAIN', $url, $this->files->get(__DIR__.'/../stubs/openssl.conf'));
$this->files->putAsUser($path, $config);
@@ -625,7 +675,7 @@ function buildCertificateConf($path, $url)
* @param string $siteConf (optional) Nginx site config file content
* @return string
*/
function buildSecureNginxServer($url, $siteConf = null)
public function buildSecureNginxServer($url, $siteConf = null)
{
if ($siteConf === null) {
$siteConf = $this->replaceOldLoopbackWithNew(
@@ -649,14 +699,61 @@ function buildSecureNginxServer($url, $siteConf = null)
);
}
/**
* Create new nginx config or modify existing nginx config to isolate this site
* to a custom version of PHP.
*
* @param string $valetSite
* @param string $phpVersion
* @return void
*/
public function isolate($valetSite, $phpVersion)
{
if ($this->files->exists($this->nginxPath($valetSite))) {
// Modify the existing config if it exists (likely because it's secured)
$siteConf = $this->files->get($this->nginxPath($valetSite));
$siteConf = $this->replaceSockFile($siteConf, $phpVersion);
} else {
$siteConf = str_replace(
['VALET_HOME_PATH', 'VALET_SERVER_PATH', 'VALET_STATIC_PREFIX', 'VALET_SITE', 'VALET_PHP_FPM_SOCKET', 'VALET_ISOLATED_PHP_VERSION'],
[VALET_HOME_PATH, VALET_SERVER_PATH, VALET_STATIC_PREFIX, $valetSite, PhpFpm::fpmSockName($phpVersion), $phpVersion],
$this->replaceLoopback($this->files->get(__DIR__.'/../stubs/site.valet.conf'))
);
}
$this->files->putAsUser($this->nginxPath($valetSite), $siteConf);
}
/**
* Remove PHP Version isolation from a specific site.
*
* @param string $valetSite
* @return void
*/
public function removeIsolation($valetSite)
{
// If a site has an SSL certificate, we need to keep its custom config file, but we can
// just re-generate it without defining a custom `valet.sock` file
if ($this->files->exists($this->certificatesPath($valetSite, 'crt'))) {
$siteConf = $this->buildSecureNginxServer($valetSite);
$this->files->putAsUser($this->nginxPath($valetSite), $siteConf);
} else {
// When site doesn't have SSL, we can remove the custom nginx config file to remove isolation
$this->files->unlink($this->nginxPath($valetSite));
}
}
/**
* Unsecure the given URL so that it will use HTTP again.
*
* @param string $url
* @return void
*/
function unsecure($url)
public function unsecure($url)
{
// Extract in order to later preserve custom PHP version config when unsecuring. Example output: "74"
$phpVersion = $this->customPhpVersion($url);
if ($this->files->exists($this->certificatesPath($url, 'crt'))) {
$this->files->unlink($this->nginxPath($url));
@@ -672,9 +769,14 @@ function unsecure($url)
'sudo security find-certificate -e "%s%s" -a -Z | grep SHA-1 | sudo awk \'{system("security delete-certificate -Z \'$NF\' /Library/Keychains/System.keychain")}\'',
$url, '@laravel.valet'
));
// If the user had isolated the PHP version for this site, swap out .sock file
if ($phpVersion) {
$this->isolate($url, $phpVersion);
}
}
function unsecureAll()
public function unsecureAll()
{
$tld = $this->config->read()['tld'];
@@ -691,7 +793,7 @@ function unsecureAll()
table(['Site', 'SSL', 'URL', 'Path'], $secured->toArray());
foreach ($secured->pluck('site') as $url) {
$this->unsecure($url . '.' . $tld);
$this->unsecure($url.'.'.$tld);
}
$remaining = $this->parked()
@@ -706,21 +808,21 @@ function unsecureAll()
}
/**
* Build the Nginx proxy config for the specified domain
* Build the Nginx proxy config for the specified domain.
*
* @param string $url The domain name to serve
* @param string $host The URL to proxy to, eg: http://127.0.0.1:8080
* @param bool $secure
* @param string $url The domain name to serve
* @param string $host The URL to proxy to, eg: http://127.0.0.1:8080
* @param bool $secure
* @return string
*/
function proxyCreate($url, $host, $secure = false)
public function proxyCreate($url, $host, $secure = false)
{
if (!preg_match('~^https?://.*$~', $host)) {
if (! preg_match('~^https?://.*$~', $host)) {
throw new \InvalidArgumentException(sprintf('"%s" is not a valid URL', $host));
}
$tld = $this->config->read()['tld'];
if (!ends_with($url, '.'.$tld)) {
if (! ends_with($url, '.'.$tld)) {
$url .= '.'.$tld;
}
@@ -755,10 +857,10 @@ function proxyCreate($url, $host, $secure = false)
* @param string $url
* @return void
*/
function proxyDelete($url)
public function proxyDelete($url)
{
$tld = $this->config->read()['tld'];
if (!ends_with($url, '.'.$tld)) {
if (! ends_with($url, '.'.$tld)) {
$url .= '.'.$tld;
}
@@ -775,7 +877,7 @@ function proxyDelete($url)
* @param string $siteConf pregenerated Nginx config file contents
* @return void
*/
function put($url, $siteConf)
public function put($url, $siteConf)
{
$this->unsecure($url);
@@ -793,7 +895,7 @@ function put($url, $siteConf)
* @param string $loopback
* @return void
*/
function aliasLoopback($oldLoopback, $loopback)
public function aliasLoopback($oldLoopback, $loopback)
{
if ($oldLoopback !== VALET_LOOPBACK) {
$this->removeLoopbackAlias($oldLoopback);
@@ -812,7 +914,7 @@ function aliasLoopback($oldLoopback, $loopback)
* @param string $loopback
* @return void
*/
function removeLoopbackAlias($loopback)
public function removeLoopbackAlias($loopback)
{
$this->cli->run(sprintf(
'sudo ifconfig lo0 -alias %s', $loopback
@@ -827,7 +929,7 @@ function removeLoopbackAlias($loopback)
* @param string $loopback
* @return void
*/
function addLoopbackAlias($loopback)
public function addLoopbackAlias($loopback)
{
$this->cli->run(sprintf(
'sudo ifconfig lo0 alias %s', $loopback
@@ -842,7 +944,7 @@ function addLoopbackAlias($loopback)
* @param string $loopback
* @return void
*/
function updateLoopbackPlist($loopback)
public function updateLoopbackPlist($loopback)
{
$this->removeLoopbackPlist();
@@ -865,7 +967,7 @@ function updateLoopbackPlist($loopback)
*
* @return void
*/
function removeLoopbackPlist()
public function removeLoopbackPlist()
{
if ($this->files->exists($this->plistPath())) {
$this->files->unlink($this->plistPath());
@@ -879,7 +981,7 @@ function removeLoopbackPlist()
*
* @return void
*/
function uninstallLoopback()
public function uninstallLoopback()
{
if (($loopback = $this->valetLoopback()) !== VALET_LOOPBACK) {
$this->removeLoopbackAlias($loopback);
@@ -888,12 +990,12 @@ function uninstallLoopback()
$this->removeLoopbackPlist();
}
function valetHomePath()
public function valetHomePath()
{
return VALET_HOME_PATH;
}
function valetLoopback()
public function valetLoopback()
{
return $this->config->read()['loopback'];
}
@@ -903,7 +1005,7 @@ function valetLoopback()
*
* @return string
*/
function plistPath()
public function plistPath()
{
return '/Library/LaunchDaemons/com.laravel.valet.loopback.plist';
}
@@ -911,7 +1013,7 @@ function plistPath()
/**
* Get the path to Nginx site configuration files.
*/
function nginxPath($additionalPath = null)
public function nginxPath($additionalPath = null)
{
return $this->valetHomePath().'/Nginx'.($additionalPath ? '/'.$additionalPath : '');
}
@@ -921,7 +1023,7 @@ function nginxPath($additionalPath = null)
*
* @return string
*/
function sitesPath($link = null)
public function sitesPath($link = null)
{
return $this->valetHomePath().'/Sites'.($link ? '/'.$link : '');
}
@@ -931,7 +1033,7 @@ function sitesPath($link = null)
*
* @return string
*/
function caPath($caFile = null)
public function caPath($caFile = null)
{
return $this->valetHomePath().'/CA'.($caFile ? '/'.$caFile : '');
}
@@ -941,7 +1043,7 @@ function caPath($caFile = null)
*
* @return string
*/
function certificatesPath($url = null, $extension = null)
public function certificatesPath($url = null, $extension = null)
{
$url = $url ? '/'.$url : '';
$extension = $extension ? '.'.$extension : '';
@@ -966,4 +1068,80 @@ public function domain($domain)
return ($domain ?: $this->host(getcwd())).'.'.$this->config->read()['tld'];
}
/**
* Replace Loopback configuration line in Valet site configuration file contents.
*
* @param string $siteConf
* @return string
*/
public function replaceLoopback($siteConf)
{
$loopback = $this->config->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
);
}
/**
* Extract PHP version of exising nginx conifg.
*
* @param string $url
* @return string|void
*/
public function customPhpVersion($url)
{
if ($this->files->exists($this->nginxPath($url))) {
$siteConf = $this->files->get($this->nginxPath($url));
if (starts_with($siteConf, '# '.ISOLATED_PHP_VERSION)) {
$firstLine = explode(PHP_EOL, $siteConf)[0];
return preg_replace("/[^\d]*/", '', $firstLine); // Example output: "74" or "81"
}
}
}
/**
* Replace .sock file in an Nginx site configuration file contents.
*
* @param string $siteConf
* @param string $phpVersion
* @return string
*/
public function replaceSockFile($siteConf, $phpVersion)
{
$sockFile = PhpFpm::fpmSockName($phpVersion);
$siteConf = preg_replace('/valet[0-9]*.sock/', $sockFile, $siteConf);
$siteConf = preg_replace('/# '.ISOLATED_PHP_VERSION.'.*\n/', '', $siteConf); // Remove ISOLATED_PHP_VERSION line from config
return '# '.ISOLATED_PHP_VERSION.'='.$phpVersion.PHP_EOL.$siteConf;
}
/**
* Get PHP version from .valetphprc for a site.
*
* @param string $site
* @return string|null
*/
public function phpRcVersion($site)
{
if ($site = $this->parked()->merge($this->links())->where('site', $site)->first()) {
$path = data_get($site, 'path').'/.valetphprc';
if ($this->files->exists($path)) {
return PhpFpm::normalizePhpVersion(trim($this->files->get($path)));
}
}
}
}

View File

@@ -2,13 +2,14 @@
namespace Valet;
use Httpful\Request;
use GuzzleHttp\Client;
class Valet
{
var $cli, $files;
public $cli;
public $files;
var $valetBin = BREW_PREFIX.'/bin/valet';
public $valetBin = BREW_PREFIX.'/bin/valet';
/**
* Create a new Valet instance.
@@ -16,7 +17,7 @@ class Valet
* @param CommandLine $cli
* @param Filesystem $files
*/
function __construct(CommandLine $cli, Filesystem $files)
public function __construct(CommandLine $cli, Filesystem $files)
{
$this->cli = $cli;
$this->files = $files;
@@ -27,7 +28,7 @@ function __construct(CommandLine $cli, Filesystem $files)
*
* @return void
*/
function symlinkToUsersBin()
public function symlinkToUsersBin()
{
$this->unlinkFromUsersBin();
@@ -39,7 +40,7 @@ function symlinkToUsersBin()
*
* @return void
*/
function unlinkFromUsersBin()
public function unlinkFromUsersBin()
{
$this->cli->quietlyAsUser('rm '.$this->valetBin);
}
@@ -49,7 +50,7 @@ function unlinkFromUsersBin()
*
* @return array
*/
function extensions()
public function extensions()
{
if (! $this->files->isDir(VALET_HOME_PATH.'/Extensions')) {
return [];
@@ -70,13 +71,15 @@ function extensions()
*
* @param string $currentVersion
* @return bool
* @throws \Httpful\Exception\ConnectionErrorException
*
* @throws \GuzzleHttp\Exception\GuzzleException
*/
function onLatestVersion($currentVersion)
public function onLatestVersion($currentVersion)
{
$response = Request::get('https://api.github.com/repos/laravel/valet/releases/latest')->send();
$url = 'https://api.github.com/repos/laravel/valet/releases/latest';
$response = json_decode((new Client())->get($url)->getBody());
return version_compare($currentVersion, trim($response->body->tag_name, 'v'), '>=');
return version_compare($currentVersion, trim($response->tag_name, 'v'), '>=');
}
/**
@@ -84,7 +87,7 @@ function onLatestVersion($currentVersion)
*
* @return void
*/
function createSudoersEntry()
public function createSudoersEntry()
{
$this->files->ensureDirExists('/etc/sudoers.d');
@@ -97,23 +100,23 @@ function createSudoersEntry()
*
* @return void
*/
function removeSudoersEntry()
public function removeSudoersEntry()
{
$this->cli->quietly('rm /etc/sudoers.d/valet');
}
/**
* Run composer global diagnose
* Run composer global diagnose.
*/
function composerGlobalDiagnose()
public function composerGlobalDiagnose()
{
$this->cli->runAsUser('composer global diagnose');
}
/**
* Run composer global update
* Run composer global update.
*/
function composerGlobalUpdate()
public function composerGlobalUpdate()
{
$this->cli->runAsUser('composer global update');
}

View File

@@ -48,10 +48,10 @@ public function isStaticFile($sitePath, $siteName, $uri)
*/
public function frontControllerPath($sitePath, $siteName, $uri)
{
$_SERVER['PHP_SELF'] = $uri;
$_SERVER['PHP_SELF'] = $uri;
$_SERVER['SERVER_ADDR'] = '127.0.0.1';
$_SERVER['SERVER_NAME'] = $_SERVER['HTTP_HOST'];
$dynamicCandidates = [
$this->asActualFile($sitePath, $uri),
$this->asPhpIndexFileInDirectory($sitePath, $uri),
@@ -63,14 +63,15 @@ public function frontControllerPath($sitePath, $siteName, $uri)
$_SERVER['SCRIPT_FILENAME'] = $candidate;
$_SERVER['SCRIPT_NAME'] = str_replace($sitePath, '', $candidate);
$_SERVER['DOCUMENT_ROOT'] = $sitePath;
return $candidate;
}
}
$fixedCandidatesAndDocroots = [
$this->asRootPhpIndexFile($sitePath) => $sitePath,
$this->asPublicPhpIndexFile($sitePath) => $sitePath . '/public',
$this->asPublicHtmlIndexFile($sitePath) => $sitePath . '/public',
$this->asPublicPhpIndexFile($sitePath) => $sitePath.'/public',
$this->asPublicHtmlIndexFile($sitePath) => $sitePath.'/public',
];
foreach ($fixedCandidatesAndDocroots as $candidate => $docroot) {
@@ -78,6 +79,7 @@ public function frontControllerPath($sitePath, $siteName, $uri)
$_SERVER['SCRIPT_FILENAME'] = $candidate;
$_SERVER['SCRIPT_NAME'] = '/index.php';
$_SERVER['DOCUMENT_ROOT'] = $docroot;
return $candidate;
}
}

View File

@@ -56,19 +56,24 @@ public function frontControllerPath($sitePath, $siteName, $uri)
: $sitePath.'/web'.$uri;
}
if ($uri !== '/' && file_exists($sitePath.'/web'.$uri)) {
return $sitePath.'/web'.$uri;
}
return $sitePath.'/web/index.php';
}
/**
* Redirect to uri with trailing slash.
*
* @param string $uri
* @param string $uri
* @return string
*/
private function forceTrailingSlash($uri)
{
if (substr($uri, -1 * strlen('/wp/wp-admin')) == '/wp/wp-admin') {
header('Location: '.$uri.'/'); die;
header('Location: '.$uri.'/');
exit;
}
return $uri;

View File

@@ -2,28 +2,28 @@
class Concrete5ValetDriver extends BasicValetDriver
{
/**
* If a concrete directory exists, it's probably c5
* @param string $sitePath
* @param string $siteName
* @param string $uri
* If a concrete directory exists, it's probably c5.
*
* @param string $sitePath
* @param string $siteName
* @param string $uri
* @return bool
*/
public function serves($sitePath, $siteName, $uri)
{
return file_exists($sitePath . "/concrete/config/install/base");
return file_exists($sitePath.'/concrete/config/install/base');
}
/**
* @param string $sitePath
* @param string $siteName
* @param string $uri
* @param string $sitePath
* @param string $siteName
* @param string $uri
* @return string
*/
public function frontControllerPath($sitePath, $siteName, $uri)
{
if (!getenv('CONCRETE5_ENV')) {
if (! getenv('CONCRETE5_ENV')) {
putenv('CONCRETE5_ENV=valet');
}
@@ -35,14 +35,13 @@ public function frontControllerPath($sitePath, $siteName, $uri)
$_SERVER['SCRIPT_FILENAME'] = $sitePath.$filename;
$_SERVER['SCRIPT_NAME'] = $filename;
return $sitePath . $filename;
return $sitePath.$filename;
}
}
$_SERVER['SCRIPT_FILENAME'] = $sitePath . '/index.php';
$_SERVER['SCRIPT_FILENAME'] = $sitePath.'/index.php';
$_SERVER['SCRIPT_NAME'] = '/index.php';
return $sitePath . '/index.php';
return $sitePath.'/index.php';
}
}

View File

@@ -14,11 +14,11 @@ public function serves($sitePath, $siteName, $uri)
{
$sitePath = $this->addSubdirectory($sitePath);
/**
* /misc/drupal.js = Drupal 7
* /core/lib/Drupal.php = Drupal 8
*/
if (file_exists($sitePath.'/misc/drupal.js') ||
/**
* /misc/drupal.js = Drupal 7
* /core/lib/Drupal.php = Drupal 8.
*/
if (file_exists($sitePath.'/misc/drupal.js') ||
file_exists($sitePath.'/core/lib/Drupal.php')) {
return true;
}
@@ -57,8 +57,8 @@ public function frontControllerPath($sitePath, $siteName, $uri)
{
$sitePath = $this->addSubdirectory($sitePath);
if (!isset($_GET['q']) && !empty($uri) && $uri !== '/' && strpos($uri, '/jsonapi/') === false) {
$_GET['q'] = $uri;
if (! isset($_GET['Q']) && ! empty($uri) && $uri !== '/' && strpos($uri, '/jsonapi/') === false) {
$_GET['Q'] = $uri;
}
$matches = [];
@@ -67,6 +67,7 @@ public function frontControllerPath($sitePath, $siteName, $uri)
if (file_exists($sitePath.$filename) && ! is_dir($sitePath.$filename)) {
$_SERVER['SCRIPT_FILENAME'] = $sitePath.$filename;
$_SERVER['SCRIPT_NAME'] = $filename;
return $sitePath.$filename;
}
}
@@ -74,6 +75,7 @@ public function frontControllerPath($sitePath, $siteName, $uri)
// Fallback
$_SERVER['SCRIPT_FILENAME'] = $sitePath.'/index.php';
$_SERVER['SCRIPT_NAME'] = '/index.php';
return $sitePath.'/index.php';
}
@@ -91,7 +93,7 @@ public function addSubdirectory($sitePath)
});
// If paths are found, return the first one.
if (!empty($foundPaths)) {
if (! empty($foundPaths)) {
return array_shift($foundPaths);
}

View File

@@ -31,7 +31,7 @@ public function isStaticFile($sitePath, $siteName, $uri)
return $staticFilePath;
}
return false;
return false;
}
/**

View File

@@ -58,7 +58,7 @@ public function frontControllerPath($sitePath, $siteName, $uri)
if (isset($_SERVER['HTTP_X_ORIGINAL_HOST'], $_SERVER['HTTP_X_FORWARDED_HOST'])) {
$_SERVER['HTTP_HOST'] = $_SERVER['HTTP_X_FORWARDED_HOST'];
}
return $sitePath.'/public/index.php';
}
}

View File

@@ -2,9 +2,8 @@
class Magento2ValetDriver extends ValetDriver
{
/**
* Holds the MAGE_MODE from app/etc/config.php or $ENV
* Holds the MAGE_MODE from app/etc/config.php or $ENV.
*
* @var string
*/
@@ -13,22 +12,22 @@ class Magento2ValetDriver extends ValetDriver
/**
* Determine if the driver serves the request.
*
* @param string $sitePath
* @param string $siteName
* @param string $uri
* @return boolean
* @param string $sitePath
* @param string $siteName
* @param string $uri
* @return bool
*/
public function serves($sitePath, $siteName, $uri)
{
return file_exists($sitePath . '/bin/magento') && file_exists($sitePath . '/pub/index.php');
return file_exists($sitePath.'/bin/magento') && file_exists($sitePath.'/pub/index.php');
}
/**
* Determine if the incoming request is for a static file.
*
* @param string $sitePath
* @param string $siteName
* @param string $uri
* @param string $sitePath
* @param string $siteName
* @param string $uri
* @return string|false
*/
public function isStaticFile($sitePath, $siteName, $uri)
@@ -43,7 +42,7 @@ public function isStaticFile($sitePath, $siteName, $uri)
$pub = 'pub/';
}
if (!$this->isPubDirectory($sitePath, $route, $pub)) {
if (! $this->isPubDirectory($sitePath, $route, $pub)) {
return false;
}
@@ -52,21 +51,21 @@ public function isStaticFile($sitePath, $siteName, $uri)
$magentoPackagePubDir .= '/pub';
}
$file = $magentoPackagePubDir . '/' . $route;
$file = $magentoPackagePubDir.'/'.$route;
if (file_exists($file)) {
return $magentoPackagePubDir . $uri;
return $magentoPackagePubDir.$uri;
}
if (strpos($route, $pub . 'static/') === 0) {
$route = preg_replace('#' . $pub . 'static/#', '', $route, 1);
if (strpos($route, $pub.'static/') === 0) {
$route = preg_replace('#'.$pub.'static/#', '', $route, 1);
$_GET['resource'] = $route;
include $magentoPackagePubDir . '/' . $pub . 'static.php';
include $magentoPackagePubDir.'/'.$pub.'static.php';
exit;
}
if (strpos($route, $pub . 'media/') === 0) {
include $magentoPackagePubDir . '/' . $pub . 'get.php';
if (strpos($route, $pub.'media/') === 0) {
include $magentoPackagePubDir.'/'.$pub.'get.php';
exit;
}
@@ -75,9 +74,9 @@ public function isStaticFile($sitePath, $siteName, $uri)
/**
* Rewrite URLs that look like "versions12345/" to remove
* the versions12345/ part
* the versions12345/ part.
*
* @param string $route
* @param string $route
*/
private function handleForVersions($route)
{
@@ -85,9 +84,9 @@ private function handleForVersions($route)
}
/**
* Determine the current MAGE_MODE
* Determine the current MAGE_MODE.
*
* @param string $sitePath
* @param string $sitePath
*/
private function checkMageMode($sitePath)
{
@@ -95,13 +94,14 @@ private function checkMageMode($sitePath)
// We have already figure out mode, no need to check it again
return;
}
if (!file_exists($sitePath . '/index.php')) {
if (! file_exists($sitePath.'/index.php')) {
$this->mageMode = 'production'; // Can't use developer mode without index.php in project root
return;
}
$mageConfig = [];
if (file_exists($sitePath . '/app/etc/env.php')) {
$mageConfig = require $sitePath . '/app/etc/env.php';
if (file_exists($sitePath.'/app/etc/env.php')) {
$mageConfig = require $sitePath.'/app/etc/env.php';
}
if (array_key_exists('MAGE_MODE', $mageConfig)) {
$this->mageMode = $mageConfig['MAGE_MODE'];
@@ -112,31 +112,32 @@ private function checkMageMode($sitePath)
* Checks to see if route is referencing any directory inside pub. This is a dynamic check so that if any new
* directories are added to pub this driver will not need to be updated.
*
* @param string $sitePath
* @param string $route
* @param string $pub
* @param string $sitePath
* @param string $route
* @param string $pub
* @return bool
*/
private function isPubDirectory($sitePath, $route, $pub = '')
{
$sitePath .= '/pub/';
$dirs = glob($sitePath . '*', GLOB_ONLYDIR);
$dirs = glob($sitePath.'*', GLOB_ONLYDIR);
$dirs = str_replace($sitePath, '', $dirs);
foreach ($dirs as $dir) {
if (strpos($route, $pub . $dir . '/') === 0) {
if (strpos($route, $pub.$dir.'/') === 0) {
return true;
}
}
return false;
}
/**
* Get the fully resolved path to the application's front controller.
*
* @param string $sitePath
* @param string $siteName
* @param string $uri
* @param string $sitePath
* @param string $siteName
* @param string $uri
* @return string
*/
public function frontControllerPath($sitePath, $siteName, $uri)
@@ -145,9 +146,11 @@ public function frontControllerPath($sitePath, $siteName, $uri)
if ('developer' === $this->mageMode) {
$_SERVER['DOCUMENT_ROOT'] = $sitePath;
return $sitePath . '/index.php';
return $sitePath.'/index.php';
}
$_SERVER['DOCUMENT_ROOT'] = $sitePath . '/pub';
return $sitePath . '/pub/index.php';
$_SERVER['DOCUMENT_ROOT'] = $sitePath.'/pub';
return $sitePath.'/pub/index.php';
}
}

View File

@@ -28,6 +28,7 @@ public function isStaticFile($sitePath, $siteName, $uri)
if ($this->isActualFile($staticFilePath = $sitePath.'/Web'.$uri)) {
return $staticFilePath;
}
return false;
}
@@ -45,6 +46,7 @@ public function frontControllerPath($sitePath, $siteName, $uri)
putenv('FLOW_REWRITEURLS=1');
$_SERVER['SCRIPT_FILENAME'] = $sitePath.'/Web/index.php';
$_SERVER['SCRIPT_NAME'] = '/index.php';
return $sitePath.'/Web/index.php';
}
}

View File

@@ -69,26 +69,26 @@ public function frontControllerPath($sitePath, $siteName, $uri)
$sitePathPrefix = ($isAboveWebroot) ? $sitePath.'/public' : $sitePath;
if ($locale = $this->getUriLocale($uri)) {
if ($this->isActualFile($localeIndexPath = $sitePathPrefix . '/' . $locale . '/index.php')) {
if ($this->isActualFile($localeIndexPath = $sitePathPrefix.'/'.$locale.'/index.php')) {
// Force trailing slashes on locale roots.
if ($uri === '/' . $locale) {
header('Location: ' . $uri . '/');
die;
if ($uri === '/'.$locale) {
header('Location: '.$uri.'/');
exit;
}
$indexPath = $localeIndexPath;
$scriptName = '/' . $locale . '/index.php';
$scriptName = '/'.$locale.'/index.php';
}
}
$_SERVER['SCRIPT_NAME'] = $scriptName;
$_SERVER['SCRIPT_FILENAME'] = $sitePathPrefix . $scriptName;
$_SERVER['SCRIPT_FILENAME'] = $sitePathPrefix.$scriptName;
return $indexPath;
}
/**
* Get the locale from this URI
* Get the locale from this URI.
*
* @param string $uri
* @return string|null
@@ -106,7 +106,7 @@ public function getUriLocale($uri)
}
/**
* Get the list of possible locales used in the first segment of a URI
* Get the list of possible locales used in the first segment of a URI.
*
* @return array
*/
@@ -131,9 +131,9 @@ public function getLocales()
}
/**
* Get the path to a statically cached page
* Get the path to a statically cached page.
*
* @param string $sitePath
* @param string $sitePath
* @return string
*/
protected function getStaticPath($sitePath)
@@ -141,6 +141,6 @@ protected function getStaticPath($sitePath)
$parts = parse_url($_SERVER['REQUEST_URI']);
$query = isset($parts['query']) ? $parts['query'] : '';
return $sitePath . '/static' . $parts['path'] . '_' . $query . '.html';
return $sitePath.'/static'.$parts['path'].'_'.$query.'.html';
}
}

View File

@@ -14,8 +14,7 @@ public function serves($sitePath, $siteName, $uri)
{
return (file_exists($sitePath.'/web/app_dev.php') || file_exists($sitePath.'/web/app.php')) &&
(file_exists($sitePath.'/app/AppKernel.php')) || (file_exists($sitePath.'/public/index.php')) &&
(file_exists($sitePath.'/src/Kernel.php'))
;
(file_exists($sitePath.'/src/Kernel.php'));
}
/**
@@ -58,6 +57,7 @@ public function frontControllerPath($sitePath, $siteName, $uri)
}
$_SERVER['SCRIPT_FILENAME'] = $frontControllerPath;
return $frontControllerPath;
}
}

View File

@@ -12,7 +12,7 @@ class Typo3ValetDriver extends ValetDriver
| Document Root Subdirectory
|--------------------------------------------------------------------------
|
| This subdirectory contains the public server resources, such as the
| This subdirectory contains the public server resources, such as the
| index.php, the typo3 and fileadmin system directories. Change it
| to '', if you don't use a subdirectory but valet link directly.
|
@@ -48,7 +48,8 @@ class Typo3ValetDriver extends ValetDriver
*/
public function serves($sitePath, $siteName, $uri)
{
$typo3Dir = $sitePath . $this->documentRoot . '/typo3';
$typo3Dir = $sitePath.$this->documentRoot.'/typo3';
return file_exists($typo3Dir) && is_dir($typo3Dir);
}
@@ -66,17 +67,15 @@ public function isStaticFile($sitePath, $siteName, $uri)
{
// May the file contains a cache busting version string like filename.12345678.css
// If that is the case, the file cannot be found on disk, so remove the version
// identifier before retrying below.
if (!$this->isActualFile($filePath = $sitePath . $this->documentRoot . $uri))
{
$uri = preg_replace("@^(.+)\.(\d+)\.(js|css|png|jpg|gif|gzip)$@", "$1.$3", $uri);
// identifier before retrying below.
if (! $this->isActualFile($filePath = $sitePath.$this->documentRoot.$uri)) {
$uri = preg_replace("@^(.+)\.(\d+)\.(js|css|png|jpg|gif|gzip)$@", '$1.$3', $uri);
}
// Now that any possible version string is cleared from the filename, the resulting
// URI should be a valid file on disc. So assemble the absolut file name with the
// same schema as above and if it exists, authorize access and return its path.
if ($this->isActualFile($filePath = $sitePath . $this->documentRoot . $uri))
{
if ($this->isActualFile($filePath = $sitePath.$this->documentRoot.$uri)) {
return $this->isAccessAuthorized($uri) ? $filePath : false;
}
@@ -87,18 +86,17 @@ public function isStaticFile($sitePath, $siteName, $uri)
/**
* Determines if the given URI is blacklisted so that access is prevented.
*
* @param string $uri
* @return boolean
* @param string $uri
* @return bool
*/
private function isAccessAuthorized($uri)
{
foreach ($this->forbiddenUriPatterns as $forbiddenUriPattern)
{
if (preg_match("@$forbiddenUriPattern@", $uri))
{
foreach ($this->forbiddenUriPatterns as $forbiddenUriPattern) {
if (preg_match("@$forbiddenUriPattern@", $uri)) {
return false;
}
}
return true;
}
@@ -121,24 +119,18 @@ public function frontControllerPath($sitePath, $siteName, $uri)
$uri = rtrim($uri, '/');
// try to find the responsible script file for the requested folder / script URI
if (file_exists($absoluteFilePath = $sitePath . $this->documentRoot . $uri))
{
if (is_dir($absoluteFilePath))
{
if (file_exists($absoluteFilePath . '/index.php'))
{
if (file_exists($absoluteFilePath = $sitePath.$this->documentRoot.$uri)) {
if (is_dir($absoluteFilePath)) {
if (file_exists($absoluteFilePath.'/index.php')) {
// this folder can be served by index.php
return $this->serveScript($sitePath, $siteName, $uri . '/index.php');
return $this->serveScript($sitePath, $siteName, $uri.'/index.php');
}
if (file_exists($absoluteFilePath . '/index.html'))
{
if (file_exists($absoluteFilePath.'/index.html')) {
// this folder can be served by index.html
return $absoluteFilePath . '/index.html';
return $absoluteFilePath.'/index.html';
}
}
else if (pathinfo($absoluteFilePath, PATHINFO_EXTENSION) === 'php')
{
} elseif (pathinfo($absoluteFilePath, PATHINFO_EXTENSION) === 'php') {
// this file can be served directly
return $this->serveScript($sitePath, $siteName, $uri);
}
@@ -153,20 +145,18 @@ public function frontControllerPath($sitePath, $siteName, $uri)
* sysext install script. domain.dev/typo3 will be redirected to /typo3/, because
* the generated JavaScript URIs on the login screen would be broken on /typo3.
*
* @param string $uri
* @param string $uri
*/
private function handleRedirectBackendShorthandUris($uri)
{
if (rtrim($uri, '/') === '/typo3/install')
{
if (rtrim($uri, '/') === '/typo3/install') {
header('Location: /typo3/sysext/install/Start/Install.php');
die();
exit();
}
if ($uri === '/typo3')
{
if ($uri === '/typo3') {
header('Location: /typo3/');
die();
exit();
}
}
@@ -182,10 +172,10 @@ private function handleRedirectBackendShorthandUris($uri)
*/
private function serveScript($sitePath, $siteName, $uri)
{
$docroot = $sitePath . $this->documentRoot;
$abspath = $docroot . $uri;
$docroot = $sitePath.$this->documentRoot;
$abspath = $docroot.$uri;
$_SERVER['SERVER_NAME'] = $siteName . '.dev';
$_SERVER['SERVER_NAME'] = $siteName.'.dev';
$_SERVER['DOCUMENT_ROOT'] = $docroot;
$_SERVER['DOCUMENT_URI'] = $uri;
$_SERVER['SCRIPT_FILENAME'] = $abspath;

View File

@@ -166,7 +166,7 @@ public function serveStaticFile($staticFilePath, $sitePath, $siteName, $uri)
header('Content-Type: text/html');
header_remove('Content-Type');
header('X-Accel-Redirect: /' . VALET_STATIC_PREFIX . $staticFilePath);
header('X-Accel-Redirect: /'.VALET_STATIC_PREFIX.$staticFilePath);
}
/**
@@ -182,7 +182,7 @@ protected function isActualFile($path)
/**
* Load server environment variables if available.
* Processes any '*' entries first, and then adds site-specific entries
* Processes any '*' entries first, and then adds site-specific entries.
*
* @param string $sitePath
* @param string $siteName
@@ -190,9 +190,9 @@ protected function isActualFile($path)
*/
public function loadServerEnvironmentVariables($sitePath, $siteName)
{
$varFilePath = $sitePath . '/.valet-env.php';
$varFilePath = $sitePath.'/.valet-env.php';
if (! file_exists($varFilePath)) {
$varFilePath = VALET_HOME_PATH . '/.valet-env.php';
$varFilePath = VALET_HOME_PATH.'/.valet-env.php';
}
if (! file_exists($varFilePath)) {
return;
@@ -207,11 +207,12 @@ public function loadServerEnvironmentVariables($sitePath, $siteName)
}
foreach ($variablesToSet as $key => $value) {
if (! is_string($key)) continue;
if (! is_string($key)) {
continue;
}
$_SERVER[$key] = $value;
$_ENV[$key] = $value;
putenv($key . '=' . $value);
putenv($key.'='.$value);
}
}
}

View File

@@ -25,7 +25,7 @@ public function serves($sitePath, $siteName, $uri)
*/
public function frontControllerPath($sitePath, $siteName, $uri)
{
$_SERVER['PHP_SELF'] = $uri;
$_SERVER['PHP_SELF'] = $uri;
$_SERVER['SERVER_ADDR'] = '127.0.0.1';
$_SERVER['SERVER_NAME'] = $_SERVER['HTTP_HOST'];
@@ -37,13 +37,14 @@ public function frontControllerPath($sitePath, $siteName, $uri)
/**
* Redirect to uri with trailing slash.
*
* @param string $uri
* @param string $uri
* @return string
*/
private function forceTrailingSlash($uri)
{
if (substr($uri, -1 * strlen('/wp-admin')) == '/wp-admin') {
header('Location: '.$uri.'/'); die;
header('Location: '.$uri.'/');
exit;
}
return $uri;

View File

@@ -1,6 +1,6 @@
<?php
if (php_sapi_name() !== "cli") {
if (php_sapi_name() !== 'cli') {
// Allow bypassing these checks if using Valet in a non-CLI app
return;
}
@@ -16,8 +16,8 @@
exit(1);
}
if (version_compare(PHP_VERSION, '5.6.0', '<')) {
echo "Valet requires PHP 5.6 or later.";
if (version_compare(PHP_VERSION, '7.0', '<')) {
echo 'Valet requires PHP 7.0 or later.';
exit(1);
}

View File

@@ -29,14 +29,36 @@ public static function __callStatic($method, $parameters)
}
}
class Brew extends Facade {}
class Nginx extends Facade {}
class CommandLine extends Facade {}
class Configuration extends Facade {}
class Diagnose extends Facade {}
class DnsMasq extends Facade {}
class Filesystem extends Facade {}
class Ngrok extends Facade {}
class PhpFpm extends Facade {}
class Site extends Facade {}
class Valet extends Facade {}
class Brew extends Facade
{
}
class Nginx extends Facade
{
}
class CommandLine extends Facade
{
}
class Configuration extends Facade
{
}
class Diagnose extends Facade
{
}
class DnsMasq extends Facade
{
}
class Filesystem extends Facade
{
}
class Ngrok extends Facade
{
}
class PhpFpm extends Facade
{
}
class Site extends Facade
{
}
class Valet extends Facade
{
}

View File

@@ -8,18 +8,22 @@
use Symfony\Component\Console\Output\ConsoleOutput;
/**
* Define constants
* Define constants.
*/
if (! defined('VALET_LOOPBACK')) {
define('VALET_LOOPBACK', '127.0.0.1');
define('VALET_HOME_PATH', $_SERVER['HOME'] . '/.config/valet');
define('VALET_SERVER_PATH', realpath(__DIR__ . '/../../server.php'));
define('VALET_STATIC_PREFIX', '41c270e4-5535-4daa-b23e-c269744c2f45');
define('VALET_LEGACY_HOME_PATH', $_SERVER['HOME'] . '/.valet');
define('BREW_PREFIX', (new CommandLine())->runAsUser('printf $(brew --prefix)'));
if (! defined('VALET_HOME_PATH')) {
define('VALET_HOME_PATH', $_SERVER['HOME'].'/.config/valet');
}
if (! defined('VALET_STATIC_PREFIX')) {
define('VALET_STATIC_PREFIX', '41c270e4-5535-4daa-b23e-c269744c2f45');
}
define('VALET_LOOPBACK', '127.0.0.1');
define('VALET_SERVER_PATH', realpath(__DIR__.'/../../server.php'));
define('VALET_LEGACY_HOME_PATH', $_SERVER['HOME'].'/.valet');
define('BREW_PREFIX', (new CommandLine())->runAsUser('printf $(brew --prefix)'));
define('ISOLATED_PHP_VERSION', 'ISOLATED_PHP_VERSION');
/**
* Output the given text to the console.
@@ -29,7 +33,7 @@
*/
function info($output)
{
output('<info>' . $output . '</info>');
output('<info>'.$output.'</info>');
}
/**
@@ -40,14 +44,14 @@ function info($output)
*/
function warning($output)
{
output('<fg=red>' . $output . '</>');
output('<fg=red>'.$output.'</>');
}
/**
* Output a table to the console.
*
* @param array $headers
* @param array $rows
* @param array $headers
* @param array $rows
* @return void
*/
function table(array $headers = [], array $rows = [])
@@ -165,12 +169,14 @@ function tap($value, callable $callback)
* @param string|array $needles
* @return bool
*/
function ends_with($haystack, $needles) {
function ends_with($haystack, $needles)
{
foreach ((array) $needles as $needle) {
if (substr($haystack, -strlen($needle)) === (string) $needle) {
return true;
}
}
return false;
}
}
@@ -196,7 +202,7 @@ function starts_with($haystack, $needles)
}
/**
* Get the user
* Get the user.
*/
function user()
{

40
cli/stubs/site.valet.conf Normal file
View File

@@ -0,0 +1,40 @@
# ISOLATED_PHP_VERSION=VALET_ISOLATED_PHP_VERSION
server {
listen 127.0.0.1:80;
server_name VALET_SITE www.VALET_SITE *.VALET_SITE;
#listen VALET_LOOPBACK:80; # valet loopback
root /;
charset utf-8;
client_max_body_size 128M;
location /VALET_STATIC_PREFIX/ {
internal;
alias /;
try_files $uri $uri/;
}
location / {
rewrite ^ "VALET_SERVER_PATH" last;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
access_log off;
error_log "VALET_HOME_PATH/Log/nginx-error.log";
error_page 404 "VALET_SERVER_PATH";
location ~ [^/]\.php(/|$) {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass "unix:VALET_HOME_PATH/VALET_PHP_FPM_SOCKET";
fastcgi_index "VALET_SERVER_PATH";
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME "VALET_SERVER_PATH";
fastcgi_param PATH_INFO $fastcgi_path_info;
}
location ~ /\.ht {
deny all;
}
}

View File

@@ -12,8 +12,8 @@
require getenv('HOME').'/.composer/vendor/autoload.php';
}
use Silly\Application;
use Illuminate\Container\Container;
use Silly\Application;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use function Valet\info;
use function Valet\output;
@@ -23,7 +23,7 @@
/**
* Relocate config dir to ~/.config/valet/ if found in old location.
*/
if (is_dir(VALET_LEGACY_HOME_PATH) && !is_dir(VALET_HOME_PATH)) {
if (is_dir(VALET_LEGACY_HOME_PATH) && ! is_dir(VALET_HOME_PATH)) {
Configuration::createConfigurationDirectory();
}
@@ -32,7 +32,7 @@
*/
Container::setInstance(new Container);
$version = '2.16.0';
$version = '3.1.0';
$app = new Application('Laravel Valet', $version);
@@ -66,7 +66,7 @@
*/
if (is_dir(VALET_HOME_PATH)) {
/**
* Upgrade helper: ensure the tld config exists or the loopback config exists
* Upgrade helper: ensure the tld config exists or the loopback config exists.
*/
if (empty(Configuration::read()['tld']) || empty(Configuration::read()['loopback'])) {
Configuration::writeBaseConfiguration();
@@ -81,7 +81,8 @@
}
DnsMasq::updateTld(
$oldTld = Configuration::read()['tld'], $tld = trim($tld, '.')
$oldTld = Configuration::read()['tld'],
$tld = trim($tld, '.')
);
Configuration::updateKey('tld', $tld);
@@ -117,7 +118,6 @@
Nginx::restart();
info('Your valet loopback address has been updated to ['.$loopback.']');
})->descriptions('Get or set the loopback address used for Valet sites');
/**
@@ -126,11 +126,11 @@
$app->command('park [path]', function ($path = null) {
Configuration::addPath($path ?: getcwd());
info(($path === null ? "This" : "The [{$path}]") . " directory has been added to Valet's paths.");
info(($path === null ? 'This' : "The [{$path}]")." directory has been added to Valet's paths.");
})->descriptions('Register the current working (or specified) directory with Valet');
/**
* Get all the current sites within paths parked with 'park {path}'
* Get all the current sites within paths parked with 'park {path}'.
*/
$app->command('parked', function () {
$parked = Site::parked();
@@ -144,7 +144,7 @@
$app->command('forget [path]', function ($path = null) {
Configuration::removePath($path ?: getcwd());
info(($path === null ? "This" : "The [{$path}]") . " directory has been removed from Valet's paths.");
info(($path === null ? 'This' : "The [{$path}]")." directory has been removed from Valet's paths.");
}, ['unpark'])->descriptions('Remove the current working (or specified) directory from Valet\'s list of paths');
/**
@@ -179,15 +179,17 @@
/**
* Secure the given domain with a trusted TLS certificate.
*/
$app->command('secure [domain]', function ($domain = null) {
$app->command('secure [domain] [--expireIn=]', function ($domain = null, $expireIn = 368) {
$url = Site::domain($domain);
Site::secure($url);
Site::secure($url, null, $expireIn);
Nginx::restart();
info('The ['.$url.'] site has been secured with a fresh TLS certificate.');
})->descriptions('Secure the given domain with a trusted TLS certificate');
})->descriptions('Secure the given domain with a trusted TLS certificate', [
'--expireIn' => 'The amount of days the self signed certificate is valid for. Default is set to "368"',
]);
/**
* Stop serving the given domain over HTTPS and remove the trusted TLS certificate.
@@ -195,6 +197,7 @@
$app->command('unsecure [domain] [--all]', function ($domain = null, $all = null) {
if ($all) {
Site::unsecureAll();
return;
}
@@ -208,25 +211,32 @@
})->descriptions('Stop serving the given domain over HTTPS and remove the trusted TLS certificate');
/**
* Create an Nginx proxy config for the specified domain
* Get all the current secured sites.
*/
$app->command('secured', function () {
$sites = collect(Site::secured())->map(function ($url) {
return ['Site' => $url];
});
table(['Site'], $sites->all());
});
/**
* Create an Nginx proxy config for the specified domain.
*/
$app->command('proxy domain host [--secure]', function ($domain, $host, $secure) {
Site::proxyCreate($domain, $host, $secure);
Nginx::restart();
})->descriptions('Create an Nginx proxy site for the specified host. Useful for docker, mailhog etc.', [
'--secure' => 'Create a proxy with a trusted TLS certificate'
'--secure' => 'Create a proxy with a trusted TLS certificate',
]);
/**
* Delete an Nginx proxy config
* Delete an Nginx proxy config.
*/
$app->command('unproxy domain', function ($domain) {
Site::proxyDelete($domain);
Nginx::restart();
})->descriptions('Delete an Nginx proxy config.');
/**
@@ -278,7 +288,7 @@
* Generate a publicly accessible URL for your project.
*/
$app->command('share', function () {
warning("It looks like you are running `cli/valet.php` directly, please use the `valet` script in the project root instead.");
warning('It looks like you are running `cli/valet.php` directly; please use the `valet` script in the project root instead.');
})->descriptions('Generate a publicly accessible URL for your project');
/**
@@ -395,7 +405,8 @@
info('Removing sudoers entries...');
Brew::removeSudoersEntry();
Valet::removeSudoersEntry();
return output("<fg=red>NOTE:</>
return output('<fg=red>NOTE:</>
<comment>Valet has attempted to uninstall itself, but there are some steps you need to do manually:</comment>
Run <info>php -v</info> to see what PHP version you are now really using.
Run <info>composer global update</info> to update your globally-installed Composer packages to work with your default PHP.
@@ -403,17 +414,17 @@
Thus, you may need to delete things from your <info>~/.composer/composer.json</info> file before running <info>composer global update</info> successfully.
Then to finish removing any Composer fragments of Valet:
Run <info>composer global remove laravel/valet</info>
and then <info>rm ".BREW_PREFIX."/bin/valet</info> to remove the Valet bin link if it still exists.
and then <info>rm '.BREW_PREFIX.'/bin/valet</info> to remove the Valet bin link if it still exists.
Optional:
- <info>brew list --formula</info> will show any other Homebrew services installed, in case you want to make changes to those as well.
- <info>brew doctor</info> can indicate if there might be any broken things left behind.
- <info>brew cleanup</info> can purge old cached Homebrew downloads.
<fg=red>If you had customized your Mac DNS settings in System Preferences->Network, you will need to remove 127.0.0.1 from that list.</>
Additionally you might also want to open Keychain Access and search for <comment>valet</comment> to remove any leftover trust certificates.
");
');
}
output("WAIT! Before you uninstall things, consider cleaning things up in the following order. (Or skip to the bottom for troubleshooting suggestions.):
output('WAIT! Before you uninstall things, consider cleaning things up in the following order. (Or skip to the bottom for troubleshooting suggestions.):
<info>You did not pass the <fg=red>--force</> parameter so we are NOT ACTUALLY uninstalling anything.</info>
A --force removal WILL delete your custom configuration information, so you will want to make backups first.
@@ -432,13 +443,13 @@
<info>4. Homebrew Services</info>
<fg=red>You may remove the core services (php, nginx, dnsmasq) by running:</> <comment>brew uninstall --force php nginx dnsmasq</comment>
<fg=red>You can then remove selected leftover configurations for these services manually</> in both <comment>".BREW_PREFIX."/etc/</comment> and <comment>".BREW_PREFIX."/logs/</comment>.
<fg=red>You can then remove selected leftover configurations for these services manually</> in both <comment>'.BREW_PREFIX.'/etc/</comment> and <comment>'.BREW_PREFIX.'/logs/</comment>.
(If you have other PHP versions installed, run <info>brew list --formula | grep php</info> to see which versions you should also uninstall manually.)
<error>BEWARE:</error> Uninstalling PHP via Homebrew will leave your Mac with its original PHP version, which may not be compatible with other Composer dependencies you have installed. Thus you may get unexpected errors.
Some additional services which you may have installed (but which Valet does not directly configure or manage) include: <comment>mariadb mysql mailhog</comment>.
If you wish to also remove them, you may manually run <comment>brew uninstall SERVICENAME</comment> and clean up their configurations in ".BREW_PREFIX."/etc if necessary.
If you wish to also remove them, you may manually run <comment>brew uninstall SERVICENAME</comment> and clean up their configurations in '.BREW_PREFIX.'/etc if necessary.
You can discover more Homebrew services by running: <comment>brew services list</comment> and <comment>brew list --formula</comment>
@@ -453,7 +464,7 @@
<comment>composer global update</comment> to apply updates to packages
<comment>composer global outdated</comment> to identify outdated packages
<comment>composer global diagnose</comment> to run diagnostics
");
');
// Stopping PHP so the ~/.config/valet/valet.sock file is released so the directory can be deleted if desired
PhpFpm::stopRunning();
Nginx::stop();
@@ -469,7 +480,7 @@
output(sprintf('Your version of Valet (%s) is not the latest version available.', $version));
output('Upgrade instructions can be found in the docs: https://laravel.com/docs/valet#upgrading-valet');
}
})->descriptions('Determine if this is the latest version of Valet');
}, ['latest'])->descriptions('Determine if this is the latest version of Valet');
/**
* Install the sudoers.d entries so password is no longer required.
@@ -487,39 +498,105 @@
info('Sudoers entries have been added for Brew and Valet.');
})->descriptions('Add sudoers files for Brew and Valet to make Valet commands run without passwords', [
'--off' => 'Remove the sudoers files so normal sudo password prompts are required.'
'--off' => 'Remove the sudoers files so normal sudo password prompts are required.',
]);
/**
* Allow the user to change the version of php valet uses
* Allow the user to change the version of php Valet uses.
*/
$app->command('use [phpVersion] [--force]', function ($phpVersion, $force) {
if (!$phpVersion) {
$path = getcwd() . '/.valetphprc';
if (! $phpVersion) {
$site = basename(getcwd());
$linkedVersion = Brew::linkedPhp();
if (!file_exists($path)) {
return info(sprintf('Valet is using %s.', $linkedVersion));
$phpVersion = Site::phpRcVersion($site);
if (! $phpVersion) {
return info("Valet is using {$linkedVersion}.");
}
$phpVersion = trim(file_get_contents($path));
info('Found \'' . $path . '\' specifying version: ' . $phpVersion);
if ($linkedVersion == $phpVersion) {
return info(sprintf('Valet is already using %s.', $linkedVersion));
if ($linkedVersion == $phpVersion && ! $force) {
return info("Valet is already using {$linkedVersion}.");
}
info("Found '{$site}/.valetphprc' specifying version: {$phpVersion}");
}
PhpFpm::validateRequestedVersion($phpVersion);
$newVersion = PhpFpm::useVersion($phpVersion, $force);
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.');
})->descriptions('Change the version of PHP used by valet', [
PhpFpm::useVersion($phpVersion, $force);
})->descriptions('Change the version of PHP used by Valet', [
'phpVersion' => 'The PHP version you want to use, e.g php@7.3',
]);
/**
* Allow the user to change the version of PHP Valet uses to serve the current site.
*/
$app->command('isolate [phpVersion] [--site=]', function ($phpVersion, $site = null) {
if (! $site) {
$site = basename(getcwd());
}
PhpFpm::isolateDirectory($site, $phpVersion);
})->descriptions('Change the version of PHP used by Valet to serve the current working directory', [
'phpVersion' => 'The PHP version you want to use; e.g php@8.1',
'--site' => 'Specify the site to isolate (e.g. if the site isn\'t linked as its directory name)',
]);
/**
* Allow the user to un-do specifying the version of PHP Valet uses to serve the current site.
*/
$app->command('unisolate [--site=]', function ($site = null) {
if (! $site) {
$site = basename(getcwd());
}
PhpFpm::unIsolateDirectory($site);
})->descriptions('Stop customizing the version of PHP used by Valet to serve the current working directory', [
'--site' => 'Specify the site to un-isolate (e.g. if the site isn\'t linked as its directory name)',
]);
/**
* List isolated sites.
*/
$app->command('isolated', function () {
$sites = PhpFpm::isolatedDirectories();
table(['Path', 'PHP Version'], $sites->all());
})->descriptions('List all sites using isolated versions of PHP.');
/**
* Get the PHP executable path for a site.
*/
$app->command('which-php [site]', function ($site) {
$phpVersion = Site::customPhpVersion(
Site::host($site ?: getcwd()).'.'.Configuration::read()['tld']
);
if (! $phpVersion) {
$phpVersion = Site::phpRcVersion($site ?: basename(getcwd()));
}
return output(Brew::getPhpExecutablePath($phpVersion));
})->descriptions('Get the PHP executable path for a given site', [
'site' => 'The site to get the PHP executable path for',
]);
/**
* Proxy commands through to an isolated site's version of PHP.
*/
$app->command('php [command]', function ($command) {
warning('It looks like you are running `cli/valet.php` directly; please use the `valet` script in the project root instead.');
})->descriptions("Proxy PHP commands with isolated site's PHP executable", [
'command' => "Command to run with isolated site's PHP executable",
]);
/**
* Proxy commands through to an isolated site's version of Composer.
*/
$app->command('composer [command]', function ($command) {
warning('It looks like you are running `cli/valet.php` directly; please use the `valet` script in the project root instead.');
})->descriptions("Proxy Composer commands with isolated site's PHP executable", [
'command' => "Composer command to run with isolated site's PHP executable",
]);
/**
* Tail log file.
*/
@@ -589,8 +666,8 @@
})->descriptions('Tail log file');
/**
* Configure or display the directory-listing setting.
*/
* Configure or display the directory-listing setting.
*/
$app->command('directory-listing [status]', function ($status = null) {
$key = 'directory-listing';
$config = Configuration::read();
@@ -598,13 +675,14 @@
if (in_array($status, ['on', 'off'])) {
$config[$key] = $status;
Configuration::write($config);
return output('Directory listing setting is now: '.$status);
}
$current = isset($config[$key]) ? $config[$key] : 'off';
output('Directory listing is '.$current);
})->descriptions('Determine directory-listing behavior. Default is off, which means a 404 will display.', [
'status' => 'on or off. (default=off) will show a 404 page; [on] will display a listing if project folder exists but requested URI not found'
'status' => 'on or off. (default=off) will show a 404 page; [on] will display a listing if project folder exists but requested URI not found',
]);
/**

View File

@@ -24,12 +24,12 @@
}
},
"require": {
"php": ">=5.6",
"illuminate/container": "~5.1|^6.0|^7.0|^8.0",
"mnapoli/silly": "~1.0",
"symfony/process": "~3.0|~4.0|~5.0",
"nategood/httpful": "~0.2",
"tightenco/collect": "^5.3|^6.0|^7.0|^8.0"
"php": "^7.0|^8.0",
"illuminate/container": "~5.1|^6.0|^7.0|^8.0|^9.0",
"mnapoli/silly": "^1.0",
"symfony/process": "^3.0|^4.0|^5.0|^6.0",
"tightenco/collect": "^5.3|^6.0|^7.0|^8.0",
"guzzlehttp/guzzle": "^6.0|^7.4"
},
"require-dev": {
"mockery/mockery": "^1.2.3",

View File

@@ -3,7 +3,6 @@
/**
* Define the user's "~/.config/valet" path.
*/
define('VALET_HOME_PATH', posix_getpwuid(fileowner(__FILE__))['dir'].'/.config/valet');
define('VALET_STATIC_PREFIX', '41c270e4-5535-4daa-b23e-c269744c2f45');
@@ -25,7 +24,7 @@ function show_directory_listing($valetSitePath, $uri)
$is_root = ($uri == '/');
$directory = ($is_root) ? $valetSitePath : $valetSitePath.$uri;
if (!file_exists($directory)) {
if (! file_exists($directory)) {
show_valet_404();
}
@@ -37,9 +36,10 @@ function show_directory_listing($valetSitePath, $uri)
// Output the HTML for the directory listing
echo "<h1>Index of $uri</h1>";
echo "<hr>";
echo '<hr>';
echo implode("<br>\n", array_map(function ($path) use ($uri, $is_root) {
$file = basename($path);
return ($is_root) ? "<a href='/$file'>/$file</a>" : "<a href='$uri/$file'>$uri/$file/</a>";
}, $paths));
@@ -49,7 +49,7 @@ function show_directory_listing($valetSitePath, $uri)
/**
* You may use wildcard DNS providers xip.io or nip.io as a tool for testing your site via an IP address.
* It's simple to use: First determine the IP address of your local computer (like 192.168.0.10).
* Then simply use http://project.your-ip.xip.io - ie: http://laravel.192.168.0.10.xip.io
* Then simply use http://project.your-ip.xip.io - ie: http://laravel.192.168.0.10.xip.io.
*/
function valet_support_wildcard_dns($domain, $config)
{
@@ -67,12 +67,12 @@ function valet_support_wildcard_dns($domain, $config)
foreach ($services as $service) {
$pattern = preg_quote($service, '#');
$pattern = str_replace('\*', '.*', $pattern);
$patterns[] = '(.*)' . $pattern;
$patterns[] = '(.*)'.$pattern;
}
$pattern = implode('|', $patterns);
if (preg_match('#(?:' . $pattern . ')\z#u', $domain, $matches)) {
if (preg_match('#(?:'.$pattern.')\z#u', $domain, $matches)) {
$domain = array_pop($matches);
}
@@ -84,8 +84,7 @@ function valet_support_wildcard_dns($domain, $config)
}
/**
* @param array $config Valet configuration array
*
* @param array $config Valet configuration array
* @return string|null If set, default site path for uncaught urls
* */
function valet_default_site_path($config)
@@ -108,7 +107,7 @@ function valet_default_site_path($config)
* Parse the URI and site / host for the incoming request.
*/
$uri = rawurldecode(
explode("?", $_SERVER['REQUEST_URI'])[0]
explode('?', $_SERVER['REQUEST_URI'])[0]
);
$siteName = basename(
@@ -190,7 +189,7 @@ function get_valet_site_path($valetConfig, $siteName, $domain)
/**
* ngrok uses the X-Original-Host to store the forwarded hostname.
*/
if (isset($_SERVER['HTTP_X_ORIGINAL_HOST']) && !isset($_SERVER['HTTP_X_FORWARDED_HOST'])) {
if (isset($_SERVER['HTTP_X_ORIGINAL_HOST']) && ! isset($_SERVER['HTTP_X_FORWARDED_HOST'])) {
$_SERVER['HTTP_X_FORWARDED_HOST'] = $_SERVER['HTTP_X_ORIGINAL_HOST'];
}

View File

@@ -1,13 +1,13 @@
<?php
use Illuminate\Container\Container;
use Illuminate\Support\Collection;
use Valet\Brew;
use Valet\Filesystem;
use Valet\CommandLine;
use function Valet\user;
use Valet\Filesystem;
use function Valet\resolve;
use function Valet\swap;
use Illuminate\Support\Collection;
use Illuminate\Container\Container;
use function Valet\user;
class BrewTest extends Yoast\PHPUnitPolyfills\TestCases\TestCase
{
@@ -93,24 +93,16 @@ public function test_has_installed_php_indicates_if_php_is_installed_via_brew()
$brew = Mockery::mock(Brew::class.'[installedPhpFormulae]', [new CommandLine, new Filesystem]);
$brew->shouldReceive('installedPhpFormulae')->andReturn(collect(['php@7.0']));
$this->assertTrue($brew->hasInstalledPhp());
$brew = Mockery::mock(Brew::class.'[installedPhpFormulae]', [new CommandLine, new Filesystem]);
$brew->shouldReceive('installedPhpFormulae')->andReturn(collect(['php@5.6']));
$this->assertTrue($brew->hasInstalledPhp());
$brew = Mockery::mock(Brew::class.'[installedPhpFormulae]', [new CommandLine, new Filesystem]);
$brew->shouldReceive('installedPhpFormulae')->andReturn(collect(['php56']));
$this->assertTrue($brew->hasInstalledPhp());
}
public function test_tap_taps_the_given_homebrew_repository()
{
$cli = Mockery::mock(CommandLine::class);
$cli->shouldReceive('passthru')->once()->with('sudo -u "'.user().'" brew tap php@8.0');
$cli->shouldReceive('passthru')->once()->with('sudo -u "'.user().'" brew tap php@7.1');
$cli->shouldReceive('passthru')->once()->with('sudo -u "'.user().'" brew tap php@7.0');
$cli->shouldReceive('passthru')->once()->with('sudo -u "'.user().'" brew tap php@5.6');
swap(CommandLine::class, $cli);
resolve(Brew::class)->tap('php@7.1', 'php@7.0', 'php@5.6');
resolve(Brew::class)->tap('php@8.0', 'php@7.1', 'php@7.0');
}
public function test_restart_restarts_the_service_using_homebrew_services()
@@ -130,6 +122,9 @@ public function test_stop_stops_the_service_using_homebrew_services()
$cli->shouldReceive('runAsUser')->once()->with('brew info dnsmasq --json')->andReturn('[{"name":"dnsmasq","full_name":"dnsmasq","aliases":[],"versioned_formulae":[],"versions":{"stable":"1"},"installed":[{"version":"1"}]}]');
$cli->shouldReceive('quietly')->once()->with('brew services stop dnsmasq');
$cli->shouldReceive('quietly')->once()->with('sudo brew services stop dnsmasq');
$cli->shouldReceive('quietly')->once()->with('sudo chown -R '.user().":admin '".BREW_PREFIX."/Cellar/dnsmasq'");
$cli->shouldReceive('quietly')->once()->with('sudo chown -R '.user().":admin '".BREW_PREFIX."/opt/dnsmasq'");
$cli->shouldReceive('quietly')->once()->with('sudo chown -R '.user().":admin '".BREW_PREFIX."/var/homebrew/linked/dnsmasq'");
swap(CommandLine::class, $cli);
resolve(Brew::class)->stopService('dnsmasq');
}
@@ -139,6 +134,7 @@ public function test_linked_php_returns_linked_php_formula_name()
$getBrewMock = function ($filesystem) {
$brewMock = Mockery::mock(Brew::class, [new CommandLine, $filesystem])->makePartial();
$brewMock->shouldReceive('hasLinkedPhp')->once()->andReturn(true);
return $brewMock;
};
@@ -161,10 +157,6 @@ public function test_linked_php_returns_linked_php_formula_name()
$files = Mockery::mock(Filesystem::class);
$files->shouldReceive('readLink')->once()->with(BREW_PREFIX.'/bin/php')->andReturn('/test/path/php72/7.2.9_2/test');
$this->assertSame('php@7.2', $getBrewMock($files)->linkedPhp());
$files = Mockery::mock(Filesystem::class);
$files->shouldReceive('readLink')->once()->with(BREW_PREFIX.'/bin/php')->andReturn('/test/path/php56/test');
$this->assertSame('php@5.6', $getBrewMock($files)->linkedPhp());
}
public function test_linked_php_throws_exception_if_no_php_link()
@@ -306,7 +298,7 @@ public function test_getRunningServices_will_throw_exception_on_failure()
$onError(1, 'test error output');
});
swap(CommandLine::class, $cli);
resolve(Brew::class)->getRunningServices();
resolve(Brew::class)->getRunningServices(true);
}
public function test_getRunningServices_will_pass_to_brew_services_list_and_return_array()
@@ -314,11 +306,11 @@ public function test_getRunningServices_will_pass_to_brew_services_list_and_retu
$cli = Mockery::mock(CommandLine::class);
$cli->shouldReceive('runAsUser')->once()->withArgs([
'brew services list | grep started | awk \'{ print $1; }\'',
Mockery::type('callable')
])->andReturn('service1' . PHP_EOL . 'service2' . PHP_EOL . PHP_EOL . 'service3' . PHP_EOL);
Mockery::type('callable'),
])->andReturn('service1'.PHP_EOL.'service2'.PHP_EOL.PHP_EOL.'service3'.PHP_EOL);
swap(CommandLine::class, $cli);
$result = resolve(Brew::class)->getRunningServices('term');
$result = resolve(Brew::class)->getRunningServices(true);
$this->assertInstanceOf(Collection::class, $result);
$this->assertSame([
'service1',
@@ -327,6 +319,42 @@ public function test_getRunningServices_will_pass_to_brew_services_list_and_retu
], array_values($result->all()));
}
public function test_getAllRunningServices_will_return_both_root_and_user_services()
{
$cli = Mockery::mock(CommandLine::class);
$cli->shouldReceive('run')->once()->withArgs([
'sudo brew services list | grep started | awk \'{ print $1; }\'',
Mockery::type('callable'),
])->andReturn('sudo_ran_service');
$cli->shouldReceive('runAsUser')->once()->withArgs([
'brew services list | grep started | awk \'{ print $1; }\'',
Mockery::type('callable'),
])->andReturn('user_ran_service');
swap(CommandLine::class, $cli);
$result = resolve(Brew::class)->getAllRunningServices();
$this->assertSame([
'sudo_ran_service',
'user_ran_service',
], array_values($result->all()));
}
public function test_getAllRunningServices_will_return_unique_services()
{
$cli = Mockery::mock(CommandLine::class);
$cli->shouldReceive('run')->once()->andReturn('service1'.PHP_EOL.'service2'.PHP_EOL.'service1'.PHP_EOL);
$cli->shouldReceive('runAsUser')->once()->andReturn('service3'.PHP_EOL.'service4'.PHP_EOL.'service2'.PHP_EOL);
swap(CommandLine::class, $cli);
$result = resolve(Brew::class)->getAllRunningServices();
$this->assertSame([
'service1',
'service2',
'service3',
'service4',
], array_values($result->all()));
}
/**
* @dataProvider supportedPhpLinkPathProvider
*
@@ -338,6 +366,7 @@ public function test_get_parsed_linked_php_will_return_matches_for_linked_php($p
$getBrewMock = function ($filesystem) {
$brewMock = Mockery::mock(Brew::class, [new CommandLine, $filesystem])->makePartial();
$brewMock->shouldReceive('hasLinkedPhp')->once()->andReturn(true);
return $brewMock;
};
@@ -369,8 +398,73 @@ public function test_restart_linked_php_will_pass_through_linked_php_formula_to_
$brewMock->restartLinkedPhp();
}
public function test_it_can_get_php_binary_path_from_php_version()
{
// Check the default `/opt/homebrew/opt/php@8.1/bin/php` location first
$brewMock = Mockery::mock(Brew::class, [
Mockery::mock(CommandLine::class),
$files = Mockery::mock(Filesystem::class),
])->makePartial();
$files->shouldReceive('exists')->once()->with(BREW_PREFIX.'/opt/php@7.4/bin/php')->andReturn(true);
$files->shouldNotReceive('exists')->with(BREW_PREFIX.'/opt/php@74/bin/php');
$this->assertEquals(BREW_PREFIX.'/opt/php@7.4/bin/php', $brewMock->getPhpExecutablePath('php@7.4'));
// Check the `/opt/homebrew/opt/php71/bin/php` location for older installations
$brewMock = Mockery::mock(Brew::class, [
Mockery::mock(CommandLine::class),
$files = Mockery::mock(Filesystem::class),
])->makePartial();
$files->shouldReceive('exists')->once()->with(BREW_PREFIX.'/opt/php@7.4/bin/php')->andReturn(false);
$files->shouldReceive('exists')->with(BREW_PREFIX.'/opt/php74/bin/php')->andReturn(true);
$this->assertEquals(BREW_PREFIX.'/opt/php74/bin/php', $brewMock->getPhpExecutablePath('php@7.4'));
// When the default PHP is the version we are looking for
$brewMock = Mockery::mock(Brew::class, [
Mockery::mock(CommandLine::class),
$files = Mockery::mock(Filesystem::class),
])->makePartial();
$files->shouldReceive('exists')->once()->with(BREW_PREFIX.'/opt/php@7.4/bin/php')->andReturn(false);
$files->shouldReceive('exists')->with(BREW_PREFIX.'/opt/php74/bin/php')->andReturn(false);
$files->shouldReceive('isLink')->with(BREW_PREFIX.'/opt/php')->andReturn(true);
$files->shouldReceive('readLink')->with(BREW_PREFIX.'/opt/php')->andReturn('../Cellar/php@7.4/7.4.13/bin/php');
$this->assertEquals(BREW_PREFIX.'/opt/php/bin/php', $brewMock->getPhpExecutablePath('php@7.4'));
// When the default PHP is not the version we are looking for
$brewMock = Mockery::mock(Brew::class, [
Mockery::mock(CommandLine::class),
$files = Mockery::mock(Filesystem::class),
])->makePartial();
$files->shouldReceive('exists')->once()->with(BREW_PREFIX.'/opt/php@7.4/bin/php')->andReturn(false);
$files->shouldReceive('exists')->with(BREW_PREFIX.'/opt/php74/bin/php')->andReturn(false);
$files->shouldReceive('isLink')->with(BREW_PREFIX.'/opt/php')->andReturn(true);
$files->shouldReceive('readLink')->with(BREW_PREFIX.'/opt/php')->andReturn('../Cellar/php@8.1/8.1.13/bin/php');
$this->assertEquals(BREW_PREFIX.'/bin/php', $brewMock->getPhpExecutablePath('php@7.4')); // Could not find a version, so retuned the default binary
// When no PHP Version is provided
$brewMock = Mockery::mock(Brew::class, [
Mockery::mock(CommandLine::class),
Mockery::mock(Filesystem::class),
])->makePartial();
$this->assertEquals(BREW_PREFIX.'/bin/php', $brewMock->getPhpExecutablePath(null));
}
public function test_it_can_compare_two_php_versions()
{
$this->assertTrue(resolve(Brew::class)->arePhpVersionsEqual('php71', 'php@7.1'));
$this->assertTrue(resolve(Brew::class)->arePhpVersionsEqual('php71', 'php@71'));
$this->assertTrue(resolve(Brew::class)->arePhpVersionsEqual('php71', '71'));
$this->assertFalse(resolve(Brew::class)->arePhpVersionsEqual('php71', 'php@70'));
$this->assertFalse(resolve(Brew::class)->arePhpVersionsEqual('php71', '72'));
}
/**
* Provider of php links and their expected split matches
* Provider of php links and their expected split matches.
*
* @return array
*/
@@ -397,7 +491,7 @@ public function supportedPhpLinkPathProvider()
'7.4',
'.13',
],
'php@7.4'
'php@7.4',
],
[
'/test/path/php/7.4.9_2/test',
@@ -422,15 +516,15 @@ public function supportedPhpLinkPathProvider()
'php74',
],
[
'/test/path/php56/test',
'/test/path/php71/test',
[
'path/php56/test',
'path/php71/test',
'php',
'56',
'71',
'',
'',
],
'php56',
'php71',
],
];
}

View File

@@ -1,13 +1,13 @@
<?php
use Illuminate\Container\Container;
use Valet\Brew;
use Valet\Valet;
use Valet\Filesystem;
use Valet\Configuration;
use function Valet\user;
use Valet\Filesystem;
use function Valet\resolve;
use function Valet\swap;
use Illuminate\Container\Container;
use function Valet\user;
use Valet\Valet;
class ConfigurationTest extends Yoast\PHPUnitPolyfills\TestCases\TestCase
{

View File

@@ -1,14 +1,14 @@
<?php
use Illuminate\Container\Container;
use Valet\Brew;
use Valet\DnsMasq;
use Valet\Filesystem;
use Valet\CommandLine;
use Valet\Configuration;
use function Valet\user;
use Valet\DnsMasq;
use Valet\Filesystem;
use function Valet\resolve;
use function Valet\swap;
use Illuminate\Container\Container;
use function Valet\user;
class DnsMasqTest extends Yoast\PHPUnitPolyfills\TestCases\TestCase
{
@@ -38,7 +38,6 @@ public function test_install_installs_and_places_configuration_files_in_proper_l
$dnsMasq = resolve(StubForCreatingCustomDnsMasqConfigFiles::class);
$dnsMasq->dnsmasqMasterConfigFile = __DIR__.'/output/dnsmasq.conf';
$dnsMasq->dnsmasqSystemConfDir = __DIR__.'/output/dnsmasq.d';
$dnsMasq->resolverPath = __DIR__.'/output/resolver';
@@ -50,7 +49,7 @@ public function test_install_installs_and_places_configuration_files_in_proper_l
$this->assertSame('nameserver '.VALET_LOOPBACK.PHP_EOL, file_get_contents(__DIR__.'/output/resolver/test'));
$this->assertSame('address=/.test/'.VALET_LOOPBACK.PHP_EOL.'listen-address='.VALET_LOOPBACK.PHP_EOL, file_get_contents(__DIR__.'/output/tld-test.conf'));
$this->assertSame('test-contents
' . PHP_EOL . 'conf-dir='.BREW_PREFIX.'/etc/dnsmasq.d/,*.conf' . PHP_EOL,
'.PHP_EOL.'conf-dir='.BREW_PREFIX.'/etc/dnsmasq.d/,*.conf'.PHP_EOL,
file_get_contents($dnsMasq->dnsmasqMasterConfigFile)
);
}

View File

@@ -1,13 +1,13 @@
<?php
use Valet\Site;
use Valet\Nginx;
use Valet\Filesystem;
use Valet\Configuration;
use function Valet\user;
use function Valet\resolve;
use function Valet\swap;
use Illuminate\Container\Container;
use Valet\Configuration;
use Valet\Filesystem;
use Valet\Nginx;
use function Valet\resolve;
use Valet\Site;
use function Valet\swap;
use function Valet\user;
class NginxTest extends Yoast\PHPUnitPolyfills\TestCases\TestCase
{
@@ -86,4 +86,26 @@ public function test_install_nginx_directories_rewrites_secure_nginx_files()
$site->shouldHaveReceived('resecureForNewConfiguration', [$data, $data]);
}
public function test_it_gets_configured_sites()
{
$files = Mockery::mock(Filesystem::class);
$files->shouldReceive('scandir')
->once()
->with(VALET_HOME_PATH.'/Nginx')
->andReturn(['.gitkeep', 'isolated-site-71.test', 'isolated-site-72.test', 'isolated-site-73.test']);
swap(Filesystem::class, $files);
swap(Configuration::class, $config = Mockery::spy(Configuration::class, ['read' => ['tld' => 'test', 'loopback' => VALET_LOOPBACK]]));
swap(Site::class, Mockery::mock(Site::class));
$nginx = resolve(Nginx::class);
$output = $nginx->configuredSites();
$this->assertEquals(
['isolated-site-71.test', 'isolated-site-72.test', 'isolated-site-73.test'],
$output->values()->all()
);
}
}

View File

@@ -1,13 +1,16 @@
<?php
use Valet\Brew;
use Valet\PhpFpm;
use Valet\Filesystem;
use Valet\CommandLine;
use function Valet\user;
use function Valet\swap;
use function Valet\resolve;
use Illuminate\Container\Container;
use Valet\Brew;
use Valet\CommandLine;
use Valet\Configuration;
use Valet\Filesystem;
use Valet\Nginx;
use Valet\PhpFpm;
use function Valet\resolve;
use Valet\Site;
use function Valet\swap;
use function Valet\user;
class PhpFpmTest extends Yoast\PHPUnitPolyfills\TestCases\TestCase
{
@@ -30,23 +33,215 @@ public function tear_down()
public function test_fpm_is_configured_with_the_correct_user_group_and_port()
{
copy(__DIR__.'/files/fpm.conf', __DIR__.'/output/fpm.conf');
copy(__DIR__.'/files/fpm.conf', __DIR__.'/output/www.conf');
mkdir(__DIR__.'/output/conf.d');
copy(__DIR__.'/files/php-memory-limits.ini', __DIR__.'/output/conf.d/php-memory-limits.ini');
resolve(StubForUpdatingFpmConfigFiles::class)->updateConfiguration();
resolve(StubForUpdatingFpmConfigFiles::class)->createConfigurationFiles('php@7.2');
$contents = file_get_contents(__DIR__.'/output/fpm.conf');
$this->assertStringContainsString(sprintf("\nuser = %s", user()), $contents);
$this->assertStringContainsString("\ngroup = staff", $contents);
$this->assertStringContainsString("\nlisten = ".VALET_HOME_PATH."/valet.sock", $contents);
$this->assertStringContainsString("\nlisten = ".VALET_HOME_PATH.'/valet72.sock', $contents);
// It should disable old or default FPM Pool configuration
$this->assertFileDoesNotExist(__DIR__.'/output/www.conf');
$this->assertFileExists(__DIR__.'/output/www.conf-backup');
}
public function test_it_can_generate_sock_file_name_from_php_version()
{
$this->assertEquals('valet72.sock', resolve(PhpFpm::class)->fpmSockName('php@7.2'));
$this->assertEquals('valet72.sock', resolve(PhpFpm::class)->fpmSockName('php@72'));
$this->assertEquals('valet72.sock', resolve(PhpFpm::class)->fpmSockName('php72'));
$this->assertEquals('valet72.sock', resolve(PhpFpm::class)->fpmSockName('72'));
}
public function test_it_normalizes_php_versions()
{
$this->assertEquals('php@8.1', resolve(PhpFpm::class)->normalizePhpVersion('php@8.1'));
$this->assertEquals('php@8.1', resolve(PhpFpm::class)->normalizePhpVersion('php8.1'));
$this->assertEquals('php@8.1', resolve(PhpFpm::class)->normalizePhpVersion('php81'));
$this->assertEquals('php@8.1', resolve(PhpFpm::class)->normalizePhpVersion('8.1'));
$this->assertEquals('php@8.1', resolve(PhpFpm::class)->normalizePhpVersion('81'));
}
public function test_it_validates_php_versions_when_installed()
{
$brewMock = Mockery::mock(Brew::class);
$brewMock->shouldReceive('supportedPhpVersions')->andReturn(collect(['php@7.4']));
$brewMock->shouldReceive('determineAliasedVersion')->andReturn('7.4');
swap(Brew::class, $brewMock);
$this->assertEquals('php@7.4', resolve(PhpFpm::class)->validateRequestedVersion('7.4'));
}
public function test_it_validates_php_versions_when_uninstalled()
{
$brewMock = Mockery::mock(Brew::class);
$brewMock->shouldReceive('supportedPhpVersions')->andReturn(collect(['php@7.4']));
$brewMock->shouldReceive('determineAliasedVersion')->andReturn('ERROR - NO BREW ALIAS FOUND');
swap(Brew::class, $brewMock);
$this->assertEquals('php@7.4', resolve(PhpFpm::class)->validateRequestedVersion('7.4'));
}
public function test_it_throws_when_validating_invalid_php()
{
$this->expectException(DomainException::class);
$brewMock = Mockery::mock(Brew::class);
$brewMock->shouldReceive('supportedPhpVersions')->andReturn(collect(['php@7.4']));
$brewMock->shouldReceive('determineAliasedVersion')->andReturn('ERROR - NO BREW ALIAS FOUND');
swap(Brew::class, $brewMock);
$this->assertEquals('php@7.4', resolve(PhpFpm::class)->validateRequestedVersion('9.1'));
}
public function test_utilized_php_versions()
{
$brewMock = Mockery::mock(Brew::class);
$nginxMock = Mockery::mock(Nginx::class);
$fileSystemMock = Mockery::mock(Filesystem::class);
$brewMock->shouldReceive('supportedPhpVersions')->andReturn(collect([
'php@7.1',
'php@7.2',
'php@7.3',
'php@7.4',
]));
$brewMock->shouldReceive('getLinkedPhpFormula')->andReturn('php@7.3');
$nginxMock->shouldReceive('configuredSites')
->once()
->andReturn(collect(['isolated-site-71.test', 'isolated-site-72.test', 'isolated-site-73.test']));
$sites = [
[
'site' => 'isolated-site-71.test',
'conf' => '# '.ISOLATED_PHP_VERSION.'=71'.PHP_EOL.'valet71.sock',
],
[
'site' => 'isolated-site-72.test',
'conf' => '# '.ISOLATED_PHP_VERSION.'=php@7.2'.PHP_EOL.'valet72.sock',
],
[
'site' => 'isolated-site-73.test',
'conf' => '# '.ISOLATED_PHP_VERSION.'=73'.PHP_EOL.'valet.sock',
],
];
foreach ($sites as $site) {
$fileSystemMock->shouldReceive('get')->once()->with(VALET_HOME_PATH.'/Nginx/'.$site['site'])->andReturn($site['conf']);
}
swap(Filesystem::class, $fileSystemMock);
swap(Brew::class, $brewMock);
swap(Nginx::class, $nginxMock);
$this->assertEquals(['php@7.1', 'php@7.2', 'php@7.3'], resolve(PhpFpm::class)->utilizedPhpVersions());
}
public function test_it_lists_isolated_directories()
{
$nginxMock = Mockery::mock(Nginx::class);
$siteMock = Mockery::mock(Site::class);
$fileSystemMock = Mockery::mock(Filesystem::class);
$nginxMock->shouldReceive('configuredSites')
->once()
->andReturn(collect(['isolated-site-71.test', 'isolated-site-72.test', 'not-isolated-site.test']));
$siteMock->shouldReceive('customPhpVersion')->with('isolated-site-71.test')->andReturn('71');
$siteMock->shouldReceive('customPhpVersion')->with('isolated-site-72.test')->andReturn('72');
$siteMock->shouldReceive('normalizePhpVersion')->with('71')->andReturn('php@7.1');
$siteMock->shouldReceive('normalizePhpVersion')->with('72')->andReturn('php@7.2');
$sites = [
[
'site' => 'isolated-site-71.test',
'conf' => '# '.ISOLATED_PHP_VERSION.'=71'.PHP_EOL.'valet71.sock',
],
[
'site' => 'isolated-site-72.test',
'conf' => '# '.ISOLATED_PHP_VERSION.'=php@7.2'.PHP_EOL.'valet72.sock',
],
[
'site' => 'not-isolated-site.test',
'conf' => 'This one is not isolated',
],
];
foreach ($sites as $site) {
$fileSystemMock->shouldReceive('get')->once()->with(VALET_HOME_PATH.'/Nginx/'.$site['site'])->andReturn($site['conf']);
}
swap(Nginx::class, $nginxMock);
swap(Site::class, $siteMock);
swap(Filesystem::class, $fileSystemMock);
$this->assertEquals([
[
'url' => 'isolated-site-71.test',
'version' => 'php@7.1',
],
[
'url' => 'isolated-site-72.test',
'version' => 'php@7.2',
],
], resolve(PhpFpm::class)->isolatedDirectories()->toArray());
}
public function test_stop_unused_php_versions()
{
$brewMock = Mockery::mock(Brew::class);
$phpFpmMock = Mockery::mock(PhpFpm::class, [
$brewMock,
Mockery::mock(CommandLine::class),
Mockery::mock(Filesystem::class),
resolve(Configuration::class),
Mockery::mock(Site::class),
Mockery::mock(Nginx::class),
])->makePartial();
swap(PhpFpm::class, $phpFpmMock);
$phpFpmMock->shouldReceive('utilizedPhpVersions')->andReturn([
'php@7.1',
'php@7.2',
]);
// Would do nothing
resolve(PhpFpm::class)->stopIfUnused(null);
// This currently-un-used PHP version should be stopped
$brewMock->shouldReceive('stopService')->times(3)->with('php@7.3');
resolve(PhpFpm::class)->stopIfUnused('73');
resolve(PhpFpm::class)->stopIfUnused('php73');
resolve(PhpFpm::class)->stopIfUnused('php@7.3');
// These currently-used PHP versions should not be stopped
$brewMock->shouldNotReceive('stopService')->with('php@7.1');
$brewMock->shouldNotReceive('stopService')->with('php@7.2');
resolve(PhpFpm::class)->stopIfUnused('php@7.1');
resolve(PhpFpm::class)->stopIfUnused('php@7.2');
}
public function test_stopRunning_will_pass_filtered_result_of_getRunningServices_to_stopService()
{
$brewMock = Mockery::mock(Brew::class);
$brewMock->shouldReceive('getRunningServices')->once()
$brewMock->shouldReceive('getAllRunningServices')->once()
->andReturn(collect([
'php7.2',
'php@7.3',
'php56',
'php71',
'php',
'nginx',
'somethingelse',
@@ -54,7 +249,7 @@ public function test_stopRunning_will_pass_filtered_result_of_getRunningServices
$brewMock->shouldReceive('stopService')->once()->with([
'php7.2',
'php@7.3',
'php56',
'php71',
'php',
]);
@@ -65,17 +260,25 @@ public function test_stopRunning_will_pass_filtered_result_of_getRunningServices
public function test_use_version_will_convert_passed_php_version()
{
$brewMock = Mockery::mock(Brew::class);
$nginxMock = Mockery::mock(Nginx::class);
$siteMock = Mockery::mock(Site::class);
$filesystem = Mockery::mock(Filesystem::class);
$cli = Mockery::mock(CommandLine::class);
$phpFpmMock = Mockery::mock(PhpFpm::class, [
$brewMock,
resolve(CommandLine::class),
resolve(Filesystem::class),
$cli,
$filesystem,
resolve(Configuration::class),
$siteMock,
$nginxMock,
])->makePartial();
$phpFpmMock->shouldReceive('install');
$brewMock->shouldReceive('supportedPhpVersions')->twice()->andReturn(collect([
'php@7.2',
'php@5.6',
'php@7.1',
]));
$brewMock->shouldReceive('hasLinkedPhp')->andReturn(false);
$brewMock->shouldReceive('ensureInstalled')->with('php@7.2', [], $phpFpmMock->taps);
@@ -83,9 +286,15 @@ public function test_use_version_will_convert_passed_php_version()
$brewMock->shouldReceive('link')->withArgs(['php@7.2', true]);
$brewMock->shouldReceive('linkedPhp');
$brewMock->shouldReceive('installed');
$brewMock->shouldReceive('getRunningServices')->andReturn(collect());
$brewMock->shouldReceive('getAllRunningServices')->andReturn(collect());
$brewMock->shouldReceive('stopService');
$nginxMock->shouldReceive('restart');
$filesystem->shouldReceive('unlink')->with(VALET_HOME_PATH.'/valet.sock');
$cli->shouldReceive('quietly')->with('sudo rm '.VALET_HOME_PATH.'/valet.sock');
// Test both non prefixed and prefixed
$this->assertSame('php@7.2', $phpFpmMock->useVersion('php7.2'));
$this->assertSame('php@7.2', $phpFpmMock->useVersion('php72'));
@@ -109,18 +318,27 @@ public function test_use_version_will_throw_if_version_not_supported()
public function test_use_version_if_already_linked_php_will_unlink_before_installing()
{
$brewMock = Mockery::mock(Brew::class);
$nginxMock = Mockery::mock(Nginx::class);
$siteMock = Mockery::mock(Site::class);
$phpFpmMock = Mockery::mock(PhpFpm::class, [
$brewMock,
resolve(CommandLine::class),
resolve(Filesystem::class),
resolve(Configuration::class),
$siteMock,
$nginxMock,
])->makePartial();
$phpFpmMock->shouldReceive('install');
$brewMock->shouldReceive('supportedPhpVersions')->andReturn(collect([
'php@7.2',
'php@5.6',
'php@7.1',
]));
$brewMock->shouldReceive('hasLinkedPhp')->andReturn(true);
$brewMock->shouldReceive('linkedPhp')->andReturn('php@7.1');
$brewMock->shouldReceive('getLinkedPhpFormula')->andReturn('php@7.1');
$brewMock->shouldReceive('unlink')->with('php@7.1');
$brewMock->shouldReceive('ensureInstalled')->with('php@7.2', [], $phpFpmMock->taps);
@@ -128,18 +346,102 @@ public function test_use_version_if_already_linked_php_will_unlink_before_instal
$brewMock->shouldReceive('link')->withArgs(['php@7.2', true]);
$brewMock->shouldReceive('linkedPhp');
$brewMock->shouldReceive('installed');
$brewMock->shouldReceive('getRunningServices')->andReturn(collect());
$brewMock->shouldReceive('getAllRunningServices')->andReturn(collect());
$brewMock->shouldReceive('stopService');
// Test both non prefixed and prefixed
$nginxMock->shouldReceive('restart');
$this->assertSame('php@7.2', $phpFpmMock->useVersion('php@7.2'));
}
public function test_isolate_will_isolate_a_site()
{
$brewMock = Mockery::mock(Brew::class);
$nginxMock = Mockery::mock(Nginx::class);
$siteMock = Mockery::mock(Site::class);
$phpFpmMock = Mockery::mock(PhpFpm::class, [
$brewMock,
resolve(CommandLine::class),
resolve(Filesystem::class),
resolve(Configuration::class),
$siteMock,
$nginxMock,
])->makePartial();
$brewMock->shouldReceive('supportedPhpVersions')->andReturn(collect([
'php@7.2',
'php@7.1',
]));
$brewMock->shouldReceive('ensureInstalled')->with('php@7.2', [], $phpFpmMock->taps);
$brewMock->shouldReceive('installed')->with('php@7.2');
$brewMock->shouldReceive('determineAliasedVersion')->with('php@7.2')->andReturn('php@7.2');
// $brewMock->shouldReceive('linkedPhp')->once();
$siteMock->shouldReceive('getSiteUrl')->with('test')->andReturn('test.test');
$siteMock->shouldReceive('isolate')->withArgs(['test.test', 'php@7.2']);
$siteMock->shouldReceive('customPhpVersion')->with('test.test')->andReturn('72');
$phpFpmMock->shouldReceive('stopIfUnused')->with('72')->once();
$phpFpmMock->shouldReceive('createConfigurationFiles')->with('php@7.2')->once();
$phpFpmMock->shouldReceive('restart')->with('php@7.2')->once();
$nginxMock->shouldReceive('restart');
// These should only run when doing global PHP switches
$brewMock->shouldNotReceive('stopService');
$brewMock->shouldNotReceive('link');
$brewMock->shouldNotReceive('unlink');
$phpFpmMock->shouldNotReceive('stopRunning');
$phpFpmMock->shouldNotReceive('install');
$this->assertSame(null, $phpFpmMock->isolateDirectory('test', 'php@7.2'));
}
public function test_un_isolate_will_remove_isolation_for_a_site()
{
$nginxMock = Mockery::mock(Nginx::class);
$siteMock = Mockery::mock(Site::class);
$phpFpmMock = Mockery::mock(PhpFpm::class, [
Mockery::mock(Brew::class),
resolve(CommandLine::class),
resolve(Filesystem::class),
resolve(Configuration::class),
$siteMock,
$nginxMock,
])->makePartial();
$siteMock->shouldReceive('getSiteUrl')->with('test')->andReturn('test.test');
$siteMock->shouldReceive('customPhpVersion')->with('test.test')->andReturn('74');
$siteMock->shouldReceive('removeIsolation')->with('test.test')->once();
$phpFpmMock->shouldReceive('stopIfUnused')->with('74');
$nginxMock->shouldReceive('restart');
$this->assertSame(null, $phpFpmMock->unIsolateDirectory('test'));
}
public function test_isolate_will_throw_if_site_is_not_parked_or_linked()
{
$brewMock = Mockery::mock(Brew::class);
$configMock = Mockery::mock(Configuration::class);
$configMock->shouldReceive('read')->andReturn(['tld' => 'jamble', 'paths' => []]);
swap(Brew::class, $brewMock);
swap(Nginx::class, Mockery::mock(Nginx::class));
swap(Configuration::class, $configMock);
$this->expectException(DomainException::class);
$this->expectExceptionMessage("The [test] site could not be found in Valet's site list.");
resolve(PhpFpm::class)->isolateDirectory('test', 'php@8.1');
}
}
class StubForUpdatingFpmConfigFiles extends PhpFpm
{
function fpmConfigPath()
public function fpmConfigPath($phpVersion = null)
{
return __DIR__.'/output/fpm.conf';
}

View File

@@ -1,13 +1,13 @@
<?php
use Valet\CommandLine;
use Valet\Site;
use Valet\Filesystem;
use Valet\Configuration;
use function Valet\user;
use function Valet\resolve;
use function Valet\swap;
use Illuminate\Container\Container;
use Valet\CommandLine;
use Valet\Configuration;
use Valet\Filesystem;
use function Valet\resolve;
use Valet\Site;
use function Valet\swap;
use function Valet\user;
class SiteTest extends Yoast\PHPUnitPolyfills\TestCases\TestCase
{
@@ -63,7 +63,7 @@ public function test_get_sites_will_return_if_secured()
->andReturn(false);
$files->shouldReceive('realpath')
->twice()
->andReturn($dirPath . '/sitetwo', $dirPath . '/sitethree');
->andReturn($dirPath.'/sitetwo', $dirPath.'/sitethree');
$files->shouldReceive('isDir')->andReturn(true);
$files->shouldReceive('ensureDirExists')
->once()
@@ -95,13 +95,13 @@ public function test_get_sites_will_return_if_secured()
'site' => 'sitetwo',
'secured' => '',
'url' => 'http://sitetwo.local',
'path' => $dirPath . '/sitetwo',
'path' => $dirPath.'/sitetwo',
], $sites->first());
$this->assertSame([
'site' => 'sitethree',
'secured' => ' X',
'url' => 'https://sitethree.local',
'path' => $dirPath . '/sitethree',
'path' => $dirPath.'/sitethree',
], $sites->last());
}
@@ -115,13 +115,13 @@ public function test_get_sites_will_work_with_non_symlinked_path()
->andReturn(['sitetwo']);
$files->shouldReceive('isLink')
->once()
->with($dirPath . '/sitetwo')
->with($dirPath.'/sitetwo')
->andReturn(false);
$files->shouldReceive('realpath')
->once()
->with($dirPath . '/sitetwo')
->andReturn($dirPath . '/sitetwo');
$files->shouldReceive('isDir')->once()->with($dirPath . '/sitetwo')->andReturn(true);
->with($dirPath.'/sitetwo')
->andReturn($dirPath.'/sitetwo');
$files->shouldReceive('isDir')->once()->with($dirPath.'/sitetwo')->andReturn(true);
$files->shouldReceive('ensureDirExists')
->once()
->with($dirPath, user());
@@ -143,7 +143,7 @@ public function test_get_sites_will_work_with_non_symlinked_path()
'site' => 'sitetwo',
'secured' => '',
'url' => 'http://sitetwo.local',
'path' => $dirPath . '/sitetwo',
'path' => $dirPath.'/sitetwo',
], $sites->first());
}
@@ -156,7 +156,7 @@ public function test_get_sites_will_not_return_if_path_is_not_directory()
->with($dirPath)
->andReturn(['sitetwo', 'siteone']);
$files->shouldReceive('isLink')->andReturn(false);
$files->shouldReceive('realpath')->andReturn($dirPath . '/sitetwo', $dirPath . '/siteone');
$files->shouldReceive('realpath')->andReturn($dirPath.'/sitetwo', $dirPath.'/siteone');
$files->shouldReceive('isDir')->twice()
->andReturn(false, true);
$files->shouldReceive('ensureDirExists')
@@ -180,7 +180,7 @@ public function test_get_sites_will_not_return_if_path_is_not_directory()
'site' => 'siteone',
'secured' => '',
'url' => 'http://siteone.local',
'path' => $dirPath . '/siteone',
'path' => $dirPath.'/siteone',
], $sites->first());
}
@@ -194,11 +194,11 @@ public function test_get_sites_will_work_with_symlinked_path()
->andReturn(['siteone']);
$files->shouldReceive('isLink')
->once()
->with($dirPath . '/siteone')
->with($dirPath.'/siteone')
->andReturn(true);
$files->shouldReceive('readLink')
->once()
->with($dirPath . '/siteone')
->with($dirPath.'/siteone')
->andReturn($linkedPath = '/Users/usertest/linkedpath/siteone');
$files->shouldReceive('isDir')->andReturn(true);
$files->shouldReceive('ensureDirExists')
@@ -368,7 +368,6 @@ public function test_add_proxy()
], $site->proxies()->all());
}
public function test_add_non_secure_proxy()
{
$config = Mockery::mock(Configuration::class);
@@ -402,7 +401,6 @@ public function test_add_non_secure_proxy()
], $site->proxies()->all());
}
public function test_add_proxy_clears_previous_proxy_certificate()
{
$config = Mockery::mock(Configuration::class);
@@ -528,8 +526,349 @@ public function test_remove_proxy()
$this->assertEquals([], $site->proxies()->all());
}
}
public function test_gets_site_url_from_directory()
{
$config = Mockery::mock(Configuration::class);
swap(Configuration::class, $config);
$siteMock = Mockery::mock(Site::class, [
resolve(Configuration::class),
resolve(CommandLine::class),
resolve(Filesystem::class),
])->makePartial();
swap(Site::class, $siteMock);
$config->shouldReceive('read')
->andReturn(['tld' => 'test', 'loopback' => VALET_LOOPBACK, 'paths' => []]);
$siteMock->shouldReceive('parked')
->andReturn(collect([
'site1' => [
'site' => 'site1',
'secured' => '',
'url' => 'http://site1.test',
'path' => '/Users/name/code/site1',
],
]));
$siteMock->shouldReceive('links')->andReturn(collect([
'site2' => [
'site' => 'site2',
'secured' => 'X',
'url' => 'http://site2.test',
'path' => '/Users/name/code/site2',
],
]));
$siteMock->shouldReceive('host')->andReturn('site1');
$site = resolve(Site::class);
$this->assertEquals('site1.test', $site->getSiteUrl('.'));
$this->assertEquals('site1.test', $site->getSiteUrl('./'));
$this->assertEquals('site1.test', $site->getSiteUrl('site1'));
$this->assertEquals('site1.test', $site->getSiteUrl('site1.test'));
$this->assertEquals('site2.test', $site->getSiteUrl('site2'));
$this->assertEquals('site2.test', $site->getSiteUrl('site2.test'));
}
public function test_it_throws_getting_nonexistent_site()
{
$this->expectException(DomainException::class);
$config = Mockery::mock(Configuration::class);
swap(Configuration::class, $config);
$siteMock = Mockery::mock(Site::class, [
resolve(Configuration::class),
resolve(CommandLine::class),
resolve(Filesystem::class),
])->makePartial();
swap(Site::class, $siteMock);
$config->shouldReceive('read')
->andReturn(['tld' => 'test', 'loopback' => VALET_LOOPBACK, 'paths' => []]);
$siteMock->shouldReceive('parked')->andReturn(collect());
$siteMock->shouldReceive('links')->andReturn(collect([]));
$siteMock->shouldReceive('host')->andReturn('site1');
$site = resolve(Site::class);
$this->assertEquals(false, $site->getSiteUrl('site3'));
}
public function test_isolation_will_persist_when_adding_ssl_certificate()
{
$files = Mockery::mock(Filesystem::class);
$config = Mockery::mock(Configuration::class);
$siteMock = Mockery::mock(Site::class, [
$config,
Mockery::mock(CommandLine::class),
$files,
])->makePartial();
swap(Site::class, $siteMock);
$siteMock->shouldReceive('unsecure');
$files->shouldReceive('ensureDirExists');
$files->shouldReceive('putAsUser');
$siteMock->shouldReceive('createCa');
$siteMock->shouldReceive('createCertificate');
$siteMock->shouldReceive('buildSecureNginxServer');
// If site has an isolated PHP version for the site, it would replace .sock file
$siteMock->shouldReceive('customPhpVersion')->with('site1.test')->andReturn('73')->once();
$siteMock->shouldReceive('replaceSockFile')->withArgs([Mockery::any(), '73'])->once();
resolve(Site::class)->secure('site1.test');
// For sites without an isolated PHP version, nothing should be replaced
$siteMock->shouldReceive('customPhpVersion')->with('site2.test')->andReturn(null)->once();
$siteMock->shouldNotReceive('replaceSockFile');
resolve(Site::class)->secure('site2.test');
}
public function test_isolation_will_persist_when_removing_ssl_certificate()
{
$files = Mockery::mock(Filesystem::class);
$config = Mockery::mock(Configuration::class);
$cli = Mockery::mock(CommandLine::class);
$siteMock = Mockery::mock(Site::class, [
$config,
$cli,
$files,
])->makePartial();
swap(Site::class, $siteMock);
$cli->shouldReceive('run');
$files->shouldReceive('exists')->andReturn(false);
// If a site has an isolated PHP version, there should still be a custom nginx site config
$siteMock->shouldReceive('customPhpVersion')->with('site1.test')->andReturn('73')->once();
$siteMock->shouldReceive('isolate')->withArgs(['site1.test', '73'])->once();
resolve(Site::class)->unsecure('site1.test');
// If a site doesn't have an isolated PHP version, there should no longer be a custom nginx site config
$siteMock->shouldReceive('customPhpVersion')->with('site2.test')->andReturn(null)->once();
$siteMock->shouldNotReceive('isolate');
resolve(Site::class)->unsecure('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, [
$config,
resolve(CommandLine::class),
$files,
])->makePartial();
$config->shouldReceive('read')
->andReturn(['tld' => 'test', 'loopback' => VALET_LOOPBACK]);
// If Nginx config exists for the site, modify exising config
$files->shouldReceive('exists')->once()->with($siteMock->nginxPath('site1.test'))->andReturn(true);
$files->shouldReceive('get')
->once()
->with($siteMock->nginxPath('site1.test'))
->andReturn('# '.ISOLATED_PHP_VERSION.'=php@7.4'.PHP_EOL.'server { fastcgi_pass: valet74.sock }');
$files->shouldReceive('putAsUser')
->once()
->withArgs([
$siteMock->nginxPath('site1.test'),
'# '.ISOLATED_PHP_VERSION.'=php@8.0'.PHP_EOL.'server { fastcgi_pass: valet80.sock }',
]);
$siteMock->isolate('site1.test', 'php@8.0');
// When no Nginx file exists, it will create a new config file from the template
$files->shouldReceive('exists')->once()->with($siteMock->nginxPath('site2.test'))->andReturn(false);
$files->shouldReceive('get')
->once()
->with(dirname(__DIR__).'/cli/Valet/../stubs/site.valet.conf')
->andReturn(file_get_contents(__DIR__.'/../cli/stubs/site.valet.conf'));
$files->shouldReceive('putAsUser')
->once()
->withArgs([
$siteMock->nginxPath('site2.test'),
Mockery::on(function ($argument) {
return preg_match('/^# '.ISOLATED_PHP_VERSION.'=php@8.0/', $argument)
&& preg_match('#fastcgi_pass "unix:.*/valet80.sock#', $argument)
&& strpos($argument, 'server_name site2.test www.site2.test *.site2.test;') !== false;
}),
]);
$siteMock->isolate('site2.test', 'php@8.0');
}
public function test_it_removes_isolation()
{
$files = Mockery::mock(Filesystem::class);
$siteMock = Mockery::mock(Site::class, [
resolve(Configuration::class),
resolve(CommandLine::class),
$files,
])->makePartial();
swap(Site::class, $siteMock);
// SSL Site
$files->shouldReceive('exists')->once()->with($siteMock->certificatesPath('site1.test', 'crt'))->andReturn(true);
$files->shouldReceive('putAsUser')->withArgs([$siteMock->nginxPath('site1.test'), Mockery::any()])->once();
$siteMock->shouldReceive('buildSecureNginxServer')->once()->with('site1.test');
resolve(Site::class)->removeIsolation('site1.test');
// Non-SSL Site
$files->shouldReceive('exists')->once()->with($siteMock->certificatesPath('site2.test', 'crt'))->andReturn(false);
$files->shouldReceive('unlink')->with($siteMock->nginxPath('site2.test'))->once();
$siteMock->shouldNotReceive('buildSecureNginxServer')->with('site2.test');
resolve(Site::class)->removeIsolation('site2.test');
}
public function test_retrieves_custom_php_version_from_nginx_config()
{
$files = Mockery::mock(Filesystem::class);
$siteMock = Mockery::mock(Site::class, [
resolve(Configuration::class),
resolve(CommandLine::class),
$files,
])->makePartial();
swap(Site::class, $siteMock);
// Site with isolated PHP version
$files->shouldReceive('exists')->once()->with($siteMock->nginxPath('site1.test'))->andReturn(true);
$files->shouldReceive('get')
->once()
->with($siteMock->nginxPath('site1.test'))
->andReturn('# '.ISOLATED_PHP_VERSION.'=php@7.4');
$this->assertEquals('74', resolve(Site::class)->customPhpVersion('site1.test'));
// Site without any custom nginx config
$files->shouldReceive('exists')->once()->with($siteMock->nginxPath('site2.test'))->andReturn(false);
$files->shouldNotReceive('get')->with($siteMock->nginxPath('site2.test'));
$this->assertEquals(null, resolve(Site::class)->customPhpVersion('site2.test'));
// Site with a custom nginx config, but doesn't have the header
$files->shouldReceive('exists')->once()->with($siteMock->nginxPath('site3.test'))->andReturn(true);
$files->shouldReceive('get')
->once()
->with($siteMock->nginxPath('site3.test'))
->andReturn('server { }');
$this->assertEquals(null, resolve(Site::class)->customPhpVersion('site3.test'));
}
public function test_replace_sock_file_in_nginx_config()
{
$site = resolve(Site::class);
// When switching to php71, valet71.sock should be replaced with valet.sock;
// isolation header should be prepended
$this->assertEquals(
'# '.ISOLATED_PHP_VERSION.'=71'.PHP_EOL.'server { fastcgi_pass: valet71.sock }',
$site->replaceSockFile('server { fastcgi_pass: valet71.sock }', '71')
);
// When switching to php72, valet.sock should be replaced with valet72.sock
$this->assertEquals(
'# '.ISOLATED_PHP_VERSION.'=72'.PHP_EOL.'server { fastcgi_pass: valet72.sock }',
$site->replaceSockFile('server { fastcgi_pass: valet.sock }', '72')
);
// When switching to php73 from php72, valet72.sock should be replaced with valet73.sock;
// isolation header should be updated to php@7.3
$this->assertEquals(
'# '.ISOLATED_PHP_VERSION.'=73'.PHP_EOL.'server { fastcgi_pass: valet73.sock }',
$site->replaceSockFile('# '.ISOLATED_PHP_VERSION.'=72'.PHP_EOL.'server { fastcgi_pass: valet72.sock }', '73')
);
// When switching to php72 from php74, valet72.sock should be replaced with valet74.sock;
// isolation header should be updated to php@7.4
$this->assertEquals(
'# '.ISOLATED_PHP_VERSION.'=php@7.4'.PHP_EOL.'server { fastcgi_pass: valet74.sock }',
$site->replaceSockFile('# '.ISOLATED_PHP_VERSION.'=72'.PHP_EOL.'server { fastcgi_pass: valet.sock }', 'php@7.4')
);
}
public function test_it_returns_secured_sites()
{
$files = Mockery::mock(Filesystem::class);
$files->shouldReceive('scandir')
->once()
->andReturn(['helloworld.tld.crt', '.DS_Store']);
swap(Filesystem::class, $files);
$site = resolve(Site::class);
$sites = $site->secured();
$this->assertSame(['helloworld.tld'], $sites);
}
public function test_it_can_read_php_rc_version()
{
$config = Mockery::mock(Configuration::class);
$files = Mockery::mock(Filesystem::class);
swap(Configuration::class, $config);
swap(Filesystem::class, $files);
$siteMock = Mockery::mock(Site::class, [
resolve(Configuration::class),
resolve(CommandLine::class),
resolve(Filesystem::class),
])->makePartial();
swap(Site::class, $siteMock);
$config->shouldReceive('read')
->andReturn(['tld' => 'test', 'loopback' => VALET_LOOPBACK, 'paths' => []]);
$siteMock->shouldReceive('parked')
->andReturn(collect([
'site1' => [
'site' => 'site1',
'secured' => '',
'url' => 'http://site1.test',
'path' => '/Users/name/code/site1',
],
]));
$siteMock->shouldReceive('links')->andReturn(collect([
'site2' => [
'site' => 'site2',
'secured' => 'X',
'url' => 'http://site2.test',
'path' => '/Users/name/some-other-directory/site2',
],
]));
$files->shouldReceive('exists')->with('/Users/name/code/site1/.valetphprc')->andReturn(true);
$files->shouldReceive('get')->with('/Users/name/code/site1/.valetphprc')->andReturn('php@8.1');
$files->shouldReceive('exists')->with('/Users/name/some-other-directory/site2/.valetphprc')->andReturn(true);
$files->shouldReceive('get')->with('/Users/name/some-other-directory/site2/.valetphprc')->andReturn('php@8.0');
$this->assertEquals('php@8.1', $siteMock->phpRcVersion('site1'));
$this->assertEquals('php@8.0', $siteMock->phpRcVersion('site2'));
$this->assertEquals(null, $siteMock->phpRcVersion('site3')); // Site doesn't exists
}
}
class CommandLineFake extends CommandLine
{
@@ -545,7 +884,6 @@ public function runCommand($command, callable $onError = null)
}
}
class FixturesSiteFake extends Site
{
private $valetHomePath;
@@ -578,7 +916,7 @@ public function useOutput()
$this->valetHomePath = __DIR__.'/output';
}
public function createCa()
public function createCa($caExpireInDays)
{
// noop
//
@@ -587,7 +925,7 @@ public function createCa()
// CA for our faked Site.
}
public function createCertificate($urlWithTld)
public function createCertificate($urlWithTld, $caExpireInDays)
{
// We're not actually going to generate a real certificate
// here. We are going to do something basic to include
@@ -611,7 +949,7 @@ public function fakeSecure($urlWithTld)
// forcing a fake creation of a URL (including .tld) and passes
// through to createCertificate() directly.
$this->files->ensureDirExists($this->certificatesPath(), user());
$this->createCertificate($urlWithTld);
$this->createCertificate($urlWithTld, 368);
}
public function assertNginxExists($urlWithTld)
@@ -652,7 +990,6 @@ public function assertCertificateExistsWithCounterValue($urlWithTld, $counter)
}
}
class StubForRemovingLinks extends Site
{
public function sitesPath($additionalPath = null)

37
valet
View File

@@ -20,12 +20,6 @@ then
DIR=$(php -r "echo realpath('$DIR/../laravel/valet');")
fi
if [[ "$EUID" -ne 0 ]]
then
sudo USER="$USER" --preserve-env "$SOURCE" "$@"
exit
fi
# If the command is the "share" command we will need to resolve out any
# symbolic links for the site. Before starting Ngrok, we will fire a
# process to retrieve the live Ngrok tunnel URL in the background.
@@ -77,13 +71,40 @@ then
# Fetch Ngrok URL In Background...
bash "$DIR/cli/scripts/fetch-share-url.sh" "$HOST" &
sudo -u "$USER" "$DIR/bin/ngrok" http "$HOST.$TLD:$PORT" -host-header=rewrite $PARAMS
ARCH=$(uname -m)
if [[ $ARCH == 'arm64' ]]; then
"$DIR/bin/ngrok-arm" http "$HOST.$TLD:$PORT" -host-header=rewrite $PARAMS
else
"$DIR/bin/ngrok" http "$HOST.$TLD:$PORT" -host-header=rewrite $PARAMS
fi
exit
# Proxy PHP commands to the "php" executable on the isolated site
elif [[ "$1" = "php" ]]
then
$(php "$DIR/cli/valet.php" which-php) "${@:2}"
exit
# Proxy Composer commands with the "php" executable on the isolated site
elif [[ "$1" = "composer" ]]
then
$(php "$DIR/cli/valet.php" which-php) $(which composer) "${@:2}"
exit
# Finally, for every other command we will just proxy into the PHP tool
# and let it handle the request. These are commands which can be run
# without sudo and don't require taking over terminals like Ngrok.
else
if [[ "$EUID" -ne 0 ]]
then
sudo USER="$USER" --preserve-env "$SOURCE" "$@"
exit
fi
php "$DIR/cli/valet.php" "$@"
fi