diff --git a/.editorconfig b/.editorconfig
index 51c425c5a..e87171768 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -2,7 +2,7 @@ root = true
[*]
charset = utf-8
-end_of_line = lf
+end_of_line = crlf
insert_final_newline = true
indent_style = tab
indent_size = 4
diff --git a/.prettierrc.json b/.prettierrc.json
index 5a3cb37e6..3e0993082 100644
--- a/.prettierrc.json
+++ b/.prettierrc.json
@@ -4,5 +4,6 @@
"semi": true,
"printWidth": 150,
"bracketSameLine": true,
- "bracketSpacing": false
+ "bracketSpacing": false,
+ "endOfLine": "crlf"
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bea5f5940..796a0cf3f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,100 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased](https://github.com/yCodeTech/valet-windows/tree/master)
+## [3.3.0](https://github.com/yCodeTech/valet-windows/tree/v3.3.0) - 2025-10-22
+
+This is a large [Release PR](https://github.com/yCodeTech/valet-windows/pull/29) that focuses on code-parity with macOS Valet, enhanced emergency uninstallation, significant code quality improvements and lots of refactoring for improved maintainability. Valet will also no longer ship with the executable for Acrylic, instead the latest version is dynamically downloaded and installed from SourceForge.
+
+For the full changelog please view the PR commits.
+
+### Added
+
+- Added the ability for nginx to display http status 500 error page. This is important because if a PHP error occurs internally while trying to access a site, nginx fails silently, and the browser just displays a generic 500 error page. To help users debug the error, and narrow down the cause of the error, the new valet 500 error page will tell users where to look for the error in the logs.
+
+- Added a command to the `Diagnose` class to get the `winsw` version via reading it's README.
+
+- Added the ability to get the structure of both Valet's home directory and the Valet bin directory and output it in the `Diagnose` output as a directory tree diagram. This will help diagnose any issues in the future by making sure the Valet home directory and Valet bin directory has all the required files and named correctly. A new `Filesystem::scanDirRecursive` method has also been added to assist with scanning and retrieving of the directories and files.
+
+- Added the ability to download the latest version of `Acrylic`.
+
+ - Added new `Acrylic` class with the namespace `Valet\Packages` that extend the new base `Package` class. This class will download the latest version from SourceForge.
+
+ - Added new `Acrylic_IPv4_Binding_Address.ini` stub file to add the custom IPv4 binding localhost loopback address as fixed in PR [#15](https://github.com/yCodeTech/valet-windows/pull/15).
+
+ - Added a `changeLocalIpv4BindingAddress` method to replace the default value of `LocalIPv4BindingAddress` with our custom value from the new stub file.
+
+### Changed
+
+- Extract much of `GithubPackage` class into a new `Package` class as the new base class, refactor and make the method and variable names generic.
+
+- Improved the emergency uninstall services creation during Valet installation. The emergency uninstall files are now located in `~/.config/valet/Emergency Uninstall`.
+
+ - Fixed the script to automatically run with admin privileges if it doesn't already have them.
+
+ - Added `Ansicon` to the script to handle cases where `Ansicon` fails to uninstall properly.
+
+ In cases where valet has uninstalled for a composer update, there is sometimes a remnants of the Ansicon path in the registry. This will cause installation issues where a system message will appear `"The system cannot find the path specified"` or similar, and installation will fail. This emergency script has a copy of Ansicon in the `~/.config/valet/Emergency Uninstall` directory and will attempt to uninstall it again. This is just a workaround, and not a direct fix for issue [#28](https://github.com/yCodeTech/valet-windows/issues/28).
+
+ - Fixed the script to remove all the `valet\Services` files after uninstalling them.
+
+- Code-parity with the Mac version (v4.8.7):
+
+ - Moved the basic and laravel drivers into a new `Drivers` directory within the `Valet` directory, and namespaced them as `Valet\Drivers`.
+
+ Moved the rest of the drivers into the `Specific` subdirectory of `Drivers`, and namespaced them as `Valet\Drivers\Specific`.
+
+ This is so they can be autoloaded by PSR-4.
+
+ - Introduced empty legacy driver classes that extend the namespaced drivers so that user custom drivers still work. These legacy drivers are deprecated by default.
+
+ - Added the requirement for custom drivers to be namespaced as `Valet\Drivers\Custom`.
+
+ - Added a more robust check for `Bedrock` environments and checking the composer dependencies for the required package.
+
+ - Fixed Drupal JSON:API support for non-standard urls. More info on the Valet for Mac PR [#1218](https://github.com/laravel/valet/pull/1218).
+
+ - Added new drivers for `Nette`, `Radicle`, and `Statamic V2`.
+
+ - Fixed `Craft` driver to prevent sites breaking when applying security patches to Craft. More info on the Valet for Mac PR [#1516](https://github.com/laravel/valet/pull/1516).
+
+ - Gracefully handle the old `SampleValetDriver.php` and upgrade it to the new namespaced sample via a new `Upgrader::fixOldSampleValetDriver` method.
+
+ - Added upgrade instructions for custom drivers in [UPGRADE.md](/UPGRADE.md), including namespace and inheritance requirements.
+
+ - Update logos to support GitHub dark/light theme.
+
+- Moved the `services` command from being an installed-only command to being available without installation.
+
+- Various refactoring for better organisation, maintainability, and improved code quality.
+
+### Fixed
+
+- Fixed the copying of Unicode character in the `Diagnose` output. The Windows clip.exe program doesn't support copying of all Unicode characters, so if there's a character it doesn't know, it will display `????` in the output instead of the character. This is especially true for the box characters (eg. `┣━`) used for directory trees. The `copyToClipboard` method now writes the output to a temp file as UTF-8 with BOM encoding to allow usage of copyable Unicode characters, and uses the Powershell command `Set-Clipboard` to copy to the clipboard.
+
+- Fixed errors when the Valet home directory (`~/.config/valet`) doesn't exist during the `Upgrader` run. `Upgrader` now only runs if the home path exists.
+
+- Fixed `Upgrader::upgradeSymbolicLinks` method running when the directory is empty causing redundant console messages. It now only runs when the config key is missing and there are symlinks in the directory.
+
+- Fixed system compatibility check to PHP 7.4 as a minimum.
+
+- Fixed `set-ngrok-token` command errors that was caused by a method name change. The `Ngrok::getNgrokConfig` method was changed to the generic `Ngrok::getConfig` in the release PR of v3.2.0 and specifically in commit [db085bd](https://github.com/yCodeTech/valet-windows/commit/db085bd1ddb0be1d58a6b59b33acae5ee1050924), but it's usage in the command was never changed. Changed the method call to the new name.
+
+- Fixed updating old isolated site conf files on `valet install`. Secured site conf files have always been updated on installing valet via the `Nginx::rewriteSecureNginxFiles` method, but isolated sites were stuck using old configurations. All conf files are now rewritten and updated to prevent old nginx configurations causing issues.
+
+- Fixed `Site::unisolate` and `Site::isolate` methods to not output info directly to the terminal, as this causes progress bars to mess up in the terminal when using the `Site::reisolateForNewTld` method which calls the `un/isolate` methods. The info output is now moved to the commands that called the methods.
+
+### Removed
+
+- Removed the `CommandLine::runAsUser` method because it is a duplicate method of `run`. It doesn't do anything different or special. So we can just use `run` instead.
+
+- Removed the unused `Valet::composerGlobalDiagnose`, `Valet::composerGlobalUpdate`, and `ValetException::githubApiRateLimitExceededError` methods.
+
+- Removed all bin files for `Acrylic`, in favour of downloading the latest versions from SourceForge.
+
+### Deprecated
+
+- The new legacy drivers are all deprecated by default and will be removed in v4.0.0.
+
## [3.2.1](https://github.com/yCodeTech/valet-windows/tree/v3.2.1) - 2025-08-03
### Added
diff --git a/README.md b/README.md
index 922426fdc..aa665b975 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-
+
@@ -10,7 +10,7 @@
This is a Windows port of the popular Mac development environment Laravel Valet.
Laravel Valet Windows 3 is a much needed updated fork of cretueusebiu/valet-windows, with lots of improvements and new commands. This version hopes to achieve as much parity as possible with the Mac version. For command parity, please refer to the parity checker.
-
+
```sh
composer global require ycodetech/valet-windows
@@ -152,10 +152,9 @@ This is 3.0 of Valet Windows, branded under the name _Laravel Valet Windows 3_,
Valet ships with and installs these services:
-| Service | Version |
-| ----------------------------------------------------------------------------: | ------- |
-| [Acrylic DNS Proxy](https://mayakron.altervista.org/support/acrylic/Home.htm) | 2.1.0 |
-| [ngrok](https://ngrok.com/) | 3.3.1 |
+| Service | Version |
+| --------------------------: | ------- |
+| [ngrok](https://ngrok.com/) | 3.3.1 |
> [!IMPORTANT]
> ngrok may error out that it is "too old" when running it for some accounts. In the upcoming v3.2.0 release, Valet will ship with the latest version of ngrok.
@@ -166,14 +165,15 @@ Valet ships with and installs these services:
>
> For both commands, valet automatically appends the config file location as a flag to the command, so ngrok will already know where the config file is.
-As of v3.2.0, Valet no longer ships with the executables for the following services, instead they will be downloaded and installed automatically from GitHub's API. This is so that the latest versions with bug fixes and security updates can be installed.
+As of v3.2.0 and v3.3.0, Valet no longer ships with the executables for the following services, instead they will be downloaded and installed automatically from it's online source (GitHub, SourceForge, etc.). This is so that the latest versions with bug fixes and security updates can be installed.
-| Service | Latest Version as of 13/05/2025 |
-| ------------------------------------------: | ------------------------------- |
-| [Ansicon](https://github.com/adoxa/ansicon) | 1.89 |
-| [gsudo](https://github.com/gerardog/gsudo) | 2.6.0 |
-| [nginx](https://nginx.org/) | 1.28.0 |
-| [WinSW](https://github.com/winsw/winsw) | 2.12.0 |
+| Service | Latest Version as of 2025-10-22 |
+| ----------------------------------------------------------------------------: | ------------------------------- |
+| [Ansicon](https://github.com/adoxa/ansicon) | 1.89 |
+| [gsudo](https://github.com/gerardog/gsudo) | 2.6.1 |
+| [nginx](https://nginx.org/) | 1.29.2 |
+| [WinSW](https://github.com/winsw/winsw) | 2.12.0 |
+| [Acrylic DNS Proxy](https://mayakron.altervista.org/support/acrylic/Home.htm) | 2.2.1 |
> [!IMPORTANT]
>
@@ -205,7 +205,7 @@ Also make sure to open your preferred terminal (Windows Terminal, CMD, Git Bash,
- Install Valet with Composer via `composer global require ycodetech/valet-windows`.
-
+
> [!WARNING]
>
@@ -1537,17 +1537,25 @@ Doesn't affect valet functionality.
## Emergency Stop and Uninstall Services
-As of v3.1.6, Valet has an emergency stop and uninstall services script. This script is copied to the `/.config/valet` directory for safe keeping.
+As of v3.1.6, Valet has an emergency stop and uninstall services script. This script is copied to the `/.config/valet/Emergency Uninstall` directory for safe keeping.
So if `composer global update` is ran before valet is uninstalled, and several running services prevent composer from removing the files and updating valet, then you can run the `emergency_uninstall_services.bat` file to stop and uninstall the services.
To run, open a CMD terminal with Admin privileges and run this:
```sh
-> %UserProfile%\.config\valet\emergency_uninstall_services.bat
+> "%UserProfile%\.config\valet\Emergency Uninstall\emergency_uninstall_services.bat"
```
-All services will have been stopped and you can then be able to run `composer global update`.
+Alternatively, find it in the File Explorer and double clicking the file will also run it.
+
+All services will have been stopped and removed and you can then be able to run `composer global update`.
+
+> [!IMPORTANT]
+>
+> If you receive the `The system cannot find the path specified` error or similar while installing valet, then usually it's to do with Ansicon leaving it's path in the registry after uninstalling, which then fails Valet's installation.
+>
+> The emergency uninstall script can help to uninstall Ansicon again and hopefully fully removing it from the registry.
## Valet Directories
@@ -1595,7 +1603,13 @@ Upon installation, Valet creates the following directories and config files:
- `~/.config/valet/config.json`
This is the main Valet config file.
-- `~/.config/valet/emergency_uninstall_services.bat`
+- `~/.config/valet/Emergency Uninstall`
+ Contains the emergency uninstall files.
+
+- `~/.config/valet/Emergency Uninstall/ansicon`
+ Contains the Ansicon files and executable to help uninstall it.
+
+- `~/.config/valet/Emergency Uninstall/emergency_uninstall_services.bat`
This is an batch file to do an emergency stop and uninstall of all services. See the [Emergency Stop and Uninstall Services section](#emergency-stop-and-uninstall-services).
> [!WARNING]
@@ -1655,6 +1669,12 @@ Upon installation, Valet creates the following directories and config files:
If the WMI error does occur, try running the command again. If different WMI errors occur, please submit an issue with all relevant details.
+- If there is a large error and it's file trace output to the terminal, the top of the error may be cut off/overwritten. Apparently Symfony can only write to the terminal that is viewable, if it goes outside of the viewable area (ie. you need to scroll up to view) then the output is overwritten and the most important part of the error, the description at the start is cut off. (See https://github.com/symfony/symfony/issues/35012). If this happens, make the terminal larger in height and try the command again to try and view the full error.
+
+- When trying to install Valet after updating via Composer, and you receive this error or similiar: `The system cannot find the path specified`; it could be that Ansicon hasn't uninstalled properly and left it's path in the registry. Because it happens unpredictably, there is no way of debugging or fixing it.
+
+ The only "workaround" is to use the emergency uninstall script (`emergency_uninstall_services.bat`) in the `~/.config/valet/Emergency Uninstall` directory to allow Ansicon another chance to uninstall itself properly. (See [issue 28](https://github.com/yCodeTech/valet-windows/issues/28)). See the [Emergency Stop and Uninstall Services section](#emergency-stop-and-uninstall-services).
+
## Xdebug Installation
Valet only installs a specific Xdebug PHP CGI service on a separate port to work alongside the PHP service. To install Xdebug itself, follow the [official guide](https://xdebug.org/docs/install).
diff --git a/UPGRADE.md b/UPGRADE.md
new file mode 100644
index 000000000..1a6dc4408
--- /dev/null
+++ b/UPGRADE.md
@@ -0,0 +1,9 @@
+# Upgrading
+
+- If you have any issues with your drivers, all custom drivers (including the `SampleValetDriver` published by previous versions of Valet) must have the following:
+
+ - Have the namespace of `Valet\Drivers\Custom`.
+
+ - Extend the new namespaced drivers instead of the old non-namespaced drivers by specifying a `use` keyword like `use Valet\Drivers\ValetDriver;`
+
+ See the [new SampleValetDriver](https://github.com/yCodeTech/valet-windows/blob/master/cli/stubs/SampleValetDriver.php) for an example.
diff --git a/art/composer_laravel_valet_windows_3_logo.svg b/art/composer_laravel_valet_windows_3_logo.svg
new file mode 100644
index 000000000..252990d2b
--- /dev/null
+++ b/art/composer_laravel_valet_windows_3_logo.svg
@@ -0,0 +1,33 @@
+
diff --git a/art/laravel_valet_windows_3_logo.svg b/art/laravel_valet_windows_3_logo.svg
new file mode 100644
index 000000000..308e9b16c
--- /dev/null
+++ b/art/laravel_valet_windows_3_logo.svg
@@ -0,0 +1,36 @@
+
diff --git a/bin/acrylic/AcrylicConfiguration.ini b/bin/acrylic/AcrylicConfiguration.ini
deleted file mode 100644
index 991ba8a20..000000000
--- a/bin/acrylic/AcrylicConfiguration.ini
+++ /dev/null
@@ -1,599 +0,0 @@
-;
-; IF YOU MAKE ANY CHANGES TO THIS FILE YOU HAVE TO RESTART THE ACRYLIC DNS PROXY SERVICE OR CONSOLE IN ORDER TO SEE
-; THEIR EFFECTS.
-;
-[GlobalSection]
-;
-; The IP address of your primary DNS server. You can use an IPv4 address in quad-dotted notation or an IPv6 address in
-; colon-separated groups.
-;
-; Upon installation it points to the primary Google Public DNS server.
-;
-PrimaryServerAddress=8.8.8.8
-;
-; The TCP/UDP port your primary DNS server is supposed to be listening to. The default value of 53 is the standard port
-; for DNS resolution. You should change this value only if you are using a non standard DNS server or a protocol
-; different than UDP/TCP (e.g. the standard port for the DNS-over-HTTPS protocol is 443).
-;
-PrimaryServerPort=53
-;
-; The protocol to use with your primary DNS server.
-;
-; The currently supported protocols are UDP, TCP, SOCKS5 and DOH (DNS-over-HTTPS).
-;
-; When using the UDP protocol, Acrylic forwards DNS requests using UDP or TCP, in accordance with RFC 5625.
-;
-; When using the TCP protocol, Acrylic forwards DNS requests using TCP only. Since establishing a TCP connection for
-; every DNS request requires a significant amount of time and the number of TCP connections that can be opened in a unit
-; of time is limited by the Operating System, this protocol should be used only in situations where using the UDP
-; protocol isn't possible for some reason.
-;
-; When using the SOCKS5 protocol, Acrylic forwards DNS requests to your primary DNS server using a SOCKS 5 proxy as an
-; intermediary, in accordance with RFC 1928. Currently, only the NO AUTHENTICATION REQUIRED method for SOCKS 5 is
-; supported.
-;
-; When using the DOH protocol, Acrylic forwards DNS requests to your primary DNS server using DNS-over-HTTPS, a protocol
-; for performing DNS resolution via HTTPS and thus increase user privacy and security by preventing eavesdropping and
-; manipulation of DNS data by man-in-the-middle attacks. Be aware though that when using DNS-over-HTTPS, the encryption
-; algorithms (e.g. the TLS version) your Operating System can use must match at least one your primary DNS server
-; accepts, and this is not always the case depending on the combination of your Operating System version and the chosen
-; DNS server. For example, old Windows versions can have a hard time establishing an HTTPS connection with DNS servers
-; requiring TLS 1.1 or 1.2, and thus may require the installation of specific updates.
-;
-PrimaryServerProtocol=UDP
-;
-; When using the DNS-over-HTTPS protocol, you must specify below the query path of your primary DNS server.
-;
-PrimaryServerDoHProtocolPath=
-;
-; When using the DNS-over-HTTPS protocol, you must specify below the host name of your primary DNS server.
-;
-PrimaryServerDoHProtocolHost=
-;
-; When using the DNS-over-HTTPS protocol, you can specify below whether Acrylic should connect to the internet using
-; your system proxy configuration (System) or directly without using a proxy (Direct).
-;
-PrimaryServerDoHProtocolConnectionType=System
-;
-; When using the DNS-over-HTTPS protocol, you can specify below whether or not Acrylic is allowed to reuse existing TCP
-; connections when sending requests to your primary DNS server. Since establishing a TCP connection for every DNS
-; request requires a significant amount of time and the number of TCP connections that can be opened in a unit of time
-; is limited by the Operating System, reusing existing TCP connections is an effective way to improve the performance of
-; the DNS-over-HTTPS protocol.
-;
-PrimaryServerDoHProtocolReuseConnections=Yes
-;
-; When using the DNS-over-HTTPS protocol, you can specify below whether or not Acrylic should use the WinHttp library,
-; instead of the WinINet library, when sending requests to your primary DNS server. For Acrylic's purposes WinHttp is
-; more efficient and therefore preferable, but having the possibility to choose between the two at the DNS server level
-; can help with some compatibility issues that may arise.
-;
-PrimaryServerDoHProtocolUseWinHttp=Yes
-;
-; Here is a known good DNS-over-HTTPS configuration for the Quad9 Public DNS server:
-;
-; PrimaryServerAddress=9.9.9.9
-; PrimaryServerPort=443
-; PrimaryServerProtocol=DOH
-; PrimaryServerDoHProtocolPath=dns-query
-; PrimaryServerDoHProtocolHost=dns.quad9.net
-;
-; Here is a known good DNS-over-HTTPS configuration for the Google Public DNS server:
-;
-; PrimaryServerAddress=8.8.8.8
-; PrimaryServerPort=443
-; PrimaryServerProtocol=DOH
-; PrimaryServerDoHProtocolPath=dns-query
-; PrimaryServerDoHProtocolHost=dns.google
-;
-; Here is a known good DNS-over-HTTPS configuration for the CloudFlare Public DNS server:
-;
-; PrimaryServerAddress=1.1.1.1
-; PrimaryServerPort=443
-; PrimaryServerProtocol=DOH
-; PrimaryServerDoHProtocolPath=dns-query
-; PrimaryServerDoHProtocolHost=cloudflare-dns.com
-;
-; When using the SOCKS5 protocol, you can specify below the IP address of the SOCKS 5 proxy server to use as an
-; intermediary to your primary DNS server. You can use an IPv4 address in quad-dotted notation or an IPv6 address in
-; colon-separated groups.
-;
-PrimaryServerSocks5ProtocolProxyAddress=
-;
-; When using the SOCKS5 protocol, you can specify below the TCP port the SOCKS 5 proxy server described above is
-; supposed to be listening to.
-;
-PrimaryServerSocks5ProtocolProxyPort=
-;
-; The domain name affinity mask is a list of semicolon separated values or wildcards that allows to restrict which DNS
-; server particular domain names get forwarded to.
-;
-; In the following example only the requests for domain names ending with ".com" get forwarded to the primary DNS
-; server:
-;
-; PrimaryServerDomainNameAffinityMask=*.com
-;
-; In the following example only the requests for domain names ending with ".com" and ".org" get forwarded to the primary
-; DNS server:
-;
-; PrimaryServerDomainNameAffinityMask=*.com;*.org
-;
-; Negations can be expressed by prepending a caret (^) to the value or wildcard.
-;
-; In the following example only the requests for domain names NOT ending with ".com" or ".org" get forwarded to the
-; primary DNS server (the last catch-all value is particularly important in this case because, if missing, no request
-; would ever be forwarded to the primary DNS server):
-;
-; PrimaryServerDomainNameAffinityMask=^*.com;^*.org;*
-;
-; Rules for domain name affinity masks are evaluated according to the order in which they are written, with the first
-; positive rule that matches and the first negative rule that doesn't match determining, respectively, a positive result
-; (i.e. the DNS query is forwarded to the DNS server) or a negative result (i.e. the DNS query is NOT forwarded to the
-; DNS server). If none of the defined rules match then a negative result is determined.
-;
-PrimaryServerDomainNameAffinityMask=
-;
-; The query type affinity mask is list of semicolon separated values that allows to restrict which DNS server particular
-; query types get forwarded to.
-;
-; In the following example only the requests for A, AAAA, MX and SRV query types get forwarded to the primary DNS
-; server:
-;
-; PrimaryServerQueryTypeAffinityMask=A;AAAA;MX;SRV
-;
-; All DNS query types are supported, either explicitly using A, AAAA, CNAME, MX, NS, PTR, SOA, SRV and TXT or implicitly
-; using their decimal values.
-;
-PrimaryServerQueryTypeAffinityMask=
-;
-; You can specify below whether to ignore failure responses coming from the primary DNS server.
-;
-IgnoreFailureResponsesFromPrimaryServer=No
-;
-; You can specify below whether to ignore negative responses coming from the primary DNS server.
-;
-IgnoreNegativeResponsesFromPrimaryServer=No
-;
-; The configuration of your secondary DNS server.
-; For more details refer to the primary DNS server configuration comments.
-;
-; Upon installation it points to the secondary Google Public DNS server.
-;
-SecondaryServerAddress=8.8.4.4
-SecondaryServerPort=53
-SecondaryServerProtocol=UDP
-SecondaryServerDoHProtocolPath=
-SecondaryServerDoHProtocolHost=
-SecondaryServerDoHProtocolConnectionType=System
-SecondaryServerDoHProtocolReuseConnections=Yes
-SecondaryServerDoHProtocolUseWinHttp=Yes
-SecondaryServerSocks5ProtocolProxyAddress=
-SecondaryServerSocks5ProtocolProxyPort=
-SecondaryServerDomainNameAffinityMask=
-SecondaryServerQueryTypeAffinityMask=
-IgnoreFailureResponsesFromSecondaryServer=No
-IgnoreNegativeResponsesFromSecondaryServer=No
-;
-; The configuration of your tertiary DNS server.
-; For more details refer to the primary DNS server configuration comments.
-;
-TertiaryServerAddress=
-TertiaryServerPort=53
-TertiaryServerProtocol=UDP
-TertiaryServerDoHProtocolPath=
-TertiaryServerDoHProtocolHost=
-TertiaryServerDoHProtocolConnectionType=System
-TertiaryServerDoHProtocolReuseConnections=Yes
-TertiaryServerDoHProtocolUseWinHttp=Yes
-TertiaryServerSocks5ProtocolProxyAddress=
-TertiaryServerSocks5ProtocolProxyPort=
-TertiaryServerDomainNameAffinityMask=
-TertiaryServerQueryTypeAffinityMask=
-IgnoreFailureResponsesFromTertiaryServer=No
-IgnoreNegativeResponsesFromTertiaryServer=No
-;
-; The configuration of your quaternary DNS server.
-; For more details refer to the primary DNS server configuration comments.
-;
-QuaternaryServerAddress=
-QuaternaryServerPort=53
-QuaternaryServerProtocol=UDP
-QuaternaryServerDoHProtocolPath=
-QuaternaryServerDoHProtocolHost=
-QuaternaryServerDoHProtocolConnectionType=System
-QuaternaryServerDoHProtocolReuseConnections=Yes
-QuaternaryServerDoHProtocolUseWinHttp=Yes
-QuaternaryServerSocks5ProtocolProxyAddress=
-QuaternaryServerSocks5ProtocolProxyPort=
-QuaternaryServerDomainNameAffinityMask=
-QuaternaryServerQueryTypeAffinityMask=
-IgnoreFailureResponsesFromQuaternaryServer=No
-IgnoreNegativeResponsesFromQuaternaryServer=No
-;
-; The configuration of your quinary DNS server.
-; For more details refer to the primary DNS server configuration comments.
-;
-QuinaryServerAddress=
-QuinaryServerPort=53
-QuinaryServerProtocol=UDP
-QuinaryServerDoHProtocolPath=
-QuinaryServerDoHProtocolHost=
-QuinaryServerDoHProtocolConnectionType=System
-QuinaryServerDoHProtocolReuseConnections=Yes
-QuinaryServerDoHProtocolUseWinHttp=Yes
-QuinaryServerSocks5ProtocolProxyAddress=
-QuinaryServerSocks5ProtocolProxyPort=
-QuinaryServerDomainNameAffinityMask=
-QuinaryServerQueryTypeAffinityMask=
-IgnoreFailureResponsesFromQuinaryServer=No
-IgnoreNegativeResponsesFromQuinaryServer=No
-;
-; The configuration of your senary DNS server.
-; For more details refer to the primary DNS server configuration comments.
-;
-SenaryServerAddress=
-SenaryServerPort=53
-SenaryServerProtocol=UDP
-SenaryServerDoHProtocolPath=
-SenaryServerDoHProtocolHost=
-SenaryServerDoHProtocolConnectionType=System
-SenaryServerDoHProtocolReuseConnections=Yes
-SenaryServerDoHProtocolUseWinHttp=Yes
-SenaryServerSocks5ProtocolProxyAddress=
-SenaryServerSocks5ProtocolProxyPort=
-SenaryServerDomainNameAffinityMask=
-SenaryServerQueryTypeAffinityMask=
-IgnoreFailureResponsesFromSenaryServer=No
-IgnoreNegativeResponsesFromSenaryServer=No
-;
-; The configuration of your septenary DNS server.
-; For more details refer to the primary DNS server configuration comments.
-;
-SeptenaryServerAddress=
-SeptenaryServerPort=53
-SeptenaryServerProtocol=UDP
-SeptenaryServerDoHProtocolPath=
-SeptenaryServerDoHProtocolHost=
-SeptenaryServerDoHProtocolConnectionType=System
-SeptenaryServerDoHProtocolReuseConnections=Yes
-SeptenaryServerDoHProtocolUseWinHttp=Yes
-SeptenaryServerSocks5ProtocolProxyAddress=
-SeptenaryServerSocks5ProtocolProxyPort=
-SeptenaryServerDomainNameAffinityMask=
-SeptenaryServerQueryTypeAffinityMask=
-IgnoreFailureResponsesFromSeptenaryServer=No
-IgnoreNegativeResponsesFromSeptenaryServer=No
-;
-; The configuration of your octonary DNS server.
-; For more details refer to the primary DNS server configuration comments.
-;
-OctonaryServerAddress=
-OctonaryServerPort=53
-OctonaryServerProtocol=UDP
-OctonaryServerDoHProtocolPath=
-OctonaryServerDoHProtocolHost=
-OctonaryServerDoHProtocolConnectionType=System
-OctonaryServerDoHProtocolReuseConnections=Yes
-OctonaryServerDoHProtocolUseWinHttp=Yes
-OctonaryServerSocks5ProtocolProxyAddress=
-OctonaryServerSocks5ProtocolProxyPort=
-OctonaryServerDomainNameAffinityMask=
-OctonaryServerQueryTypeAffinityMask=
-IgnoreFailureResponsesFromOctonaryServer=No
-IgnoreNegativeResponsesFromOctonaryServer=No
-;
-; The configuration of your nonary DNS server.
-; For more details refer to the primary DNS server configuration comments.
-;
-NonaryServerAddress=
-NonaryServerPort=53
-NonaryServerProtocol=UDP
-NonaryServerDoHProtocolPath=
-NonaryServerDoHProtocolHost=
-NonaryServerDoHProtocolConnectionType=System
-NonaryServerDoHProtocolReuseConnections=Yes
-NonaryServerDoHProtocolUseWinHttp=Yes
-NonaryServerSocks5ProtocolProxyAddress=
-NonaryServerSocks5ProtocolProxyPort=
-NonaryServerDomainNameAffinityMask=
-NonaryServerQueryTypeAffinityMask=
-IgnoreFailureResponsesFromNonaryServer=No
-IgnoreNegativeResponsesFromNonaryServer=No
-;
-; The configuration of your denary DNS server.
-; For more details refer to the primary DNS server configuration comments.
-;
-DenaryServerAddress=
-DenaryServerPort=53
-DenaryServerProtocol=UDP
-DenaryServerDoHProtocolPath=
-DenaryServerDoHProtocolHost=
-DenaryServerDoHProtocolConnectionType=System
-DenaryServerDoHProtocolReuseConnections=Yes
-DenaryServerDoHProtocolUseWinHttp=Yes
-DenaryServerSocks5ProtocolProxyAddress=
-DenaryServerSocks5ProtocolProxyPort=
-DenaryServerDomainNameAffinityMask=
-DenaryServerQueryTypeAffinityMask=
-IgnoreFailureResponsesFromDenaryServer=No
-IgnoreNegativeResponsesFromDenaryServer=No
-;
-; You can specify below whether Acrylic should sinkhole IPv6 lookups (also known as DNS requests of AAAA type) or not.
-;
-SinkholeIPv6Lookups=No
-;
-; You can direct Acrylic to forward reverse lookups (also known as DNS requests of PTR type) for private IP ranges to
-; your DNS servers by choosing Yes instead of No. Aside from protecting you and your DNS servers from the traffic of
-; these usually needless queries, choosing No is usually a better choice also to avoid leaking information about your
-; private address space.
-;
-ForwardPrivateReverseLookups=No
-;
-; THE ACRYLIC DNS PROXY CACHING MECHANISM EXPLAINED
-;
-; When Acrylic receives a DNS request from a client the hosts cache (an in-memory static cache derived from the
-; AcrylicHosts.txt file) is searched first. If nothing is found there the request is then searched in the address cache
-; (an in-memory dynamic cache backed up by the AcrylicCache.dat file). At this point one of the following three cases
-; can happen:
-;
-; [1] The request is not found in the address cache or its corresponding response is older than
-; "AddressCacheScavengingTime" minutes: In this case the original request is forwarded to all of the configured DNS
-; servers simultaneously. The response to the client is delayed until the first one of the DNS servers comes out with a
-; valid response. All the other responses coming from the other DNS servers will be discarded.
-;
-; [2] The request is found in the address cache and its corresponding response is older than
-; "AddressCacheSilentUpdateTime" minutes but not older than "AddressCacheScavengingTime minutes": In this case the
-; response to the client is sent immediately from the address cache and the original request is also forwarded to all of
-; the configured DNS servers simultaneously like in the previous case. The first valid response coming from one of the
-; DNS servers will be used to silently update the address cache, while all the other responses coming from the other DNS
-; servers will be discarded.
-;
-; [3] The request is found in the address cache and its corresponding response is younger than
-; "AddressCacheSilentUpdateTime" minutes: In this case the response to the client is sent immediately from the address
-; cache and no network activity with any of the configured DNS servers will occur.
-;
-; Be aware that to minimize disk activity the address cache is flushed from memory to disk only when Acrylic is stopped
-; or the system is shut down.
-;
-; And now about the caching parameters:
-;
-; The time to live (in minutes) of a failure response in the address cache.
-;
-AddressCacheFailureTime=0
-;
-; The time to live (in minutes) of a negative response in the address cache.
-;
-AddressCacheNegativeTime=60
-;
-; The time to live (in minutes) of a positive response in the address cache.
-;
-AddressCacheScavengingTime=5760
-;
-; The time (in minutes) elapsed which an item in the address cache must be silently updated should a request occur.
-;
-AddressCacheSilentUpdateTime=1440
-;
-; The time (in minutes) elapsed which the address cache is pruned of obsolete items. A value of 0 indicates that no
-; pruning of the address cache is ever done.
-;
-AddressCachePeriodicPruningTime=360
-;
-; The address cache domain name affinity mask is a list of semicolon separated values or wildcards that allows to
-; restrict DNS responses for which domain names are to be cached in the address cache.
-;
-AddressCacheDomainNameAffinityMask=^dns.msftncsi.com;^ipv6.msftncsi.com;^www.msftncsi.com;*
-;
-; The address cache query type affinity mask is list of semicolon separated values that allows to restrict DNS responses
-; for which query types are to be cached in the address cache.
-;
-; All DNS query types are supported, either explicitly using A, AAAA, CNAME, MX, NS, PTR, SOA, SRV and TXT or implicitly
-; using their decimal values.
-;
-AddressCacheQueryTypeAffinityMask=A;AAAA;CNAME;MX;NS;PTR;SOA;SRV;TXT
-;
-; You can disable any disk activity related to the address cache by choosing Yes instead of No. If you do that Acrylic
-; will use the address cache only in memory.
-;
-AddressCacheInMemoryOnly=No
-;
-; You can disable the address cache altogether by choosing Yes instead of No. If you do that Acrylic will work as a
-; forwarding-only DNS proxy.
-;
-AddressCacheDisabled=No
-;
-; The local IPv4 address to which Acrylic binds. A value of 0.0.0.0 indicates that Acrylic should bind to all available
-; addresses and as such it will be able to receive DNS requests coming from all of your network interfaces. A value
-; corresponding to the IPv4 address of one of your network interfaces instead will allow Acrylic to receive DNS requests
-; only from that specific network interface. An empty value instead indicates that no binding should occur on IPv4.
-;
-; Binding to Acrylic to 0.0.0.0 has a known issue of causing problems with WSL2 which depends on port 53
-; By binding Acrylic to the localhost loopback address (127.0.0.1), this prevents it from blocking port 53
-; on the WSL2 vEthernet interface.
-; If Acrylic should handle DNS request for all network interfaces, then as an alternative, this should be set to 0.0.0.0
-; and LocalIPv4BindingPort should be changed to a port other than 53
-; See: https://github.com/microsoft/WSL/issues/4364#issuecomment-866700145
-;
-LocalIPv4BindingAddress=127.0.0.1
-;
-; The local UDPv4 port to which Acrylic binds. The default value of 53 is the standard port for DNS resolution. You
-; should change this value only if you are using a non standard DNS client.
-;
-LocalIPv4BindingPort=53
-;
-; The local IPv6 address to which Acrylic binds. A value of 0:0:0:0:0:0:0:0 indicates that Acrylic should bind to all
-; available addresses and as such it will be able to receive DNS requests coming from all of your network interfaces. A
-; value corresponding to the IPv6 address of one of your network interfaces instead will allow Acrylic to receive DNS
-; requests only from that specific network interface. An empty value instead indicates that no binding should occur on
-; IPv6.
-;
-LocalIPv6BindingAddress=0:0:0:0:0:0:0:0
-;
-; The local UDPv6 port to which Acrylic binds. The default value of 53 is the standard port for DNS resolution. You
-; should change this value only if you are using a non standard DNS client.
-;
-LocalIPv6BindingPort=53
-;
-; On Windows versions prior to Windows Vista or Windows Server 2008 the IPv6 protocol is usually not installed by
-; default. For Windows 2000 there is a Microsoft IPv6 Technology Preview package available for download while for
-; Windows XP the IPv6 protocol must be added to the list of available network protocols in your network connection
-; Properties window.
-;
-; If you want to enable local IPv6 binding for Acrylic on Windows versions prior to Windows Vista or Windows Server 2008
-; you can choose Yes below after having installed all the necessary prerequisites.
-;
-LocalIPv6BindingEnabledOnWindowsVersionsPriorToWindowsVistaOrWindowsServer2008=No
-;
-; The time to live (in seconds) set for DNS responses generated by Acrylic (e.g. the ones generated from mappings
-; contained in the AcrylicHosts.txt file).
-;
-GeneratedResponseTimeToLive=300
-;
-; The maximum time (in milliseconds) to wait for a response coming from a DNS server configured with the UDP protocol.
-;
-ServerUdpProtocolResponseTimeout=4999
-;
-; The maximum time (in milliseconds) to wait for the first byte of a response coming from a DNS server configured with
-; the TCP protocol.
-;
-ServerTcpProtocolResponseTimeout=4999
-;
-; The maximum time (in milliseconds) to wait for the other bytes of a response coming from a DNS server configured with
-; the TCP protocol.
-;
-ServerTcpProtocolInternalTimeout=2477
-;
-; The maximum times (in milliseconds) to wait for the below events when communicating with an intermediary SOCKS 5 proxy
-; server on behalf of a DNS server configured with the SOCKS5 protocol.
-;
-ServerSocks5ProtocolProxyFirstByteTimeout=2477
-ServerSocks5ProtocolProxyOtherBytesTimeout=2477
-ServerSocks5ProtocolProxyRemoteConnectTimeout=2477
-ServerSocks5ProtocolProxyRemoteResponseTimeout=4999
-;
-; The hit log is a text file into which every DNS request and DNS response received by Acrylic can be logged.
-;
-; It is activated by specifying a non-empty value for the HitLogFileName parameter and contains lines with the following
-; TAB-separated fields:
-;
-; [01] The timestamp of the DNS request or response in the format YYYY-MM-DD HH:MM:SS.FFF (local time).
-; [02] The IP address from where the DNS request originates from or the DNS response is destined to.
-; [03] The status code of the DNS request or response:
-; X => Resolved directly by Acrylic
-; H => Resolved using the hosts cache
-; C => Resolved using the address cache
-; F => Forwarded to at least one of your DNS servers
-; R => Response accepted from one of your DNS servers
-; U => Silent update accepted from one of your DNS servers
-; [04] The index of the DNS server the DNS response is coming from.
-; [05] The time it took (in milliseconds) for the DNS server to produce a DNS response.
-; [06] The dissected DNS request or response.
-;
-; A dissected DNS request looks like:
-;
-; OC=0;RD=1;QDC=1;Q[1]=x.com;T[1]=A
-;
-; Where:
-;
-; [01] OC=0 means that the DNS operation code (OPCODE) is 0. Possible values are: 0 = a standard query (QUERY), 1 = an
-; inverse query (IQUERY), 2 = a server status request (STATUS).
-; [02] RD=1 means that the DNS response recursion desired bit (RD) is 1. If RD is set, it directs the name server to
-; pursue the query recursively.
-; [03] QDC=1 means that the number of queries (QDCOUNT) contained in the DNS request is 1.
-; [04] Q[1]=x.com means that DNS query 1 refers to the "x.com" domain name.
-; [05] T[1]=A means that DNS query 1 is of type A (IPv4).
-;
-; A dissected DNS response looks like:
-;
-; OC=0;RC=0;TC=0;RD=1;RA=1;AA=0;QDC=1;ANC=2;NSC=0;ARC=0;Q[1]=x.com;T[1]=CNAME;A[1]=x.com>y.com;T[2]=A;A[2]=y.com>1.2.3.4
-;
-; Where:
-;
-; [01] OC=0 means that the DNS operation code (OPCODE) is 0. Possible values are: 0 = a standard query (QUERY), 1 = an
-; inverse query (IQUERY), 2 = a server status request (STATUS).
-; [02] RC=0 means that the DNS response code (RCODE) is 0. Possible values are: 0 = no error condition, 1 = format error
-; (the name server was unable to interpret the query), 2 = server failure (the name server was unable to process this
-; query due to a problem with the name server), 3 = name error (meaningful only for responses from an authoritative name
-; server, this code signifies that the domain name referenced in the query does not exist), 4 = not implemented (the
-; name server does not support the requested kind of query), 5 = refused (the name server refuses to perform the
-; specified operation for policy reasons).
-; [03] TC=0 means that the DNS response truncated bit (TC) is 0. This bit specifies that this message was truncated due
-; to length greater than that permitted on the transmission channel.
-; [04] RD=1 means that the DNS response recursion desired bit (RD) is 0. If RD is set, it directs the name server to
-; pursue the query recursively.
-; [05] RA=1 means that the DNS response recursion available bit (RA) is 0. This bit denotes whether recursive query
-; support is available in the name server.
-; [06] AA=0 means that the DNS response authoritative answer bit (AA) is 0. This bit specifies that the responding name
-; server is an authority for the domain name in question section.
-; [07] QDC=1 means that the number of queries (QDCOUNT) contained in the DNS response is 1.
-; [08] ANC=2 means that the number of answers (ANCOUNT) contained in the DNS response is 2.
-; [09] NSC=0 means that the number of nameserver records (NSCOUNT) contained in the DNS response is 0.
-; [10] ARC=0 means that the number of additional records (ARCOUNT) contained in the DNS response is 0.
-; [11] Q[1]=x.com means that the DNS query 1 refers to the "x.com" domain name.
-; [12] T[1]=CNAME means that the DNS answer 1 is of type CNAME (canonical name).
-; [13] A[1]=x.com>y.com means that the DNS answer 1 that refers to the "x.com" domain name is "y.com".
-; [14] T[2]=A means that the DNS answer 2 is of type A (IPv4).
-; [15] A[2]=y.com>1.2.3.4 means that the DNS answer 2 that refers to the "y.com" domain name is "1.2.3.4".
-;
-; Regarding the HitLogFileName you can use an absolute or a relative path and a kind of daily log rotation can be
-; achieved by including the %DATE% template within the file name. A complete list of all the templates you can use
-; within the file name is shown below:
-;
-; %DATE%
-; The current date in YYYYMMDD format.
-;
-; %TEMP%
-; The current value of the TEMP environment variable.
-;
-; %APPDATA%
-; The current value of the APPDATA environment variable.
-;
-; %LOCALAPPDATA%
-; The current value of the LOCALAPPDATA environment variable.
-;
-; Examples:
-;
-; HitLogFileName=HitLog.%DATE%.txt
-; HitLogFileName=%TEMP%\AcrylicDNSProxyHitLog.%DATE%.txt
-;
-HitLogFileName=
-;
-; The filter (a combination of one or more of the status codes explained above) which controls what gets written into
-; the hit log.
-;
-HitLogFileWhat=XHCF
-;
-; You can enable the full dump (in addition to the DNS format dissections explained above) of DNS requests and responses
-; into the hit log by choosing Yes instead of No.
-;
-HitLogFullDump=No
-;
-; The maximum number of hit log items that can be kept in memory before they are flushed to disk. For performance
-; reasons the hit log is flushed to disk only when the hit log memory buffer is full, when Acrylic is stopped or when
-; the system is shutdown, therefore you might experience a delay from when a DNS request or response is received to when
-; its details get written into the hit log.
-;
-HitLogMaxPendingHits=512
-;
-; ALLOWING REQUESTS FROM OTHER COMPUTERS
-;
-; Although for security reasons the default behaviour of Acrylic is to refuse to handle requests coming from other
-; computers, it is possible to specify below in the AllowedAddressesSection a list of IP addresses (wildcards are
-; allowed) from which can come requests that Acrylic is allowed to handle. You have to specify a different key name for
-; each entry, like in the following example:
-;
-; [AllowedAddressesSection]
-; IP1=192.168.45.254 -- A single IP address
-; IP2=192.168.44.100 -- Another single IP address
-; IP3=192.168.100.* -- All addresses starting with 192.168.100
-; IP4=172.16.* -- All addresses starting with 172.16
-;
-; Although not recommended for security reasons you can also allow Acrylic to handle requests coming from any IP
-; address, like in the following example:
-;
-; [AllowedAddressesSection]
-; IP1=*
-;
-; You must also create a firewall rule to allow incoming traffic directed to the two Acrylic executables:
-; "AcrylicService.exe" and "AcrylicConsole.exe".
-;
-[AllowedAddressesSection]
\ No newline at end of file
diff --git a/bin/acrylic/AcrylicConsole.exe b/bin/acrylic/AcrylicConsole.exe
deleted file mode 100644
index d299cf992..000000000
Binary files a/bin/acrylic/AcrylicConsole.exe and /dev/null differ
diff --git a/bin/acrylic/AcrylicService.exe b/bin/acrylic/AcrylicService.exe
deleted file mode 100644
index b60ba732e..000000000
Binary files a/bin/acrylic/AcrylicService.exe and /dev/null differ
diff --git a/bin/acrylic/AcrylicUI.exe b/bin/acrylic/AcrylicUI.exe
deleted file mode 100644
index c99dacc81..000000000
Binary files a/bin/acrylic/AcrylicUI.exe and /dev/null differ
diff --git a/bin/acrylic/AcrylicUI.exe.manifest b/bin/acrylic/AcrylicUI.exe.manifest
deleted file mode 100644
index 921e76063..000000000
--- a/bin/acrylic/AcrylicUI.exe.manifest
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/bin/acrylic/License.txt b/bin/acrylic/License.txt
deleted file mode 100644
index 56f54c3cd..000000000
--- a/bin/acrylic/License.txt
+++ /dev/null
@@ -1,257 +0,0 @@
-GNU GENERAL PUBLIC LICENSE
-
-Version 2, June 1991
-
-Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth
-Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute
-verbatim copies of this license document, but changing it is not allowed.
-
-Preamble
-
-The licenses for most software are designed to take away your freedom to share
-and change it. By contrast, the GNU General Public License is intended to
-guarantee your freedom to share and change free software--to make sure the
-software is free for all its users. This General Public License applies to most
-of the Free Software Foundation's software and to any other program whose
-authors commit to using it. (Some other Free Software Foundation software is
-covered by the GNU Library General Public License instead.) You can apply it to
-your programs, too.
-
-When we speak of free software, we are referring to freedom, not price. Our
-General Public Licenses are designed to make sure that you have the freedom to
-distribute copies of free software (and charge for this service if you wish),
-that you receive source code or can get it if you want it, that you can change
-the software or use pieces of it in new free programs; and that you know you can
-do these things.
-
-To protect your rights, we need to make restrictions that forbid anyone to deny
-you these rights or to ask you to surrender the rights. These restrictions
-translate to certain responsibilities for you if you distribute copies of the
-software, or if you modify it.
-
-For example, if you distribute copies of such a program, whether gratis or for a
-fee, you must give the recipients all the rights that you have. You must make
-sure that they, too, receive or can get the source code. And you must show them
-these terms so they know their rights.
-
-We protect your rights with two steps: (1) copyright the software, and (2) offer
-you this license which gives you legal permission to copy, distribute and/or
-modify the software.
-
-Also, for each author's protection and ours, we want to make certain that
-everyone understands that there is no warranty for this free software. If the
-software is modified by someone else and passed on, we want its recipients to
-know that what they have is not the original, so that any problems introduced by
-others will not reflect on the original authors' reputations.
-
-Finally, any free program is threatened constantly by software patents. We wish
-to avoid the danger that redistributors of a free program will individually
-obtain patent licenses, in effect making the program proprietary. To prevent
-this, we have made it clear that any patent must be licensed for everyone's free
-use or not licensed at all.
-
-The precise terms and conditions for copying, distribution and modification
-follow.
-
-TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-0. This License applies to any program or other work which contains a notice
-placed by the copyright holder saying it may be distributed under the terms of
-this General Public License. The "Program", below, refers to any such program or
-work, and a "work based on the Program" means either the Program or any
-derivative work under copyright law: that is to say, a work containing the
-Program or a portion of it, either verbatim or with modifications and/or
-translated into another language. (Hereinafter, translation is included without
-limitation in the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not covered by
-this License; they are outside its scope. The act of running the Program is not
-restricted, and the output from the Program is covered only if its contents
-constitute a work based on the Program (independent of having been made by
-running the Program). Whether that is true depends on what the Program does.
-
-1. You may copy and distribute verbatim copies of the Program's source code as
-you receive it, in any medium, provided that you conspicuously and appropriately
-publish on each copy an appropriate copyright notice and disclaimer of warranty;
-keep intact all the notices that refer to this License and to the absence of any
-warranty; and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and you may at
-your option offer warranty protection in exchange for a fee.
-
-2. You may modify your copy or copies of the Program or any portion of it, thus
-forming a work based on the Program, and copy and distribute such modifications
-or work under the terms of Section 1 above, provided that you also meet all of
-these conditions:
-
-a) You must cause the modified files to carry prominent notices stating that you
-changed the files and the date of any change.
-
-b) You must cause any work that you distribute or publish, that in whole or in
-part contains or is derived from the Program or any part thereof, to be licensed
-as a whole at no charge to all third parties under the terms of this License.
-
-c) If the modified program normally reads commands interactively when run, you
-must cause it, when started running for such interactive use in the most
-ordinary way, to print or display an announcement including an appropriate
-copyright notice and a notice that there is no warranty (or else, saying that
-you provide a warranty) and that users may redistribute the program under these
-conditions, and telling the user how to view a copy of this License. (Exception:
-if the Program itself is interactive but does not normally print such an
-announcement, your work based on the Program is not required to print an
-announcement.)
-
-These requirements apply to the modified work as a whole. If identifiable
-sections of that work are not derived from the Program, and can be reasonably
-considered independent and separate works in themselves, then this License, and
-its terms, do not apply to those sections when you distribute them as separate
-works. But when you distribute the same sections as part of a whole which is a
-work based on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the entire whole,
-and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest your
-rights to work written entirely by you; rather, the intent is to exercise the
-right to control the distribution of derivative or collective works based on the
-Program.
-
-In addition, mere aggregation of another work not based on the Program with the
-Program (or with a work based on the Program) on a volume of a storage or
-distribution medium does not bring the other work under the scope of this
-License.
-
-3. You may copy and distribute the Program (or a work based on it, under Section
-2) in object code or executable form under the terms of Sections 1 and 2 above
-provided that you also do one of the following:
-
-a) Accompany it with the complete corresponding machine-readable source code,
-which must be distributed under the terms of Sections 1 and 2 above on a medium
-customarily used for software interchange; or,
-
-b) Accompany it with a written offer, valid for at least three years, to give
-any third party, for a charge no more than your cost of physically performing
-source distribution, a complete machine-readable copy of the corresponding
-source code, to be distributed under the terms of Sections 1 and 2 above on a
-medium customarily used for software interchange; or,
-
-c) Accompany it with the information you received as to the offer to distribute
-corresponding source code. (This alternative is allowed only for noncommercial
-distribution and only if you received the program in object code or executable
-form with such an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for making
-modifications to it. For an executable work, complete source code means all the
-source code for all modules it contains, plus any associated interface
-definition files, plus the scripts used to control compilation and installation
-of the executable. However, as a special exception, the source code distributed
-need not include anything that is normally distributed (in either source or
-binary form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component itself
-accompanies the executable.
-
-If distribution of executable or object code is made by offering access to copy
-from a designated place, then offering equivalent access to copy the source code
-from the same place counts as distribution of the source code, even though third
-parties are not compelled to copy the source along with the object code.
-
-4. You may not copy, modify, sublicense, or distribute the Program except as
-expressly provided under this License. Any attempt otherwise to copy, modify,
-sublicense or distribute the Program is void, and will automatically terminate
-your rights under this License. However, parties who have received copies, or
-rights, from you under this License will not have their licenses terminated so
-long as such parties remain in full compliance.
-
-5. You are not required to accept this License, since you have not signed it.
-However, nothing else grants you permission to modify or distribute the Program
-or its derivative works. These actions are prohibited by law if you do not
-accept this License. Therefore, by modifying or distributing the Program (or any
-work based on the Program), you indicate your acceptance of this License to do
-so, and all its terms and conditions for copying, distributing or modifying the
-Program or works based on it.
-
-6. Each time you redistribute the Program (or any work based on the Program),
-the recipient automatically receives a license from the original licensor to
-copy, distribute or modify the Program subject to these terms and conditions.
-You may not impose any further restrictions on the recipients' exercise of the
-rights granted herein. You are not responsible for enforcing compliance by third
-parties to this License.
-
-7. If, as a consequence of a court judgment or allegation of patent infringement
-or for any other reason (not limited to patent issues), conditions are imposed
-on you (whether by court order, agreement or otherwise) that contradict the
-conditions of this License, they do not excuse you from the conditions of this
-License. If you cannot distribute so as to satisfy simultaneously your
-obligations under this License and any other pertinent obligations, then as a
-consequence you may not distribute the Program at all. For example, if a patent
-license would not permit royalty-free redistribution of the Program by all those
-who receive copies directly or indirectly through you, then the only way you
-could satisfy both it and this License would be to refrain entirely from
-distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under any
-particular circumstance, the balance of the section is intended to apply and the
-section as a whole is intended to apply in other circumstances.
-
-It is not the purpose of this section to induce you to infringe any patents or
-other property right claims or to contest validity of any such claims; this
-section has the sole purpose of protecting the integrity of the free software
-distribution system, which is implemented by public license practices. Many
-people have made generous contributions to the wide range of software
-distributed through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing to
-distribute software through any other system and a licensee cannot impose that
-choice.
-
-This section is intended to make thoroughly clear what is believed to be a
-consequence of the rest of this License.
-
-8. If the distribution and/or use of the Program is restricted in certain
-countries either by patents or by copyrighted interfaces, the original copyright
-holder who places the Program under this License may add an explicit
-geographical distribution limitation excluding those countries, so that
-distribution is permitted only in or among countries not thus excluded. In such
-case, this License incorporates the limitation as if written in the body of this
-License.
-
-9. The Free Software Foundation may publish revised and/or new versions of the
-General Public License from time to time. Such new versions will be similar in
-spirit to the present version, but may differ in detail to address new problems
-or concerns.
-
-Each version is given a distinguishing version number. If the Program specifies
-a version number of this License which applies to it and "any later version",
-you have the option of following the terms and conditions either of that version
-or of any later version published by the Free Software Foundation. If the
-Program does not specify a version number of this License, you may choose any
-version ever published by the Free Software Foundation.
-
-10. If you wish to incorporate parts of the Program into other free programs
-whose distribution conditions are different, write to the author to ask for
-permission. For software which is copyrighted by the Free Software Foundation,
-write to the Free Software Foundation; we sometimes make exceptions for this.
-Our decision will be guided by the two goals of preserving the free status of
-all derivatives of our free software and of promoting the sharing and reuse of
-software generally.
-
-NO WARRANTY
-
-11. Because the program is licensed free of charge, there is no warranty for the
-program, to the extent permitted by applicable law. Except when otherwise stated
-in writing the copyright holders and/or other parties provide the program "as
-is" without warranty of any kind, either expressed or implied, including, but
-not limited to, the implied warranties of merchantability and fitness for a
-particular purpose. The entire risk as to the quality and performance of the
-program is with you. Should the program prove defective, you assume the cost of
-all necessary servicing, repair or correction.
-
-12. In no event unless required by applicable law or agreed to in writing will
-any copyright holder, or any other party who may modify and/or redistribute the
-program as permitted above, be liable to you for damages, including any general,
-special, incidental or consequential damages arising out of the use or inability
-to use the program (including but not limited to loss of data or data being
-rendered inaccurate or losses sustained by you or third parties or a failure of
-the program to operate with any other programs), even if such holder or other
-party has been advised of the possibility of such damages.
-
-END OF TERMS AND CONDITIONS
\ No newline at end of file
diff --git a/bin/acrylic/Readme.txt b/bin/acrylic/Readme.txt
deleted file mode 100644
index 89bdb5c1f..000000000
--- a/bin/acrylic/Readme.txt
+++ /dev/null
@@ -1,83 +0,0 @@
-========================================================================================================================
-ACRYLIC DNS PROXY
-========================================================================================================================
-
-Acrylic is a local DNS proxy for Windows which improves the performance of your computer by actively caching the
-responses coming from your DNS servers and helps you fight unwanted ads through the use of a custom HOSTS file
-(optimized for handling hundreds of thousands of domain names) with support for wildcards and regular expressions.
-
-When you browse a web page a portion of the loading time is dedicated to name resolution (usually from a few
-milliseconds to 1 second or more) while the rest is dedicated to the transfer of the web page contents and resources to
-your browser. What Acrylic does is to reduce the time dedicated to name resolution for frequently visited addresses
-closest to zero possible. It may not seem such a great optimization but in a few weeks of Internet browsing you will
-probably save an hour or so, which is definitely not such a bad thing. Furthermore Acrylic's sliding expiration caching
-mechanism and DNS silent updates are able to improve the browsing experience independently of the browser.
-
-With Acrylic you can also gracefully overcome downtimes of your DNS servers without disrupting your work, because in
-that case you will at least be able to connect to your favourite websites and to your email server.
-
-Another good thing is that Acrylic is released as open source, which means that it's free and its source code, written
-with Borland Delphi 7, is freely available to anyone under the GNU General Public License.
-
-For more informations please use the "Acrylic Home Page" shortcut available from the "Start Menu", or go directly to:
-
-https://mayakron.altervista.org/support/acrylic/Home.htm
-
-Installed version is:
-2.1.0 released on June 30, 2021.
-
-========================================================================================================================
-ACRYLIC DNS PROXY UI
-========================================================================================================================
-
-The Acrylic DNS Proxy UI desktop application understands the following command line options:
-
- InstallAcrylicService
- Performs all the necessary operations to install Acrylic as a Windows service on your computer.
- UninstallAcrylicService
- Performs all the necessary operations to uninstall Acrylic as a Windows service from your computer.
- StartAcrylicService
- Starts the Acrylic Windows service.
- StopAcrylicService
- Stops the Acrylic Windows service.
- RestartAcrylicService
- Restarts the Acrylic Windows service.
- PurgeAcrylicCacheData
- Purges Acrylic cache data, restarting the Acrylic Windows service if needed.
- ActivateAcrylicDebugLog
- Activates the Acrylic debug log, restarting the Acrylic Windows service if needed.
- DeactivateAcrylicDebugLog
- Deactivates the Acrylic debug log, restarting the Acrylic Windows service if needed.
- OpenAcrylicConfigurationFile
- Opens the Acrylic configuration file right after the application's startup.
- OpenAcrylicHostsFile
- Opens the Acrylic HOSTS file right after the application's startup.
-
-Note: The Acrylic DNS Proxy UI desktop application icon has been designed by http://www.aha-soft.com/
-
-========================================================================================================================
-ACRYLIC DNS PROXY CONSOLE
-========================================================================================================================
-
-The console version (AcrylicConsole.exe) and the Windows service version of Acrylic (AcrylicService.exe) are
-functionally identical, as they share the same code.
-
-You may want to use the console version if you don't want to install anything on your computer (in this case I suppose
-you chose the portable version of Acrylic, didn't you?) or if you are experimenting with Acrylic and you don't want to
-restart the Windows service every time you change something in the configuration.
-
-You cannot have both versions running at the same time because they will try to listen from the same UDP or TCP ports
-and this can't happen. In this case you might see an error message similar to the following one written into the
-AcrylicDebug.txt file (in the case of the Windows service version) or written to the standard output (in the case of the
-console version):
-
-Binding to IPv4 address 0.0.0.0 and port 53 failed with Windows Sockets error code 10048.
-
-The console version of Acrylic currently understands the following command line options:
-
- /NoBanner
- Does not write the application banner to the console on startup.
- /NoLog
- Does not write the application log to the console while running.
-
-========================================================================================================================
\ No newline at end of file
diff --git a/cli/Valet/Acrylic.php b/cli/Valet/Acrylic.php
index c16ba0efa..b5bd58407 100644
--- a/cli/Valet/Acrylic.php
+++ b/cli/Valet/Acrylic.php
@@ -19,7 +19,6 @@ class Acrylic {
* @param CommandLine $cli
* @param Filesystem $files
* @param Site $site
- * @return void
*/
public function __construct(CommandLine $cli, Filesystem $files) {
$this->cli = $cli;
@@ -30,9 +29,11 @@ public function __construct(CommandLine $cli, Filesystem $files) {
* Install Acrylic DNS.
*
* @param string $tld
- * @return void
*/
public function install(string $tld = 'test') {
+ // Install the Acrylic package if it is not already installed.
+ resolve(Packages\Acrylic::class)->install();
+ // Install the Acrylic service and hosts file.
$this->createHostsFile($tld);
$this->installService();
}
@@ -41,7 +42,6 @@ public function install(string $tld = 'test') {
* Create the AcrylicHosts file.
*
* @param string $tld
- * @return void
*/
protected function createHostsFile(string $tld) {
$contents = $this->files->getStub('AcrylicHosts.txt');
@@ -58,8 +58,6 @@ protected function createHostsFile(string $tld) {
/**
* Install the Acrylic DNS service.
- *
- * @return void
*/
protected function installService() {
$this->uninstall();
@@ -78,8 +76,6 @@ function ($code, $output) {
/**
* Configure the Network DNS.
- *
- * @return void
*/
protected function configureNetworkDNS() {
$array = [
@@ -94,7 +90,6 @@ protected function configureNetworkDNS() {
* Update the tld used by Acrylic DNS.
*
* @param string $tld
- * @return void
*/
public function updateTld(string $tld) {
$this->stop();
@@ -106,8 +101,6 @@ public function updateTld(string $tld) {
/**
* Uninstall the Acrylic DNS service.
- *
- * @return void
*/
public function uninstall() {
if (!$this->installed()) {
@@ -139,8 +132,6 @@ protected function installed(): bool {
/**
* Remove the Network DNS.
- *
- * @return void
*/
protected function removeNetworkDNS() {
$array = [
@@ -153,8 +144,6 @@ protected function removeNetworkDNS() {
/**
* Start the Acrylic DNS service.
- *
- * @return void
*/
public function start() {
$this->cli->runOrExit(
@@ -169,8 +158,6 @@ function ($code, $output) {
/**
* Stop the Acrylic DNS service.
- *
- * @return void
*/
public function stop() {
$this->cli->run(
@@ -185,8 +172,6 @@ function ($code, $output) {
/**
* Restart the Acrylic DNS service.
- *
- * @return void
*/
public function restart() {
$this->cli->run(
@@ -201,8 +186,6 @@ function ($code, $output) {
/**
* Flush Windows DNS.
- *
- * @return void
*/
public function flushdns() {
$this->cli->run('cmd "/C ipconfig /flushdns"');
@@ -212,6 +195,7 @@ public function flushdns() {
* Get the Acrylic path.
*
* @param string $path
+ *
* @return string
*/
public function path(string $path = ''): string {
@@ -219,4 +203,4 @@ public function path(string $path = ''): string {
return $basePath . ($path ? "/$path" : $path);
}
-}
\ No newline at end of file
+}
diff --git a/cli/Valet/Application.php b/cli/Valet/Application.php
index 203216b5d..7e792a51a 100644
--- a/cli/Valet/Application.php
+++ b/cli/Valet/Application.php
@@ -5,7 +5,9 @@
use Silly\Application as SillyApplication;
class Application extends SillyApplication {
- /** @var string */
+ /**
+ * @var string
+ */
private static $logo =
" _ _ __ __ _ _ ____________
| | | | \ \ / / | | | | | \
@@ -23,4 +25,4 @@ class Application extends SillyApplication {
public function getHelp(): string {
return self::$logo . parent::getHelp();
}
-}
+}
\ No newline at end of file
diff --git a/cli/Valet/CommandLine.php b/cli/Valet/CommandLine.php
index 43932d2b2..f719867a5 100644
--- a/cli/Valet/CommandLine.php
+++ b/cli/Valet/CommandLine.php
@@ -11,7 +11,6 @@ class CommandLine {
* Pass the command to the command line and display the output.
*
* @param string $command
- * @return void
*/
public function passthru($command) {
passthru($command);
@@ -21,6 +20,7 @@ public function passthru($command) {
* Execute command via shell and return the complete output as a string
*
* @param string $command
+ *
* @return bool|string|null The output.
*/
public function shellExec($command) {
@@ -46,27 +46,18 @@ public function sudo($valetCommand, $asTrustedInstaller = false, $quiet = false)
$this->passthru($gsudo . ' ' . $valetCommand . ($quiet ? ' > nul 2>&1' : ''));
}
- /**
- * Run the given command as the non-root user.
- *
- * @param string $command
- * @param callable|null $onError
- * @param boolean $realTimeOutput Set to `true` to get the output in real time as the command is running. Default: `false`
- * @return ProcessOutput
- */
- public function run($command, ?callable $onError = null, $realTimeOutput = false) {
- return $this->runCommand($command, $onError, $realTimeOutput);
- }
-
/**
* Run the given command.
*
+ * Uses the Symfony Process component to run the command.
+ *
* @param string $command
* @param callable|null $onError
- * @param boolean $realTimeOutput Set to `true` to get the output in real time as the command is running. Default: `false`
+ * @param bool $realTimeOutput Set to `true` to get the output in real time as the command is running. Default: `false`
+ *
* @return ProcessOutput
*/
- public function runAsUser($command, ?callable $onError = null, $realTimeOutput = false) {
+ public function run($command, ?callable $onError = null, $realTimeOutput = false) {
return $this->runCommand($command, $onError, $realTimeOutput);
}
@@ -75,7 +66,8 @@ public function runAsUser($command, ?callable $onError = null, $realTimeOutput =
*
* @param string $command
* @param callable|null $onError
- * @param boolean $realTimeOutput Set to `true` to get the output in real time as the command is running. Default: `false`
+ * @param bool $realTimeOutput Set to `true` to get the output in real time as the command is running. Default: `false`
+ *
* @return ProcessOutput
*/
public function powershell(string $command, ?callable $onError = null, $realTimeOutput = false) {
@@ -87,7 +79,8 @@ public function powershell(string $command, ?callable $onError = null, $realTime
*
* @param string $command
* @param callable|null $onError
- * @param boolean $realTimeOutput Set to `true` to get the output in real time as the command is running. Default: `false`
+ * @param bool $realTimeOutput Set to `true` to get the output in real time as the command is running. Default: `false`
+ *
* @return ProcessOutput
*/
public function runOrExit($command, ?callable $onError = null, $realTimeOutput = false) {
@@ -109,7 +102,8 @@ public function runOrExit($command, ?callable $onError = null, $realTimeOutput =
*
* @param string $command
* @param callable|null $onError
- * @param boolean $realTimeOutput Set to `true` to get the output in real time as the command is running. Default: `false`
+ * @param bool $realTimeOutput Set to `true` to get the output in real time as the command is running. Default: `false`
+ *
* @return ProcessOutput|void Returns a ProcessOutput only if the real time is `false`, otherwise it doesn't return anything (void) as it's echoing out in real time.
*/
public function runCommand($command, ?callable $onError = null, $realTimeOutput = false) {
diff --git a/cli/Valet/Configuration.php b/cli/Valet/Configuration.php
index 0ff89f020..6492809e4 100644
--- a/cli/Valet/Configuration.php
+++ b/cli/Valet/Configuration.php
@@ -13,8 +13,7 @@ class Configuration {
/**
* Create a new Valet configuration class instance.
*
- * @param Filesystem $filesystem
- * @return void
+ * @param Filesystem $files
*/
public function __construct(Filesystem $files) {
$this->files = $files;
@@ -22,45 +21,63 @@ public function __construct(Filesystem $files) {
/**
* Install the Valet configuration file.
- *
- * @return void
*/
public function install() {
+ $this->createDirectories();
+ $this->writeBaseConfiguration();
+
+ $this->files->chown($this->path(), user());
+ }
+
+ /**
+ * Create the Valet configuration directories.
+ */
+ public function createDirectories() {
+ // Create the .config directory and the Valet home directory if they don't exist.
$this->createConfigurationDirectory();
+
+ // Create the directories within the Valet home directory.
+ $this->createCaDirectory();
+ $this->createCertificatesDirectory();
$this->createDriversDirectory();
- $this->createSitesDirectory();
+ $this->createEmergencyUninstallDirectory();
$this->createExtensionsDirectory();
$this->createLogDirectory();
- $this->createCertificatesDirectory();
+ $this->createNginxDirectory();
$this->createServicesDirectory();
+ $this->createSitesDirectory();
$this->createXdebugDirectory();
- $this->writeBaseConfiguration();
-
- // Copy the emergency stop and uninstall services script to the Valet home
- // directory for safe keeping.
- $this->files->copy(
- realpath(__DIR__ . '/../../emergency_uninstall_services.bat'),
- $this->valetHomePath("emergency_uninstall_services.bat")
- );
-
- $this->files->chown($this->path(), user());
}
/**
- * Create the Valet configuration directory.
- *
- * @return void
+ * Create the `~\.config` directory and the Valet configuration directory (`~\.config\valet`).
*/
public function createConfigurationDirectory() {
+ // Create the `.config` directory if it doesn't exist.
+
// The preg_replace gets "C:/Users/Username/.config"
$this->files->ensureDirExists(preg_replace('~/valet$~', '', $this->valetHomePath()), user());
+
+ // Create the Valet home directory if it doesn't exist.
$this->files->ensureDirExists($this->valetHomePath(), user());
}
+ /**
+ * Create the directory for the Valet self-signed Certificate Authority (CA) certificates.
+ */
+ public function createCaDirectory() {
+ $this->files->ensureDirExists($this->valetHomePath('CA'), user());
+ }
+
+ /**
+ * Create the directory for SSL/TLS certificates.
+ */
+ public function createCertificatesDirectory() {
+ $this->files->ensureDirExists($this->valetHomePath('Certificates'), user());
+ }
+
/**
* Create the Valet drivers directory.
- *
- * @return void
*/
public function createDriversDirectory() {
$driversPath = $this->valetHomePath('Drivers');
@@ -78,18 +95,14 @@ public function createDriversDirectory() {
}
/**
- * Create the Valet sites directory.
- *
- * @return void
+ * Create the directory for the Emergency Uninstall files.
*/
- public function createSitesDirectory() {
- $this->files->ensureDirExists($this->valetHomePath('Sites'), user());
+ public function createEmergencyUninstallDirectory() {
+ $this->files->ensureDirExists($this->valetHomePath('Emergency Uninstall'), user());
}
/**
* Create the directory for the Valet extensions.
- *
- * @return void
*/
public function createExtensionsDirectory() {
$this->files->ensureDirExists(Valet::homePath('Extensions'), user());
@@ -97,8 +110,6 @@ public function createExtensionsDirectory() {
/**
* Create the directory for logs.
- *
- * @return void
*/
public function createLogDirectory() {
$this->files->ensureDirExists($path = $this->valetHomePath('Log'), user());
@@ -107,27 +118,28 @@ public function createLogDirectory() {
}
/**
- * Create the directory for SSL/TLS certificates.
- *
- * @return void
+ * Create the directory for the site-specific Nginx server config files.
*/
- public function createCertificatesDirectory() {
- $this->files->ensureDirExists($this->valetHomePath('Certificates'), user());
+ public function createNginxDirectory() {
+ $this->files->ensureDirExists($this->valetHomePath('Nginx'), user());
}
/**
* Create the directory for the Windows services.
- *
- * @return void
*/
public function createServicesDirectory() {
$this->files->ensureDirExists($this->valetHomePath('Services'), user());
}
+ /**
+ * Create the Valet sites directory.
+ */
+ public function createSitesDirectory() {
+ $this->files->ensureDirExists($this->valetHomePath('Sites'), user());
+ }
+
/**
* Create the directory for the Xdebug profiler.
- *
- * @return void
*/
public function createXdebugDirectory() {
$this->files->ensureDirExists($this->valetHomePath('Xdebug'), user());
@@ -135,8 +147,6 @@ public function createXdebugDirectory() {
/**
* Write the base, initial configuration for Valet.
- *
- * @return void
*/
public function writeBaseConfiguration() {
if (!$this->files->exists($this->path())) {
@@ -170,8 +180,6 @@ public function writeBaseConfiguration() {
/**
* Forcefully delete the Valet home configuration directory and contents.
- *
- * @return void
*/
public function uninstall() {
$this->files->unlink($this->valetHomePath());
@@ -179,8 +187,6 @@ public function uninstall() {
/**
* Add the given php path to the configuration.
- *
- * @return void
*/
public function addDefaultPhp() {
$phpPath = lcfirst(\PhpCgi::findDefaultPhpPath());
@@ -196,6 +202,7 @@ public function addDefaultPhp() {
* Get the php configuration by path.
*
* @param string $phpPath
+ *
* @return mixed
*/
public function getPhp($phpPath) {
@@ -212,6 +219,7 @@ public function getPhp($phpPath) {
* Get the php configuration by version.
*
* @param string $phpVersion
+ *
* @return mixed
*/
public function getPhpByVersion($phpVersion) {
@@ -234,7 +242,8 @@ public function getPhpByVersion($phpVersion) {
* Determine if the given PHP version is the alias.
*
* @param string $phpVersion
- * @return boolean
+ *
+ * @return bool
*/
public function isPhpAlias($phpVersion) {
$php = $this->getPhpByVersion($phpVersion);
@@ -256,6 +265,7 @@ public function getPhpFullVersionByAlias($phpVersionAlias) {
* Add the given php path to the configuration.
*
* @param string $phpPath
+ *
* @return mixed
*/
public function addPhp($phpPath) {
@@ -314,6 +324,7 @@ public function addPhp($phpPath) {
* Remove the given php path from the configuration.
*
* @param string $phpPath
+ *
* @return mixed
*/
public function removePhp($phpPath) {
@@ -353,7 +364,6 @@ public function removePhp($phpPath) {
*
* @param string $path
* @param bool $prepend
- * @return void
*/
public function addPath(string $path, bool $prepend = false) {
$path = str_replace('\\', "/", $path);
@@ -368,7 +378,6 @@ public function addPath(string $path, bool $prepend = false) {
* Prepend the given path to the configuration.
*
* @param string $path
- * @return void
*/
public function prependPath(string $path) {
$this->addPath($path, true);
@@ -379,7 +388,6 @@ public function prependPath(string $path) {
* Used by `valet forget`
*
* @param string $path
- * @return void
*/
public function removePath(string $path) {
if ($path == $this->valetHomePath('Sites')) {
@@ -396,8 +404,6 @@ public function removePath(string $path) {
/**
* Prune all non-existent paths from the configuration.
- *
- * @return void
*/
public function prune() {
if (!$this->files->exists($this->path())) {
@@ -429,6 +435,7 @@ public function read(): array {
*
* @param string|int|null $key
* @param mixed $default
+ *
* @return mixed
*/
public function get($key, $default = null) {
@@ -440,6 +447,7 @@ public function get($key, $default = null) {
*
* @param string $key
* @param mixed $value
+ *
* @return array
*/
public function updateKey(string $key, $value): array {
@@ -454,7 +462,6 @@ public function updateKey(string $key, $value): array {
* Write the given configuration to disk.
*
* @param array $config
- * @return void
*/
public function write(array $config) {
$this->files->putAsUser($this->path(), json_encode(
@@ -476,6 +483,7 @@ public function path(): string {
* Get the Valet home path.
*
* @param string $path
+ *
* @return string
*/
protected function valetHomePath(string $path = ''): string {
diff --git a/cli/Valet/Diagnose.php b/cli/Valet/Diagnose.php
index 88e1b2da5..30b646c05 100644
--- a/cli/Valet/Diagnose.php
+++ b/cli/Valet/Diagnose.php
@@ -1,5 +1,7 @@
cli = $cli;
@@ -32,16 +33,21 @@ public function __construct(CommandLine $cli, Filesystem $files) {
$this->commands = [
'systeminfo',
'valet --version',
+
+ 'Valet Home Structure placeholder',
+ 'Valet Bin Structure placeholder',
+
'cat ' . \Configuration::path(),
$nginxPkgClass->packageExe() . ' -v 2>&1',
$nginxPkgClass->packageExe() . ' -c \"' . $nginxPkgClass->packagePath() . '/conf/nginx.conf\" -t -p ' . $nginxPkgClass->packagePath() . ' 2>&1',
- 'foreach ($file in get-ChildItem -Path "' . $nginxPkgClass->packagePath() . '/conf/nginx.conf", "' . $nginxPkgClass->packagePath() . '/valet/valet.conf", "' . VALET_HOME_PATH . '/Nginx/*.conf"){echo $file.fullname --------------------`n; Get-Content -Path $file; echo `n;}',
+ 'foreach ($file in get-ChildItem -Path "' . $nginxPkgClass->packagePath() . '/conf/nginx.conf", "' . $nginxPkgClass->packagePath() . '/valet/valet.conf", "' . Valet::homePath() . '/Nginx/*.conf"){echo $file.fullname --------------------`n; Get-Content -Path $file; echo `n;}',
valetBinPath() . 'ngrok.exe version',
resolve(Packages\Gsudo::class)->packageExe() . ' -v',
resolve(Packages\Ansicon::class)->packageExe() . ' /?',
'cat "' . valetBinPath() . 'acrylic/Readme.txt"',
+ 'cat "' . valetBinPath() . 'winsw/README.md"',
'php -v',
'cmd /C "where /f php"',
'php --ini',
@@ -49,7 +55,7 @@ public function __construct(CommandLine $cli, Filesystem $files) {
'php --ri curl',
'cmd /C curl --version',
'cat "' . pathFilter(trim(\Valet::getComposerGlobalPath())) . '/composer.json"',
- 'composer global diagnose --no-ansi 1>' . VALET_HOME_PATH . '/composer.txt',
+ 'composer global diagnose --no-ansi 1>' . Valet::homePath() . '/composer.txt',
'composer global outdated --format json'
];
}
@@ -57,8 +63,8 @@ public function __construct(CommandLine $cli, Filesystem $files) {
/**
* Run diagnostics.
*
- * @param boolean $print Print the output as the commands are running.
- * @param boolean $plain Print and format the output as plain text (aka pretty print).
+ * @param bool $print Print the output as the commands are running.
+ * @param bool $plainText Print and format the output as plain text (aka pretty print).
*/
public function run($print, $plainText) {
$this->print = $print;
@@ -69,7 +75,13 @@ public function run($print, $plainText) {
$this->beforeCommand($command);
- $output = $this->cli->powershell($command);
+ if ($this->isNonCliCommand($command)) {
+ $output = $this->runNonCliCommand($command);
+ }
+ else {
+ $output = $this->cli->powershell($command);
+ }
+
if ($this->ignoreOutput($command)) {
return;
@@ -100,6 +112,42 @@ public function run($print, $plainText) {
$this->afterRun();
}
+ /**
+ * Run a non-CLI command, ie. run a task that is not a commandline command,
+ * and only achievable via PHP to generate the output.
+ *
+ * @param string $command The command placeholder to check for to run the task.
+ *
+ * @return string|array The output of the task.
+ */
+ protected function runNonCliCommand($command) {
+
+ /** Valet Home Structure & Valet Bin Structure **/
+ if (str_contains($command, "Valet Home Structure") || str_contains($command, "Valet Bin Structure")) {
+ /** Valet Home Structure **/
+ if (str_contains($command, "Valet Home Structure")) {
+ // Recursively scan all directories and files in valet's home path.
+ $dirsArray = $this->files->scanDirRecursive(Valet::homePath());
+ }
+ /** Valet Bin Structure **/
+ elseif (str_contains($command, "Valet Bin Structure")) {
+ // Recursively scan all directories and files in valet's bin path.
+ $dirsArray = $this->files->scanDirRecursive(valetBinPath());
+ }
+
+ // Generate a directory tree structure from the output array.
+
+ $isBin = str_contains($command, "Bin");
+
+ $parentDir = $isBin ? "valet/bin" : ".config/valet";
+ $skip = $isBin ? ["temp"] : ['Log', 'Ngrok', "Xdebug"];
+
+ $output = $this->generateDirectoryTree($dirsArray, $parentDir, $skip);
+ }
+
+ return $output;
+ }
+
/**
* Before running the diagnostics.
*/
@@ -134,6 +182,9 @@ protected function afterRun() {
*/
protected function beforeCommand($command) {
if ($this->print) {
+ if ($this->isNonCliCommand($command)) {
+ $command = str_replace("placeholder", "", $command);
+ }
info(PHP_EOL . "$ $command");
}
}
@@ -157,12 +208,26 @@ protected function afterCommand($command, $output) {
* Determines if Valet should ignore the output of a command.
*
* @param string $command
- * @return boolean
+ *
+ * @return bool
*/
protected function ignoreOutput($command) {
return strpos($command, '> /dev/null 2>&1') !== false;
}
+ /**
+ * Determines if the command is a non-CLI command,
+ * which is not a real command, but a placeholder for
+ * a command or task that is run via PHP instead of the CLI.
+ *
+ * @param string $command
+ *
+ * @return bool
+ */
+ protected function isNonCliCommand($command) {
+ return strpos($command, 'placeholder') !== false;
+ }
+
/**
* Edit the output of a specific command, to improve
* the human readability of the diagnostics, and/or
@@ -170,15 +235,18 @@ protected function ignoreOutput($command) {
*
* @param string $command
* @param string|ProcessOutput $output
+ *
* @return string $output The edited output.
*/
protected function editOutput($command, $output) {
+ /** System Info **/
// Extract the OS Name and OS Version, lines 2 and 3.
if (str_contains($command, "systeminfo")) {
$output = explode("\n", $output);
$output = implode("\n", [$output[2], $output[3]]);
}
+ /** Nginx **/
if (str_contains($command, "nginx.exe")) {
$output = $output->__toString();
@@ -196,11 +264,13 @@ protected function editOutput($command, $output) {
}
}
+ /** Composer Diagnose **/
if (str_contains($command, "composer global diagnose")) {
- $output = $this->cli->powershell('cat '. VALET_HOME_PATH .'/composer.txt');
- $this->files->unlink(VALET_HOME_PATH .'/composer.txt');
+ $output = $this->cli->powershell('cat ' . Valet::homePath() . '/composer.txt');
+ $this->files->unlink(Valet::homePath() . '/composer.txt');
}
+ /** Composer Outdated **/
if (str_contains($command, "composer global outdated")) {
$output = json_decode($output, true);
$output = $output["installed"];
@@ -227,20 +297,133 @@ protected function editOutput($command, $output) {
$output = json_encode($output, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
}
+ /** Acrylic **/
if (str_contains($command, "acrylic")) {
if (preg_match("/version is:\s+\d+(\.\d+)+/", $output, $matches)) {
$output = "Acrylic " . preg_replace("/:\s+/", " ", $matches[0]);
}
}
+ /** WinSW **/
+ if (str_contains($command, "winsw")) {
+ if (preg_match("/\(v\d+(\.\d+)+\)/", $output, $matches)) {
+ $output = "WinSW version is " . preg_replace("/(\(|\))/", "", $matches[0]);
+ }
+ }
+
return $output;
}
+ /**
+ * Generate a directory tree structure from the output array.
+ * This is used to visually represent the directory structures.
+ *
+ * @param array $array The array to generate the tree from.
+ * @param string $parentDir The parent directory name to start the tree from.
+ * @param array $skip An array of directory names to skip looping their files.
+ * @param string $indent The indentation string to use for the tree structure.
+ * This is very important as it determines the visual representation of the tree.
+ * It uses combinations of spaces and tree characters to create the structure.
+ *
+ * @return string The generated directory tree structure.
+ */
+ private function generateDirectoryTree($array, $parentDir, $skip, $indent = '') {
+ // Setup the tree characters
+ $treeItemChar = "┣━";
+ $treeSeparatorChar = "┃";
+ $treeEndItemChar = "┗━";
+ $dirEmoji = "📁";
+
+ // Helper to check if any skip string is in the dir name
+ $shouldSkip = function ($name) use ($skip) {
+ foreach ($skip as $s) {
+ if (stripos($name, $s) !== false) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+ // If the indent is empty, then this is the first line of the tree,
+ // so start the tree with the parent directory name, otherwise an empty string.
+ $tree = $indent === '' ? "$parentDir\n$treeSeparatorChar\n" : '';
+
+ $entries = [];
+ foreach ($array as $key => $value) {
+ $name = is_string($key) ? $key : $value;
+ $entries[] = ['name' => $name, 'value' => $value];
+ }
+ $count = count($entries);
+
+ foreach ($entries as $i => $entry) {
+ $name = $entry['name'];
+ $value = $entry['value'];
+ $isLast = ($i === $count - 1);
+ $isDir = is_array($value);
+ $isNextItemDir = ($i < $count - 1 && is_array($entries[$i + 1]['value']));
+
+ // If the item is a directory, then add the directory emoji to the item character.
+ // Otherwise, just use the item character.
+ // This is so that we can better visually determine which items are directories.
+ $itemChar = $isDir ? $treeItemChar . $dirEmoji : $treeItemChar;
+ $endItemChar = $isDir ? $treeEndItemChar . $dirEmoji : $treeEndItemChar;
+
+ // If the item is a directory and should be skipped, then just output the directory name
+ if ($shouldSkip($name)) {
+ // Add the directory name to the tree.
+ $tree .= $indent . ($isLast ? $endItemChar : $itemChar) . " $name\n";
+ // Add a line separation.
+ $tree .= $indent . "$treeSeparatorChar\n";
+ continue;
+ }
+
+ // If the item is not a directory, ie a file, then add the file name to the tree.
+ $tree .= $indent . ($isLast ? $endItemChar : $itemChar) . " $name\n";
+
+ if (!$isDir && !$isLast && $isNextItemDir) {
+ // Add a line separation between the file and the next item.
+ $tree .= $indent . "$treeSeparatorChar\n";
+ }
+
+ // If the item is a directory...
+ if ($isDir) {
+ // Recurse into subdirectory and increase the indentation.
+ $tree .= $this->generateDirectoryTree(
+ $value,
+ $name,
+ $skip,
+ $indent . ($isLast ? " " : "$treeSeparatorChar ")
+ );
+ }
+
+ // Get the current line in the string (the last line added to $tree)
+ $lines = explode("\n", rtrim($tree, "\n"));
+ $currentLine = trim(end($lines));
+
+ // If item is a directory AND the next item is also a directory,
+ // OR
+ // the current line contains a tree end item character AND
+ // the next item is not a directory (ie. is a file) AND
+ // is also not last,
+ // then add a line separation.
+ //
+ // This separates consecutive empty directories
+ // and also separates a directory's tree from the next file.
+ if (($isDir && $isNextItemDir) || (str_contains($currentLine, $treeEndItemChar) && !$isNextItemDir && !$isLast)) {
+ // Add a line separation.
+ $tree .= $indent . "$treeSeparatorChar\n";
+ }
+ }
+
+ return $tree;
+ }
+
/**
* Format the output for the terminal.
*
* @param \Illuminate\Support\Collection $results A collection of the outputs.
- * @param boolean $plainText
+ * @param bool $plainText
+ *
* @return array|string The formatted output as a `string`,
* or if `plainText` is `true`, then an `array` of the formatted plain output
* as a `string` for the terminal
@@ -316,6 +499,8 @@ protected function format($results, $plainText) {
* @param string $command
* @param string $output
* @param string $heading The heading to output before the command name.
+ *
+ * @return string The formatted output as HTML.
*/
protected function formatForCopy($command, $output, $heading) {
if (str_contains($command, "composer global outdated")) {
@@ -369,7 +554,7 @@ protected function formatForCopy($command, $output, $heading) {
}
return sprintf(
- '%s%s%s
%s
%s
%s
%s',
+ '%s%s%s
%s
%s
%s
%s',
PHP_EOL,
$heading,
PHP_EOL,
@@ -396,9 +581,12 @@ protected function copyToClipboard($output) {
// Code based on https://stackoverflow.com/a/40731340/2358222
$output = preg_replace('/(\e)|([[]|[]])[A-Za-z0-9];*[0-9]*m?/', '', $output);
- $this->files->put(VALET_HOME_PATH . '/valet_diagnostics.txt', $output);
- $this->cli->powershell('type ' . VALET_HOME_PATH . '/valet_diagnostics.txt | clip');
- $this->files->unlink(VALET_HOME_PATH . '/valet_diagnostics.txt');
+ $file = Valet::homePath() . '/valet_diagnostics.txt';
+
+ // Write to file as UTF-8 with BOM to help Set-Clipboard recognize Unicode characters.
+ $this->files->put($file, "\xEF\xBB\xBF" . $output);
+ $this->cli->powershell('type ' . $file . ' | Set-Clipboard');
+ $this->files->unlink($file);
}
/**
@@ -406,12 +594,15 @@ protected function copyToClipboard($output) {
* creating a new collection with the headings as the keys.
*
* @param \Illuminate\Support\Collection $results A collection of the outputs.
+ *
* @return \Illuminate\Support\Collection The new combined collection
*/
protected function combineWithHeadings($results) {
return collect([
"System Version",
"Valet Version",
+ "Valet Home Structure",
+ "Valet Bin Structure",
"Valet Config",
"nginx Version",
"nginx Config Check",
@@ -420,6 +611,7 @@ protected function combineWithHeadings($results) {
"gsudo Version",
"Ansicon Version",
"Acrylic Version",
+ "WinSW Version",
"PHP Version",
"PHP Location",
"PHP Ini Location",
@@ -431,4 +623,4 @@ protected function combineWithHeadings($results) {
"Outdated Composer Packages"
])->combine($results);
}
-}
+}
\ No newline at end of file
diff --git a/cli/Valet/Drivers/BasicValetDriver.php b/cli/Valet/Drivers/BasicValetDriver.php
new file mode 100644
index 000000000..6b7293db2
--- /dev/null
+++ b/cli/Valet/Drivers/BasicValetDriver.php
@@ -0,0 +1,84 @@
+isActualFile($staticFilePath = $sitePath . $uri)) {
+ return $staticFilePath;
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the fully resolved path to the application's front controller.
+ *
+ * @param string $sitePath
+ * @param string $siteName
+ * @param string $uri
+ *
+ * @return string|null
+ */
+ public function frontControllerPath($sitePath, $siteName, $uri) {
+ $uri = rtrim($uri, '/');
+
+ $candidates = [
+ $sitePath . $uri,
+ $sitePath . "$uri/index.php",
+ "$sitePath/index.php",
+ "$sitePath/index.html"
+ ];
+
+ foreach ($candidates as $candidate) {
+ if ($this->isActualFile($candidate)) {
+ $_SERVER['SCRIPT_FILENAME'] = $candidate;
+ $_SERVER['SCRIPT_NAME'] = str_replace($sitePath, '', $candidate);
+ $_SERVER['DOCUMENT_ROOT'] = $sitePath;
+
+ return $candidate;
+ }
+ }
+
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/cli/Valet/Drivers/BasicWithPublicValetDriver.php b/cli/Valet/Drivers/BasicWithPublicValetDriver.php
new file mode 100644
index 000000000..d74409c8b
--- /dev/null
+++ b/cli/Valet/Drivers/BasicWithPublicValetDriver.php
@@ -0,0 +1,86 @@
+isActualFile($publicPath)) {
+ return $publicPath;
+ }
+ elseif (file_exists("$publicPath/index.html")) {
+ return "$publicPath/index.html";
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the fully resolved path to the application's front controller.
+ *
+ * @param string $sitePath
+ * @param string $siteName
+ * @param string $uri
+ *
+ * @return string|null
+ */
+ public function frontControllerPath($sitePath, $siteName, $uri) {
+ $docRoot = "$sitePath/public";
+ $uri = rtrim($uri, '/');
+
+ $candidates = [
+ $docRoot . $uri,
+ $docRoot . "$uri/index.php",
+ "$docRoot/index.php",
+ "$docRoot/index.html"
+ ];
+
+ foreach ($candidates as $candidate) {
+ if ($this->isActualFile($candidate)) {
+ $_SERVER['SCRIPT_FILENAME'] = $candidate;
+ $_SERVER['SCRIPT_NAME'] = str_replace("$sitePath/public", '', $candidate);
+ $_SERVER['DOCUMENT_ROOT'] = "$sitePath/public";
+
+ return $candidate;
+ }
+ }
+
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/cli/Valet/Drivers/LaravelValetDriver.php b/cli/Valet/Drivers/LaravelValetDriver.php
new file mode 100644
index 000000000..4ff98c60b
--- /dev/null
+++ b/cli/Valet/Drivers/LaravelValetDriver.php
@@ -0,0 +1,77 @@
+isActualFile($storagePath = "$sitePath/storage/app/public$storageUri")) {
+ return $storagePath;
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the fully resolved path to the application's front controller.
+ *
+ * @param string $sitePath
+ * @param string $siteName
+ * @param string $uri
+ *
+ * @return string|null
+ */
+ public function frontControllerPath($sitePath, $siteName, $uri) {
+ if (file_exists($staticFilePath = "$sitePath/public$uri") && $this->isActualFile($staticFilePath)) {
+ return $staticFilePath;
+ }
+
+ return "$sitePath/public/index.php";
+ }
+}
\ No newline at end of file
diff --git a/cli/Valet/Drivers/Specific/BedrockValetDriver.php b/cli/Valet/Drivers/Specific/BedrockValetDriver.php
new file mode 100644
index 000000000..c2b00cb15
--- /dev/null
+++ b/cli/Valet/Drivers/Specific/BedrockValetDriver.php
@@ -0,0 +1,76 @@
+composerRequires($sitePath, 'roots/bedrock-autoloader')
+ || file_exists("$sitePath/web/app/mu-plugins/bedrock-autoloader.php")
+ || (is_dir("$sitePath/web/app/")
+ && file_exists("$sitePath/web/wp-config.php")
+ && file_exists("$sitePath/config/application.php"));
+ }
+
+ /**
+ * Determine if the incoming request is for a static file.
+ *
+ * @param string $sitePath
+ * @param string $siteName
+ * @param string $uri
+ *
+ * @return string|false
+ */
+ public function isStaticFile($sitePath, $siteName, $uri) {
+ $staticFilePath = "$sitePath/web$uri";
+
+ if ($this->isActualFile($staticFilePath)) {
+ return $staticFilePath;
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the fully resolved path to the application's front controller.
+ *
+ * @param string $sitePath
+ * @param string $siteName
+ * @param string $uri
+ *
+ * @return string|null
+ */
+ public function frontControllerPath($sitePath, $siteName, $uri) {
+ return parent::frontControllerPath(
+ "{$sitePath}/web",
+ $siteName,
+ $this->forceTrailingSlash($uri)
+ );
+ }
+
+ /**
+ * Redirect to uri with trailing slash.
+ *
+ * @param string $uri
+ *
+ * @return string
+ */
+ private function forceTrailingSlash($uri) {
+ if (substr($uri, -1 * strlen('/wp/wp-admin')) == '/wp/wp-admin') {
+ header("Location: $uri/");
+ exit;
+ }
+
+ return $uri;
+ }
+}
diff --git a/cli/Valet/Drivers/Specific/CakeValetDriver.php b/cli/Valet/Drivers/Specific/CakeValetDriver.php
new file mode 100644
index 000000000..8f37b56df
--- /dev/null
+++ b/cli/Valet/Drivers/Specific/CakeValetDriver.php
@@ -0,0 +1,64 @@
+isActualFile($staticFilePath = "{$sitePath}/webroot/{$uri}")) {
+ return $staticFilePath;
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the fully resolved path to the application's front controller.
+ *
+ * @param string $sitePath
+ * @param string $siteName
+ * @param string $uri
+ *
+ * @return string|null
+ */
+ public function frontControllerPath($sitePath, $siteName, $uri) {
+ return "{$sitePath}/webroot/index.php";
+ }
+}
\ No newline at end of file
diff --git a/cli/Valet/Drivers/Specific/Concrete5ValetDriver.php b/cli/Valet/Drivers/Specific/Concrete5ValetDriver.php
new file mode 100644
index 000000000..c808fe17a
--- /dev/null
+++ b/cli/Valet/Drivers/Specific/Concrete5ValetDriver.php
@@ -0,0 +1,69 @@
+isActualFile($staticFilePath = "{$sitePath}/web{$uri}")) {
+ return $staticFilePath;
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the fully resolved path to the application's front controller.
+ *
+ * @param string $sitePath
+ * @param string $siteName
+ * @param string $uri
+ *
+ * @return string|null
+ */
+ public function frontControllerPath($sitePath, $siteName, $uri) {
+ if ($uri === '/install.php') {
+ return "{$sitePath}/web/install.php";
+ }
+
+ if (strncmp($uri, '/app_dev.php', 12) === 0) {
+ $_SERVER['SCRIPT_NAME'] = '/app_dev.php';
+ $_SERVER['SCRIPT_FILENAME'] = "{$sitePath}/app_dev.php";
+
+ return "{$sitePath}/web/app_dev.php";
+ }
+
+ return "{$sitePath}/web/app.php";
+ }
+}
diff --git a/cli/Valet/Drivers/Specific/CraftValetDriver.php b/cli/Valet/Drivers/Specific/CraftValetDriver.php
new file mode 100644
index 000000000..2652b2f99
--- /dev/null
+++ b/cli/Valet/Drivers/Specific/CraftValetDriver.php
@@ -0,0 +1,117 @@
+frontControllerDirectory($sitePath);
+
+ if ($this->isActualFile($staticFilePath = "{$sitePath}/{$frontControllerDirectory}{$uri}")) {
+ return $staticFilePath;
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the fully resolved path to the application's front controller.
+ *
+ * @param string $sitePath
+ * @param string $siteName
+ * @param string $uri
+ *
+ * @return string|null
+ */
+ public function frontControllerPath($sitePath, $siteName, $uri) {
+ $frontControllerDirectory = $this->frontControllerDirectory($sitePath);
+
+ $indexPath = "{$sitePath}/{$frontControllerDirectory}/index.php";
+ $scriptName = '/index.php';
+
+ // Check if the first URL segment matches any of the defined locales
+ $locales = $this->getLocales();
+
+ $parts = explode('/', $uri);
+
+ if (count($parts) > 1 && in_array($parts[1], $locales)) {
+ $indexLocalizedPath = "$sitePath/$frontControllerDirectory/{$parts[1]}/index.php";
+
+ // Check if index.php exists in the localized folder, this is optional in Craft 3
+ if (file_exists($indexLocalizedPath)) {
+ $indexPath = $indexLocalizedPath;
+ $scriptName = "/{$parts[1]}/index.php";
+ }
+ }
+
+ $_SERVER['SCRIPT_FILENAME'] = $indexPath;
+ $_SERVER['SERVER_NAME'] = $_SERVER['HTTP_HOST'];
+ $_SERVER['SCRIPT_NAME'] = $scriptName;
+ $_SERVER['PHP_SELF'] = $scriptName;
+ $_SERVER['DOCUMENT_ROOT'] = "$sitePath/$frontControllerDirectory";
+
+ if (isset($_SERVER['argv'])) {
+ unset($_SERVER['argv']);
+ }
+
+ return $indexPath;
+ }
+
+ public function getLocales() {
+ // phpcs:disable Squiz.Arrays.ArrayDeclaration.ValueNoNewline
+ return [
+ 'ar', 'ar_sa', 'bg', 'bg_bg', 'ca_es', 'cs', 'cy_gb', 'da', 'da_dk', 'de', 'de_at', 'de_ch', 'de_de',
+ 'el', 'el_gr', 'en', 'en_as', 'en_au', 'en_bb', 'en_be', 'en_bm', 'en_bw', 'en_bz', 'en_ca', 'en_dsrt',
+ 'en_dsrt_us', 'en_gb', 'en_gu', 'en_gy', 'en_hk', 'en_ie', 'en_in', 'en_jm', 'en_mh', 'en_mp', 'en_mt',
+ 'en_mu', 'en_na', 'en_nz', 'en_ph', 'en_pk', 'en_sg', 'en_shaw', 'en_tt', 'en_um', 'en_us', 'en_us_posix',
+ 'en_vi', 'en_za', 'en_zw', 'en_zz', 'es', 'es_cl', 'es_es', 'es_mx', 'es_us', 'es_ve', 'et', 'fi', 'fi_fi',
+ 'fil', 'fr', 'fr_be', 'fr_ca', 'fr_ch', 'fr_fr', 'fr_ma', 'he', 'hr', 'hr_hr', 'hu', 'hu_hu', 'id', 'id_id',
+ 'it', 'it_ch', 'it_it', 'ja', 'ja_jp', 'ko', 'ko_kr', 'lt', 'lv', 'ms', 'ms_my', 'nb', 'nb_no', 'nl',
+ 'nl_be', 'nl_nl', 'nn', 'nn_no', 'no', 'pl', 'pl_pl', 'pt', 'pt_br', 'pt_pt', 'ro', 'ro_ro', 'ru', 'ru_ru',
+ 'sk', 'sl', 'sr', 'sv', 'sv_se', 'th', 'th_th', 'tr', 'tr_tr', 'uk', 'vi', 'zh', 'zh_cn', 'zh_tw'
+ ];
+ // phpcs:enable
+ }
+}
diff --git a/cli/Valet/Drivers/Specific/DrupalValetDriver.php b/cli/Valet/Drivers/Specific/DrupalValetDriver.php
new file mode 100644
index 000000000..653ad2fcd
--- /dev/null
+++ b/cli/Valet/Drivers/Specific/DrupalValetDriver.php
@@ -0,0 +1,129 @@
+addSubdirectory($sitePath);
+
+ /**
+ * /misc/drupal.js = Drupal 7
+ * /core/lib/Drupal.php = Drupal 8.
+ */
+ return file_exists("{$sitePath}/misc/drupal.js")
+ || file_exists("{$sitePath}/core/lib/Drupal.php");
+ }
+
+ /**
+ * Determine if the incoming request is for a static file.
+ *
+ * @param string $sitePath
+ * @param string $siteName
+ * @param string $uri
+ *
+ * @return string|false
+ */
+ public function isStaticFile($sitePath, $siteName, $uri) {
+ $sitePath = $this->addSubdirectory($sitePath);
+
+ // phpcs:disable PSR2.ControlStructures.ControlStructureSpacing.SpacingAfterOpenBrace, PSR12.ControlStructures.ControlStructureSpacing.FirstExpressionLine
+ if (file_exists("{$sitePath}{$uri}")
+ && !is_dir("{$sitePath}{$uri}")
+ && pathinfo("{$sitePath}{$uri}")['extension'] != 'php'
+ ) {
+ // phpcs:enable
+ return "{$sitePath}{$uri}";
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the fully resolved path to the application's front controller.
+ *
+ * @param string $sitePath
+ * @param string $siteName
+ * @param string $uri
+ *
+ * @return string|null
+ */
+ public function frontControllerPath($sitePath, $siteName, $uri) {
+ $sitePath = $this->addSubdirectory($sitePath);
+
+ // phpcs:disable PSR2.ControlStructures.ControlStructureSpacing.SpacingAfterOpenBrace, PSR12.ControlStructures.ControlStructureSpacing.FirstExpressionLine
+ if (!isset($_GET['Q'])
+ && !empty($uri)
+ && $uri !== '/'
+ && strpos($uri, '/jsonapi/') === false
+ ) {
+ // phpcs:enable
+ $_GET['Q'] = $uri;
+ }
+
+ $matches = [];
+ if (preg_match('/^\/(.*?)\.php/', $uri, $matches)) {
+ $filename = $matches[0];
+ // phpcs:disable PSR2.ControlStructures.ControlStructureSpacing.SpacingAfterOpenBrace, PSR12.ControlStructures.ControlStructureSpacing.FirstExpressionLine
+ if (file_exists("{$sitePath}{$filename}")
+ && !is_dir("{$sitePath}{$filename}")
+ ) {
+ // phpcs:enable
+ $_SERVER['SCRIPT_FILENAME'] = "{$sitePath}{$filename}";
+ $_SERVER['SCRIPT_NAME'] = $filename;
+
+ return "{$sitePath}{$filename}";
+ }
+ }
+
+ // Fallback
+ $_SERVER['SCRIPT_FILENAME'] = "{$sitePath}/index.php";
+ $_SERVER['SCRIPT_NAME'] = '/index.php';
+
+ return "{$sitePath}/index.php";
+ }
+
+ /**
+ * Add any matching subdirectory to the site path.
+ *
+ * @param string $sitePath
+ *
+ * @return string
+ */
+ public function addSubdirectory($sitePath) {
+ $paths = array_map(function ($subDir) use ($sitePath) {
+ return "{$sitePath}/{$subDir}";
+ }, $this->possibleSubdirectories());
+
+ $foundPaths = array_filter($paths, function ($path) {
+ return file_exists($path);
+ });
+
+ // If paths are found, return the first one.
+ if (!empty($foundPaths)) {
+ return array_shift($foundPaths);
+ }
+
+ // If there are no matches, return the original path.
+ return $sitePath;
+ }
+
+ /**
+ * Return an array of possible subdirectories.
+ *
+ * @return array
+ */
+ private function possibleSubdirectories() {
+ return ['docroot', 'public', 'web'];
+ }
+}
diff --git a/cli/Valet/Drivers/Specific/JigsawValetDriver.php b/cli/Valet/Drivers/Specific/JigsawValetDriver.php
new file mode 100644
index 000000000..79af949e8
--- /dev/null
+++ b/cli/Valet/Drivers/Specific/JigsawValetDriver.php
@@ -0,0 +1,31 @@
+isActualFile($staticFilePath = "{$sitePath}{$uri}")) {
+ return $staticFilePath;
+ }
+ elseif ($this->isActualFile($staticFilePath = "{$sitePath}/public{$uri}")) {
+ return $staticFilePath;
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the fully resolved path to the application's front controller.
+ *
+ * @param string $sitePath
+ * @param string $siteName
+ * @param string $uri
+ *
+ * @return string|null
+ */
+ public function frontControllerPath($sitePath, $siteName, $uri) {
+ $scriptName = '/index.php';
+
+ if ($this->isActualFile("$sitePath/index.php")) {
+ $indexPath = "$sitePath/index.php";
+ }
+
+ if ($isAboveWebroot = $this->isActualFile("$sitePath/public/index.php")) {
+ $indexPath = "$sitePath/public/index.php";
+ }
+
+ if (preg_match('/^\/panel/', $uri) && $this->isActualFile("$sitePath/panel/index.php")) {
+ $scriptName = '/panel/index.php';
+ $indexPath = "$sitePath/panel/index.php";
+ }
+
+ if (preg_match('/^\/(?!(kirby|site|content)\/).+\.php$/', $uri)) {
+ // phpcs:disable PSR2.ControlStructures.ControlStructureSpacing.SpacingAfterOpenBrace, PSR12.ControlStructures.ControlStructureSpacing.FirstExpressionLine
+ if ($this->isActualFile("{$sitePath}{$uri}")
+ || $isAboveWebroot
+ && $this->isActualFile("{$sitePath}/public{$uri}")
+ ) {
+ // phpcs:enable
+ $scriptName = $uri;
+ $indexPath = "{$sitePath}{$scriptName}";
+ }
+ }
+
+ $sitePathPrefix = ($isAboveWebroot) ? "$sitePath/public" : $sitePath;
+
+ $_SERVER['SERVER_NAME'] = $_SERVER['HTTP_HOST'];
+ $_SERVER['SCRIPT_NAME'] = $scriptName;
+ $_SERVER['SCRIPT_FILENAME'] = "{$sitePathPrefix}{$scriptName}";
+
+ return $indexPath;
+ }
+}
diff --git a/cli/Valet/Drivers/Specific/Magento2ValetDriver.php b/cli/Valet/Drivers/Specific/Magento2ValetDriver.php
new file mode 100644
index 000000000..a7ce68584
--- /dev/null
+++ b/cli/Valet/Drivers/Specific/Magento2ValetDriver.php
@@ -0,0 +1,66 @@
+isActualFile($staticFilePath = "{$sitePath}/Web{$uri}")) {
+ return $staticFilePath;
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the fully resolved path to the application's front controller.
+ *
+ * @param string $sitePath
+ * @param string $siteName
+ * @param string $uri
+ *
+ * @return string|null
+ */
+ public function frontControllerPath($sitePath, $siteName, $uri) {
+ return "{$sitePath}/Web/index.php";
+ }
+}
\ No newline at end of file
diff --git a/cli/Valet/Drivers/Specific/NetteValetDriver.php b/cli/Valet/Drivers/Specific/NetteValetDriver.php
new file mode 100644
index 000000000..89042b79c
--- /dev/null
+++ b/cli/Valet/Drivers/Specific/NetteValetDriver.php
@@ -0,0 +1,67 @@
+isActualFile($staticFilePath = "{$sitePath}/www/{$uri}")) {
+ return $staticFilePath;
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the fully resolved path to the application's front controller.
+ *
+ * @param string $sitePath
+ * @param string $siteName
+ * @param string $uri
+ *
+ * @return string|null
+ */
+ public function frontControllerPath($sitePath, $siteName, $uri) {
+ return "{$sitePath}/www/index.php";
+ }
+}
\ No newline at end of file
diff --git a/cli/Valet/Drivers/Specific/RadicleValetDriver.php b/cli/Valet/Drivers/Specific/RadicleValetDriver.php
new file mode 100644
index 000000000..6dd891612
--- /dev/null
+++ b/cli/Valet/Drivers/Specific/RadicleValetDriver.php
@@ -0,0 +1,86 @@
+isActualFile($staticFilePath)) {
+ return $staticFilePath;
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the fully resolved path to the application's front controller.
+ *
+ * @param string $sitePath
+ * @param string $siteName
+ * @param string $uri
+ *
+ * @return string|null
+ */
+ public function frontControllerPath($sitePath, $siteName, $uri) {
+ if (strpos($uri, '/wp/') === 0) {
+ return is_dir("{$sitePath}/public{$uri}")
+ ? "{$sitePath}/public" . $this->forceTrailingSlash($uri) . '/index.php'
+ : "{$sitePath}/public{$uri}";
+ }
+
+ return "{$sitePath}/public/index.php";
+ }
+
+ /**
+ * Redirect to URI with trailing slash.
+ *
+ * @param string $uri
+ *
+ * @return string
+ */
+ private function forceTrailingSlash($uri) {
+ if (substr($uri, -1 * strlen('/wp/wp-admin')) == '/wp/wp-admin') {
+ header("Location: {$uri}/");
+ exit;
+ }
+
+ return $uri;
+ }
+}
\ No newline at end of file
diff --git a/cli/Valet/Drivers/Specific/SculpinValetDriver.php b/cli/Valet/Drivers/Specific/SculpinValetDriver.php
new file mode 100644
index 000000000..2a3a1af52
--- /dev/null
+++ b/cli/Valet/Drivers/Specific/SculpinValetDriver.php
@@ -0,0 +1,56 @@
+isModernSculpinProject($sitePath)
+ || $this->isLegacySculpinProject($sitePath);
+ }
+
+ /**
+ * Determine if the project is a modern Sculpin project.
+ *
+ * @param string $sitePath
+ *
+ * @return bool
+ */
+ private function isModernSculpinProject($sitePath) {
+ return is_dir("{$sitePath}/source")
+ && is_dir("{$sitePath}/output_dev")
+ && $this->composerRequires($sitePath, 'sculpin/sculpin');
+ }
+
+ /**
+ * Determine if the project is a legacy Sculpin project.
+ *
+ * @param string $sitePath
+ *
+ * @return bool
+ */
+ private function isLegacySculpinProject($sitePath) {
+ return is_dir("{$sitePath}/.sculpin");
+ }
+
+ /**
+ * Mutate the incoming URI.
+ *
+ * @param string $uri
+ *
+ * @return string
+ */
+ public function mutateUri($uri) {
+ return rtrim("/output_dev{$uri}", '/');
+ }
+}
diff --git a/cli/Valet/Drivers/Specific/StatamicV1ValetDriver.php b/cli/Valet/Drivers/Specific/StatamicV1ValetDriver.php
new file mode 100644
index 000000000..447ebccc5
--- /dev/null
+++ b/cli/Valet/Drivers/Specific/StatamicV1ValetDriver.php
@@ -0,0 +1,77 @@
+isActualFile($staticFilePath = "{$sitePath}{$uri}")) {
+ return $staticFilePath;
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the fully resolved path to the application's front controller.
+ *
+ * @param string $sitePath
+ * @param string $siteName
+ * @param string $uri
+ *
+ * @return string|null
+ */
+ public function frontControllerPath($sitePath, $siteName, $uri) {
+ if (strpos($uri, '/admin.php') === 0) {
+ $_SERVER['SCRIPT_NAME'] = '/admin.php';
+
+ return "{$sitePath}/admin.php";
+ }
+
+ if ($uri === '/admin') {
+ $_SERVER['SCRIPT_NAME'] = '/admin/index.php';
+
+ return "{$sitePath}/admin/index.php";
+ }
+
+ $_SERVER['SCRIPT_NAME'] = '/index.php';
+
+ return "{$sitePath}/index.php";
+ }
+}
diff --git a/cli/Valet/Drivers/Specific/StatamicV2ValetDriver.php b/cli/Valet/Drivers/Specific/StatamicV2ValetDriver.php
new file mode 100644
index 000000000..0d97577dc
--- /dev/null
+++ b/cli/Valet/Drivers/Specific/StatamicV2ValetDriver.php
@@ -0,0 +1,153 @@
+isActualFile($staticFilePath = "{$sitePath}{$uri}")) {
+ return $staticFilePath;
+ }
+ elseif ($this->isActualFile($staticFilePath = "{$sitePath}/public{$uri}")) {
+ return $staticFilePath;
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the fully resolved path to the application's front controller.
+ *
+ * @param string $sitePath
+ * @param string $siteName
+ * @param string $uri
+ *
+ * @return string|null
+ */
+ public function frontControllerPath($sitePath, $siteName, $uri) {
+ if ($_SERVER['REQUEST_METHOD'] === 'GET' && $this->isActualFile($staticPath = $this->getStaticPath($sitePath))) {
+ return $staticPath;
+ }
+
+ if ($uri === '/installer.php') {
+ return "{$sitePath}/installer.php";
+ }
+
+ $scriptName = '/index.php';
+
+ if ($this->isActualFile("{$sitePath}/index.php")) {
+ $indexPath = "{$sitePath}/index.php";
+ }
+
+ if ($isAboveWebroot = $this->isActualFile("{$sitePath}/public/index.php")) {
+ $indexPath = "{$sitePath}/public/index.php";
+ }
+
+ $sitePathPrefix = $isAboveWebroot ? "{$sitePath}/public" : $sitePath;
+
+ if ($locale = $this->getUriLocale($uri)) {
+ if ($this->isActualFile($localeIndexPath = "{$sitePathPrefix}/{$locale}/index.php")) {
+ // Force trailing slashes on locale roots.
+ if ($uri === "/{$locale}") {
+ header("Location: {$uri}/");
+ exit;
+ }
+
+ $indexPath = $localeIndexPath;
+ $scriptName = "/{$locale}/index.php";
+ }
+ }
+
+ $_SERVER['SCRIPT_NAME'] = $scriptName;
+ $_SERVER['SCRIPT_FILENAME'] = "{$sitePathPrefix}{$scriptName}";
+
+ return $indexPath;
+ }
+
+ /**
+ * Get the locale from this URI.
+ *
+ * @param string $uri
+ *
+ * @return string|null
+ */
+ public function getUriLocale($uri) {
+ $parts = explode('/', $uri);
+ $locale = $parts[1];
+
+ if (count($parts) < 2 || !in_array($locale, $this->getLocales())) {
+ return null;
+ }
+
+ return $locale;
+ }
+
+ /**
+ * Get the list of possible locales used in the first segment of a URI.
+ *
+ * @return string[]
+ */
+ public function getLocales() {
+ // phpcs:disable Squiz.Arrays.ArrayDeclaration.ValueNoNewline
+ return [
+ 'af', 'ax', 'al', 'dz', 'as', 'ad', 'ao', 'ai', 'aq', 'ag', 'ar', 'am', 'aw', 'au', 'at', 'az', 'bs', 'bh',
+ 'bd', 'bb', 'by', 'be', 'bz', 'bj', 'bm', 'bt', 'bo', 'bq', 'ba', 'bw', 'bv', 'br', 'io', 'bn', 'bg', 'bf',
+ 'bi', 'cv', 'kh', 'cm', 'ca', 'ky', 'cf', 'td', 'cl', 'cn', 'cx', 'cc', 'co', 'km', 'cg', 'cd', 'ck', 'cr',
+ 'ci', 'hr', 'cu', 'cw', 'cy', 'cz', 'dk', 'dj', 'dm', 'do', 'ec', 'eg', 'sv', 'gq', 'er', 'ee', 'et', 'fk',
+ 'fo', 'fj', 'fi', 'fr', 'gf', 'pf', 'tf', 'ga', 'gm', 'ge', 'de', 'gh', 'gi', 'gr', 'gl', 'gd', 'gp', 'gu',
+ 'gt', 'gg', 'gn', 'gw', 'gy', 'ht', 'hm', 'va', 'hn', 'hk', 'hu', 'is', 'in', 'id', 'ir', 'iq', 'ie', 'im',
+ 'il', 'it', 'jm', 'jp', 'je', 'jo', 'kz', 'ke', 'ki', 'kp', 'kr', 'kw', 'kg', 'la', 'lv', 'lb', 'ls', 'lr',
+ 'ly', 'li', 'lt', 'lu', 'mo', 'mk', 'mg', 'mw', 'my', 'mv', 'ml', 'mt', 'mh', 'mq', 'mr', 'mu', 'yt', 'mx',
+ 'fm', 'md', 'mc', 'mn', 'me', 'ms', 'ma', 'mz', 'mm', 'na', 'nr', 'np', 'nl', 'nc', 'nz', 'ni', 'ne', 'ng',
+ 'nu', 'nf', 'mp', 'no', 'om', 'pk', 'pw', 'ps', 'pa', 'pg', 'py', 'pe', 'ph', 'pn', 'pl', 'pt', 'pr', 'qa',
+ 're', 'ro', 'ru', 'rw', 'bl', 'sh', 'kn', 'lc', 'mf', 'pm', 'vc', 'ws', 'sm', 'st', 'sa', 'sn', 'rs', 'sc',
+ 'sl', 'sg', 'sx', 'sk', 'si', 'sb', 'so', 'za', 'gs', 'ss', 'es', 'lk', 'sd', 'sr', 'sj', 'sz', 'se', 'ch',
+ 'sy', 'tw', 'tj', 'tz', 'th', 'tl', 'tg', 'tk', 'to', 'tt', 'tn', 'tr', 'tm', 'tc', 'tv', 'ug', 'ua', 'ae',
+ 'gb', 'us', 'um', 'uy', 'uz', 'vu', 've', 'vn', 'vg', 'vi', 'wf', 'eh', 'ye', 'zm', 'zw', 'en', 'zh'
+ ];
+ // phpcs:enable
+ }
+
+ /**
+ * Get the path to a statically cached page.
+ *
+ * @param string $sitePath
+ *
+ * @return string
+ */
+ protected function getStaticPath($sitePath) {
+ $parts = parse_url($_SERVER['REQUEST_URI']);
+ $query = isset($parts['query']) ? $parts['query'] : '';
+
+ return "{$sitePath}/static{$parts['path']}_{$query}.html";
+ }
+}
diff --git a/cli/Valet/Drivers/Specific/StatamicValetDriver.php b/cli/Valet/Drivers/Specific/StatamicValetDriver.php
new file mode 100644
index 000000000..b28b1269a
--- /dev/null
+++ b/cli/Valet/Drivers/Specific/StatamicValetDriver.php
@@ -0,0 +1,58 @@
+getStaticPath($sitePath);
+
+ if ($staticPath && $this->isActualFile($staticPath)) {
+ return $staticPath;
+ }
+
+ return parent::frontControllerPath($sitePath, $siteName, $uri);
+ }
+
+ /**
+ * Get the path to the static file.
+ *
+ * @param string $sitePath
+ *
+ * @return string|void
+ */
+ protected function getStaticPath($sitePath) {
+ if (! $uri = $_SERVER['REQUEST_URI'] ?? null) {
+ return;
+ }
+
+ $parts = parse_url($uri);
+ $query = $parts['query'] ?? '';
+
+ return "{$sitePath}/public/static{$parts['path']}_{$query}.html";
+ }
+}
diff --git a/cli/Valet/Drivers/Specific/SymfonyValetDriver.php b/cli/Valet/Drivers/Specific/SymfonyValetDriver.php
new file mode 100644
index 000000000..058523c04
--- /dev/null
+++ b/cli/Valet/Drivers/Specific/SymfonyValetDriver.php
@@ -0,0 +1,71 @@
+isActualFile($staticFilePath = "{$sitePath}/web/{$uri}")) {
+ return $staticFilePath;
+ }
+ elseif ($this->isActualFile($staticFilePath = "{$sitePath}/public/{$uri}")) {
+ return $staticFilePath;
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the fully resolved path to the application's front controller.
+ *
+ * @param string $sitePath
+ * @param string $siteName
+ * @param string $uri
+ *
+ * @return string|null
+ */
+ public function frontControllerPath($sitePath, $siteName, $uri) {
+ $frontControllerPath = null;
+
+ if (file_exists($path = "{$sitePath}/web/app_dev.php")) {
+ $frontControllerPath = $path;
+ }
+ elseif (file_exists($path = "{$sitePath}/web/app.php")) {
+ $frontControllerPath = $path;
+ }
+ elseif (file_exists($path = "{$sitePath}/public/index.php")) {
+ $frontControllerPath = $path;
+ }
+
+ $_SERVER['SCRIPT_FILENAME'] = $frontControllerPath;
+
+ return $frontControllerPath;
+ }
+}
diff --git a/cli/drivers/Typo3ValetDriver.php b/cli/Valet/Drivers/Specific/Typo3ValetDriver.php
similarity index 73%
rename from cli/drivers/Typo3ValetDriver.php
rename to cli/Valet/Drivers/Specific/Typo3ValetDriver.php
index 7e044e40c..4b4a4c98a 100644
--- a/cli/drivers/Typo3ValetDriver.php
+++ b/cli/Valet/Drivers/Specific/Typo3ValetDriver.php
@@ -1,9 +1,13 @@
documentRoot . '/typo3';
+ $typo3Dir = "{$sitePath}{$this->documentRoot}/typo3";
return file_exists($typo3Dir) && is_dir($typo3Dir);
}
+ /**
+ * Take any steps necessary before loading the front controller for this driver.
+ *
+ * @param string $sitePath
+ * @param string $siteName
+ * @param string $uri
+ */
+ public function beforeLoading($sitePath, $siteName, $uri) {
+ // without modifying the URI, redirect if necessary
+ $this->handleRedirectBackendShorthandUris($uri);
+
+ $_SERVER['SERVER_NAME'] = "{$siteName}.dev";
+ $_SERVER['DOCUMENT_URI'] = $uri;
+ $_SERVER['SCRIPT_NAME'] = $uri;
+ $_SERVER['PHP_SELF'] = $uri;
+ }
+
/**
* Determine if the incoming request is for a static file. That is, it is
* no PHP script file and the URI points to a valid file (no folder) on
* the disk. Access to those static files will be authorized.
*
- * @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) {
// 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)) {
+ 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 absolute 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;
}
@@ -83,7 +106,8 @@ public function isStaticFile($sitePath, $siteName, $uri) {
/**
* Determines if the given URI is blacklisted so that access is prevented.
*
- * @param string $uri
+ * @param string $uri
+ *
* @return bool
*/
private function isAccessAuthorized($uri) {
@@ -101,39 +125,37 @@ private function isAccessAuthorized($uri) {
* This can be the currently requested PHP script, a folder that
* contains an index.php or the global index.php otherwise.
*
- * @param string $sitePath
- * @param string $siteName
- * @param string $uri
- * @return string
+ * @param string $sitePath
+ * @param string $siteName
+ * @param string $uri
+ *
+ * @return string|null
*/
public function frontControllerPath($sitePath, $siteName, $uri) {
- // without modifying the URI, redirect if necessary
- $this->handleRedirectBackendShorthandUris($uri);
-
// from now on, remove trailing / for convenience for all the following join operations
$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 (file_exists($absoluteFilePath = "{$sitePath}{$this->documentRoot}{$uri}")) {
if (is_dir($absoluteFilePath)) {
- if (file_exists($absoluteFilePath . '/index.php')) {
+ 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, "{$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";
}
}
elseif (pathinfo($absoluteFilePath, PATHINFO_EXTENSION) === 'php') {
// this file can be served directly
- return $this->serveScript($sitePath, $siteName, $uri);
+ return $this->serveScript($sitePath, $uri);
}
}
// the global index.php will handle all other cases
- return $this->serveScript($sitePath, $siteName, '/index.php');
+ return $this->serveScript($sitePath, '/index.php');
}
/**
@@ -141,7 +163,7 @@ 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') {
@@ -159,23 +181,18 @@ private function handleRedirectBackendShorthandUris($uri) {
* Configures the $_SERVER globals for serving the script at
* the specified URI and returns it absolute file path.
*
- * @param string $sitePath
- * @param string $siteName
- * @param string $uri
- * @param string $script
+ * @param string $sitePath
+ * @param string $uri
+ *
* @return string
*/
- private function serveScript($sitePath, $siteName, $uri) {
- $docroot = $sitePath . $this->documentRoot;
- $abspath = $docroot . $uri;
+ private function serveScript($sitePath, $uri) {
+ $docroot = "{$sitePath}{$this->documentRoot}";
+ $abspath = "{$docroot}{$uri}";
- $_SERVER['SERVER_NAME'] = $siteName . '.dev';
$_SERVER['DOCUMENT_ROOT'] = $docroot;
- $_SERVER['DOCUMENT_URI'] = $uri;
$_SERVER['SCRIPT_FILENAME'] = $abspath;
- $_SERVER['SCRIPT_NAME'] = $uri;
- $_SERVER['PHP_SELF'] = $uri;
return $abspath;
}
-}
+}
\ No newline at end of file
diff --git a/cli/drivers/WordPressValetDriver.php b/cli/Valet/Drivers/Specific/WordPressValetDriver.php
similarity index 53%
rename from cli/drivers/WordPressValetDriver.php
rename to cli/Valet/Drivers/Specific/WordPressValetDriver.php
index 6db3d9b83..31bf43afb 100644
--- a/cli/drivers/WordPressValetDriver.php
+++ b/cli/Valet/Drivers/Specific/WordPressValetDriver.php
@@ -1,31 +1,34 @@
serves($sitePath, $siteName, $driver->mutateUri($uri))) {
return $driver;
@@ -83,17 +88,18 @@ public static function assign($sitePath, $siteName, $uri) {
}
/**
- * Get the custom driver class from the site path, if one exists.
+ * Get the custom driver class from the local site path, if one exists.
+ *
+ * @param string $sitePath
*
- * @param string $sitePath
- * @return void|string
+ * @return string|null
*/
public static function customSiteDriver($sitePath) {
- if (!file_exists($sitePath . '/LocalValetDriver.php')) {
- return;
+ if (!file_exists("$sitePath/LocalValetDriver.php")) {
+ return null;
}
- require_once $sitePath . '/LocalValetDriver.php';
+ require_once "$sitePath/LocalValetDriver.php";
return 'LocalValetDriver';
}
@@ -101,7 +107,8 @@ public static function customSiteDriver($sitePath) {
/**
* Get all of the driver classes in a given path.
*
- * @param string $path
+ * @param string $path
+ *
* @return array
*/
public static function driversIn($path) {
@@ -124,10 +131,44 @@ public static function driversIn($path) {
return $drivers;
}
+ /**
+ * Get all of the specific drivers shipped with Valet.
+ *
+ * @return array
+ */
+ public static function specificDrivers() {
+ return array_map(function ($item) {
+ return "Specific\\$item";
+ }, static::driversIn(__DIR__ . '/Specific'));
+ }
+
+ /**
+ * Get all of the custom drivers defined by the user in `~/.config/valet/Drivers`.
+ *
+ * @return array
+ */
+ public static function customDrivers() {
+ return array_map(function ($item) {
+ return "Custom\\$item";
+ }, static::driversIn(VALET_HOME_PATH . '/Drivers'));
+ }
+
+ /**
+ * Take any steps necessary before loading the front controller for this driver.
+ *
+ * @param string $sitePath
+ * @param string $siteName
+ * @param string $uri
+ */
+ public function beforeLoading($sitePath, $siteName, $uri) {
+ // Do nothing
+ }
+
/**
* Mutate the incoming URI.
*
- * @param string $uri
+ * @param string $uri
+ *
* @return string
*/
public function mutateUri($uri) {
@@ -137,11 +178,10 @@ public function mutateUri($uri) {
/**
* Serve the static file at the given path.
*
- * @param string $staticFilePath
- * @param string $sitePath
- * @param string $siteName
- * @param string $uri
- * @return void
+ * @param string $staticFilePath
+ * @param string $sitePath
+ * @param string $siteName
+ * @param string $uri
*/
public function serveStaticFile($staticFilePath, $sitePath, $siteName, $uri) {
/**
@@ -168,7 +208,8 @@ public function serveStaticFile($staticFilePath, $sitePath, $siteName, $uri) {
/**
* Determine if the path is a file and not a directory.
*
- * @param string $path
+ * @param string $path
+ *
* @return bool
*/
protected function isActualFile($path) {
@@ -179,12 +220,15 @@ protected function isActualFile($path) {
* Load server environment variables if available.
* Processes any '*' entries first, and then adds site-specific entries.
*
- * @param string $sitePath
- * @param string $siteName
- * @return void
+ * @param string $sitePath
+ * @param string $siteName
*/
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';
+ }
if (!file_exists($varFilePath)) {
return;
}
@@ -203,7 +247,30 @@ public function loadServerEnvironmentVariables($sitePath, $siteName) {
}
$_SERVER[$key] = $value;
$_ENV[$key] = $value;
- putenv($key . '=' . $value);
+ putenv("$key=$value");
}
}
+
+ /**
+ * Check the site's Composer dependencies
+ *
+ * @param string $sitePath
+ * @param string $namespacedPackage
+ *
+ * @return bool
+ */
+ public function composerRequires($sitePath, $namespacedPackage) {
+ if (! file_exists("$sitePath/composer.json")) {
+ return false;
+ }
+
+ $composer_json_source = file_get_contents("$sitePath/composer.json");
+ $composer_json = json_decode($composer_json_source, true);
+
+ if (json_last_error() !== JSON_ERROR_NONE) {
+ return false;
+ }
+
+ return isset($composer_json['require'][$namespacedPackage]);
+ }
}
\ No newline at end of file
diff --git a/cli/Valet/Filesystem.php b/cli/Valet/Filesystem.php
index 5b416cb54..dc5f7934f 100644
--- a/cli/Valet/Filesystem.php
+++ b/cli/Valet/Filesystem.php
@@ -9,6 +9,7 @@ class Filesystem {
* Determine if the given path is a directory.
*
* @param string $path
+ *
* @return bool
*/
public function isDir($path) {
@@ -21,7 +22,6 @@ public function isDir($path) {
* @param string $path
* @param string|null $owner
* @param int $mode
- * @return void
*/
public function mkdir($path, $owner = null, $mode = 0755) {
mkdir($path, $mode, true);
@@ -32,28 +32,26 @@ public function mkdir($path, $owner = null, $mode = 0755) {
}
/**
- * Ensure that the given directory exists.
+ * Create a directory as the non-root user.
*
* @param string $path
- * @param string|null $owner
* @param int $mode
- * @return void
*/
- public function ensureDirExists($path, $owner = null, $mode = 0755) {
- if (!$this->isDir($path)) {
- $this->mkdir($path, $owner, $mode);
- }
+ public function mkdirAsUser($path, $mode = 0755) {
+ $this->mkdir($path, user(), $mode);
}
/**
- * Create a directory as the non-root user.
+ * Ensure that the given directory exists.
*
* @param string $path
+ * @param string|null $owner
* @param int $mode
- * @return void
*/
- public function mkdirAsUser($path, $mode = 0755) {
- $this->mkdir($path, user(), $mode);
+ public function ensureDirExists($path, $owner = null, $mode = 0755) {
+ if (!$this->isDir($path)) {
+ $this->mkdir($path, $owner, $mode);
+ }
}
/**
@@ -61,6 +59,7 @@ public function mkdirAsUser($path, $mode = 0755) {
*
* @param string $path
* @param string|null $owner
+ *
* @return string
*/
public function touch($path, $owner = null) {
@@ -77,6 +76,7 @@ public function touch($path, $owner = null) {
* Touch the given path as the non-root user.
*
* @param string $path
+ *
* @return string
*/
public function touchAsUser($path) {
@@ -87,6 +87,7 @@ public function touchAsUser($path) {
* Determine if the given file exists.
*
* @param string $path
+ *
* @return bool
*/
public function exists($path) {
@@ -97,6 +98,7 @@ public function exists($path) {
* Read the contents of the given file.
*
* @param string $path
+ *
* @return string
*/
public function get($path) {
@@ -109,7 +111,6 @@ public function get($path) {
* @param string $path
* @param string $contents
* @param string|null $owner
- * @return void
*/
public function put($path, $contents, $owner = null) {
file_put_contents($path, $contents);
@@ -124,7 +125,6 @@ public function put($path, $contents, $owner = null) {
*
* @param string $path
* @param string $contents
- * @return void
*/
public function putAsUser($path, $contents) {
$this->put($path, $contents, user());
@@ -136,7 +136,6 @@ public function putAsUser($path, $contents) {
* @param string $path
* @param string $contents
* @param string|null $owner
- * @return void
*/
public function append($path, $contents, $owner = null) {
file_put_contents($path, $contents, FILE_APPEND);
@@ -151,7 +150,6 @@ public function append($path, $contents, $owner = null) {
*
* @param string $path
* @param string $contents
- * @return void
*/
public function appendAsUser($path, $contents) {
$this->append($path, $contents, user());
@@ -162,7 +160,6 @@ public function appendAsUser($path, $contents) {
*
* @param string $from
* @param string $to
- * @return void
*/
public function copy($from, $to) {
// The @ operator suppresses pre-error messages that occur in PHP internally.
@@ -175,7 +172,6 @@ public function copy($from, $to) {
*
* @param string $from
* @param string $to
- * @return void
*/
public function copyAsUser($from, $to) {
copy($from, $to);
@@ -203,7 +199,6 @@ public function move($from, $to) {
*
* @param string $target
* @param string $link
- * @return void
*/
public function symlink($target, $link) {
if ($this->exists($link)) {
@@ -219,7 +214,6 @@ public function symlink($target, $link) {
* Delete the file at the given path.
*
* @param string $path
- * @return void
*/
public function unlink($path) {
// If the path is a symlinked directory OR is a file, remove it.
@@ -238,7 +232,6 @@ public function unlink($path) {
*
* @param string $path
* @param string $user
- * @return void
*/
public function chown($path, $user) {
chown($path, $user);
@@ -249,7 +242,6 @@ public function chown($path, $user) {
*
* @param string $path
* @param string $group
- * @return void
*/
public function chgrp($path, $group) {
chgrp($path, $group);
@@ -259,16 +251,31 @@ public function chgrp($path, $group) {
* Resolve the given path.
*
* @param string $path
+ * @param bool $normalise Whether to normalise the path by replacing backslashes with forward slashes.
+ *
+ * @return string
+ */
+ public function realpath($path, $normalise = true) {
+ $realPath = realpath($path);
+ return $normalise ? $this->normalisePath($realPath) : $realPath;
+ }
+
+ /**
+ * Normalise the given path by replacing backslashes with forward slashes.
+ *
+ * @param string $path
+ *
* @return string
*/
- public function realpath($path) {
- return realpath($path);
+ public function normalisePath($path) {
+ return str_replace('\\', '/', $path);
}
/**
* Determine if the given path is a symbolic link.
*
* @param string $path
+ *
* @return bool
*/
public function isLink($path) {
@@ -279,6 +286,7 @@ public function isLink($path) {
* Resolve the given symbolic link.
*
* @param string $path
+ *
* @return string
*/
public function readLink($path) {
@@ -289,6 +297,7 @@ public function readLink($path) {
* Determine if the given path is a file.
*
* @param string $path
+ *
* @return bool
*/
public function isFile($path) {
@@ -299,7 +308,6 @@ public function isFile($path) {
* Remove all of the broken symbolic links at the given path.
*
* @param string $path
- * @return void
*/
public function removeBrokenLinksAt($path) {
collect($this->scandir($path))->filter(function ($file) use ($path) {
@@ -313,6 +321,7 @@ public function removeBrokenLinksAt($path) {
* Determine if the given path is a broken symbolic link.
*
* @param string $path
+ *
* @return bool
*/
public function isBrokenLink($path) {
@@ -330,17 +339,21 @@ public function convertJunctionsToSymlinks($path) {
*/
$collection = $this->getJunctionLinks($path);
+ // If there are no junction links to convert, we can exit early.
if ($collection->isEmpty()) {
return;
}
+
// Remove all the junction links and create new symlinks to the same path.
$collection->each(function ($link) use ($path) {
- $output = CommandLine::run('cmd /C rmdir /s /q "' . $path . '/' . $link['linkName']. '"');
+ $output = CommandLine::run('cmd /C rmdir /s /q "' . $path . '/' . $link['linkName'] . '"');
if ($output->isSuccessful()) {
$this->symlink($link['path'], $link['linkName']);
}
});
+
+ return;
}
/**
@@ -380,6 +393,7 @@ public function getJunctionLinks($path) {
* Scan the given directory path.
*
* @param string $path
+ *
* @return array
*/
public function scandir($path) {
@@ -388,6 +402,53 @@ public function scandir($path) {
})->values()->all();
}
+
+ /**
+ * Scan the given directory path recursively, returning an
+ * associative array of all files and directories.
+ *
+ * @param string $dir The directory to scan.
+ *
+ * @return array An associative array of all files and directories.
+ */
+ public function scanDirRecursive($dir) {
+ $result = [];
+ $items = $this->scandir($dir);
+
+ // Loop through each item in the directory.
+ foreach ($items as $item) {
+ // Skip the current and parent directory entries.
+ if ($item === '.' || $item === '..') {
+ continue;
+ }
+ // Get the full path of the item.
+ $fullPath = rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $item;
+
+ // If the item is a directory and not a symlink...
+ if ($this->isDir($fullPath) && !$this->isLink($fullPath)) {
+ // Recursively scan the directory and add the directory name as the key,
+ // and the result of the recursive scan as the value of the array.
+ $result[$item] = $this->scanDirRecursive($fullPath);
+ }
+ // Otherwise, add the item to the result array.
+ else {
+ $result[] = $item;
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Check if the given directory is empty.
+ *
+ * @param string $path The path to the directory to check.
+ *
+ * @return bool
+ */
+ public function isDirEmpty($path) {
+ return $this->isDir($path) && collect($this->scandir($path))->isEmpty();
+ }
+
/**
* Unzip the given zip file to the given path.
*
@@ -412,6 +473,7 @@ public function unzip($zipFilePath, $extractToPath) {
* The -f option specifies the zip file to list.
*
* @param mixed $zipFilePath
+ *
* @return string[] Array of top-level directories in the zip file.
*/
public function listTopLevelZipDirs($zipFilePath) {
@@ -443,9 +505,9 @@ public function listTopLevelZipDirs($zipFilePath) {
* @return string
*/
public function getStub($filename) {
- $default = __DIR__.'/../stubs/'.$filename;
+ $default = __DIR__ . '/../stubs/' . $filename;
- $custom = VALET_HOME_PATH . "/stubs/$filename";
+ $custom = Valet::homePath() . "/stubs/$filename";
$path = file_exists($custom) ? $custom : $default;
diff --git a/cli/Valet/Nginx.php b/cli/Valet/Nginx.php
index 245175cc6..db1768c7e 100644
--- a/cli/Valet/Nginx.php
+++ b/cli/Valet/Nginx.php
@@ -38,7 +38,6 @@ class Nginx {
* @param Configuration $configuration
* @param Site $site
* @param WinSwFactory $winsw
- * @return void
*/
public function __construct(CommandLine $cli, Filesystem $files, Configuration $configuration, Site $site, WinSwFactory $winsw) {
$this->cli = $cli;
@@ -50,8 +49,6 @@ public function __construct(CommandLine $cli, Filesystem $files, Configuration $
/**
* Install the configuration files for Nginx.
- *
- * @return void
*/
public function install() {
// Install the Nginx package if it is not already installed.
@@ -59,14 +56,12 @@ public function install() {
// Install the Nginx configs, server, and service.
$this->installConfiguration();
$this->installServer();
- $this->installNginxDirectory();
+ $this->rewriteNginxFiles();
$this->installService();
}
/**
* Install the Nginx configuration file.
- *
- * @return void
*/
public function installConfiguration() {
$defaultPhpVersion = $this->configuration->get('default_php');
@@ -76,7 +71,7 @@ public function installConfiguration() {
$this->path('conf/nginx.conf'),
str_replace(
['VALET_USER', 'VALET_HOME_PATH', '__VALET_PHP_PORT__', '__VALET_PHP_XDEBUG_PORT__'],
- [user(), VALET_HOME_PATH, $defaultPhp['port'], $defaultPhp['xdebug_port']],
+ [user(), Valet::homePath(), $defaultPhp['port'], $defaultPhp['xdebug_port']],
$this->files->getStub('nginx.conf')
)
);
@@ -84,8 +79,6 @@ public function installConfiguration() {
/**
* Install the Valet Nginx server configuration file.
- *
- * @return void
*/
public function installServer() {
$defaultPhpVersion = $this->configuration->get('default_php');
@@ -93,11 +86,13 @@ public function installServer() {
$this->files->ensureDirExists($this->path('valet'));
+ $valetErrorTemplatePath = $this->files->realpath(valetBinPath() . '../cli/templates');
+
$this->files->putAsUser(
$this->path('valet/valet.conf'),
str_replace(
- ['VALET_HOME_PATH', 'VALET_SERVER_PATH', 'VALET_STATIC_PREFIX', 'HOME_PATH', 'VALET_PHP_PORT'],
- [VALET_HOME_PATH, VALET_SERVER_PATH, VALET_STATIC_PREFIX, $_SERVER['HOME'], $defaultPhp['port']],
+ ['VALET_HOME_PATH', 'VALET_SERVER_PATH', 'VALET_STATIC_PREFIX', 'HOME_PATH', 'VALET_PHP_PORT', 'VALET_ERROR_TEMPLATE_PATH'],
+ [Valet::homePath(), VALET_SERVER_PATH, VALET_STATIC_PREFIX, $_SERVER['HOME'], $defaultPhp['port'], $valetErrorTemplatePath],
$this->files->getStub('valet.conf')
)
);
@@ -108,25 +103,10 @@ public function installServer() {
);
}
- /**
- * Install the Nginx configuration directory to the ~/.config/valet directory.
- *
- * This directory contains all site-specific Nginx servers.
- *
- * @return void
- */
- public function installNginxDirectory() {
- if (!$this->files->isDir($nginxDirectory = Valet::homePath('Nginx'))) {
- $this->files->mkdirAsUser($nginxDirectory);
- }
-
- $this->files->putAsUser($nginxDirectory . '/.keep', "\n");
-
- $this->rewriteSecureNginxFiles();
- }
-
/**
* Check nginx.conf and all linked site configurations for errors.
+ *
+ * @return string
*/
public function lint($returnOutput = false) {
$output = $this->cli->run(
@@ -157,19 +137,16 @@ function ($exitCode, $outputMessage) {
/**
* Generate fresh Nginx servers for existing secure sites.
- *
- * @return void
*/
- public function rewriteSecureNginxFiles() {
+ public function rewriteNginxFiles() {
$tld = $this->configuration->read()['tld'];
$this->site->resecureForNewTld($tld, $tld);
+ $this->site->reisolateForNewTld($tld, $tld);
}
/**
* Install the Windows service.
- *
- * @return void
*/
public function installService() {
if ($this->winsw->installed()) {
@@ -185,8 +162,6 @@ public function installService() {
/**
* Restart the Nginx service.
- *
- * @return void
*/
public function restart() {
$this->killProcess();
@@ -198,8 +173,6 @@ public function restart() {
/**
* Stop the Nginx service.
- *
- * @return void
*/
public function stop() {
$this->killProcess();
@@ -209,8 +182,6 @@ public function stop() {
/**
* Prepare Nginx for uninstallation.
- *
- * @return void
*/
public function uninstall() {
$this->killProcess();
@@ -229,6 +200,7 @@ public function killProcess() {
* Get the Nginx path.
*
* @param string $path
+ *
* @return string
*/
public function path(string $path = ''): string {
@@ -241,7 +213,7 @@ public function path(string $path = ''): string {
* For use in valet.php to check if Valet is installed
* to enable most of the commands.
*
- * @return boolean
+ * @return bool
*/
public function isInstalled() {
return $this->winsw->installed();
diff --git a/cli/Valet/Packages/Acrylic.php b/cli/Valet/Packages/Acrylic.php
new file mode 100644
index 000000000..7a85ba181
--- /dev/null
+++ b/cli/Valet/Packages/Acrylic.php
@@ -0,0 +1,77 @@
+isInstalled()) {
+ $acrylicPath = $this->packagePath();
+ $zipFilePath = $this->packageZipFilePath();
+
+ $this->files->ensureDirExists($acrylicPath);
+
+ $this->download('https://sourceforge.net/projects/acrylic/best_release.json', 'Acrylic-Portable.zip', $zipFilePath);
+
+ $this->unzip();
+
+ $this->cleanUpPackageDirectory("", [".bat"]);
+
+ $this->changeLocalIpv4BindingAddress();
+ }
+ }
+
+ /**
+ * Download the Acrylic package from SourceForge.
+ */
+ protected function download(string $url, string $filename, string $filePath) {
+ // Find the latest release from SourceForge.
+ // NOTE: SourceForge does not have a proper API for releases, so we use the best_release.json endpoint.
+ $latestRelease = $this->getApiResponse($url);
+
+ // The download URL for the latest .exe file.
+ $downloadUrl = $latestRelease->release->url;
+
+ // Replace the Acrylic.exe with the portable zip filename; ie. Acrylic-Portable.zip.
+ // This is a workaround since the API doesn't provide a direct link to the
+ // portable version.
+ $downloadUrl = str_replace("Acrylic.exe", $filename, $downloadUrl);
+
+ if (!isset($downloadUrl)) {
+ error("The download URL was not found in the response. The API URL queried is: $url\n", true);
+ }
+
+ // Download the file via Guzzle.
+ $this->downloadFile($downloadUrl, $filePath);
+ }
+
+ /**
+ * Change the local IPv4 binding address in the Acrylic configuration file to `127.0.0.1`.
+ */
+ private function changeLocalIpv4BindingAddress() {
+ // Get the original contents of the AcrylicConfiguration.ini file.
+ $contents = $this->files->get($this->packagePath() . '/AcrylicConfiguration.ini');
+ // Get the stub for the IPv4 binding address.
+ $ipv4Address = $this->files->getStub('Acrylic_IPv4_Binding_Address.ini');
+
+ // Replace the default Local IPv4 Binding Address in the config file with the stub.
+ $this->files->put(
+ $this->packagePath() . '/AcrylicConfiguration.ini',
+ str_replace("LocalIPv4BindingAddress=0.0.0.0", $ipv4Address, $contents)
+ );
+ }
+}
diff --git a/cli/Valet/Packages/Ansicon.php b/cli/Valet/Packages/Ansicon.php
index aad90dc4c..203afa15c 100644
--- a/cli/Valet/Packages/Ansicon.php
+++ b/cli/Valet/Packages/Ansicon.php
@@ -14,19 +14,17 @@ class Ansicon extends GithubPackage {
/**
* Install Ansicon.
- *
- * @return void
*/
public function install() {
if (!$this->isInstalled()) {
$ansiconPath = $this->packagePath();
- $zipFilePath = "$ansiconPath/ansicon.zip";
+ $zipFilePath = $this->packageZipFilePath();
$this->files->ensureDirExists($ansiconPath);
$this->download('https://api.github.com/repos/adoxa/ansicon/releases/latest', 'ansi189-bin.zip', $zipFilePath);
- $this->files->unzip($zipFilePath, $ansiconPath);
+ $this->unzip();
// Get the contents of the readme.txt file.
$readmeContents = $this->files->get("$ansiconPath/readme.txt");
@@ -34,7 +32,7 @@ public function install() {
$this->moveFiles("x64");
// Clean up the package directory.
- $this->cleanUpPackageDirectory($zipFilePath, "x64");
+ $this->cleanUpPackageDirectory("x64");
// Create a readme.md file with the contents of the readme.txt file.
// This is just for easier reading.
@@ -52,8 +50,6 @@ function ($code, $output) {
/**
* Uninstall Ansicon.
- *
- * @return void
*/
public function uninstall() {
$this->cli->runOrExit(
@@ -63,4 +59,4 @@ function ($code, $output) {
}
);
}
-}
\ No newline at end of file
+}
diff --git a/cli/Valet/Packages/GithubPackage.php b/cli/Valet/Packages/GithubPackage.php
index 083b8497e..ec1770407 100644
--- a/cli/Valet/Packages/GithubPackage.php
+++ b/cli/Valet/Packages/GithubPackage.php
@@ -2,120 +2,55 @@
namespace Valet\Packages;
-use Valet\CommandLine;
-use Valet\Filesystem;
-
-use GuzzleHttp\Client;
-use GuzzleHttp\Exception\ClientException;
-use Composer\CaBundle\CaBundle;
-
use function Valet\info;
use function Valet\info_dump;
use function Valet\error;
-use function Valet\valetBinPath;
-
-abstract class GithubPackage {
- /**
- * @var CommandLine
- */
- protected $cli;
-
- /**
- * @var Filesystem
- */
- protected $files;
-
- /**
- * @var Client
- */
- protected $client;
-
- /**
- * @var string The name of the package.
- */
- protected $packageName;
-
- public function __construct(CommandLine $cli, Filesystem $files) {
- $this->cli = $cli;
- $this->files = $files;
- $this->client = new Client([
- \GuzzleHttp\RequestOptions::VERIFY => CaBundle::getSystemCaRootBundlePath()
- ]);
- }
+abstract class GithubPackage extends Package {
/*--------------------------------------------------------------*
* Abstract methods that must be implemented by the subclasses. *
*--------------------------------------------------------------*/
- /**
- * Install the package.
- *
- * @return void
- */
- abstract public function install();
-
+ // Abstract methods are defined in the parent class `Package`,
+ // so we don't need to define them here.
/*------------------------------------------------*
* Common methods that don't need to be abstracts *
*------------------------------------------------*/
-
- /**
- * Check if the package is installed.
- *
-
- * @return bool
- */
- public function isInstalled() {
- return $this->files->exists($this->packageExe());
- }
-
-
/**
* Download the package from GitHub.
*
* @param string $githubApiUrl The GitHub API URL for the release.
* @param string $filename The name of the file to download.
* @param string $filePath The path where the file will be saved and as what name.
- *
- * @return void
*/
protected function download(string $githubApiUrl, string $filename, string $filePath) {
- // Try to get the information from the GitHub API, otherwise
- // catch the exception and handle it.
- try {
- // Process response normally...
- $response = json_decode($this->client->get($githubApiUrl)->getBody());
- }
- catch (ClientException $e) {
- // An exception was raised but there is an HTTP response body
- // with the exception (in case of 404 and similar errors)
- $response = $e->getResponse();
+ // Get the response from the GitHub API OR handle errors.
+ $response = $this->getApiResponse($githubApiUrl,
+ function ($errorCode, $responseHeaders, $responseMsg) use ($githubApiUrl) {
- $errorCode = $response->getStatusCode();
+ if (str_contains($responseMsg, 'API rate limit exceeded')) {
+ $rateLimit = $responseHeaders["X-RateLimit-Limit"][0];
- $responseHeaders = $response->getHeaders();
- $responseBody = json_decode($response->getBody());
- $responseMsg = $responseBody->message;
+ $timeLeftToReset = $this->calculateTimeToApiRateLimitReset($responseHeaders["X-RateLimit-Reset"][0]);
- if (str_contains($responseMsg, 'API rate limit exceeded')) {
- [$ip, $rateLimit, $timeLeftToReset] = \ValetException::githubApiRateLimitExceededError($responseHeaders, $responseMsg);
+ // Print the error messages.
+ error("\n\nThe GitHub API rate limit has been exceeded for your IP address. The rate limit is $rateLimit requests per hour.\n\n");
- // Print the error messages.
- error("\n\nThe GitHub API rate limit has been exceeded for your IP address ($ip). The rate limit is $rateLimit requests per hour.\n\n");
+ info("\nThe rate limit will reset in $timeLeftToReset.");
- info("\nThe rate limit will reset in $timeLeftToReset.");
-
- error("API rate limit exceeded", true);
- }
- else {
- $error = "Error Code: $errorCode\n";
- $error .= "Error Message: $responseMsg\n";
- $error .= "The GitHub API URL queried is: $githubApiUrl\n";
+ error("API rate limit exceeded", true);
+ }
+ else {
+ $error = "Error Code: $errorCode\n";
+ $error .= "Error Message: $responseMsg\n";
+ $error .= "The GitHub API URL queried is: $githubApiUrl\n";
- error($error, true);
+ error($error, true);
+ }
}
- }
+ );
// If the 'assets' property exists in the response, it means we are downloading
// a release asset.
@@ -149,94 +84,7 @@ protected function download(string $githubApiUrl, string $filename, string $file
error("The download URL was not found in the response. The API URL queried is: $githubApiUrl\n", true);
}
- // Download the file via Guzzle.
- $this->client->get($downloadUrl, [
- \GuzzleHttp\RequestOptions::SINK => $filePath
- ]);
- }
-
- /**
- * Get the path to the package directory.
- *
- * @param string $packageName
- *
- * @return string
- */
- public function packagePath(): string {
- return valetBinPath() . $this->packageName;
- }
-
- /**
- * Get the path to the package executable.
- *
- * @return string
- */
- public function packageExe(): string {
- return $this->packagePath() . "/$this->packageName.exe";
- }
-
- /**
- * Clean up the package directory that was downloaded and unzipped from GitHub.
- * Remove all unnecessary directories and files.
- *
- * @param mixed $zipFilePath
- *
- * @param array $unnecessaryDirsToRemove
- *
- * @return void
- */
- protected function cleanUpPackageDirectory($zipFilePath, $requiredDir = "") {
- // For each unnecessary directory in the package directory...
- foreach ($this->getUnnecessaryDirs($zipFilePath, $requiredDir) as $dir) {
- // Remove all unnecessary directories and their files.
- $this->files->unlink($this->packagePath() . "/$dir");
- }
-
- $this->removeZip($zipFilePath);
- }
-
- /**
- * Get the unnecessary directories to remove in the package directory.
- *
- * @param string $zipFilePath
- * @param string $requiredDir
- *
- * @return array
- */
- protected function getUnnecessaryDirs($zipFilePath, $requiredDir) {
- return collect($this->files->listTopLevelZipDirs($zipFilePath))->reject(function ($dir) use ($requiredDir) {
- // If the directory name contains the required directory name, then we remove
- // it from the collection of unnecessary directories that we want to delete.
- return str_contains($dir, $requiredDir);
- })->all();
- }
-
- /**
- * Remove the zip file after extracting its contents.
- *
- * @param string $zipFilePath
- */
- protected function removeZip($zipFilePath) {
- $this->files->unlink($zipFilePath);
- }
-
- /**
- * Move files to the main package directory.
- *
- * @param string $dirName
- */
- protected function moveFiles($dirName) {
- $packagePath = $this->packagePath();
-
- // For each item in the specified directory...
- foreach ($this->files->scandir("$packagePath/$dirName") as $item) {
- // Move the item to the main package directory.
- // The item could be a file or a directory.
- $this->files->move("$packagePath/$dirName/$item", "$packagePath/$item");
- }
-
- // Remove the original directory after moving its contents.
- $this->files->unlink("$packagePath/$dirName");
+ $this->downloadFile($downloadUrl, $filePath);
}
/**
@@ -268,4 +116,34 @@ protected function getVersionedFilename($filename, $assetName) {
return $matches[0];
}
}
+
+ /**
+ * Calculate the time left to reset the GitHub API rate limit.
+ *
+ * @param string $resetTime The reset time in UTC epoch seconds.
+ *
+ * @return string The time left to reset the rate limit in a human-readable format.
+ */
+ private function calculateTimeToApiRateLimitReset($resetTime) {
+ // Create new DateTime objects for the reset time and the current time.
+ $reset_time = new \DateTime("@$resetTime");
+ $current_time = new \DateTime("now");
+
+ // Get the difference between the 2 times.
+ $timeDifference = $reset_time->diff($current_time);
+
+ // Get the difference in minutes and seconds.
+ // The DateInterval object has many properties, including minutes and seconds,
+ // which we can directly access.
+ $mins = $timeDifference->i;
+ $secs = $timeDifference->s;
+
+ // Format the minutes and seconds into a human-readable string.
+ // If the minutes or seconds equals 1, we need to use the singular form
+ // of "minute" or "second".
+ $minsTxt = $mins === 1 ? "$mins minute" : "$mins minutes";
+ $secsTxt = $secs === 1 ? "$secs second" : "$secs seconds";
+
+ return "$minsTxt and $secsTxt";
+ }
}
diff --git a/cli/Valet/Packages/Gsudo.php b/cli/Valet/Packages/Gsudo.php
index 5e0557a65..5a2df73b9 100644
--- a/cli/Valet/Packages/Gsudo.php
+++ b/cli/Valet/Packages/Gsudo.php
@@ -17,18 +17,18 @@ class Gsudo extends GithubPackage {
public function install() {
if (!$this->isInstalled()) {
$gsudoPath = $this->packagePath();
- $zipFilePath = "$gsudoPath/gsudo.portable.zip";
+ $zipFilePath = $this->packageZipFilePath();
$this->files->ensureDirExists($gsudoPath);
$this->download('https://api.github.com/repos/gerardog/gsudo/releases/latest', 'gsudo.portable.zip', $zipFilePath);
- $this->files->unzip($zipFilePath, $gsudoPath);
+ $this->unzip();
$this->moveFiles("x64");
// Clean up the package directory.
- $this->cleanUpPackageDirectory($zipFilePath, "x64");
+ $this->cleanUpPackageDirectory("x64");
$this->configureGsudo();
}
@@ -44,6 +44,7 @@ private function configureGsudo() {
/**
* Run as the Local System account (NT AUTHORITY\SYSTEM).
+ *
* @return string
*/
public function runAsSystem() {
@@ -51,9 +52,10 @@ public function runAsSystem() {
}
/**
* Run as the Trusted Installer account (NT SERVICE\TrustedInstaller).
+ *
* @return string
*/
public function runAsTrustedInstaller() {
return '"' . $this->packageExe() . '" --ti -d ';
}
-}
\ No newline at end of file
+}
diff --git a/cli/Valet/Packages/Nginx.php b/cli/Valet/Packages/Nginx.php
index b20339ef8..53f9ec8a8 100644
--- a/cli/Valet/Packages/Nginx.php
+++ b/cli/Valet/Packages/Nginx.php
@@ -17,28 +17,26 @@ class Nginx extends GithubPackage {
public function install() {
if (!$this->isInstalled()) {
$nginxPath = $this->packagePath();
- $zipFilePath = "$nginxPath/nginx.zip";
+ $zipFilePath = $this->packageZipFilePath();
$this->files->ensureDirExists($nginxPath);
$this->download("https://api.github.com/repos/nginx/nginx/releases/latest", "nginx-VERSION.zip", $zipFilePath);
- $this->files->unzip($zipFilePath, $nginxPath);
+ $this->unzip();
- $this->moveNginxFiles($zipFilePath);
+ $this->moveNginxFiles();
- $this->cleanUpPackageDirectory($zipFilePath);
+ $this->cleanUpPackageDirectory();
}
}
/**
* Move the required Nginx files into the package directory.
- *
- * @param mixed $zipFilePath
*/
- private function moveNginxFiles($zipFilePath) {
+ private function moveNginxFiles() {
// Loop through a list of directory names from the zip file path.
- foreach ($this->files->listTopLevelZipDirs($zipFilePath) as $dir) {
+ foreach ($this->files->listTopLevelZipDirs($this->packageZipFilePath()) as $dir) {
// If the directory name contains 'nginx-', set it as the required directory.
if (str_contains($dir, 'nginx-')) {
$requiredDir = $dir;
@@ -55,4 +53,4 @@ private function moveNginxFiles($zipFilePath) {
info("Nginx required directory not found.");
}
}
-}
\ No newline at end of file
+}
diff --git a/cli/Valet/Packages/Package.php b/cli/Valet/Packages/Package.php
new file mode 100644
index 000000000..8b9603499
--- /dev/null
+++ b/cli/Valet/Packages/Package.php
@@ -0,0 +1,286 @@
+cli = $cli;
+ $this->files = $files;
+ $this->client = new Client([
+ \GuzzleHttp\RequestOptions::VERIFY => CaBundle::getSystemCaRootBundlePath()
+ ]);
+ }
+
+ /*--------------------------------------------------------------*
+ * Abstract methods that must be implemented by the subclasses. *
+ *--------------------------------------------------------------*/
+
+ /**
+ * Install the package.
+ */
+ abstract public function install();
+
+ /**
+ * Download the package.
+ *
+ * @param string $url The URL for the download.
+ * @param string $filename The name of the file to download.
+ * @param string $filePath The path where the file will be saved and as what name.
+ */
+ abstract protected function download(string $url, string $filename, string $filePath);
+
+
+ /*------------------------------------------------*
+ * Common methods that don't need to be abstracts *
+ *------------------------------------------------*/
+
+
+ /**
+ * Check if the package is installed.
+ *
+ * @return bool
+ */
+ public function isInstalled() {
+ return $this->files->exists($this->packageExe());
+ }
+
+ /**
+ * Get the response of the API.
+ *
+ * @param string $apiUrl The API URL to query.
+ * @param callable|null $onError Optional callback to handle errors.
+ *
+ * @return mixed The response from the API, if successful.
+ * @throws \ValetException
+ */
+ protected function getApiResponse(string $apiUrl, ?callable $onError = null) {
+
+ // If no error callback is provided, set it to a default function that does nothing.
+ $onError = $onError ?: function () {
+ };
+
+ // Try to get the information from the API, otherwise
+ // catch the exception and handle it.
+ try {
+ // Process response normally...
+ $response = json_decode($this->client->get($apiUrl)->getBody());
+ }
+ catch (ClientException $e) {
+ // An exception was raised but there is an HTTP response body
+ // with the exception (in case of 404 and similar errors)
+ $response = $e->getResponse();
+
+ $errorCode = $response->getStatusCode();
+
+ $responseHeaders = $response->getHeaders();
+ $responseBody = json_decode($response->getBody());
+ $responseMsg = $responseBody->message;
+
+ // If an error callback is provided...
+ if ($onError) {
+ // Call the error callback with the error code and message.
+ $onError($errorCode, $responseHeaders, $responseMsg);
+ }
+ // Otherwise if no error callback is provided...
+ else {
+ // Throw the default error with the details.
+ $error = "Error Code: $errorCode\n";
+ $error .= "Error Message: $responseMsg\n";
+ $error .= "The API URL queried is: $apiUrl\n";
+
+ error($error, true);
+ }
+ }
+
+ return $response;
+ }
+
+ /**
+ * Download a file from the specified URL and save it to the specified path.
+ *
+ * @param string $downloadUrl The URL to download the file from.
+ * @param string $filePath The path where the file will be saved.
+ *
+ * @uses GuzzleHttp\Client::get
+ */
+ public function downloadFile(string $downloadUrl, string $filePath) {
+ // Download the file via Guzzle.
+ $this->client->get($downloadUrl, [
+ \GuzzleHttp\RequestOptions::SINK => $filePath
+ ]);
+ }
+
+ /**
+ * Get the path to the package directory.
+ *
+ * @param string $packageName
+ *
+ * @return string
+ */
+ public function packagePath(): string {
+ return valetBinPath() . $this->packageName;
+ }
+
+ /**
+ * Get the path to the package executable.
+ *
+ * @return string
+ */
+ public function packageExe(): string {
+ $name = $this->packageExeName ?: $this->packageName;
+ return $this->packagePath() . "/$name.exe";
+ }
+
+ /**
+ * Get the path to the package zip file.
+ *
+ * @return string
+ */
+ protected function packageZipFilePath(): string {
+ return $this->packagePath() . "/$this->packageName.zip";
+ }
+
+ /**
+ * Clean up the package directory that was downloaded and unzipped.
+ * Remove all unnecessary directories and files.
+ *
+ * @param string $requiredDir The name of the required directory to keep.
+ * If empty, no directories will be removed.
+ * @param array $searchStrings An array of strings to search for in the filenames.
+ * If empty, no files will be removed.
+ */
+ protected function cleanUpPackageDirectory($requiredDir = "", $searchStrings = []) {
+
+ /** Remove unnecessary directories **/
+
+ // If the required directory is not empty...
+ if (!empty($requiredDir)) {
+ // For each unnecessary directory in the package directory...
+ foreach ($this->getUnnecessaryDirs($requiredDir) as $dir) {
+ // Remove all unnecessary directories and their files.
+ $this->files->unlink($this->packagePath() . "/$dir");
+ }
+ }
+
+ /** Remove unnecessary files **/
+
+ // If the search strings array is not empty...
+ if (!count($searchStrings) != 0) {
+ // For each unnecessary file in the package directory...
+ foreach ($this->getUnnecessaryFiles($searchStrings) as $file) {
+ // Remove the file.
+ $this->files->unlink($this->packagePath() . "/$file");
+ }
+ }
+
+ /** Remove the zip file **/
+
+ $this->removeZip();
+ }
+
+ /**
+ * Get the unnecessary directories to remove in the package directory.
+ *
+ * @param string $requiredDir
+ *
+ * @return array
+ */
+ protected function getUnnecessaryDirs($requiredDir) {
+ return collect($this->files->listTopLevelZipDirs($this->packageZipFilePath()))->reject(function ($dir) use ($requiredDir) {
+ // If the directory name contains the required directory name, then we remove
+ // it from the collection of unnecessary directories that we want to delete.
+ return str_contains($dir, $requiredDir);
+ })->all();
+ }
+
+ /**
+ * Get the unnecessary files by a search string to remove in the package directory.
+ *
+ * @param array $searchStrings Strings to search for in the filenames.
+ *
+ * @return array
+ */
+ protected function getUnnecessaryFiles(array $searchStrings): array {
+ return collect($this->files->scandir($this->packagePath()))->filter(function ($file) use ($searchStrings) {
+ foreach ($searchStrings as $string) {
+ // If the filename contains the string, then we add it to the
+ // collection of unnecessary files that we want to delete.
+ return str_contains($file, $string);
+ }
+ })->all();
+ }
+
+ /**
+ * Unzip the package zip file into the package directory.
+ */
+ protected function unzip() {
+ $this->files->unzip($this->packageZipFilePath(), $this->packagePath());
+ }
+
+ /**
+ * Remove the zip file after extracting its contents.
+ */
+ protected function removeZip() {
+ $this->files->unlink($this->packageZipFilePath());
+ }
+
+ /**
+ * Move files to the main package directory.
+ *
+ * @param string $dirName
+ */
+ protected function moveFiles($dirName) {
+ $packagePath = $this->packagePath();
+
+ // For each item in the specified directory...
+ foreach ($this->files->scandir("$packagePath/$dirName") as $item) {
+ // Move the item to the main package directory.
+ // The item could be a file or a directory.
+ $this->files->move("$packagePath/$dirName/$item", "$packagePath/$item");
+ }
+
+ // Remove the original directory after moving its contents.
+ $this->files->unlink("$packagePath/$dirName");
+ }
+}
diff --git a/cli/Valet/Packages/WinSW.php b/cli/Valet/Packages/WinSW.php
index 50716b0fa..a5469ecec 100644
--- a/cli/Valet/Packages/WinSW.php
+++ b/cli/Valet/Packages/WinSW.php
@@ -60,8 +60,6 @@ public function install() {
* source code on GitHub.
*
* The readme is mainly for dev purposes, to help understand how to use the package.
- *
- * @return void
*/
private function changeReadme() {
$winswPath = $this->packagePath();
@@ -98,4 +96,4 @@ private function changeReadme() {
// Change the file with the new contents.
$this->files->put("$winswPath/readme.md", $contents);
}
-}
\ No newline at end of file
+}
diff --git a/cli/Valet/PhpCgi.php b/cli/Valet/PhpCgi.php
index 0bc014310..a735f2bce 100644
--- a/cli/Valet/PhpCgi.php
+++ b/cli/Valet/PhpCgi.php
@@ -27,7 +27,8 @@ class PhpCgi {
*/
protected $winswFactory;
- /**@var array
+ /**
+ * @var array
*/
protected $phpWinSws;
@@ -43,7 +44,6 @@ class PhpCgi {
* @param Filesystem $files
* @param WinSwFactory $winsw
* @param Configuration $configuration
- * @return void
*/
public function __construct(CommandLine $cli, Filesystem $files, WinSwFactory $winswFactory, Configuration $configuration) {
$this->cli = $cli;
@@ -68,8 +68,6 @@ public function __construct(CommandLine $cli, Filesystem $files, WinSwFactory $w
/**
* Install and configure PHP CGI service.
- *
- * @return void
*/
public function install($phpVersion = null) {
if ($phpVersion) {
@@ -95,8 +93,6 @@ public function install($phpVersion = null) {
/**
* Install the Windows service.
- *
- * @return void
*/
public function installService($phpVersion, $phpCgiServiceConfig = null, $installConfig = null) {
$phpWinSw = $this->phpWinSws[$phpVersion];
@@ -124,8 +120,6 @@ public function installService($phpVersion, $phpCgiServiceConfig = null, $instal
/**
* Uninstall the PHP CGI service.
- *
- * @return void
*/
public function uninstall($phpVersion = null) {
if ($phpVersion) {
@@ -150,8 +144,6 @@ public function uninstall($phpVersion = null) {
/**
* Install the Windows service.
- *
- * @return void
*/
public function uninstallService($phpVersion) {
$phpWinSw = $this->phpWinSws[$phpVersion];
@@ -163,8 +155,6 @@ public function uninstallService($phpVersion) {
/**
* Restart the PHP CGI service.
- *
- * @return void
*/
public function restart() {
foreach ($this->phpWinSws as $phpWinSw) {
@@ -176,8 +166,6 @@ public function restart() {
/**
* Stop the PHP CGI service.
- *
- * @return void
*/
public function stop() {
foreach ($this->phpWinSws as $phpWinSw) {
@@ -207,6 +195,7 @@ public function findDefaultPhpPath(): string {
*
* @param string $phpPath The path to the PHP executable.
* @param bool $getExecPath Optional. Determines whether to return the executable path. Default: `false`.
+ *
* @return string|bool The PHP version or the executable path determined by the `$getExecPath` param, returns `false` on error.
*/
public function findPhpVersion($phpPath, $getExecPath = false) {
@@ -228,6 +217,7 @@ function ($code, $output) use ($phpPath) {
/**
* Get the PHP path by version.
+ *
* @param string $phpVersion
*
* @return string|null The path to the PHP executable.
@@ -248,6 +238,7 @@ public function getPhpPath($phpVersion) {
* Get the CGI name
*
* @param string $phpVersion
+ *
* @return string
*/
public function getPhpCgiName($phpVersion) {
diff --git a/cli/Valet/PhpCgiXdebug.php b/cli/Valet/PhpCgiXdebug.php
index d1b0d8465..a557cc131 100644
--- a/cli/Valet/PhpCgiXdebug.php
+++ b/cli/Valet/PhpCgiXdebug.php
@@ -25,7 +25,8 @@ public function __construct(CommandLine $cli, Filesystem $files, WinSwFactory $w
* Install and configure PHP CGI service.
*
* @param null|string $phpVersion The PHP version
- * @return array|void $versionArray
+ *
+ * @return array $versionArray
*/
public function install($phpVersion = null) {
if ($phpVersion) {
@@ -57,8 +58,6 @@ public function install($phpVersion = null) {
/**
* Install the Windows service.
- *
- * @return void
*/
public function installService($phpVersion, $phpCgiServiceConfig = null, $installConfig = null) {
$phpWinSw = $this->phpWinSws[$phpVersion];
@@ -77,6 +76,7 @@ public function installService($phpVersion, $phpCgiServiceConfig = null, $instal
* Determine if the Xdebug is installed.
*
* @param string|null $phpVersion
+ *
* @return bool
*/
public function installed($phpVersion = null) {
diff --git a/cli/Valet/ProcessOutput.php b/cli/Valet/ProcessOutput.php
index 089f4254e..1f6d34e6f 100644
--- a/cli/Valet/ProcessOutput.php
+++ b/cli/Valet/ProcessOutput.php
@@ -14,7 +14,6 @@ class ProcessOutput {
* Create a new test process instance.
*
* @param \Symfony\Component\Process\Process $process
- * @return void
*/
public function __construct(Process $process) {
$this->process = $process;
@@ -50,6 +49,7 @@ public function __toString() {
*
* @param string $method
* @param array $args
+ *
* @return mixed
*/
public function __call($method, $args) {
diff --git a/cli/Valet/Server.php b/cli/Valet/Server.php
index a01e80e76..6cca3140c 100644
--- a/cli/Valet/Server.php
+++ b/cli/Valet/Server.php
@@ -36,7 +36,7 @@ public function siteNameFromHttpHost($httpHost) {
$siteName = basename(
// Filter host to support wildcard dns feature
$this->supportWildcardDnsDomains($httpHost),
- '.'.$this->config['tld']
+ '.' . $this->config['tld']
);
if (strpos($siteName, 'www.') === 0) {
diff --git a/cli/Valet/Share.php b/cli/Valet/Share.php
index f5eed7b67..e8dafa15c 100644
--- a/cli/Valet/Share.php
+++ b/cli/Valet/Share.php
@@ -23,6 +23,7 @@ class Share {
/**
* The supported share tools.
+ *
* @var string[]
*/
protected $share_tools;
@@ -38,7 +39,6 @@ class Share {
*
* @param CommandLine $cli
* @param Configuration $config
- * @return void
*/
public function __construct(CommandLine $cli, Configuration $config, Filesystem $files) {
$this->cli = $cli;
@@ -69,8 +69,6 @@ public function shareTool() {
/**
* Create the share tool class namespace using the current tool
* and create the child class instance.
- *
- * @return void
*/
private function createShareToolInstance() {
$tool = $this->getCurrentShareTool();
@@ -85,6 +83,7 @@ private function createShareToolInstance() {
/**
* Get the share tool child class instance.
+ *
* @return object
*/
private function getShareToolInstance() {
@@ -107,6 +106,7 @@ public function getShareTools() {
* Check if the specified tool is valid.
*
* @param string $tool
+ *
* @return bool
*/
public function isToolValid($tool) {
@@ -121,4 +121,4 @@ public function isToolValid($tool) {
public function getCurrentShareTool() {
return $this->config->get("share-tool") ?? null;
}
-}
\ No newline at end of file
+}
diff --git a/cli/Valet/ShareTools/Ngrok.php b/cli/Valet/ShareTools/Ngrok.php
index a0a3ca2b2..010cecba0 100644
--- a/cli/Valet/ShareTools/Ngrok.php
+++ b/cli/Valet/ShareTools/Ngrok.php
@@ -19,8 +19,6 @@ class Ngrok extends ShareTool {
* @param string $site The site
* @param int $port The site's port
* @param array $options Options/flags to pass to ngrok
- *
- * @return void
*/
public function start(string $site, int $port, array $options = []) {
if ($port === 443 && !$this->hasAuthToken()) {
@@ -60,8 +58,6 @@ public function start(string $site, int $port, array $options = []) {
* Run ngrok CLI commands.
*
* @param string $command
- *
- * @return void
*/
public function run(string $command) {
$ngrok = realpath(valetBinPath() . 'ngrok.exe');
diff --git a/cli/Valet/ShareTools/ShareTool.php b/cli/Valet/ShareTools/ShareTool.php
index 505ad7edc..9c325a526 100644
--- a/cli/Valet/ShareTools/ShareTool.php
+++ b/cli/Valet/ShareTools/ShareTool.php
@@ -34,7 +34,6 @@ abstract class ShareTool {
*
* @param CommandLine $cli
* @param Filesystem $files
- * @return void
*/
public function __construct(CommandLine $cli, Filesystem $files) {
$this->cli = $cli;
@@ -47,8 +46,6 @@ public function __construct(CommandLine $cli, Filesystem $files) {
* @param string $site The site
* @param int $port The site's port
* @param array $options Options/flags to pass to ngrok
- *
- * @return void
*/
abstract public function start(string $site, int $port, array $options = []);
@@ -56,8 +53,6 @@ abstract public function start(string $site, int $port, array $options = []);
* Run CLI commands
*
* @param string $command
- *
- * @return void
*/
abstract public function run(string $command);
@@ -76,6 +71,7 @@ abstract public function getConfig();
/**
* Get the current tunnel URL from the API.
+ *
* @param string $site The site
*
* @return string The current tunnel URL
diff --git a/cli/Valet/Site.php b/cli/Valet/Site.php
index 5a443eab2..ea5435d8c 100644
--- a/cli/Valet/Site.php
+++ b/cli/Valet/Site.php
@@ -17,7 +17,6 @@ class Site {
* @param Configuration $config
* @param CommandLine $cli
* @param Filesystem $files
- * @return void
*/
public function __construct(Configuration $config, CommandLine $cli, Filesystem $files) {
$this->cli = $cli;
@@ -29,6 +28,7 @@ public function __construct(Configuration $config, CommandLine $cli, Filesystem
* Get the name of the site.
*
* @param string|null $name
+ *
* @return string
*/
private function getRealSiteName($name) {
@@ -64,6 +64,7 @@ public function getLinkNameByCurrentDir() {
* Get the real hostname for the given path, checking links.
*
* @param string $path
+ *
* @return string|null
*/
public function host($path) {
@@ -81,6 +82,7 @@ public function host($path) {
*
* @param string $target
* @param string $link
+ *
* @return string
*/
public function link($target, $link) {
@@ -100,7 +102,6 @@ public function link($target, $link) {
* Unlink the given symbolic link.
*
* @param string $name
- * @return void
*/
public function unlink($name) {
$name = $this->getRealSiteName($name);
@@ -112,8 +113,6 @@ public function unlink($name) {
/**
* Remove all broken symbolic links.
- *
- * @return void
*/
public function pruneLinks() {
$this->files->ensureDirExists($this->sitesPath(), user());
@@ -176,6 +175,7 @@ public function getSiteConfigFileContents($site, $suffix = null) {
* Get all certificates from config folder.
*
* @param string $path
+ *
* @return \Illuminate\Support\Collection
*/
public function getCertificates($path = null) {
@@ -205,6 +205,7 @@ public function getCertificates($path = null) {
* Create a site object (aka associative array) using Laravel Collection with site details, like name, php version, etc.
*
* @param string $path Either the path to the parked sites directory or the symbolic link sites directory.
+ *
* @return \Illuminate\Support\Collection
*/
public function createSiteObject($path) {
@@ -230,6 +231,7 @@ public function createSiteObject($path) {
*
* @param string $path
* @param \Illuminate\Support\Collection $certs
+ *
* @return \Illuminate\Support\Collection
*/
public function getSites($path, $certs) {
@@ -276,8 +278,10 @@ public function getSites($path, $certs) {
/**
* Get the PHP version for the given site.
+ *
* @param string $site
- * @param boolean $symlink Are we getting the version for a symbolic link site? Default `false`.
+ * @param bool $symlink Are we getting the version for a symbolic link site? Default `false`.
+ *
* @return string PHP version
*/
public function getPhpVersion($site, $symlink = false) {
@@ -308,7 +312,8 @@ public function getPhpVersion($site, $symlink = false) {
* Extract PHP version of exising nginx config.
*
* @param string $url
- * @return string|void
+ *
+ * @return string
*/
public function customPhpVersion($url) {
if ($this->files->exists($this->nginxPath($url))) {
@@ -326,6 +331,7 @@ public function customPhpVersion($url) {
* Determines which PHP version the current working directory is using.
*
* @param string $cwd The current working directory (cwd)
+ *
* @return array|null [ "site" => [sitename], "php" => [PHP version] ]
*/
public function whichPhp($cwd) {
@@ -358,7 +364,6 @@ public function whichPhp($cwd) {
*
* @param string $phpVersion
* @param string $directory
- * @return void
*/
public function isolate($phpVersion, $directory) {
$site = $this->getSiteUrl($directory);
@@ -366,13 +371,14 @@ public function isolate($phpVersion, $directory) {
$php = $this->config->getPhpByVersion($phpVersion);
$this->installSiteConfig($site, $php['version']);
-
- info("The site [$site] is now using $phpVersion.");
}
/**
* Remove PHP version isolation for a given directory.
+ *
* @param bool $isOldTLD Is it the old TLD? Only used in the `reisolateForNewTld` function. Default: `false`.
+ *
+ * @return bool
*/
public function unisolate($directory, $isOldTLD = false) {
$site = $isOldTLD ? $directory : $this->getSiteUrl($directory);
@@ -393,13 +399,14 @@ public function unisolate($directory, $isOldTLD = false) {
// When site doesn't have SSL/TLS, we can remove the custom nginx config file to remove isolation
$this->files->unlink($this->nginxPath($site));
}
-
- info(sprintf('The site [%s] is now using the default PHP version.', $site));
}
/**
* Get list of isolated sites.
+ *
* @param string $oldTld The old TLD. Only used by `reisolateForNewTld()` when changing the TLD.
+ *
+ * @return mixed
*/
public function isolated($oldTld = null) {
$dir = $this->nginxPath();
@@ -420,7 +427,9 @@ public function isolated($oldTld = null) {
/**
* Determine if site is isolated.
+ *
* @param string $site
+ *
* @return bool
*/
public function isIsolated($site) {
@@ -436,7 +445,6 @@ public function isIsolated($site) {
*
* @param string $oldTld
* @param string $tld
- * @return void
*/
public function reisolateForNewTld($oldTld, $tld) {
$isolated = $this->isolated($oldTld)->pluck("site")->all();
@@ -462,6 +470,7 @@ public function reisolateForNewTld($oldTld, $tld) {
* Get the site URL from a directory if it's a valid Valet site.
*
* @param string $directory
+ *
* @return string|false
*/
public function getSiteUrl($directory) {
@@ -492,7 +501,6 @@ public function getSiteUrl($directory) {
*
* @param string $url
* @param string $siteConf pregenerated Nginx config file contents
- * @return void
*/
public function secure($url, $siteConf = null) {
// Extract in order to later preserve custom PHP version config when securing
@@ -527,7 +535,6 @@ public function secure($url, $siteConf = null) {
* Unsecure the given URL so that it will use HTTP again.
*
* @param string $url
- * @return void
*/
public function unsecure($url) {
// Extract in order to later preserve custom PHP version config when unsecuring. Example output: "8.1.2"
@@ -553,6 +560,7 @@ public function unsecure($url) {
/**
* Unsecure all URLs so that they will use HTTP again.
+ *
* @param bool $fromUninstall Determine if the function call was from the
* `uninstall` command or not. Default: `false`
*/
@@ -605,7 +613,9 @@ public function secured() {
/**
* Determine if site is secured.
+ *
* @param string $site
+ *
* @return bool
*/
public function isSecured($site) {
@@ -622,7 +632,6 @@ public function isSecured($site) {
*
* @param string $oldTld
* @param string $tld
- * @return void
*/
public function resecureForNewTld($oldTld, $tld) {
if (!$this->files->exists($this->certificatesPath())) {
@@ -654,6 +663,7 @@ public function resecureForNewTld($oldTld, $tld) {
* @param string $siteConf Nginx site config content
* @param string $old Old domain
* @param string $new New domain
+ *
* @return string
*/
public function replaceOldDomainWithNew($siteConf, $old, $new) {
@@ -678,6 +688,7 @@ public function replaceOldDomainWithNew($siteConf, $old, $new) {
* Get the port of the given host.
*
* @param string $url
+ *
* @return int
*/
public function port(string $url): int {
@@ -692,8 +703,6 @@ public function port(string $url): int {
/**
* If CA and root certificates are nonexistent, create them and trust the root cert.
- *
- * @return void
*/
public function createCa() {
$caPemPath = $this->caPath('LaravelValetCASelfSigned.crt');
@@ -750,7 +759,6 @@ function ($code, $output) {
* Create and trust a certificate for the given URL.
*
* @param string $url
- * @return void
*/
public function createCertificate($url) {
$keyPath = $this->certificatesPath($url, 'key');
@@ -770,10 +778,11 @@ public function createCertificate($url) {
* Create the private key for the TLS certificate.
*
* @param string $keyPath
- * @return void
*/
public function createPrivateKey(string $keyPath) {
- /** @var \phpseclib3\Crypt\RSA\PrivateKey */
+ /**
+ * @var \phpseclib3\Crypt\RSA\PrivateKey
+ */
$key = RSA::createKey();
$this->files->putAsUser($keyPath, $key->toString('PKCS1'));
@@ -785,10 +794,11 @@ public function createPrivateKey(string $keyPath) {
* @param string $url
* @param string $keyPath
* @param string $csrPath
- * @return void
*/
public function createSigningRequest(string $url, string $keyPath, string $csrPath) {
- /** @var \phpseclib3\Crypt\RSA\PrivateKey */
+ /**
+ * @var \phpseclib3\Crypt\RSA\PrivateKey
+ */
$privKey = RSA::load($this->files->get($keyPath));
$x509 = new X509();
@@ -821,10 +831,11 @@ public function createSigningRequest(string $url, string $keyPath, string $csrPa
* @param string $crtPath
* @param string $caPemPath
* @param string $caKeyPath
- * @return void
*/
public function createSignedCertificate(string $keyPath, string $csrPath, string $crtPath, string $caPemPath, string $caKeyPath) {
- /** @var \phpseclib3\Crypt\RSA\PrivateKey */
+ /**
+ * @var \phpseclib3\Crypt\RSA\PrivateKey
+ */
$privKey = RSA::load($this->files->get($keyPath));
$privKey = $privKey->withPadding(RSA::SIGNATURE_PKCS1);
@@ -861,7 +872,6 @@ public function createSignedCertificate(string $keyPath, string $csrPath, string
* Trust the given root certificate file in the Windows Certmgr.
*
* @param string $pemPath
- * @return void
*/
public function trustCa($caPemPath) {
$this->cli->runOrExit(
@@ -876,7 +886,6 @@ function ($code, $output) {
* Trust the given certificate file in the Windows Certmgr.
*
* @param string $crtPath
- * @return void
*/
public function trustCertificate(string $crtPath) {
$this->cli->runOrExit(
@@ -889,8 +898,6 @@ function ($code, $output) {
/**
* Untrust all certificates.
- *
- * @return void
*/
public function untrustCertificates() {
$secured = $this->parked()
@@ -916,7 +923,6 @@ public function untrustCertificates() {
*
* @param string $valetSite
* @param string $phpVersion
- * @return string|null
*/
public function installSiteConfig($valetSite, $phpVersion) {
$phpVersion = $phpVersion ? $phpVersion : $this->config->get('default_php');
@@ -927,10 +933,11 @@ public function installSiteConfig($valetSite, $phpVersion) {
$siteConf = $this->files->get($this->nginxPath($valetSite));
}
else {
+ $valetErrorTemplatePath = $this->files->realpath(valetBinPath() . '../cli/templates');
$siteConf = $this->files->getStub('unsecure.valet.conf');
$siteConf = str_replace(
- ['VALET_HOME_PATH', 'VALET_SERVER_PATH', 'VALET_STATIC_PREFIX', 'VALET_SITE', 'HOME_PATH'],
- [$this->valetHomePath(), VALET_SERVER_PATH, VALET_STATIC_PREFIX, $valetSite, $_SERVER['HOME']],
+ ['VALET_HOME_PATH', 'VALET_SERVER_PATH', 'VALET_STATIC_PREFIX', 'VALET_SITE', 'HOME_PATH', 'VALET_ERROR_TEMPLATE_PATH'],
+ [$this->valetHomePath(), VALET_SERVER_PATH, VALET_STATIC_PREFIX, $valetSite, $_SERVER['HOME'], $valetErrorTemplatePath],
$siteConf
);
}
@@ -945,6 +952,7 @@ public function installSiteConfig($valetSite, $phpVersion) {
*
* @param string $url
* @param string $siteConf (optional) Nginx site config file content
+ *
* @return string
*/
public function buildSecureNginxServer($url, $siteConf = null) {
@@ -954,9 +962,11 @@ public function buildSecureNginxServer($url, $siteConf = null) {
$path = $this->certificatesPath();
+ $valetErrorTemplatePath = $this->files->realpath(valetBinPath() . '../cli/templates');
+
return str_replace(
- ['VALET_HOME_PATH', 'VALET_SERVER_PATH', 'VALET_STATIC_PREFIX', 'VALET_SITE', 'VALET_CERT', 'VALET_KEY', 'HOME_PATH'],
- [$this->valetHomePath(), VALET_SERVER_PATH, VALET_STATIC_PREFIX, $url, $path . '/' . $url . '.crt', $path . '/' . $url . '.key', $_SERVER['HOME']],
+ ['VALET_HOME_PATH', 'VALET_SERVER_PATH', 'VALET_STATIC_PREFIX', 'VALET_SITE', 'VALET_CERT', 'VALET_KEY', 'HOME_PATH', 'VALET_ERROR_TEMPLATE_PATH'],
+ [$this->valetHomePath(), VALET_SERVER_PATH, VALET_STATIC_PREFIX, $url, $path . '/' . $url . '.crt', $path . '/' . $url . '.key', $_SERVER['HOME'], $valetErrorTemplatePath],
$siteConf
);
}
@@ -966,6 +976,8 @@ public function buildSecureNginxServer($url, $siteConf = null) {
*
* @param string $siteConf
* @param string $phpPort
+ *
+ * @return array|string|null
*/
public function replacePhpVersionInSiteConf($siteConf, $phpPort, $phpVersion = null) {
$siteConf = str_replace('127.0.0.1:$valet_php_port;', "127.0.0.1:{$phpPort};", $siteConf);
@@ -985,8 +997,7 @@ public function replacePhpVersionInSiteConf($siteConf, $phpPort, $phpVersion = n
*
* @param string $url The site to serve
* @param string $host The URL to proxy to, eg: http://127.0.0.1:8080
- * @param boolean $secure Is the proxy going to be secured? Default: `false`
- * @return void
+ * @param bool $secure Is the proxy going to be secured? Default: `false`
*/
public function proxyCreate($url, $host, $secure = false) {
if (!preg_match('~^https?://.*$~', $host)) {
@@ -1003,9 +1014,11 @@ public function proxyCreate($url, $host, $secure = false) {
$stub = $secure ? 'secure.proxy.valet.conf' : 'proxy.valet.conf';
$siteConf = $this->files->getStub($stub);
+ $valetErrorTemplatePath = $this->files->realpath(valetBinPath() . '../cli/templates');
+
$siteConf = str_replace(
- ['VALET_HOME_PATH', 'VALET_SERVER_PATH', 'VALET_STATIC_PREFIX', 'VALET_SITE', 'VALET_PROXY_HOST'],
- [$this->valetHomePath(), VALET_SERVER_PATH, VALET_STATIC_PREFIX, $proxyUrl, $host],
+ ['VALET_HOME_PATH', 'VALET_SERVER_PATH', 'VALET_STATIC_PREFIX', 'VALET_SITE', 'VALET_PROXY_HOST', 'VALET_ERROR_TEMPLATE_PATH'],
+ [$this->valetHomePath(), VALET_SERVER_PATH, VALET_STATIC_PREFIX, $proxyUrl, $host, $valetErrorTemplatePath],
$siteConf
);
@@ -1029,7 +1042,6 @@ public function proxyCreate($url, $host, $secure = false) {
* Unsecure the given URL so that it will use HTTP again.
*
* @param string $url
- * @return void
*/
public function proxyDelete($url) {
$tld = $this->config->read()['tld'];
@@ -1098,6 +1110,7 @@ public function proxies() {
*
* @param string $site Site name without TLD
* @param string $configContents Config file contents
+ *
* @return string|null
*/
public function getProxyHostForSite($site, $configContents = null) {
@@ -1116,11 +1129,13 @@ public function getProxyHostForSite($site, $configContents = null) {
}
public function valetHomePath() {
- return VALET_HOME_PATH;
+ return Valet::homePath();
}
/**
* Get the path to Nginx site configuration files.
+ *
+ * @return string
*/
public function nginxPath($additionalPath = null) {
if ($additionalPath && !str_ends_with($additionalPath, '.conf')) {
diff --git a/cli/Valet/Upgrader.php b/cli/Valet/Upgrader.php
index 128b9288b..a42cf6d8d 100644
--- a/cli/Valet/Upgrader.php
+++ b/cli/Valet/Upgrader.php
@@ -28,11 +28,15 @@ public function __construct(Filesystem $files, Configuration $config, Site $site
* Run all the upgrades that should be run every time Valet commands are run.
*/
public function onEveryRun() {
- $this->prunePathsFromConfig();
- $this->pruneSymbolicLinks();
- $this->upgradeSymbolicLinks();
- $this->lintNginxConfigs();
- $this->upgradeNginxSiteConfigs();
+ // Only run if the Valet home path exists.
+ if ($this->files->isDir(Valet::homePath())) {
+ $this->prunePathsFromConfig();
+ $this->pruneSymbolicLinks();
+ $this->upgradeSymbolicLinks();
+ $this->lintNginxConfigs();
+ $this->upgradeNginxSiteConfigs();
+ $this->fixOldSampleValetDriver();
+ }
}
/**
@@ -61,12 +65,11 @@ private function pruneSymbolicLinks() {
* This is a one-time upgrade that will be run when Valet is first installed.
*/
private function upgradeSymbolicLinks() {
- // Check if the symlinks have already been upgraded, by checking if a key exists in
- // the config. If not, then upgrade them.
- if ($this->config->get("symlinks_upgraded", false) === false) {
+ if ($this->shouldUpgradeSymbolicLinks()) {
info("Upgrading your linked sites from the old junction links to symbolic links...");
-
+ // Convert all junction links to symbolic links.
$this->files->convertJunctionsToSymlinks($this->site->sitesPath());
+
// Add a new key to the config file to indicate that the symlinks have been upgraded.
// This will prevent the upgrade from running again, since it is a one-time upgrade.
$this->config->updateKey("symlinks_upgraded", true);
@@ -75,10 +78,32 @@ private function upgradeSymbolicLinks() {
}
}
+ /**
+ * Check if the symbolic links should be upgraded.
+ *
+ * @return bool Returns a boolean indicating whether the symlinks should be upgraded.
+ *
+ * The symlinks should be upgraded if:
+ *
+ * 1. The symlinks have not been upgraded yet (`$symlinksUpgraded` is `false`).
+ * 2. The sites directory is not empty (`$isDirEmpty` is `false`).
+ */
+ private function shouldUpgradeSymbolicLinks() {
+ // Get the value of the "symlinks_upgraded" key from the config.
+ // If the key doesn't exist, it will return false.
+ $symlinksUpgraded = $this->config->get("symlinks_upgraded", false);
+
+ // Check if the sites directory is empty.
+ $isDirEmpty = $this->files->isDirEmpty($this->site->sitesPath());
+
+ // Check if the symlinks have not been upgraded yet AND the sites directory is not empty.
+ return !$symlinksUpgraded && !$isDirEmpty;
+ }
+
/**
* Lint the Nginx configuration files.
*
- * @param boolean $returnOutput
+ * @param bool $returnOutput
*
* @return string|null
*/
@@ -137,4 +162,33 @@ private function upgradeNginxSiteConfigs() {
}
}
}
+
+ /**
+ * If the user has the old `SampleValetDriver` without the Valet namespace,
+ * replace it with the new `SampleValetDriver` that uses the namespace.
+ */
+ public function fixOldSampleValetDriver(): void {
+ $samplePath = Valet::homePath() . '/Drivers/SampleValetDriver.php';
+
+ if ($this->files->exists($samplePath)) {
+ $contents = $this->files->get($samplePath);
+
+ if (! str_contains($contents, 'namespace')) {
+ if ($contents !== $this->files->getStub('LegacySampleValetDriver.php')) {
+ warning('Existing SampleValetDriver.php has been customized.');
+ warning("Backing up at '$samplePath.bak'");
+
+ $this->files->putAsUser(
+ "$samplePath.bak",
+ $contents
+ );
+ }
+
+ $this->files->putAsUser(
+ $samplePath,
+ $this->files->getStub('SampleValetDriver.php')
+ );
+ }
+ }
+ }
}
diff --git a/cli/Valet/Valet.php b/cli/Valet/Valet.php
index a3b833913..5bf25c367 100644
--- a/cli/Valet/Valet.php
+++ b/cli/Valet/Valet.php
@@ -14,7 +14,6 @@ class Valet {
*
* @param CommandLine $cli
* @param Filesystem $files
- * @return void
*/
public function __construct(CommandLine $cli, Filesystem $files) {
$this->cli = $cli;
@@ -104,6 +103,7 @@ public function services($disable = false): array {
* Determine if this is the latest version of Valet.
*
* @param string $currentVersion
+ *
* @return bool
*
* @throws \GuzzleHttp\Exception\GuzzleException
@@ -133,9 +133,9 @@ public function onLatestVersion($currentVersion): bool {
/**
* Get a calculation of the percentage of parity completion against Laravel Valet for macOS
+ *
* @param string $url The URL to the raw code in Github of `app.php` of Laravel Valet on a released version.
* eg. https://raw.githubusercontent.com/laravel/valet/v4.3.0/cli/app.php
- * @return void
*/
public function parity($url) {
@@ -233,20 +233,6 @@ public function parity($url) {
info("Parity at $parityPercentage% out of a total $total_PossibleParityPercentage% possible parity with the Laravel Valet (macOS) $macVersion");
}
- /**
- * Run composer global diagnose.
- */
- public function composerGlobalDiagnose() {
- $this->cli->runAsUser('composer global diagnose');
- }
-
- /**
- * Run composer global update.
- */
- public function composerGlobalUpdate() {
- $this->cli->runAsUser('composer global update');
- }
-
/**
* Get the path to the home directory of composer global.
*
@@ -259,16 +245,50 @@ public function composerGlobalUpdate() {
* @return string The path to the global composer directory.
*/
public function getComposerGlobalPath() {
- return $this->cli->runAsUser('composer -n config --global home');
+ return $this->cli->run('composer -n config --global home');
}
/**
* Get the Valet home path (VALET_HOME_PATH = ~/.config/valet).
*
* @param string $path
+ *
* @return string
*/
public static function homePath(string $path = ''): string {
return VALET_HOME_PATH . ($path ? "/$path" : $path);
}
+
+ /**
+ * Create the emergency uninstall files.
+ *
+ * This is used to emergency uninstall leftover Valet services, if `composer global update`
+ * was ran before uninstalling Valet.
+ */
+ public function createEmergencyUninstallFiles() {
+ $emergencyUninstallPath = $this->homePath("Emergency Uninstall");
+
+ // Copy the emergency stop and uninstall services script to the Valet home
+ // directory for safe keeping.
+ $this->files->copy(
+ realpath(__DIR__ . '/../../emergency_uninstall_services.bat'),
+ "$emergencyUninstallPath/emergency_uninstall_services.bat"
+ );
+
+ // Copy the Ansicon bin files into the Valet home directory for emergency uninstall.
+ //
+ // This is because Ansicon uses the Windows Registry to autorun, so we must ensure it's
+ // safely removed from the Registry too by uninstalling Ansicon officially instead of
+ // hacking it out of the system.
+
+ $this->files->ensureDirExists("$emergencyUninstallPath/ansicon", user());
+ $ansiconBinPath = resolve(Packages\Ansicon::class)->packagePath();
+
+ collect($this->files->scandir($ansiconBinPath))->each(function ($file) use ($emergencyUninstallPath, $ansiconBinPath) {
+ $this->files->copy(
+ realpath("$ansiconBinPath/$file"),
+ "$emergencyUninstallPath/ansicon/$file"
+ );
+ });
+ }
}
diff --git a/cli/Valet/ValetException.php b/cli/Valet/ValetException.php
index 2540eb3a8..4551ae799 100644
--- a/cli/Valet/ValetException.php
+++ b/cli/Valet/ValetException.php
@@ -23,6 +23,7 @@ public function getError() {
* Eg.: Inputs error code `0`, outputs error name `"FATAL"`
*
* @param mixed $code The numeric error type/code
+ *
* @return string The error type name
*/
private function getErrorTypeName($code) {
@@ -50,64 +51,4 @@ private function constructTrace() {
}
return implode("\n", $constructTrace);
}
-
- /**
- * If the GitHub API rate limit has exceeded, we need to handle it.
- *
- * The API rate limit is 60 requests per hour for unauthenticated requests.
- *
- * @param array $headers The headers from the response.
- * @param string $responseMsg The error message from the response.
- *
- * @return array `[$ip, $rateLimit, $timeLeftToReset]` An array containing the IP address, rate limit, and time left to reset.
- */
- public function githubApiRateLimitExceededError($headers, $responseMsg) {
- // Get the rate limit.
- $rateLimit = $headers["X-RateLimit-Limit"][0];
- // Get the reset time (UTC epoch seconds).
- $resetTime = $headers["X-RateLimit-Reset"][0];
-
- $timeLeftToReset = $this->calculateTimeToGithubApiLimitReset($resetTime);
-
- // Get the IP address from the original error response message using regex.
- // The regex pattern matches an IPv4 address.
- // The pattern looks for 1 to 3 digits followed by a dot, repeated 3 times,
- // and then 1 to 3 digits.
- preg_match('/\b(?:\d{1,3}\.){3}\d{1,3}\b/', $responseMsg, $matches);
-
- // Assign the IP address to a variable if the regex matched, otherwise set it to "unknown".
- $ip = $matches[0] ?: "unknown";
-
- return [$ip, $rateLimit, $timeLeftToReset];
- }
-
- /**
- * Calculate the time left to reset the GitHub API rate limit.
- *
- * @param string $resetTime The reset time in UTC epoch seconds.
- *
- * @return string The time left to reset the rate limit in a human-readable format.
- */
- private function calculateTimeToGithubApiLimitReset($resetTime) {
- // Create new DateTime objects for the reset time and the current time.
- $reset_time = new \DateTime("@$resetTime");
- $current_time = new \DateTime("now");
-
- // Get the difference between the 2 times.
- $timeDifference = $reset_time->diff($current_time);
-
- // Get the difference in minutes and seconds.
- // The DateInterval object has many properties, including minutes and seconds,
- // which we can directly access.
- $mins = $timeDifference->i;
- $secs = $timeDifference->s;
-
- // Format the minutes and seconds into a human-readable string.
- // If the minutes or seconds equals 1, we need to use the singular form
- // of "minute" or "second".
- $minsTxt = $mins === 1 ? "$mins minute" : "$mins minutes";
- $secsTxt = $secs === 1 ? "$secs second" : "$secs seconds";
-
- return "$minsTxt and $secsTxt";
- }
}
diff --git a/cli/Valet/WinSW.php b/cli/Valet/WinSW.php
index 9927fba37..56543181f 100644
--- a/cli/Valet/WinSW.php
+++ b/cli/Valet/WinSW.php
@@ -28,7 +28,6 @@ class WinSW {
*
* @param CommandLine $cli
* @param Filesystem $files
- * @return void
*/
public function __construct(string $service, string $serviceId, CommandLine $cli, Filesystem $files) {
$this->cli = $cli;
@@ -41,7 +40,6 @@ public function __construct(string $service, string $serviceId, CommandLine $cli
* Install the service.
*
* @param array $args
- * @return void
*/
public function install(array $args = []) {
$this->createConfiguration($args);
@@ -57,7 +55,6 @@ public function install(array $args = []) {
* Create the .exe and .xml files.
*
* @param array $args
- * @return void
*/
protected function createConfiguration(array $args = []) {
$args['VALET_HOME_PATH'] = Valet::homePath();
@@ -77,8 +74,6 @@ protected function createConfiguration(array $args = []) {
/**
* Uninstall the service.
- *
- * @return void
*/
public function uninstall() {
if ($this->isRunning()) {
@@ -104,8 +99,6 @@ public function installed(): bool {
/**
* Restart the service.
- *
- * @return void
*/
public function restart() {
$command = 'cmd /C cd "' . $this->servicesPath() . '" && "' . $this->servicesPath($this->service) . '" restart';
@@ -121,8 +114,6 @@ public function restart() {
/**
* Stop the service.
- *
- * @return void
*/
public function stop() {
$command = 'cmd /C cd "' . $this->servicesPath() . '" && "' . $this->servicesPath($this->service) . '" stop';
@@ -135,7 +126,7 @@ public function stop() {
/**
* Is a service running?
*
- * @return boolean
+ * @return bool
*/
public function isRunning() {
$output = $this->cli->powershell('Get-Service -Name ' . $this->serviceId)->__toString();
@@ -168,9 +159,10 @@ protected function binaryPath(): string {
* Get the services path.
*
* @param string $path
+ *
* @return string
*/
protected function servicesPath(string $path = ''): string {
return Valet::homePath('Services' . ($path ? "/$path" : $path));
}
-}
+}
\ No newline at end of file
diff --git a/cli/Valet/WinSwFactory.php b/cli/Valet/WinSwFactory.php
index aa241e467..c5070ba9e 100644
--- a/cli/Valet/WinSwFactory.php
+++ b/cli/Valet/WinSwFactory.php
@@ -18,7 +18,6 @@ class WinSwFactory {
*
* @param CommandLine $cli
* @param Filesystem $files
- * @return void
*/
public function __construct(CommandLine $cli, Filesystem $files) {
$this->cli = $cli;
@@ -33,6 +32,7 @@ public function __construct(CommandLine $cli, Filesystem $files) {
*
* @param string $service
* @param string $serviceId
+ *
* @return WinSW
*/
public function make(string $service, string $serviceId) {
diff --git a/cli/drivers/BasicValetDriver.php b/cli/drivers/BasicValetDriver.php
deleted file mode 100644
index 673ae779d..000000000
--- a/cli/drivers/BasicValetDriver.php
+++ /dev/null
@@ -1,149 +0,0 @@
-isActualFile($staticFilePath = $sitePath . $uri)) {
- return $staticFilePath;
- }
-
- return false;
- }
-
- /**
- * Get the fully resolved path to the application's front controller.
- *
- * @param string $sitePath
- * @param string $siteName
- * @param string $uri
- * @return string
- */
- public function frontControllerPath($sitePath, $siteName, $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),
- $this->asHtmlIndexFileInDirectory($sitePath, $uri)
- ];
-
- foreach ($dynamicCandidates as $candidate) {
- if ($this->isActualFile($candidate)) {
- $_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'
- ];
-
- foreach ($fixedCandidatesAndDocroots as $candidate => $docroot) {
- if ($this->isActualFile($candidate)) {
- $_SERVER['SCRIPT_FILENAME'] = $candidate;
- $_SERVER['SCRIPT_NAME'] = '/index.php';
- $_SERVER['DOCUMENT_ROOT'] = $docroot;
-
- return $candidate;
- }
- }
- }
-
- /**
- * Concatenate the site path and URI as a single file name.
- *
- * @param string $sitePath
- * @param string $uri
- * @return string
- */
- protected function asActualFile($sitePath, $uri) {
- return $sitePath . $uri;
- }
-
- /**
- * Format the site path and URI with a trailing "index.php".
- *
- * @param string $sitePath
- * @param string $uri
- * @return string
- */
- protected function asPhpIndexFileInDirectory($sitePath, $uri) {
- return $sitePath . rtrim($uri, '/') . '/index.php';
- }
-
- /**
- * Format the site path and URI with a trailing "index.html".
- *
- * @param string $sitePath
- * @param string $uri
- * @return string
- */
- protected function asHtmlIndexFileInDirectory($sitePath, $uri) {
- return $sitePath . rtrim($uri, '/') . '/index.html';
- }
-
- /**
- * Format the incoming site path as root "index.php" file path.
- *
- * @param string $sitePath
- * @return string
- */
- protected function asRootPhpIndexFile($sitePath) {
- return $sitePath . '/index.php';
- }
-
- /**
- * Format the incoming site path as a "public/index.php" file path.
- *
- * @param string $sitePath
- * @return string
- */
- protected function asPublicPhpIndexFile($sitePath) {
- return $sitePath . '/public/index.php';
- }
-
- /**
- * Format the incoming site path as a "public/index.php" file path.
- *
- * @param string $sitePath
- * @return string
- */
- protected function asPublicHtmlIndexFile($sitePath) {
- return $sitePath . '/public/index.html';
- }
-}
diff --git a/cli/drivers/BedrockValetDriver.php b/cli/drivers/BedrockValetDriver.php
deleted file mode 100644
index 2704770b1..000000000
--- a/cli/drivers/BedrockValetDriver.php
+++ /dev/null
@@ -1,69 +0,0 @@
-isActualFile($staticFilePath)) {
- return $staticFilePath;
- }
-
- return false;
- }
-
- /**
- * Get the fully resolved path to the application's front controller.
- *
- * @param string $sitePath
- * @param string $siteName
- * @param string $uri
- * @return string
- */
- public function frontControllerPath($sitePath, $siteName, $uri) {
- $_SERVER['PHP_SELF'] = $uri;
- $_SERVER['SERVER_NAME'] = $_SERVER['HTTP_HOST'];
-
- if (strpos($uri, '/wp/') === 0) {
- return is_dir($sitePath . '/web' . $uri)
- ? $sitePath . '/web' . $this->forceTrailingSlash($uri) . '/index.php'
- : $sitePath . '/web' . $uri;
- }
-
- return $sitePath . '/web/index.php';
- }
-
- /**
- * Redirect to uri with trailing slash.
- *
- * @param string $uri
- * @return string
- */
- private function forceTrailingSlash($uri) {
- if (substr($uri, -1 * strlen('/wp/wp-admin')) == '/wp/wp-admin') {
- header('Location: ' . $uri . '/');
- exit;
- }
-
- return $uri;
- }
-}
diff --git a/cli/drivers/CakeValetDriver.php b/cli/drivers/CakeValetDriver.php
deleted file mode 100644
index 7fb3b1b2b..000000000
--- a/cli/drivers/CakeValetDriver.php
+++ /dev/null
@@ -1,48 +0,0 @@
-isActualFile($staticFilePath = $sitePath . '/webroot/' . $uri)) {
- return $staticFilePath;
- }
-
- return false;
- }
-
- /**
- * Get the fully resolved path to the application's front controller.
- *
- * @param string $sitePath
- * @param string $siteName
- * @param string $uri
- * @return string
- */
- public function frontControllerPath($sitePath, $siteName, $uri) {
- $_SERVER['DOCUMENT_ROOT'] = $sitePath . '/webroot';
- $_SERVER['SCRIPT_FILENAME'] = $sitePath . '/webroot/index.php';
- $_SERVER['SCRIPT_NAME'] = '/index.php';
- $_SERVER['PHP_SELF'] = '/index.php';
-
- return $sitePath . '/webroot/index.php';
- }
-}
diff --git a/cli/drivers/Concrete5ValetDriver.php b/cli/drivers/Concrete5ValetDriver.php
deleted file mode 100644
index 8ba350c54..000000000
--- a/cli/drivers/Concrete5ValetDriver.php
+++ /dev/null
@@ -1,44 +0,0 @@
-isActualFile($staticFilePath = $sitePath . '/web' . $uri)) {
- return $staticFilePath;
- }
-
- return false;
- }
-
- /**
- * Get the fully resolved path to the application's front controller.
- *
- * @param string $sitePath
- * @param string $siteName
- * @param string $uri
- * @return string
- */
- public function frontControllerPath($sitePath, $siteName, $uri) {
- if ($uri === '/install.php') {
- return $sitePath . '/web/install.php';
- }
-
- if (0 === strncmp($uri, '/app_dev.php', 12)) {
- $_SERVER['SCRIPT_NAME'] = '/app_dev.php';
- $_SERVER['SCRIPT_FILENAME'] = $sitePath . '/app_dev.php';
-
- return $sitePath . '/web/app_dev.php';
- }
-
- return $sitePath . '/web/app.php';
- }
-}
diff --git a/cli/drivers/CraftValetDriver.php b/cli/drivers/CraftValetDriver.php
deleted file mode 100644
index 8a26c4a5c..000000000
--- a/cli/drivers/CraftValetDriver.php
+++ /dev/null
@@ -1,206 +0,0 @@
-frontControllerDirectory($sitePath);
-
- if ($this->isActualFile($staticFilePath = $sitePath . '/' . $frontControllerDirectory . $uri)) {
- return $staticFilePath;
- }
-
- return false;
- }
-
- /**
- * Get the fully resolved path to the application's front controller.
- *
- * @param string $sitePath
- * @param string $siteName
- * @param string $uri
- * @return string
- */
- public function frontControllerPath($sitePath, $siteName, $uri) {
- $frontControllerDirectory = $this->frontControllerDirectory($sitePath);
-
- // Default index path
- $indexPath = $sitePath . '/' . $frontControllerDirectory . '/index.php';
- $scriptName = '/index.php';
-
- // Check if the first URL segment matches any of the defined locales
- $locales = [
- 'ar',
- 'ar_sa',
- 'bg',
- 'bg_bg',
- 'ca_es',
- 'cs',
- 'cy_gb',
- 'da',
- 'da_dk',
- 'de',
- 'de_at',
- 'de_ch',
- 'de_de',
- 'el',
- 'el_gr',
- 'en',
- 'en_as',
- 'en_au',
- 'en_bb',
- 'en_be',
- 'en_bm',
- 'en_bw',
- 'en_bz',
- 'en_ca',
- 'en_dsrt',
- 'en_dsrt_us',
- 'en_gb',
- 'en_gu',
- 'en_gy',
- 'en_hk',
- 'en_ie',
- 'en_in',
- 'en_jm',
- 'en_mh',
- 'en_mp',
- 'en_mt',
- 'en_mu',
- 'en_na',
- 'en_nz',
- 'en_ph',
- 'en_pk',
- 'en_sg',
- 'en_shaw',
- 'en_tt',
- 'en_um',
- 'en_us',
- 'en_us_posix',
- 'en_vi',
- 'en_za',
- 'en_zw',
- 'en_zz',
- 'es',
- 'es_cl',
- 'es_es',
- 'es_mx',
- 'es_us',
- 'es_ve',
- 'et',
- 'fi',
- 'fi_fi',
- 'fil',
- 'fr',
- 'fr_be',
- 'fr_ca',
- 'fr_ch',
- 'fr_fr',
- 'fr_ma',
- 'he',
- 'hr',
- 'hr_hr',
- 'hu',
- 'hu_hu',
- 'id',
- 'id_id',
- 'it',
- 'it_ch',
- 'it_it',
- 'ja',
- 'ja_jp',
- 'ko',
- 'ko_kr',
- 'lt',
- 'lv',
- 'ms',
- 'ms_my',
- 'nb',
- 'nb_no',
- 'nl',
- 'nl_be',
- 'nl_nl',
- 'nn',
- 'nn_no',
- 'no',
- 'pl',
- 'pl_pl',
- 'pt',
- 'pt_br',
- 'pt_pt',
- 'ro',
- 'ro_ro',
- 'ru',
- 'ru_ru',
- 'sk',
- 'sl',
- 'sr',
- 'sv',
- 'sv_se',
- 'th',
- 'th_th',
- 'tr',
- 'tr_tr',
- 'uk',
- 'vi',
- 'zh',
- 'zh_cn',
- 'zh_tw'
- ];
- $parts = explode('/', $uri);
-
- if (count($parts) > 1 && in_array($parts[1], $locales)) {
- $indexLocalizedPath = $sitePath . '/' . $frontControllerDirectory . '/' . $parts[1] . '/index.php';
-
- // Check if index.php exists in the localized folder, this is optional in Craft 3
- if (file_exists($indexLocalizedPath)) {
- $indexPath = $indexLocalizedPath;
- $scriptName = '/' . $parts[1] . '/index.php';
- }
- }
-
- $_SERVER['SCRIPT_FILENAME'] = $indexPath;
- $_SERVER['SERVER_NAME'] = $_SERVER['HTTP_HOST'];
- $_SERVER['SCRIPT_NAME'] = $scriptName;
- $_SERVER['PHP_SELF'] = $scriptName;
- $_SERVER['DOCUMENT_ROOT'] = $sitePath . '/' . $frontControllerDirectory;
-
- return $indexPath;
- }
-}
diff --git a/cli/drivers/DrupalValetDriver.php b/cli/drivers/DrupalValetDriver.php
deleted file mode 100644
index 3859521ae..000000000
--- a/cli/drivers/DrupalValetDriver.php
+++ /dev/null
@@ -1,105 +0,0 @@
-addSubdirectory($sitePath);
-
- /**
- * /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;
- }
- return false;
- }
-
- /**
- * Determine if the incoming request is for a static file.
- *
- * @param string $sitePath
- * @param string $siteName
- * @param string $uri
- * @return string|false
- */
- public function isStaticFile($sitePath, $siteName, $uri) {
- $sitePath = $this->addSubdirectory($sitePath);
-
- if (file_exists($sitePath . $uri) && !is_dir($sitePath . $uri) && pathinfo($sitePath . $uri)['extension'] != 'php') {
- return $sitePath . $uri;
- }
-
- return false;
- }
-
- /**
- * Get the fully resolved path to the application's front controller.
- *
- * @param string $sitePath
- * @param string $siteName
- * @param string $uri
- * @return string
- */
- public function frontControllerPath($sitePath, $siteName, $uri) {
- $sitePath = $this->addSubdirectory($sitePath);
-
- if (!isset($_GET['q']) && !empty($uri) && $uri !== '/' && strpos($uri, '/jsonapi/') === false) {
- $_GET['q'] = $uri;
- }
-
- $matches = [];
- if (preg_match('/^\/(.*?)\.php/', $uri, $matches)) {
- $filename = $matches[0];
- if (file_exists($sitePath . $filename) && !is_dir($sitePath . $filename)) {
- $_SERVER['SCRIPT_FILENAME'] = $sitePath . $filename;
- $_SERVER['SCRIPT_NAME'] = $filename;
-
- return $sitePath . $filename;
- }
- }
-
- // Fallback
- $_SERVER['SCRIPT_FILENAME'] = $sitePath . '/index.php';
- $_SERVER['SCRIPT_NAME'] = '/index.php';
-
- return $sitePath . '/index.php';
- }
-
- /**
- * Add any matching subdirectory to the site path.
- */
- public function addSubdirectory($sitePath) {
- $paths = array_map(function ($subDir) use ($sitePath) {
- return "$sitePath/$subDir";
- }, $this->possibleSubdirectories());
-
- $foundPaths = array_filter($paths, function ($path) {
- return file_exists($path);
- });
-
- // If paths are found, return the first one.
- if (!empty($foundPaths)) {
- return array_shift($foundPaths);
- }
-
- // If there are no matches, return the original path.
- return $sitePath;
- }
-
- /**
- * Return an array of possible subdirectories.
- *
- * @return array
- */
- private function possibleSubdirectories() {
- return ['docroot', 'public', 'web'];
- }
-}
diff --git a/cli/drivers/JigsawValetDriver.php b/cli/drivers/JigsawValetDriver.php
deleted file mode 100644
index 73a42f814..000000000
--- a/cli/drivers/JigsawValetDriver.php
+++ /dev/null
@@ -1,25 +0,0 @@
-isActualFile($staticFilePath = $sitePath . $uri)) {
- return $staticFilePath;
- }
- elseif ($this->isActualFile($staticFilePath = $sitePath . '/public' . $uri)) {
- return $staticFilePath;
- }
-
- return false;
- }
-
- /**
- * Get the fully resolved path to the application's front controller.
- *
- * @param string $sitePath
- * @param string $siteName
- * @param string $uri
- * @return string
- */
- public function frontControllerPath($sitePath, $siteName, $uri) {
- $scriptName = '/index.php';
-
- if ($this->isActualFile($sitePath . '/index.php')) {
- $indexPath = $sitePath . '/index.php';
- }
-
- if ($isAboveWebroot = $this->isActualFile($sitePath . '/public/index.php')) {
- $indexPath = $sitePath . '/public/index.php';
- }
-
- if (preg_match('/^\/panel/', $uri) && $this->isActualFile($sitePath . '/panel/index.php')) {
- $scriptName = '/panel/index.php';
- $indexPath = $sitePath . '/panel/index.php';
- }
-
- $sitePathPrefix = ($isAboveWebroot) ? $sitePath . '/public' : $sitePath;
-
- $_SERVER['SERVER_NAME'] = $_SERVER['HTTP_HOST'];
- $_SERVER['SCRIPT_NAME'] = $scriptName;
- $_SERVER['SCRIPT_FILENAME'] = $sitePathPrefix . $scriptName;
-
- return $indexPath;
- }
-}
diff --git a/cli/drivers/LaravelValetDriver.php b/cli/drivers/LaravelValetDriver.php
deleted file mode 100644
index f1664b928..000000000
--- a/cli/drivers/LaravelValetDriver.php
+++ /dev/null
@@ -1,58 +0,0 @@
-isActualFile($storagePath = $sitePath . '/storage/app/public' . $storageUri)) {
- return $storagePath;
- }
-
- return false;
- }
-
- /**
- * Get the fully resolved path to the application's front controller.
- *
- * @param string $sitePath
- * @param string $siteName
- * @param string $uri
- * @return string
- */
- public function frontControllerPath($sitePath, $siteName, $uri) {
- // Shortcut for getting the "local" hostname as the HTTP_HOST
- 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';
- }
-}
diff --git a/cli/drivers/Magento2ValetDriver.php b/cli/drivers/Magento2ValetDriver.php
deleted file mode 100644
index 97c0c4b5c..000000000
--- a/cli/drivers/Magento2ValetDriver.php
+++ /dev/null
@@ -1,155 +0,0 @@
-checkMageMode($sitePath);
-
- $uri = $this->handleForVersions($uri);
- $route = parse_url(substr($uri, 1))['path'];
-
- $pub = '';
- if ('developer' === $this->mageMode) {
- $pub = 'pub/';
- }
-
- if (!$this->isPubDirectory($sitePath, $route, $pub)) {
- return false;
- }
-
- $magentoPackagePubDir = $sitePath;
- if ('developer' !== $this->mageMode) {
- $magentoPackagePubDir .= '/pub';
- }
-
- $file = $magentoPackagePubDir . '/' . $route;
-
- if (file_exists($file)) {
- return $magentoPackagePubDir . $uri;
- }
-
- if (strpos($route, $pub . 'static/') === 0) {
- $route = preg_replace('#' . $pub . 'static/#', '', $route, 1);
- $_GET['resource'] = $route;
- include $magentoPackagePubDir . '/' . $pub . 'static.php';
- exit;
- }
-
- if (strpos($route, $pub . 'media/') === 0) {
- include $magentoPackagePubDir . '/' . $pub . 'get.php';
- exit;
- }
-
- return false;
- }
-
- /**
- * Rewrite URLs that look like "versions12345/" to remove
- * the versions12345/ part.
- *
- * @param string $route
- * @return string
- */
- private function handleForVersions($route) {
- return preg_replace('/version\d*\//', '', $route);
- }
-
- /**
- * Determine the current MAGE_MODE.
- *
- * @param string $sitePath
- */
- private function checkMageMode($sitePath) {
- if (null !== $this->mageMode) {
- // We have already figure out mode, no need to check it again
- return;
- }
-
- 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 (array_key_exists('MAGE_MODE', $mageConfig)) {
- $this->mageMode = $mageConfig['MAGE_MODE'];
- }
- }
-
- /**
- * 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
- * @return bool
- */
- private function isPubDirectory($sitePath, $route, $pub = '') {
- $sitePath .= '/pub/';
- $dirs = glob($sitePath . '*', GLOB_ONLYDIR);
-
- $dirs = str_replace($sitePath, '', $dirs);
- foreach ($dirs as $dir) {
- 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
- * @return string
- */
- public function frontControllerPath($sitePath, $siteName, $uri) {
- $this->checkMageMode($sitePath);
-
- if ('developer' === $this->mageMode) {
- $_SERVER['DOCUMENT_ROOT'] = $sitePath;
-
- return $sitePath . '/index.php';
- }
-
- $_SERVER['DOCUMENT_ROOT'] = $sitePath . '/pub';
-
- return $sitePath . '/pub/index.php';
- }
-}
diff --git a/cli/drivers/NeosValetDriver.php b/cli/drivers/NeosValetDriver.php
deleted file mode 100644
index b42f89e2e..000000000
--- a/cli/drivers/NeosValetDriver.php
+++ /dev/null
@@ -1,48 +0,0 @@
-isActualFile($staticFilePath = $sitePath . '/Web' . $uri)) {
- return $staticFilePath;
- }
-
- return false;
- }
-
- /**
- * Get the fully resolved path to the application's front controller.
- *
- * @param string $sitePath
- * @param string $siteName
- * @param string $uri
- * @return string
- */
- public function frontControllerPath($sitePath, $siteName, $uri) {
- putenv('FLOW_CONTEXT=Development');
- putenv('FLOW_REWRITEURLS=1');
- $_SERVER['SCRIPT_FILENAME'] = $sitePath . '/Web/index.php';
- $_SERVER['SCRIPT_NAME'] = '/index.php';
-
- return $sitePath . '/Web/index.php';
- }
-}
diff --git a/cli/drivers/SculpinValetDriver.php b/cli/drivers/SculpinValetDriver.php
deleted file mode 100644
index ec2039342..000000000
--- a/cli/drivers/SculpinValetDriver.php
+++ /dev/null
@@ -1,48 +0,0 @@
-isModernSculpinProject($sitePath) || $this->isLegacySculpinProject($sitePath);
- }
-
- private function isModernSculpinProject($sitePath) {
- return is_dir($sitePath . '/source') && is_dir($sitePath . '/output_dev') && $this->composerRequiresSculpin($sitePath);
- }
-
- private function isLegacySculpinProject($sitePath) {
- return is_dir($sitePath . '/.sculpin');
- }
-
- private function composerRequiresSculpin($sitePath) {
- if (!file_exists($sitePath . '/composer.json')) {
- return false;
- }
-
- $composer_json_source = file_get_contents($sitePath . '/composer.json');
- $composer_json = json_decode($composer_json_source, true);
-
- if (json_last_error() !== JSON_ERROR_NONE) {
- return false;
- }
-
- return isset($composer_json['require']['sculpin/sculpin']);
- }
-
- /**
- * Mutate the incoming URI.
- *
- * @param string $uri
- * @return string
- */
- public function mutateUri($uri) {
- return rtrim('/output_dev' . $uri, '/');
- }
-}
diff --git a/cli/drivers/StatamicV1ValetDriver.php b/cli/drivers/StatamicV1ValetDriver.php
deleted file mode 100644
index c87755d2d..000000000
--- a/cli/drivers/StatamicV1ValetDriver.php
+++ /dev/null
@@ -1,61 +0,0 @@
-isActualFile($staticFilePath = $sitePath . $uri)) {
- return $staticFilePath;
- }
-
- return false;
- }
-
- /**
- * Get the fully resolved path to the application's front controller.
- *
- * @param string $sitePath
- * @param string $siteName
- * @param string $uri
- * @return string
- */
- public function frontControllerPath($sitePath, $siteName, $uri) {
- if (strpos($uri, '/admin.php') === 0) {
- $_SERVER['SCRIPT_NAME'] = '/admin.php';
-
- return $sitePath . '/admin.php';
- }
-
- if ($uri === '/admin') {
- $_SERVER['SCRIPT_NAME'] = '/admin/index.php';
-
- return $sitePath . '/admin/index.php';
- }
-
- $_SERVER['SCRIPT_NAME'] = '/index.php';
-
- return $sitePath . '/index.php';
- }
-}
diff --git a/cli/drivers/StatamicValetDriver.php b/cli/drivers/StatamicValetDriver.php
deleted file mode 100644
index 340f3c2d0..000000000
--- a/cli/drivers/StatamicValetDriver.php
+++ /dev/null
@@ -1,379 +0,0 @@
-isActualFile($staticFilePath = $sitePath . $uri)) {
- return $staticFilePath;
- }
- elseif ($this->isActualFile($staticFilePath = $sitePath . '/public' . $uri)) {
- return $staticFilePath;
- }
-
- return false;
- }
-
- /**
- * Get the fully resolved path to the application's front controller.
- *
- * @param string $sitePath
- * @param string $siteName
- * @param string $uri
- * @return string
- */
- public function frontControllerPath($sitePath, $siteName, $uri) {
- if ($_SERVER['REQUEST_METHOD'] === 'GET' && $this->isActualFile($staticPath = $this->getStaticPath($sitePath))) {
- return $staticPath;
- }
-
- if ($uri === '/installer.php') {
- return $sitePath . '/installer.php';
- }
-
- $scriptName = '/index.php';
-
- if ($this->isActualFile($sitePath . '/index.php')) {
- $indexPath = $sitePath . '/index.php';
- }
-
- if ($isAboveWebroot = $this->isActualFile($sitePath . '/public/index.php')) {
- $indexPath = $sitePath . '/public/index.php';
- }
-
- $sitePathPrefix = ($isAboveWebroot) ? $sitePath . '/public' : $sitePath;
-
- if ($locale = $this->getUriLocale($uri)) {
- if ($this->isActualFile($localeIndexPath = $sitePathPrefix . '/' . $locale . '/index.php')) {
- // Force trailing slashes on locale roots.
- if ($uri === '/' . $locale) {
- header('Location: ' . $uri . '/');
- exit;
- }
-
- $indexPath = $localeIndexPath;
- $scriptName = '/' . $locale . '/index.php';
- }
- }
-
- $_SERVER['SCRIPT_NAME'] = $scriptName;
- $_SERVER['SCRIPT_FILENAME'] = $sitePathPrefix . $scriptName;
-
- return $indexPath;
- }
-
- /**
- * Get the locale from this URI.
- *
- * @param string $uri
- * @return string|null
- */
- public function getUriLocale($uri) {
- $parts = explode('/', $uri);
- $locale = $parts[1];
-
- if (count($parts) < 2 || !in_array($locale, $this->getLocales())) {
- return;
- }
-
- return $locale;
- }
-
- /**
- * Get the list of possible locales used in the first segment of a URI.
- *
- * @return array
- */
- public function getLocales() {
- return [
- 'af',
- 'ax',
- 'al',
- 'dz',
- 'as',
- 'ad',
- 'ao',
- 'ai',
- 'aq',
- 'ag',
- 'ar',
- 'am',
- 'aw',
- 'au',
- 'at',
- 'az',
- 'bs',
- 'bh',
- 'bd',
- 'bb',
- 'by',
- 'be',
- 'bz',
- 'bj',
- 'bm',
- 'bt',
- 'bo',
- 'bq',
- 'ba',
- 'bw',
- 'bv',
- 'br',
- 'io',
- 'bn',
- 'bg',
- 'bf',
- 'bi',
- 'cv',
- 'kh',
- 'cm',
- 'ca',
- 'ky',
- 'cf',
- 'td',
- 'cl',
- 'cn',
- 'cx',
- 'cc',
- 'co',
- 'km',
- 'cg',
- 'cd',
- 'ck',
- 'cr',
- 'ci',
- 'hr',
- 'cu',
- 'cw',
- 'cy',
- 'cz',
- 'dk',
- 'dj',
- 'dm',
- 'do',
- 'ec',
- 'eg',
- 'sv',
- 'gq',
- 'er',
- 'ee',
- 'et',
- 'fk',
- 'fo',
- 'fj',
- 'fi',
- 'fr',
- 'gf',
- 'pf',
- 'tf',
- 'ga',
- 'gm',
- 'ge',
- 'de',
- 'gh',
- 'gi',
- 'gr',
- 'gl',
- 'gd',
- 'gp',
- 'gu',
- 'gt',
- 'gg',
- 'gn',
- 'gw',
- 'gy',
- 'ht',
- 'hm',
- 'va',
- 'hn',
- 'hk',
- 'hu',
- 'is',
- 'in',
- 'id',
- 'ir',
- 'iq',
- 'ie',
- 'im',
- 'il',
- 'it',
- 'jm',
- 'jp',
- 'je',
- 'jo',
- 'kz',
- 'ke',
- 'ki',
- 'kp',
- 'kr',
- 'kw',
- 'kg',
- 'la',
- 'lv',
- 'lb',
- 'ls',
- 'lr',
- 'ly',
- 'li',
- 'lt',
- 'lu',
- 'mo',
- 'mk',
- 'mg',
- 'mw',
- 'my',
- 'mv',
- 'ml',
- 'mt',
- 'mh',
- 'mq',
- 'mr',
- 'mu',
- 'yt',
- 'mx',
- 'fm',
- 'md',
- 'mc',
- 'mn',
- 'me',
- 'ms',
- 'ma',
- 'mz',
- 'mm',
- 'na',
- 'nr',
- 'np',
- 'nl',
- 'nc',
- 'nz',
- 'ni',
- 'ne',
- 'ng',
- 'nu',
- 'nf',
- 'mp',
- 'no',
- 'om',
- 'pk',
- 'pw',
- 'ps',
- 'pa',
- 'pg',
- 'py',
- 'pe',
- 'ph',
- 'pn',
- 'pl',
- 'pt',
- 'pr',
- 'qa',
- 're',
- 'ro',
- 'ru',
- 'rw',
- 'bl',
- 'sh',
- 'kn',
- 'lc',
- 'mf',
- 'pm',
- 'vc',
- 'ws',
- 'sm',
- 'st',
- 'sa',
- 'sn',
- 'rs',
- 'sc',
- 'sl',
- 'sg',
- 'sx',
- 'sk',
- 'si',
- 'sb',
- 'so',
- 'za',
- 'gs',
- 'ss',
- 'es',
- 'lk',
- 'sd',
- 'sr',
- 'sj',
- 'sz',
- 'se',
- 'ch',
- 'sy',
- 'tw',
- 'tj',
- 'tz',
- 'th',
- 'tl',
- 'tg',
- 'tk',
- 'to',
- 'tt',
- 'tn',
- 'tr',
- 'tm',
- 'tc',
- 'tv',
- 'ug',
- 'ua',
- 'ae',
- 'gb',
- 'us',
- 'um',
- 'uy',
- 'uz',
- 'vu',
- 've',
- 'vn',
- 'vg',
- 'vi',
- 'wf',
- 'eh',
- 'ye',
- 'zm',
- 'zw',
- 'en',
- 'zh'
- ];
- }
-
- /**
- * Get the path to a statically cached page.
- *
- * @param string $sitePath
- * @return string
- */
- protected function getStaticPath($sitePath) {
- $parts = parse_url($_SERVER['REQUEST_URI']);
- $query = isset($parts['query']) ? $parts['query'] : '';
-
- return $sitePath . '/static' . $parts['path'] . '_' . $query . '.html';
- }
-}
diff --git a/cli/drivers/SymfonyValetDriver.php b/cli/drivers/SymfonyValetDriver.php
deleted file mode 100644
index 12c365b0b..000000000
--- a/cli/drivers/SymfonyValetDriver.php
+++ /dev/null
@@ -1,54 +0,0 @@
-isActualFile($staticFilePath = $sitePath . '/web/' . $uri)) {
- return $staticFilePath;
- }
- elseif ($this->isActualFile($staticFilePath = $sitePath . '/public/' . $uri)) {
- return $staticFilePath;
- }
-
- return false;
- }
-
- /**
- * Get the fully resolved path to the application's front controller.
- *
- * @param string $sitePath
- * @param string $siteName
- * @param string $uri
- * @return string
- */
- public function frontControllerPath($sitePath, $siteName, $uri) {
- if (file_exists($frontControllerPath = $sitePath . '/web/app_dev.php')) {
- return $frontControllerPath;
- }
- elseif (file_exists($frontControllerPath = $sitePath . '/web/app.php')) {
- return $frontControllerPath;
- }
- elseif (file_exists($frontControllerPath = $sitePath . '/public/index.php')) {
- return $frontControllerPath;
- }
- }
-}
diff --git a/cli/drivers/require.php b/cli/drivers/require.php
deleted file mode 100644
index c42bedc32..000000000
--- a/cli/drivers/require.php
+++ /dev/null
@@ -1,12 +0,0 @@
-' . $output . '');
@@ -40,7 +39,6 @@ function info($output) {
* Output the given array to the console using var_dump.
*
* @param string $output
- * @return void
*/
function info_dump($output) {
output('' . var_dump($output) . '');
@@ -50,7 +48,6 @@ function info_dump($output) {
* Output the given text to the console.
*
* @param string $output
- * @return void
*/
function warning($output) {
if (isset($_ENV['APP_ENV']) && $_ENV['APP_ENV'] === 'testing') {
@@ -64,12 +61,10 @@ function warning($output) {
* Output errors to the console.
*
* @param string $output
- * @param boolean $exception Optionally pass a boolean to indicate whether to throw an exception. If `true`, the error will be thrown as a `ValetException`. [default: `false`]
+ * @param bool $exception Optionally pass a boolean to indicate whether to throw an exception. If `true`, the error will be thrown as a `ValetException`. [default: `false`]
*
* @throws RuntimeException
* @throws ValetException
- *
- * @return void
*/
function error(string $output, $exception = false) {
if (isset($_ENV['APP_ENV']) && $_ENV['APP_ENV'] === 'testing') {
@@ -96,7 +91,6 @@ function error(string $output, $exception = false) {
* Output the given text to the console.
*
* @param string $output
- * @return void
*/
function output($output) {
if (isset($_ENV['APP_ENV']) && $_ENV['APP_ENV'] === 'testing') {
@@ -110,7 +104,6 @@ function output($output) {
*
* @param array $headers
* @param array $rows
- * @return void
*/
function table(array $headers = [], array $rows = [], $setHorizontal = false, $title = null) {
$table = new Table(new ConsoleOutput());
@@ -170,6 +163,8 @@ function changeColumnMaxWidth($table, $headers, $columns, $maxWidth) {
* Add a table separator inbetween all the rows.
*
* @param array $rows The array of rows
+ *
+ * @return mixed
*/
function addTableSeparator($rows) {
/**
@@ -191,6 +186,7 @@ function addTableSeparator($rows) {
* https://laravel.com/docs/12.x/helpers#method-resolve
*
* @param string $class
+ *
* @return mixed
*/
function resolve($class) {
@@ -203,7 +199,6 @@ function resolve($class) {
*
* @param string $class
* @param mixed $instance
- * @return void
*/
function swap($class, $instance) {
Container::getInstance()->instance($class, $instance);
@@ -218,6 +213,7 @@ function swap($class, $instance) {
* @param int $retries
* @param callable $fn
* @param int $sleep
+ *
* @return mixed
*/
function retry($retries, $fn, $sleep = 0) {
@@ -249,6 +245,7 @@ function retry($retries, $fn, $sleep = 0) {
*
* @param mixed $value
* @param callable $callback
+ *
* @return mixed
*/
function tap($value, callable $callback) {
@@ -273,6 +270,7 @@ function user() {
/**
* Get the bin path.
+ *
* @return string `"c:\Users\Username\AppData\Roaming\Composer\vendor\ycodetech\valet-windows\bin\"`
*/
function valetBinPath() {
@@ -381,6 +379,7 @@ function getTarExecutable() {
*
* @param string $haystack
* @param array $needles
+ *
* @return bool
*/
function str_contains_any($haystack, $needles) {
@@ -390,4 +389,4 @@ function str_contains_any($haystack, $needles) {
}
}
return false;
-}
+}
\ No newline at end of file
diff --git a/cli/includes/legacy_drivers/BasicValetDriver.php b/cli/includes/legacy_drivers/BasicValetDriver.php
new file mode 100644
index 000000000..a7e984d01
--- /dev/null
+++ b/cli/includes/legacy_drivers/BasicValetDriver.php
@@ -0,0 +1,4 @@
+
-