├── .github └── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ ├── feature_request.yml │ └── nginx-config-upgrade.md ├── .prettierrc.json ├── CHANGELOG.md ├── LICENSE.txt ├── README.md ├── The_same_icon.svg ├── bin ├── acrylic │ ├── AcrylicConfiguration.ini │ ├── AcrylicConsole.exe │ ├── AcrylicService.exe │ ├── AcrylicUI.exe │ ├── AcrylicUI.exe.manifest │ ├── License.txt │ └── Readme.txt └── ngrok.exe ├── cli ├── Valet │ ├── Acrylic.php │ ├── Application.php │ ├── CommandLine.php │ ├── Configuration.php │ ├── Diagnose.php │ ├── Filesystem.php │ ├── Nginx.php │ ├── Packages │ │ ├── Ansicon.php │ │ ├── GithubPackage.php │ │ ├── Gsudo.php │ │ ├── Nginx.php │ │ └── WinSW.php │ ├── PhpCgi.php │ ├── PhpCgiXdebug.php │ ├── ProcessOutput.php │ ├── Server.php │ ├── Share.php │ ├── ShareTools │ │ ├── Ngrok.php │ │ └── ShareTool.php │ ├── Site.php │ ├── Upgrader.php │ ├── Valet.php │ ├── ValetException.php │ ├── WinSW.php │ └── WinSwFactory.php ├── drivers │ ├── BasicValetDriver.php │ ├── BedrockValetDriver.php │ ├── CakeValetDriver.php │ ├── Concrete5ValetDriver.php │ ├── ContaoValetDriver.php │ ├── CraftValetDriver.php │ ├── DrupalValetDriver.php │ ├── JigsawValetDriver.php │ ├── JoomlaValetDriver.php │ ├── KatanaValetDriver.php │ ├── KirbyValetDriver.php │ ├── LaravelValetDriver.php │ ├── Magento2ValetDriver.php │ ├── NeosValetDriver.php │ ├── SculpinValetDriver.php │ ├── StatamicV1ValetDriver.php │ ├── StatamicValetDriver.php │ ├── SymfonyValetDriver.php │ ├── Typo3ValetDriver.php │ ├── ValetDriver.php │ ├── WordPressValetDriver.php │ └── require.php ├── includes │ ├── compatibility.php │ ├── facades.php │ └── helpers.php ├── stubs │ ├── AcrylicHosts.txt │ ├── SampleValetDriver.php │ ├── fastcgi_params │ ├── nginx.conf │ ├── nginxservice.xml │ ├── phpcgiservice.xml │ ├── phpcgixdebugservice.xml │ ├── proxy.valet.conf │ ├── secure.proxy.valet.conf │ ├── secure.valet.conf │ ├── unsecure.valet.conf │ └── valet.conf ├── templates │ └── 404.html ├── valet.php └── version.php ├── composer.json ├── composer_laravel_valet_windows_3_logo.svg ├── emergency_uninstall_services.bat ├── laravel_valet_windows_3_logo.svg ├── phpcs.xml ├── server.php ├── valet └── valet.bat /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: "Report a bug or general issue." 3 | title: "Bug: " 4 | labels: ["bug"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | **Did you know? Most problems can be resolved by running 2 commands:** 10 | 11 | - `composer self-update` 12 | - `composer global update` 13 | 14 | In fact, it's good to run these commands at least once a month! 15 | 16 | Alternatively, try to reboot your machine first to see if it solves your current issue. 17 | - type: textarea 18 | attributes: 19 | label: Description 20 | description: Provide a detailed description of the issue you are facing. 21 | validations: 22 | required: true 23 | - type: textarea 24 | attributes: 25 | label: Steps To Reproduce 26 | description: Provide detailed steps to reproduce your issue. 27 | validations: 28 | required: true 29 | - type: textarea 30 | attributes: 31 | label: Diagnosis 32 | description: Run `valet diagnose` and then paste the output here. 33 | validations: 34 | required: true 35 | 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: "Suggest an idea or new feature." 3 | title: "Feat: " 4 | labels: ["enhancement", "feature"] 5 | body: 6 | - type: textarea 7 | attributes: 8 | label: Is your feature request related to a problem? 9 | description: Give a clear and concise description of what the problem is. 10 | validations: 11 | required: true 12 | - type: textarea 13 | attributes: 14 | label: What's the solution you would like? 15 | description: Provide a clear and detailed description of what you want to happen and possibly provide a suggested code snippet. 16 | validations: 17 | required: true 18 | - type: textarea 19 | attributes: 20 | label: Extra details 21 | description: Provide any extra details, screenshots, etc. about the feature request 22 | 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/nginx-config-upgrade.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Nginx Config Upgrade 3 | about: Report an Nginx configuration upgrade issue. 4 | title: 'Nginx config upgrade: [option name here]' 5 | labels: nginx upgrade 6 | assignees: yCodeTech 7 | 8 | --- 9 | 10 | ## Describe 11 | Please describe the configuration option that needs to be upgraded. 12 | 13 | ## The Error or Warning 14 | Please paste the error/warning message displayed in the terminal. 15 | 16 | ## Valet Diagnose 17 | Please run the `valet diagnose` command and paste the output that was automatically copied to the clipboard. This will include all information about your system, your Valet configs, and Valet service versions, including Nginx. 18 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "tabWidth": 4, 4 | "semi": true, 5 | "printWidth": 150, 6 | "bracketSameLine": true, 7 | "bracketSpacing": false 8 | } 9 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /The_same_icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /bin/acrylic/AcrylicConsole.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yCodeTech/valet-windows/ded7c58b58257ed204f204adb8ab2e7915943f39/bin/acrylic/AcrylicConsole.exe -------------------------------------------------------------------------------- /bin/acrylic/AcrylicService.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yCodeTech/valet-windows/ded7c58b58257ed204f204adb8ab2e7915943f39/bin/acrylic/AcrylicService.exe -------------------------------------------------------------------------------- /bin/acrylic/AcrylicUI.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yCodeTech/valet-windows/ded7c58b58257ed204f204adb8ab2e7915943f39/bin/acrylic/AcrylicUI.exe -------------------------------------------------------------------------------- /bin/acrylic/AcrylicUI.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /bin/acrylic/Readme.txt: -------------------------------------------------------------------------------- 1 | ======================================================================================================================== 2 | ACRYLIC DNS PROXY 3 | ======================================================================================================================== 4 | 5 | Acrylic is a local DNS proxy for Windows which improves the performance of your computer by actively caching the 6 | responses coming from your DNS servers and helps you fight unwanted ads through the use of a custom HOSTS file 7 | (optimized for handling hundreds of thousands of domain names) with support for wildcards and regular expressions. 8 | 9 | When you browse a web page a portion of the loading time is dedicated to name resolution (usually from a few 10 | milliseconds to 1 second or more) while the rest is dedicated to the transfer of the web page contents and resources to 11 | your browser. What Acrylic does is to reduce the time dedicated to name resolution for frequently visited addresses 12 | closest to zero possible. It may not seem such a great optimization but in a few weeks of Internet browsing you will 13 | probably save an hour or so, which is definitely not such a bad thing. Furthermore Acrylic's sliding expiration caching 14 | mechanism and DNS silent updates are able to improve the browsing experience independently of the browser. 15 | 16 | With Acrylic you can also gracefully overcome downtimes of your DNS servers without disrupting your work, because in 17 | that case you will at least be able to connect to your favourite websites and to your email server. 18 | 19 | Another good thing is that Acrylic is released as open source, which means that it's free and its source code, written 20 | with Borland Delphi 7, is freely available to anyone under the GNU General Public License. 21 | 22 | For more informations please use the "Acrylic Home Page" shortcut available from the "Start Menu", or go directly to: 23 | 24 | https://mayakron.altervista.org/support/acrylic/Home.htm 25 | 26 | Installed version is: 27 | 2.1.0 released on June 30, 2021. 28 | 29 | ======================================================================================================================== 30 | ACRYLIC DNS PROXY UI 31 | ======================================================================================================================== 32 | 33 | The Acrylic DNS Proxy UI desktop application understands the following command line options: 34 | 35 | InstallAcrylicService 36 | Performs all the necessary operations to install Acrylic as a Windows service on your computer. 37 | UninstallAcrylicService 38 | Performs all the necessary operations to uninstall Acrylic as a Windows service from your computer. 39 | StartAcrylicService 40 | Starts the Acrylic Windows service. 41 | StopAcrylicService 42 | Stops the Acrylic Windows service. 43 | RestartAcrylicService 44 | Restarts the Acrylic Windows service. 45 | PurgeAcrylicCacheData 46 | Purges Acrylic cache data, restarting the Acrylic Windows service if needed. 47 | ActivateAcrylicDebugLog 48 | Activates the Acrylic debug log, restarting the Acrylic Windows service if needed. 49 | DeactivateAcrylicDebugLog 50 | Deactivates the Acrylic debug log, restarting the Acrylic Windows service if needed. 51 | OpenAcrylicConfigurationFile 52 | Opens the Acrylic configuration file right after the application's startup. 53 | OpenAcrylicHostsFile 54 | Opens the Acrylic HOSTS file right after the application's startup. 55 | 56 | Note: The Acrylic DNS Proxy UI desktop application icon has been designed by http://www.aha-soft.com/ 57 | 58 | ======================================================================================================================== 59 | ACRYLIC DNS PROXY CONSOLE 60 | ======================================================================================================================== 61 | 62 | The console version (AcrylicConsole.exe) and the Windows service version of Acrylic (AcrylicService.exe) are 63 | functionally identical, as they share the same code. 64 | 65 | You may want to use the console version if you don't want to install anything on your computer (in this case I suppose 66 | you chose the portable version of Acrylic, didn't you?) or if you are experimenting with Acrylic and you don't want to 67 | restart the Windows service every time you change something in the configuration. 68 | 69 | You cannot have both versions running at the same time because they will try to listen from the same UDP or TCP ports 70 | and this can't happen. In this case you might see an error message similar to the following one written into the 71 | AcrylicDebug.txt file (in the case of the Windows service version) or written to the standard output (in the case of the 72 | console version): 73 | 74 | Binding to IPv4 address 0.0.0.0 and port 53 failed with Windows Sockets error code 10048. 75 | 76 | The console version of Acrylic currently understands the following command line options: 77 | 78 | /NoBanner 79 | Does not write the application banner to the console on startup. 80 | /NoLog 81 | Does not write the application log to the console while running. 82 | 83 | ======================================================================================================================== -------------------------------------------------------------------------------- /bin/ngrok.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yCodeTech/valet-windows/ded7c58b58257ed204f204adb8ab2e7915943f39/bin/ngrok.exe -------------------------------------------------------------------------------- /cli/Valet/Acrylic.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 26 | $this->files = $files; 27 | } 28 | 29 | /** 30 | * Install Acrylic DNS. 31 | * 32 | * @param string $tld 33 | * @return void 34 | */ 35 | public function install(string $tld = 'test') { 36 | $this->createHostsFile($tld); 37 | $this->installService(); 38 | } 39 | 40 | /** 41 | * Create the AcrylicHosts file. 42 | * 43 | * @param string $tld 44 | * @return void 45 | */ 46 | protected function createHostsFile(string $tld) { 47 | $contents = $this->files->getStub('AcrylicHosts.txt'); 48 | 49 | $this->files->put( 50 | $this->path('AcrylicHosts.txt'), 51 | str_replace(['VALET_TLD', 'VALET_HOME_PATH'], [$tld, Valet::homePath()], $contents) 52 | ); 53 | 54 | if (!$this->files->exists($configPath = Valet::homePath('AcrylicHosts.txt'))) { 55 | $this->files->putAsUser($configPath, PHP_EOL); 56 | } 57 | } 58 | 59 | /** 60 | * Install the Acrylic DNS service. 61 | * 62 | * @return void 63 | */ 64 | protected function installService() { 65 | $this->uninstall(); 66 | 67 | $this->configureNetworkDNS(); 68 | 69 | $this->cli->runOrExit( 70 | 'cmd /C "' . $this->path('AcrylicUI.exe') . '" InstallAcrylicService', 71 | function ($code, $output) { 72 | error("Failed to install Acrylic DNS: $output"); 73 | } 74 | ); 75 | 76 | $this->flushdns(); 77 | } 78 | 79 | /** 80 | * Configure the Network DNS. 81 | * 82 | * @return void 83 | */ 84 | protected function configureNetworkDNS() { 85 | $array = [ 86 | '(Get-NetIPAddress -AddressFamily IPv4).InterfaceIndex | ForEach-Object {Set-DnsClientServerAddress -InterfaceIndex $_ -ServerAddresses (\"127.0.0.1\", \"8.8.8.8\")}', 87 | '(Get-NetIPAddress -AddressFamily IPv6).InterfaceIndex | ForEach-Object {Set-DnsClientServerAddress -InterfaceIndex $_ -ServerAddresses (\"::1\", \"2001:4860:4860::8888\")}' 88 | ]; 89 | 90 | $this->cli->powershell(implode(';', $array)); 91 | } 92 | 93 | /** 94 | * Update the tld used by Acrylic DNS. 95 | * 96 | * @param string $tld 97 | * @return void 98 | */ 99 | public function updateTld(string $tld) { 100 | $this->stop(); 101 | 102 | $this->createHostsFile($tld); 103 | 104 | $this->restart(); 105 | } 106 | 107 | /** 108 | * Uninstall the Acrylic DNS service. 109 | * 110 | * @return void 111 | */ 112 | public function uninstall() { 113 | if (!$this->installed()) { 114 | return; 115 | } 116 | 117 | $this->stop(); 118 | 119 | $this->cli->run( 120 | 'cmd /C "' . $this->path('AcrylicUI.exe') . '" UninstallAcrylicService', 121 | function ($code, $output) { 122 | warning("Failed to uninstall Acrylic DNS: $output"); 123 | } 124 | ); 125 | 126 | $this->removeNetworkDNS(); 127 | 128 | $this->flushdns(); 129 | } 130 | 131 | /** 132 | * Determine if the Acrylic DNS is installed. 133 | * 134 | * @return bool 135 | */ 136 | protected function installed(): bool { 137 | return $this->cli->powershell('Get-Service -Name "AcrylicDNSProxySvc"')->isSuccessful(); 138 | } 139 | 140 | /** 141 | * Remove the Network DNS. 142 | * 143 | * @return void 144 | */ 145 | protected function removeNetworkDNS() { 146 | $array = [ 147 | '(Get-NetIPAddress -AddressFamily IPv4).InterfaceIndex | ForEach-Object {Set-DnsClientServerAddress -InterfaceIndex $_ -ResetServerAddresses}', 148 | '(Get-NetIPAddress -AddressFamily IPv6).InterfaceIndex | ForEach-Object {Set-DnsClientServerAddress -InterfaceIndex $_ -ResetServerAddresses}' 149 | ]; 150 | 151 | $this->cli->powershell(implode(';', $array)); 152 | } 153 | 154 | /** 155 | * Start the Acrylic DNS service. 156 | * 157 | * @return void 158 | */ 159 | public function start() { 160 | $this->cli->runOrExit( 161 | 'cmd /C "' . $this->path('AcrylicUI.exe') . '" StartAcrylicService', 162 | function ($code, $output) { 163 | error("Failed to start Acrylic DNS: $output"); 164 | } 165 | ); 166 | 167 | $this->flushdns(); 168 | } 169 | 170 | /** 171 | * Stop the Acrylic DNS service. 172 | * 173 | * @return void 174 | */ 175 | public function stop() { 176 | $this->cli->run( 177 | 'cmd /C "' . $this->path('AcrylicUI.exe') . '" StopAcrylicService', 178 | function ($code, $output) { 179 | warning("Failed to stop Acrylic DNS: $output"); 180 | } 181 | ); 182 | 183 | $this->flushdns(); 184 | } 185 | 186 | /** 187 | * Restart the Acrylic DNS service. 188 | * 189 | * @return void 190 | */ 191 | public function restart() { 192 | $this->cli->run( 193 | 'cmd /C "' . $this->path('AcrylicUI.exe') . '" RestartAcrylicService', 194 | function ($code, $output) { 195 | warning("Failed to restart Acrylic DNS: $output"); 196 | } 197 | ); 198 | 199 | $this->flushdns(); 200 | } 201 | 202 | /** 203 | * Flush Windows DNS. 204 | * 205 | * @return void 206 | */ 207 | public function flushdns() { 208 | $this->cli->run('cmd "/C ipconfig /flushdns"'); 209 | } 210 | 211 | /** 212 | * Get the Acrylic path. 213 | * 214 | * @param string $path 215 | * @return string 216 | */ 217 | public function path(string $path = ''): string { 218 | $basePath = str_replace("\\", '/', realpath(valetBinPath() . 'Acrylic')); 219 | 220 | return $basePath . ($path ? "/$path" : $path); 221 | } 222 | } -------------------------------------------------------------------------------- /cli/Valet/Application.php: -------------------------------------------------------------------------------- 1 | runAsTrustedInstaller() : $gsudoClass->runAsSystem(); 45 | 46 | $this->passthru($gsudo . ' ' . $valetCommand . ($quiet ? ' > nul 2>&1' : '')); 47 | } 48 | 49 | /** 50 | * Run the given command as the non-root user. 51 | * 52 | * @param string $command 53 | * @param callable|null $onError 54 | * @param boolean $realTimeOutput Set to `true` to get the output in real time as the command is running. Default: `false` 55 | * @return ProcessOutput 56 | */ 57 | public function run($command, ?callable $onError = null, $realTimeOutput = false) { 58 | return $this->runCommand($command, $onError, $realTimeOutput); 59 | } 60 | 61 | /** 62 | * Run the given command. 63 | * 64 | * @param string $command 65 | * @param callable|null $onError 66 | * @param boolean $realTimeOutput Set to `true` to get the output in real time as the command is running. Default: `false` 67 | * @return ProcessOutput 68 | */ 69 | public function runAsUser($command, ?callable $onError = null, $realTimeOutput = false) { 70 | return $this->runCommand($command, $onError, $realTimeOutput); 71 | } 72 | 73 | /** 74 | * Run the given command with PowerShell. 75 | * 76 | * @param string $command 77 | * @param callable|null $onError 78 | * @param boolean $realTimeOutput Set to `true` to get the output in real time as the command is running. Default: `false` 79 | * @return ProcessOutput 80 | */ 81 | public function powershell(string $command, ?callable $onError = null, $realTimeOutput = false) { 82 | return $this->runCommand("powershell -command \"$command\"", $onError, $realTimeOutput); 83 | } 84 | 85 | /** 86 | * Run the given command and exit if fails. 87 | * 88 | * @param string $command 89 | * @param callable|null $onError 90 | * @param boolean $realTimeOutput Set to `true` to get the output in real time as the command is running. Default: `false` 91 | * @return ProcessOutput 92 | */ 93 | public function runOrExit($command, ?callable $onError = null, $realTimeOutput = false) { 94 | /** 95 | * @param int $code Error code 96 | * @param string $output Error output 97 | */ 98 | return $this->run($command, function ($code, $output) use ($onError) { 99 | if ($onError) { 100 | $onError($code, $output); 101 | } 102 | 103 | exit(1); 104 | }, $realTimeOutput); 105 | } 106 | 107 | /** 108 | * Run the given command. 109 | * 110 | * @param string $command 111 | * @param callable|null $onError 112 | * @param boolean $realTimeOutput Set to `true` to get the output in real time as the command is running. Default: `false` 113 | * @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. 114 | */ 115 | public function runCommand($command, ?callable $onError = null, $realTimeOutput = false) { 116 | $onError = $onError ?: function () { 117 | }; 118 | 119 | // Symfony's 4.x Process component has deprecated passing a command string 120 | // to the constructor, but older versions (which Valet's Composer 121 | // constraints allow) don't have the fromShellCommandLine method. 122 | // For more information, see: https://github.com/laravel/valet/pull/761 123 | if (method_exists(Process::class, 'fromShellCommandline')) { 124 | $process = Process::fromShellCommandline($command); 125 | } 126 | else { 127 | $process = new Process($command); 128 | } 129 | 130 | /** 131 | * Output in real time 132 | */ 133 | if ($realTimeOutput) { 134 | // Use setTimeout of 0 to allow it to run seemingly forever, until user cancels it. 135 | $process->setTimeout(0)->run(function ($type, $line) { 136 | if (Process::ERR === $type) { 137 | echo 'ERROR: ' . $line; 138 | } 139 | else { 140 | echo $line; 141 | } 142 | }); 143 | } 144 | else { 145 | $processOutput = ''; 146 | 147 | $process->setTimeout(60)->run(function ($type, $line) use (&$processOutput) { 148 | $processOutput .= $line; 149 | }); 150 | } 151 | 152 | if ($process->getExitCode() !== 0) { 153 | $onError($process->getExitCode(), $processOutput); 154 | } 155 | 156 | if (!$realTimeOutput) { 157 | return new ProcessOutput($process); 158 | } 159 | } 160 | } -------------------------------------------------------------------------------- /cli/Valet/Nginx.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 45 | $this->site = $site; 46 | $this->files = $files; 47 | $this->winsw = $winsw->make('nginxservice', 'valet_nginx'); 48 | $this->configuration = $configuration; 49 | } 50 | 51 | /** 52 | * Install the configuration files for Nginx. 53 | * 54 | * @return void 55 | */ 56 | public function install() { 57 | // Install the Nginx package if it is not already installed. 58 | resolve(Packages\Nginx::class)->install(); 59 | // Install the Nginx configs, server, and service. 60 | $this->installConfiguration(); 61 | $this->installServer(); 62 | $this->installNginxDirectory(); 63 | $this->installService(); 64 | } 65 | 66 | /** 67 | * Install the Nginx configuration file. 68 | * 69 | * @return void 70 | */ 71 | public function installConfiguration() { 72 | $defaultPhpVersion = $this->configuration->get('default_php'); 73 | $defaultPhp = $this->configuration->getPhpByVersion($defaultPhpVersion); 74 | 75 | $this->files->putAsUser( 76 | $this->path('conf/nginx.conf'), 77 | str_replace( 78 | ['VALET_USER', 'VALET_HOME_PATH', '__VALET_PHP_PORT__', '__VALET_PHP_XDEBUG_PORT__'], 79 | [user(), VALET_HOME_PATH, $defaultPhp['port'], $defaultPhp['xdebug_port']], 80 | $this->files->getStub('nginx.conf') 81 | ) 82 | ); 83 | } 84 | 85 | /** 86 | * Install the Valet Nginx server configuration file. 87 | * 88 | * @return void 89 | */ 90 | public function installServer() { 91 | $defaultPhpVersion = $this->configuration->get('default_php'); 92 | $defaultPhp = $this->configuration->getPhpByVersion($defaultPhpVersion); 93 | 94 | $this->files->ensureDirExists($this->path('valet')); 95 | 96 | $this->files->putAsUser( 97 | $this->path('valet/valet.conf'), 98 | str_replace( 99 | ['VALET_HOME_PATH', 'VALET_SERVER_PATH', 'VALET_STATIC_PREFIX', 'HOME_PATH', 'VALET_PHP_PORT'], 100 | [VALET_HOME_PATH, VALET_SERVER_PATH, VALET_STATIC_PREFIX, $_SERVER['HOME'], $defaultPhp['port']], 101 | $this->files->getStub('valet.conf') 102 | ) 103 | ); 104 | 105 | $this->files->putAsUser( 106 | $this->path() . '/conf/fastcgi_params', 107 | $this->files->getStub('fastcgi_params') 108 | ); 109 | } 110 | 111 | /** 112 | * Install the Nginx configuration directory to the ~/.config/valet directory. 113 | * 114 | * This directory contains all site-specific Nginx servers. 115 | * 116 | * @return void 117 | */ 118 | public function installNginxDirectory() { 119 | if (!$this->files->isDir($nginxDirectory = Valet::homePath('Nginx'))) { 120 | $this->files->mkdirAsUser($nginxDirectory); 121 | } 122 | 123 | $this->files->putAsUser($nginxDirectory . '/.keep', "\n"); 124 | 125 | $this->rewriteSecureNginxFiles(); 126 | } 127 | 128 | /** 129 | * Check nginx.conf and all linked site configurations for errors. 130 | */ 131 | public function lint($returnOutput = false) { 132 | $output = $this->cli->run( 133 | '"' . $this->path('nginx.exe') . '" -c "' . $this->path('conf/nginx.conf') . '" -t -q -p "' . $this->path() . '" 2>&1', 134 | function ($exitCode, $outputMessage) { 135 | $outputMessage = preg_replace("/\r\n|\n|\r/", "\r\n\r\n", $outputMessage); 136 | 137 | error("Nginx cannot start; please check your nginx.conf and all linked site configurations \r\n\r\nExit code $exitCode: \r\n\r\n$outputMessage", true); 138 | } 139 | ); 140 | 141 | $outputContent = $output->getOutput(); 142 | 143 | if ($output->isSuccessful() && !empty($outputContent)) { 144 | if ($returnOutput) { 145 | return $outputContent; 146 | } 147 | else { 148 | if (str_contains($outputContent, 'warn')) { 149 | warning($outputContent); 150 | } 151 | else { 152 | output($outputContent); 153 | } 154 | } 155 | } 156 | } 157 | 158 | /** 159 | * Generate fresh Nginx servers for existing secure sites. 160 | * 161 | * @return void 162 | */ 163 | public function rewriteSecureNginxFiles() { 164 | $tld = $this->configuration->read()['tld']; 165 | 166 | $this->site->resecureForNewTld($tld, $tld); 167 | } 168 | 169 | /** 170 | * Install the Windows service. 171 | * 172 | * @return void 173 | */ 174 | public function installService() { 175 | if ($this->winsw->installed()) { 176 | $this->uninstall(); 177 | } 178 | 179 | $this->winsw->install([ 180 | 'NGINX_PATH' => $this->path() 181 | ]); 182 | 183 | $this->winsw->restart(); 184 | } 185 | 186 | /** 187 | * Restart the Nginx service. 188 | * 189 | * @return void 190 | */ 191 | public function restart() { 192 | $this->killProcess(); 193 | 194 | $this->lint(); 195 | 196 | $this->winsw->restart(); 197 | } 198 | 199 | /** 200 | * Stop the Nginx service. 201 | * 202 | * @return void 203 | */ 204 | public function stop() { 205 | $this->killProcess(); 206 | 207 | $this->winsw->stop(); 208 | } 209 | 210 | /** 211 | * Prepare Nginx for uninstallation. 212 | * 213 | * @return void 214 | */ 215 | public function uninstall() { 216 | $this->killProcess(); 217 | 218 | $this->winsw->uninstall(); 219 | } 220 | 221 | /** 222 | * Kill all the nginx processes. 223 | */ 224 | public function killProcess() { 225 | $this->cli->run('cmd "/C taskkill /IM nginx.exe /F"'); 226 | } 227 | 228 | /** 229 | * Get the Nginx path. 230 | * 231 | * @param string $path 232 | * @return string 233 | */ 234 | public function path(string $path = ''): string { 235 | return realpath(valetBinPath() . 'nginx') . ($path ? "/$path" : $path); 236 | } 237 | 238 | /** 239 | * Check if the nginx service is installed. 240 | * 241 | * For use in valet.php to check if Valet is installed 242 | * to enable most of the commands. 243 | * 244 | * @return boolean 245 | */ 246 | public function isInstalled() { 247 | return $this->winsw->installed(); 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /cli/Valet/Packages/Ansicon.php: -------------------------------------------------------------------------------- 1 | isInstalled()) { 22 | $ansiconPath = $this->packagePath(); 23 | $zipFilePath = "$ansiconPath/ansicon.zip"; 24 | 25 | $this->files->ensureDirExists($ansiconPath); 26 | 27 | $this->download('https://api.github.com/repos/adoxa/ansicon/releases/latest', 'ansi189-bin.zip', $zipFilePath); 28 | 29 | $this->files->unzip($zipFilePath, $ansiconPath); 30 | 31 | // Get the contents of the readme.txt file. 32 | $readmeContents = $this->files->get("$ansiconPath/readme.txt"); 33 | 34 | $this->moveFiles("x64"); 35 | 36 | // Clean up the package directory. 37 | $this->cleanUpPackageDirectory($zipFilePath, "x64"); 38 | 39 | // Create a readme.md file with the contents of the readme.txt file. 40 | // This is just for easier reading. 41 | $this->files->putAsUser("$ansiconPath/readme.md", $readmeContents); 42 | } 43 | 44 | // Install Ansicon into CMD's process to automatically add ANSI support. 45 | $this->cli->runOrExit( 46 | '"' . $this->packageExe() . '" -i', 47 | function ($code, $output) { 48 | warning("Failed to install ansicon.\n$output"); 49 | } 50 | ); 51 | } 52 | 53 | /** 54 | * Uninstall Ansicon. 55 | * 56 | * @return void 57 | */ 58 | public function uninstall() { 59 | $this->cli->runOrExit( 60 | '"' . $this->packageExe() . '" -pu -u', 61 | function ($code, $output) { 62 | warning("Failed to uninstall ansicon.\n$output"); 63 | } 64 | ); 65 | } 66 | } -------------------------------------------------------------------------------- /cli/Valet/Packages/GithubPackage.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 40 | $this->files = $files; 41 | $this->client = new Client([ 42 | \GuzzleHttp\RequestOptions::VERIFY => CaBundle::getSystemCaRootBundlePath() 43 | ]); 44 | } 45 | 46 | /*--------------------------------------------------------------* 47 | * Abstract methods that must be implemented by the subclasses. * 48 | *--------------------------------------------------------------*/ 49 | 50 | /** 51 | * Install the package. 52 | * 53 | * @return void 54 | */ 55 | abstract public function install(); 56 | 57 | 58 | /*------------------------------------------------* 59 | * Common methods that don't need to be abstracts * 60 | *------------------------------------------------*/ 61 | 62 | 63 | /** 64 | * Check if the package is installed. 65 | * 66 | 67 | * @return bool 68 | */ 69 | public function isInstalled() { 70 | return $this->files->exists($this->packageExe()); 71 | } 72 | 73 | 74 | /** 75 | * Download the package from GitHub. 76 | * 77 | * @param string $githubApiUrl The GitHub API URL for the release. 78 | * @param string $filename The name of the file to download. 79 | * @param string $filePath The path where the file will be saved and as what name. 80 | * 81 | * @return void 82 | */ 83 | protected function download(string $githubApiUrl, string $filename, string $filePath) { 84 | // Try to get the information from the GitHub API, otherwise 85 | // catch the exception and handle it. 86 | try { 87 | // Process response normally... 88 | $response = json_decode($this->client->get($githubApiUrl)->getBody()); 89 | } 90 | catch (ClientException $e) { 91 | // An exception was raised but there is an HTTP response body 92 | // with the exception (in case of 404 and similar errors) 93 | $response = $e->getResponse(); 94 | 95 | $errorCode = $response->getStatusCode(); 96 | 97 | $responseHeaders = $response->getHeaders(); 98 | $responseBody = json_decode($response->getBody()); 99 | $responseMsg = $responseBody->message; 100 | 101 | if (str_contains($responseMsg, 'API rate limit exceeded')) { 102 | [$ip, $rateLimit, $timeLeftToReset] = \ValetException::githubApiRateLimitExceededError($responseHeaders, $responseMsg); 103 | 104 | // Print the error messages. 105 | 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"); 106 | 107 | info("\nThe rate limit will reset in $timeLeftToReset."); 108 | 109 | error("API rate limit exceeded", true); 110 | } 111 | else { 112 | $error = "Error Code: $errorCode\n"; 113 | $error .= "Error Message: $responseMsg\n"; 114 | $error .= "The GitHub API URL queried is: $githubApiUrl\n"; 115 | 116 | error($error, true); 117 | } 118 | } 119 | 120 | // If the 'assets' property exists in the response, it means we are downloading 121 | // a release asset. 122 | if (property_exists($response, 'assets')) { 123 | // Find the asset with the specified filename and get the download URL. 124 | foreach ($response->assets as $asset) { 125 | // If the filename contains "VERSION", then we need to use regex 126 | // to match the version number in the filename. 127 | if (str_contains($filename, "VERSION")) { 128 | $filename = $this->getVersionedFilename($filename, $asset->name)?? $filename; 129 | } 130 | 131 | // If the asset name equals the filename, then get the download URL. 132 | if ($asset->name === $filename) { 133 | $downloadUrl = $asset->browser_download_url; 134 | break; 135 | } 136 | } 137 | } 138 | // If the response's 'name' property equals "README.md", then we are downloading 139 | // the readme file directly from the repo. 140 | elseif ($response->name === "README.md") { 141 | // Get the raw readme URL. 142 | $downloadUrl = $response->download_url; 143 | } 144 | else { 145 | error("The GitHub API response doesn't have the expected properties. The API URL queried is: $githubApiUrl\n", true); 146 | } 147 | 148 | if (!isset($downloadUrl)) { 149 | error("The download URL was not found in the response. The API URL queried is: $githubApiUrl\n", true); 150 | } 151 | 152 | // Download the file via Guzzle. 153 | $this->client->get($downloadUrl, [ 154 | \GuzzleHttp\RequestOptions::SINK => $filePath 155 | ]); 156 | } 157 | 158 | /** 159 | * Get the path to the package directory. 160 | * 161 | * @param string $packageName 162 | * 163 | * @return string 164 | */ 165 | public function packagePath(): string { 166 | return valetBinPath() . $this->packageName; 167 | } 168 | 169 | /** 170 | * Get the path to the package executable. 171 | * 172 | * @return string 173 | */ 174 | public function packageExe(): string { 175 | return $this->packagePath() . "/$this->packageName.exe"; 176 | } 177 | 178 | /** 179 | * Clean up the package directory that was downloaded and unzipped from GitHub. 180 | * Remove all unnecessary directories and files. 181 | * 182 | * @param mixed $zipFilePath 183 | * 184 | * @param array $unnecessaryDirsToRemove 185 | * 186 | * @return void 187 | */ 188 | protected function cleanUpPackageDirectory($zipFilePath, $requiredDir = "") { 189 | // For each unnecessary directory in the package directory... 190 | foreach ($this->getUnnecessaryDirs($zipFilePath, $requiredDir) as $dir) { 191 | // Remove all unnecessary directories and their files. 192 | $this->files->unlink($this->packagePath() . "/$dir"); 193 | } 194 | 195 | $this->removeZip($zipFilePath); 196 | } 197 | 198 | /** 199 | * Get the unnecessary directories to remove in the package directory. 200 | * 201 | * @param string $zipFilePath 202 | * @param string $requiredDir 203 | * 204 | * @return array 205 | */ 206 | protected function getUnnecessaryDirs($zipFilePath, $requiredDir) { 207 | return collect($this->files->listTopLevelZipDirs($zipFilePath))->reject(function ($dir) use ($requiredDir) { 208 | // If the directory name contains the required directory name, then we remove 209 | // it from the collection of unnecessary directories that we want to delete. 210 | return str_contains($dir, $requiredDir); 211 | })->all(); 212 | } 213 | 214 | /** 215 | * Remove the zip file after extracting its contents. 216 | * 217 | * @param string $zipFilePath 218 | */ 219 | protected function removeZip($zipFilePath) { 220 | $this->files->unlink($zipFilePath); 221 | } 222 | 223 | /** 224 | * Move files to the main package directory. 225 | * 226 | * @param string $dirName 227 | */ 228 | protected function moveFiles($dirName) { 229 | $packagePath = $this->packagePath(); 230 | 231 | // For each item in the specified directory... 232 | foreach ($this->files->scandir("$packagePath/$dirName") as $item) { 233 | // Move the item to the main package directory. 234 | // The item could be a file or a directory. 235 | $this->files->move("$packagePath/$dirName/$item", "$packagePath/$item"); 236 | } 237 | 238 | // Remove the original directory after moving its contents. 239 | $this->files->unlink("$packagePath/$dirName"); 240 | } 241 | 242 | /** 243 | * Get the versioned filename from the asset name, replacing the "VERSION" 244 | * placeholder with the version number obtained via regex. 245 | * 246 | * @param string $filename The filename with "VERSION" placeholder. 247 | * @param string $assetName The asset name from the GitHub API response. 248 | * 249 | * @return string|null The versioned filename or null if not found. 250 | */ 251 | protected function getVersionedFilename($filename, $assetName) { 252 | 253 | $filenameArray = explode('VERSION', $filename); 254 | // Escape special regex characters in the filename parts. 255 | $filenamePart1 = preg_quote($filenameArray[0], '/'); 256 | $filenamePart2 = preg_quote($filenameArray[1], '/'); 257 | 258 | // Regex to the filename parts 1 and 2 and a version number in between. 259 | // Part 1 could be "nginx\-" and part 2 could be "\.zip" 260 | // (with escaped special characters). 261 | // So the regex would look like this: 262 | // nginx\-(\d+\.\d+\.\d+)\.zip" 263 | // Example match: "nginx-1.28.0.zip" 264 | $regex = "/$filenamePart1(\d+\.\d+\.\d+)$filenamePart2/"; 265 | preg_match($regex, $assetName, $matches); 266 | 267 | if (!empty($matches)) { 268 | return $matches[0]; 269 | } 270 | } 271 | } 272 | -------------------------------------------------------------------------------- /cli/Valet/Packages/Gsudo.php: -------------------------------------------------------------------------------- 1 | isInstalled()) { 19 | $gsudoPath = $this->packagePath(); 20 | $zipFilePath = "$gsudoPath/gsudo.portable.zip"; 21 | 22 | $this->files->ensureDirExists($gsudoPath); 23 | 24 | $this->download('https://api.github.com/repos/gerardog/gsudo/releases/latest', 'gsudo.portable.zip', $zipFilePath); 25 | 26 | $this->files->unzip($zipFilePath, $gsudoPath); 27 | 28 | $this->moveFiles("x64"); 29 | 30 | // Clean up the package directory. 31 | $this->cleanUpPackageDirectory($zipFilePath, "x64"); 32 | 33 | $this->configureGsudo(); 34 | } 35 | } 36 | 37 | /** 38 | * Configure Gsudo settings. 39 | */ 40 | private function configureGsudo() { 41 | $gsudo = '"' . $this->packageExe() . '"'; 42 | $this->cli->passthru("$gsudo config CacheMode Auto"); 43 | } 44 | 45 | /** 46 | * Run as the Local System account (NT AUTHORITY\SYSTEM). 47 | * @return string 48 | */ 49 | public function runAsSystem() { 50 | return '"' . $this->packageExe() . '" --system -d '; 51 | } 52 | /** 53 | * Run as the Trusted Installer account (NT SERVICE\TrustedInstaller). 54 | * @return string 55 | */ 56 | public function runAsTrustedInstaller() { 57 | return '"' . $this->packageExe() . '" --ti -d '; 58 | } 59 | } -------------------------------------------------------------------------------- /cli/Valet/Packages/Nginx.php: -------------------------------------------------------------------------------- 1 | isInstalled()) { 19 | $nginxPath = $this->packagePath(); 20 | $zipFilePath = "$nginxPath/nginx.zip"; 21 | 22 | $this->files->ensureDirExists($nginxPath); 23 | 24 | $this->download("https://api.github.com/repos/nginx/nginx/releases/latest", "nginx-VERSION.zip", $zipFilePath); 25 | 26 | $this->files->unzip($zipFilePath, $nginxPath); 27 | 28 | $this->moveNginxFiles($zipFilePath); 29 | 30 | $this->cleanUpPackageDirectory($zipFilePath); 31 | } 32 | } 33 | 34 | /** 35 | * Move the required Nginx files into the package directory. 36 | * 37 | * @param mixed $zipFilePath 38 | */ 39 | private function moveNginxFiles($zipFilePath) { 40 | // Loop through a list of directory names from the zip file path. 41 | foreach ($this->files->listTopLevelZipDirs($zipFilePath) as $dir) { 42 | // If the directory name contains 'nginx-', set it as the required directory. 43 | if (str_contains($dir, 'nginx-')) { 44 | $requiredDir = $dir; 45 | break; 46 | } 47 | } 48 | 49 | // Ensure $requiredDir is defined before moving files. 50 | if (isset($requiredDir)) { 51 | // Move the required files from the subdirectory to the package directory. 52 | $this->moveFiles($requiredDir); 53 | } 54 | else { 55 | info("Nginx required directory not found."); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /cli/Valet/Packages/WinSW.php: -------------------------------------------------------------------------------- 1 | "https://api.github.com/repos/winsw/winsw/releases/tags/v2.12.0", 28 | "filename" => "WinSW.NET4.exe", 29 | "filepath" => "winsw.exe" 30 | ], 31 | [ 32 | "url" => "https://api.github.com/repos/winsw/winsw/readme?ref=v2.12.0", 33 | "filename" => "README.md" 34 | ] 35 | ]; 36 | 37 | /** 38 | * Install WinSW from github releases. 39 | */ 40 | public function install() { 41 | if (!$this->isInstalled()) { 42 | $winswPath = $this->packagePath(); 43 | 44 | $this->files->ensureDirExists($winswPath); 45 | 46 | foreach ($this->downloadArgs as $arg) { 47 | $this->download($arg["url"], $arg['filename'], $winswPath . '/' . ($arg['filepath'] ?? $arg['filename'])); 48 | } 49 | 50 | $this->changeReadme(); 51 | } 52 | } 53 | 54 | /** 55 | * Change the readme file to use the correct URL for the WinSW package. 56 | * This is a workaround for the fact that the readme file in the package 57 | * contains relative links to the source code, which are not valid when 58 | * the package is installed. 59 | * The function replaces the relative links with absolute links to the 60 | * source code on GitHub. 61 | * 62 | * The readme is mainly for dev purposes, to help understand how to use the package. 63 | * 64 | * @return void 65 | */ 66 | private function changeReadme() { 67 | $winswPath = $this->packagePath(); 68 | 69 | // Get the contents of the readme file. 70 | $contents = $this->files->get("$winswPath/readme.md"); 71 | 72 | /** 73 | * Convert relative links to absolute URLs. 74 | */ 75 | 76 | // Split the contents into an array at the point of `](` to get the links. 77 | // This is because the readme file contains links in the format of `[link](url)`. 78 | $contents = preg_split("/\]\(/", $contents); 79 | 80 | 81 | foreach ($contents as $key => $item) { 82 | // If the item doesn't contain 'http', AND 83 | // if the item matches the regex pattern, then it's a relative link. 84 | // The regex pattern will match strings like `doc/yamlConfigFile.md`, 85 | // `LICENSE.txt`, etc. 86 | if (!str_contains($item, 'http') && preg_match("/[[:alpha:]]*(\/*[[:alpha:]])*\..+\)/", $item)) { 87 | // Prepend the github repo URL to the relative link and set back into the array. 88 | $contents[$key] = "https://github.com/winsw/winsw/blob/v2.12.0/$item"; 89 | } 90 | } 91 | 92 | // Join the array back into a string with the `](` separator. 93 | $contents = implode("](", $contents); 94 | 95 | // Add the version number to the readme file. 96 | $contents = str_replace("Windows Service Wrapper", "Windows Service Wrapper (v2.12.0)", $contents); 97 | 98 | // Change the file with the new contents. 99 | $this->files->put("$winswPath/readme.md", $contents); 100 | } 101 | } -------------------------------------------------------------------------------- /cli/Valet/PhpCgi.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 50 | $this->files = $files; 51 | $this->winswFactory = $winswFactory; 52 | $this->configuration = $configuration; 53 | 54 | $phps = $this->configuration->get('php', []); 55 | 56 | foreach ($phps as $php) { 57 | $phpServiceName = "php{$php['version']}cgiservice"; 58 | $serviceId = "valet_php{$php['version']}cgi-{$php['port']}"; 59 | 60 | $this->phpWinSws[$php['version']] = [ 61 | 'phpServiceName' => $phpServiceName, 62 | 'phpCgiName' => $serviceId, 63 | 'php' => $php, 64 | 'winsw' => $this->winswFactory->make($phpServiceName, $serviceId) 65 | ]; 66 | } 67 | } 68 | 69 | /** 70 | * Install and configure PHP CGI service. 71 | * 72 | * @return void 73 | */ 74 | public function install($phpVersion = null) { 75 | if ($phpVersion) { 76 | if ($this->configuration->isPhpAlias($phpVersion)) { 77 | $phpVersion = $this->configuration->getPhpFullVersionByAlias($phpVersion); 78 | } 79 | 80 | if (!isset($this->phpWinSws[$phpVersion])) { 81 | error("PHP service for version {$phpVersion} not found", true); 82 | } 83 | 84 | $this->installService($phpVersion); 85 | 86 | return; 87 | } 88 | 89 | $phps = $this->configuration->get('php', []); 90 | 91 | foreach ($phps as $php) { 92 | $this->installService($php['version']); 93 | } 94 | } 95 | 96 | /** 97 | * Install the Windows service. 98 | * 99 | * @return void 100 | */ 101 | public function installService($phpVersion, $phpCgiServiceConfig = null, $installConfig = null) { 102 | $phpWinSw = $this->phpWinSws[$phpVersion]; 103 | 104 | if ($phpWinSw['winsw']->installed()) { 105 | $phpWinSw['winsw']->uninstall(); 106 | } 107 | 108 | // copy default phpcgiservice stub to create a new one 109 | $phpCgiServiceConfigArgs['PHPCGINAME'] = $phpWinSw['phpCgiName']; 110 | $phpCgiServiceConfig ??= $this->files->getStub('phpcgiservice.xml'); 111 | 112 | $this->files->put( 113 | __DIR__ . "/../stubs/{$phpWinSw['phpServiceName']}.xml", 114 | str_replace(array_keys($phpCgiServiceConfigArgs), array_values($phpCgiServiceConfigArgs), $phpCgiServiceConfig ?: '') 115 | ); 116 | 117 | $phpWinSw['winsw']->install($installConfig ?? [ 118 | 'PHP_PATH' => $phpWinSw['php']['path'], 119 | 'PHP_PORT' => $phpWinSw['php']['port'] 120 | ]); 121 | 122 | $phpWinSw['winsw']->restart(); 123 | } 124 | 125 | /** 126 | * Uninstall the PHP CGI service. 127 | * 128 | * @return void 129 | */ 130 | public function uninstall($phpVersion = null) { 131 | if ($phpVersion) { 132 | if ($this->configuration->isPhpAlias($phpVersion)) { 133 | $phpVersion = $this->configuration->getPhpFullVersionByAlias($phpVersion); 134 | } 135 | 136 | if (!isset($this->phpWinSws[$phpVersion])) { 137 | error("PHP service for version [{$phpVersion}] not found", true); 138 | } 139 | $this->uninstallService($phpVersion); 140 | 141 | return; 142 | } 143 | 144 | $phps = $this->configuration->get('php', []); 145 | 146 | foreach ($phps as $php) { 147 | $this->uninstallService($php['version']); 148 | } 149 | } 150 | 151 | /** 152 | * Install the Windows service. 153 | * 154 | * @return void 155 | */ 156 | public function uninstallService($phpVersion) { 157 | $phpWinSw = $this->phpWinSws[$phpVersion]; 158 | 159 | if ($phpWinSw['winsw']->installed()) { 160 | $phpWinSw['winsw']->uninstall(); 161 | } 162 | } 163 | 164 | /** 165 | * Restart the PHP CGI service. 166 | * 167 | * @return void 168 | */ 169 | public function restart() { 170 | foreach ($this->phpWinSws as $phpWinSw) { 171 | if ($phpWinSw['winsw']->installed()) { 172 | $phpWinSw['winsw']->restart(); 173 | } 174 | } 175 | } 176 | 177 | /** 178 | * Stop the PHP CGI service. 179 | * 180 | * @return void 181 | */ 182 | public function stop() { 183 | foreach ($this->phpWinSws as $phpWinSw) { 184 | if ($phpWinSw['winsw']->installed()) { 185 | $phpWinSw['winsw']->stop(); 186 | } 187 | } 188 | } 189 | 190 | /** 191 | * Find the PHP path. 192 | * 193 | * @return string 194 | */ 195 | public function findDefaultPhpPath(): string { 196 | if (!$php = (new PhpExecutableFinder())->find()) { 197 | $php = $this->cli->runOrExit('where php', function () { 198 | error('Failed to find PHP. Make sure it\'s added to the path environment variables.'); 199 | }); 200 | } 201 | 202 | return pathinfo(explode("\n", $php)[0], PATHINFO_DIRNAME); 203 | } 204 | 205 | /** 206 | * Find the PHP version from the path. 207 | * 208 | * @param string $phpPath The path to the PHP executable. 209 | * @param bool $getExecPath Optional. Determines whether to return the executable path. Default: `false`. 210 | * @return string|bool The PHP version or the executable path determined by the `$getExecPath` param, returns `false` on error. 211 | */ 212 | public function findPhpVersion($phpPath, $getExecPath = false) { 213 | $phpExecPath = "{$phpPath}/php.exe"; 214 | 215 | if (!file_exists($phpExecPath)) { 216 | error("Failed to find the PHP executable in {$phpPath}"); 217 | return false; 218 | } 219 | 220 | $phpVersion = $this->cli->runOrExit("\"$phpExecPath\" -r \"echo PHP_VERSION;\"", 221 | function ($code, $output) use ($phpPath) { 222 | error("Failed to get the PHP version for {$phpPath}"); 223 | } 224 | ); 225 | 226 | return $getExecPath ? $phpExecPath : $phpVersion->getOutput(); 227 | } 228 | 229 | /** 230 | * Get the PHP path by version. 231 | * @param string $phpVersion 232 | * 233 | * @return string|null The path to the PHP executable. 234 | */ 235 | public function getPhpPath($phpVersion) { 236 | $phpPath = $this->phpWinSws[$phpVersion]["php"]["path"]; 237 | $phpExecPath = $this->findPhpVersion($phpPath, true); 238 | 239 | if (!$phpExecPath) { 240 | error("PHP executable path not found for version {$phpVersion}"); 241 | return false; 242 | } 243 | 244 | return $phpExecPath; 245 | } 246 | 247 | /** 248 | * Get the CGI name 249 | * 250 | * @param string $phpVersion 251 | * @return string 252 | */ 253 | public function getPhpCgiName($phpVersion) { 254 | return $this->phpWinSws[$phpVersion]["phpCgiName"]; 255 | } 256 | } -------------------------------------------------------------------------------- /cli/Valet/PhpCgiXdebug.php: -------------------------------------------------------------------------------- 1 | phpWinSws as $phpVersion => $phpWinSw) { 15 | $phpServiceName = "php{$phpVersion}cgi_xdebugservice"; 16 | $serviceId = "valet_php{$phpVersion}cgi_xdebug-{$phpWinSw['php']['xdebug_port']}"; 17 | 18 | $this->phpWinSws[$phpVersion]['phpServiceName'] = $phpServiceName; 19 | $this->phpWinSws[$phpVersion]['phpCgiName'] = $serviceId; 20 | $this->phpWinSws[$phpVersion]['winsw'] = $this->winswFactory->make($phpServiceName, $serviceId); 21 | } 22 | } 23 | 24 | /** 25 | * Install and configure PHP CGI service. 26 | * 27 | * @param null|string $phpVersion The PHP version 28 | * @return array|void $versionArray 29 | */ 30 | public function install($phpVersion = null) { 31 | if ($phpVersion) { 32 | if ($this->configuration->isPhpAlias($phpVersion)) { 33 | $phpVersion = $this->configuration->getPhpFullVersionByAlias($phpVersion); 34 | } 35 | 36 | if (!isset($this->phpWinSws[$phpVersion])) { 37 | error("PHP xDebug service for version {$phpVersion} not found", true); 38 | } 39 | 40 | $this->installService($phpVersion); 41 | 42 | return; 43 | } 44 | 45 | $phps = $this->configuration->get('php', []); 46 | 47 | // Set an empty array to push the version numbers into 48 | // to be able to return it to the command. 49 | $versionArray = []; 50 | 51 | foreach ($phps as $php) { 52 | $this->installService($php['version']); 53 | array_push($versionArray, $php['version']); 54 | } 55 | return $versionArray; 56 | } 57 | 58 | /** 59 | * Install the Windows service. 60 | * 61 | * @return void 62 | */ 63 | public function installService($phpVersion, $phpCgiServiceConfig = null, $installConfig = null) { 64 | $phpWinSw = $this->phpWinSws[$phpVersion]; 65 | 66 | $phpCgiServiceConfig ??= $this->files->getStub('phpcgixdebugservice.xml'); 67 | 68 | $installConfig = $installConfig ?? [ 69 | 'PHP_PATH' => $phpWinSw['php']['path'], 70 | 'PHP_XDEBUG_PORT' => $phpWinSw['php']['xdebug_port'] 71 | ]; 72 | 73 | parent::installService($phpVersion, $phpCgiServiceConfig, $installConfig); 74 | } 75 | 76 | /** 77 | * Determine if the Xdebug is installed. 78 | * 79 | * @param string|null $phpVersion 80 | * @return bool 81 | */ 82 | public function installed($phpVersion = null) { 83 | if (empty($phpVersion)) { 84 | $phps = $this->configuration->get('php', []); 85 | 86 | // Loop through the PHP array until find any version 87 | // that Xdebug is installed for. 88 | // Ie. Installed returns true if ANY Xdebug is installed at the first occurrence 89 | foreach ($phps as $php) { 90 | if ($this->phpWinSws[$php["version"]]['winsw']->installed()) { 91 | return true; 92 | } 93 | } 94 | return false; 95 | } 96 | 97 | // Check if the PHP version supplied is the alias, then get the full version. 98 | if ($this->configuration->isPhpAlias($phpVersion)) { 99 | $phpVersion = $this->configuration->getPhpFullVersionByAlias($phpVersion); 100 | } 101 | 102 | return $this->phpWinSws[$phpVersion]['winsw']->installed(); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /cli/Valet/ProcessOutput.php: -------------------------------------------------------------------------------- 1 | process = $process; 21 | } 22 | 23 | /** 24 | * @return bool 25 | */ 26 | public function isSuccessful(): bool { 27 | return $this->process->isSuccessful(); 28 | } 29 | 30 | /** 31 | * @return string 32 | */ 33 | public function getOutput(): string { 34 | if ($this->process->isSuccessful()) { 35 | return $this->process->getOutput(); 36 | } 37 | 38 | return $this->process->getErrorOutput(); 39 | } 40 | 41 | /** 42 | * @return string 43 | */ 44 | public function __toString() { 45 | return $this->getOutput(); 46 | } 47 | 48 | /** 49 | * Handle dynamic calls into macros or pass missing methods to the base process. 50 | * 51 | * @param string $method 52 | * @param array $args 53 | * @return mixed 54 | */ 55 | public function __call($method, $args) { 56 | return $this->process->{$method}(...$args); 57 | } 58 | } -------------------------------------------------------------------------------- /cli/Valet/Server.php: -------------------------------------------------------------------------------- 1 | config = $config; 13 | } 14 | 15 | /** 16 | * Extract $uri from $SERVER['REQUEST_URI'] variable. 17 | * 18 | * @param string $requestUri The request URI. 19 | * 20 | * @return string The extracted URI. 21 | */ 22 | public function uriFromRequestUri($requestUri) { 23 | return rawurldecode( 24 | explode('?', $requestUri)[0] 25 | ); 26 | } 27 | 28 | /** 29 | * Extract site name from HTTP host, stripping www. and supporting wildcard DNS. 30 | * 31 | * @param string $httpHost The HTTP host. 32 | * 33 | * @return string The extracted site name. 34 | */ 35 | public function siteNameFromHttpHost($httpHost) { 36 | $siteName = basename( 37 | // Filter host to support wildcard dns feature 38 | $this->supportWildcardDnsDomains($httpHost), 39 | '.'.$this->config['tld'] 40 | ); 41 | 42 | if (strpos($siteName, 'www.') === 0) { 43 | $siteName = substr($siteName, 4); 44 | } 45 | 46 | return $siteName; 47 | } 48 | 49 | /** 50 | * You may use wildcard DNS provider nip.io as a tool for testing your site via 51 | * an IP address. 52 | * It's simple to use: First determine the IP address of your local computer 53 | * (like 192.168.0.10). 54 | * Then simply use http://project.your-ip.nip.io - ie: http://laravel.192.168.0.10.nip.io. 55 | * 56 | * @param string $domain The domain to check. 57 | * 58 | * @return string The domain 59 | */ 60 | public function supportWildcardDnsDomains($domain) { 61 | $services = [ 62 | '.*.*.*.*.nip.io', 63 | '-*-*-*-*.nip.io' 64 | ]; 65 | 66 | if (isset($this->config['tunnel_services'])) { 67 | $services = array_merge($services, (array) $this->config['tunnel_services']); 68 | } 69 | 70 | $patterns = []; 71 | foreach ($services as $service) { 72 | $pattern = preg_quote($service, '#'); 73 | $pattern = str_replace('\*', '.*', $pattern); 74 | $patterns[] = '(.*)' . $pattern; 75 | } 76 | 77 | $pattern = implode('|', $patterns); 78 | 79 | if (preg_match('#(?:' . $pattern . ')\z#u', $domain, $matches)) { 80 | $domain = array_pop($matches); 81 | } 82 | 83 | if (strpos($domain, ':') !== false) { 84 | $domain = explode(':', $domain)[0]; 85 | } 86 | 87 | return $domain; 88 | } 89 | 90 | /** 91 | * Determine the fully qualified path to the site. 92 | * Inspects registered path directories, case-sensitive. 93 | * 94 | * @param string $siteName The site name. 95 | * 96 | * @return string|null The fully qualified path to the site, or null if not found. 97 | */ 98 | public function sitePath($siteName) { 99 | $valetSitePath = null; 100 | $domain = $this->domainFromSiteName($siteName); 101 | 102 | foreach ($this->config['paths'] as $path) { 103 | if (!is_dir($path)) { 104 | continue; 105 | } 106 | 107 | $handle = opendir($path); 108 | 109 | if ($handle === false) { 110 | continue; 111 | } 112 | 113 | $dirs = []; 114 | 115 | while (($file = readdir($handle)) !== false) { 116 | if (is_dir("$path/$file") && !in_array($file, ['.', '..'])) { 117 | $dirs[] = $file; 118 | } 119 | } 120 | 121 | closedir($handle); 122 | 123 | foreach ($dirs as $dir) { 124 | // Note: strtolower used below because Nginx only tells us lowercase names 125 | if (strtolower($dir) === $siteName) { 126 | // Early return when exact match for linked subdomain 127 | return "$path/$dir"; 128 | } 129 | 130 | if (strtolower($dir) === $domain) { 131 | // No early return here because the foreach may still have some subdomains to 132 | // process with higher priority 133 | $valetSitePath = "$path/$dir"; 134 | } 135 | } 136 | 137 | if ($valetSitePath) { 138 | return $valetSitePath; 139 | } 140 | } 141 | 142 | return null; 143 | } 144 | 145 | /** 146 | * Extract the domain from the site name. 147 | * 148 | * @param string $siteName The site name. 149 | * 150 | * @return string The extracted domain. 151 | */ 152 | public function domainFromSiteName($siteName) { 153 | return array_slice(explode('.', $siteName), -1)[0]; 154 | } 155 | 156 | /** 157 | * Get the default site path for uncaught URLs, if it's set. 158 | * 159 | * @return string|null If set, default site path for uncaught urls 160 | */ 161 | public function defaultSitePath() { 162 | if (isset($this->config['default']) && is_string($this->config['default']) && is_dir($this->config['default'])) { 163 | return $this->config['default']; 164 | } 165 | 166 | return null; 167 | } 168 | 169 | /** 170 | * Set the ngrok server forwarded host if it's not already set. 171 | * (ngrok uses the X-Original-Host to store the forwarded hostname.) 172 | */ 173 | public function setNgrokServerForwardedHost() { 174 | if (isset($_SERVER['HTTP_X_ORIGINAL_HOST']) && !isset($_SERVER['HTTP_X_FORWARDED_HOST'])) { 175 | $_SERVER['HTTP_X_FORWARDED_HOST'] = $_SERVER['HTTP_X_ORIGINAL_HOST']; 176 | } 177 | } 178 | 179 | /** 180 | * Is the incoming request for a static file? 181 | * 182 | * @param string $uri The URI of the request. 183 | * @param string $valetSitePath The path to the Valet site. 184 | * @param string $siteName The name of the site. 185 | * @param object $valetDriver The Valet driver instance. 186 | * 187 | * @return string|false The path to the static file or false if not found. 188 | */ 189 | public function isRequestStaticFile($uri, $valetSitePath, $siteName, $valetDriver) { 190 | 191 | $isPhpFile = pathinfo($uri, PATHINFO_EXTENSION) === 'php'; 192 | 193 | if ($uri !== '/' && !$isPhpFile && $staticFilePath = $valetDriver->isStaticFile($valetSitePath, $siteName, $uri)) { 194 | return $staticFilePath; 195 | } 196 | return false; 197 | } 198 | 199 | /** 200 | * Show the Valet 404 "Not Found" page. 201 | */ 202 | public function show404() { 203 | http_response_code(404); 204 | require __DIR__ . '/../templates/404.html'; 205 | exit; 206 | } 207 | 208 | /** 209 | * Show directory listing or 404 if directory doesn't exist. 210 | * 211 | * @param string $valetSitePath The path to the Valet site. 212 | * @param string $uri The URI of the request. 213 | */ 214 | public function showDirectoryListing($valetSitePath, $uri) { 215 | $is_root = ($uri == '/'); 216 | $directory = $is_root ? $valetSitePath : $valetSitePath . $uri; 217 | 218 | if (!file_exists($directory)) { 219 | $this->show404(); 220 | } 221 | 222 | // Sort directories at the top 223 | $paths = glob("$directory/*"); 224 | usort($paths, function ($a, $b) { 225 | return (is_dir($a) == is_dir($b)) ? strnatcasecmp($a, $b) : (is_dir($a) ? -1 : 1); 226 | }); 227 | 228 | // If the directory is a file, output its contents and exit. 229 | if (is_file($directory)) { 230 | echo "
";
231 | 			echo file_get_contents($directory);
232 | 			echo "
"; 233 | 234 | exit; 235 | } 236 | 237 | // Output the HTML for the directory listing 238 | echo "

Index of $uri

"; 239 | echo '
'; 240 | echo implode("
\n", array_map(function ($path) use ($uri, $is_root) { 241 | $file = basename($path); 242 | 243 | return ($is_root) ? "/$file" : "$uri/$file/"; 244 | }, $paths)); 245 | 246 | exit; 247 | } 248 | 249 | /** 250 | * Show directory listing if it's enabled or 404. 251 | * 252 | * @param string $valetSitePath The path to the Valet site. 253 | * @param string $uri The URI of the request. 254 | */ 255 | public function showDirectoryListingOr404($valetSitePath, $uri) { 256 | if (isset($this->config['directory-listing']) && $this->config['directory-listing'] == 'on') { 257 | $this->showDirectoryListing($valetSitePath, $uri); 258 | } 259 | 260 | $this->show404(); 261 | } 262 | 263 | /** 264 | * Change into front controller path when executing 265 | * 266 | * @param string $frontControllerPath The path to the front controller. 267 | */ 268 | public function changeDir($frontControllerPath) { 269 | chdir(dirname($frontControllerPath)); 270 | } 271 | } -------------------------------------------------------------------------------- /cli/Valet/Share.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 45 | $this->config = $config; 46 | $this->files = $files; 47 | 48 | /** 49 | * Define the supported share tools. 50 | */ 51 | $this->share_tools = [ 52 | "ngrok" 53 | ]; 54 | 55 | // Create the share tool instance. 56 | $this->createShareToolInstance(); 57 | } 58 | 59 | /** 60 | * Main method to use which returns the share tool's class instance 61 | * to be able to access the methods in a chainable way. 62 | * 63 | * @return object 64 | */ 65 | public function shareTool() { 66 | return $this->getShareToolInstance(); 67 | } 68 | 69 | /** 70 | * Create the share tool class namespace using the current tool 71 | * and create the child class instance. 72 | * 73 | * @return void 74 | */ 75 | private function createShareToolInstance() { 76 | $tool = $this->getCurrentShareTool(); 77 | if ($tool && !isset($this->current_tool_instance)) { 78 | // Construct the share tool's class namespace. 79 | $tool_class = "Valet\\ShareTools\\$tool"; 80 | 81 | // Create the share tool's child class instance via a dynamic class namespace. 82 | $this->current_tool_instance = new $tool_class($this->cli, $this->files); 83 | } 84 | } 85 | 86 | /** 87 | * Get the share tool child class instance. 88 | * @return object 89 | */ 90 | private function getShareToolInstance() { 91 | return $this->current_tool_instance; 92 | } 93 | 94 | /** 95 | * Get the share tools as a string. 96 | * 97 | * @return string 98 | */ 99 | public function getShareTools() { 100 | return preg_replace('/,\s([^,]+)$/', 101 | ' or $1', 102 | implode(', ', array_map(fn ($t) => "`$t`", $this->share_tools)) 103 | ); 104 | } 105 | 106 | /** 107 | * Check if the specified tool is valid. 108 | * 109 | * @param string $tool 110 | * @return bool 111 | */ 112 | public function isToolValid($tool) { 113 | return (in_array($tool, $this->share_tools) && class_exists($tool)); 114 | } 115 | 116 | /** 117 | * Get the current share tool from the config. 118 | * 119 | * @return string|null 120 | */ 121 | public function getCurrentShareTool() { 122 | return $this->config->get("share-tool") ?? null; 123 | } 124 | } -------------------------------------------------------------------------------- /cli/Valet/ShareTools/Ngrok.php: -------------------------------------------------------------------------------- 1 | hasAuthToken()) { 27 | output('Forwarding to local port 443 or a local https:// URL is only available after you sign up. 28 | Sign up at: https://ngrok.com/signup 29 | Then use: valet set-ngrok-token [token]'); 30 | exit(1); 31 | } 32 | 33 | // If host-header is not specified, 34 | // then set it into the array with a default value of rewrite. 35 | if (!stripos(json_encode($options), 'host-header')) { 36 | array_push($options, "host-header=rewrite"); 37 | } 38 | 39 | $options = prefixOptions($options); 40 | 41 | $ngrok = realpath(valetBinPath() . 'ngrok.exe'); 42 | 43 | $ngrokCommand = "\"$ngrok\" http $site:$port " . $this->getConfig() . " $options"; 44 | 45 | info("Sharing $site...\n"); 46 | info("To output the public URL, please open a new terminal and run `valet fetch-share-url $site`"); 47 | 48 | $output = $this->cli->shellExec("$ngrokCommand 2>&1"); 49 | 50 | if ($errors = strstr($output, "ERROR")) { 51 | error($errors . PHP_EOL); 52 | 53 | if (strpos($errors, 'ERR_NGROK_121') !== false) { 54 | info("To update ngrok yourself, please run `valet ngrok update` and then upgrade the config file by running `valet ngrok config upgrade`\n"); 55 | } 56 | } 57 | } 58 | 59 | /** 60 | * Run ngrok CLI commands. 61 | * 62 | * @param string $command 63 | * 64 | * @return void 65 | */ 66 | public function run(string $command) { 67 | $ngrok = realpath(valetBinPath() . 'ngrok.exe'); 68 | 69 | // If command is `update` then append the config flag. 70 | if (trim($command) === "update") { 71 | $command = "$command " . $this->getConfig(); 72 | } 73 | 74 | // If command is `config upgrade` then append the config flag. 75 | if (trim($command) === "config upgrade") { 76 | $command = "$command " . $this->getConfig(); 77 | } 78 | 79 | $this->cli->passthru("\"$ngrok\" $command"); 80 | } 81 | 82 | /** 83 | * Get the ngrok configuration path 84 | * 85 | * @param bool $asCliFlag Determines whether to return the config path as a CLI --flag. 86 | * Default `true` 87 | * 88 | * @return string Returns the ngrok config path as a CLI flag or just the path. 89 | * 90 | * `--config C:/Users/Username/.config/valet/Ngrok/ngrok.yml` 91 | * 92 | * OR 93 | * 94 | * `C:/Users/Username/.config/valet/Ngrok/ngrok.yml` 95 | */ 96 | public function getConfig(bool $asCliFlag = true) { 97 | $configPath = Valet::homePath() . "/Ngrok/ngrok.yml"; 98 | if ($asCliFlag) { 99 | return "--config $configPath"; 100 | } 101 | return $configPath; 102 | } 103 | 104 | /** 105 | * Check if ngrok config exists and the authtoken is set. 106 | * 107 | * @return bool 108 | */ 109 | protected function hasAuthToken(): bool { 110 | // If the config file exists... 111 | if (file_exists($this->getConfig(false))) { 112 | // Read and parse the config yml file and convert to an associative array. 113 | $config = Yaml::parseFile($this->getConfig(false)); 114 | 115 | // If config version is 2... 116 | if ($config["version"] === "2") { 117 | // Check the "authtoken" key exists in the array AND the value is NOT empty. 118 | // Then return the bool value. 119 | return (array_key_exists("authtoken", $config) && !empty($config["authtoken"])); 120 | } 121 | // If config version is 3... 122 | elseif ($config["version"] === "3") { 123 | // Check the "agent" key exists in the array AND the "authtoken" key exists in 124 | // the "agent" array AND the value is NOT empty. 125 | // Then return the bool value. 126 | return ((array_key_exists("agent", $config) && array_key_exists("authtoken", $config["agent"])) && !empty($config["agent"]["authtoken"])); 127 | } 128 | } 129 | return false; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /cli/Valet/ShareTools/ShareTool.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 41 | $this->files = $files; 42 | } 43 | 44 | /** 45 | * Start sharing. 46 | * 47 | * @param string $site The site 48 | * @param int $port The site's port 49 | * @param array $options Options/flags to pass to ngrok 50 | * 51 | * @return void 52 | */ 53 | abstract public function start(string $site, int $port, array $options = []); 54 | 55 | /** 56 | * Run CLI commands 57 | * 58 | * @param string $command 59 | * 60 | * @return void 61 | */ 62 | abstract public function run(string $command); 63 | 64 | /** 65 | * Get the config path. 66 | * 67 | * @return string The path. 68 | */ 69 | abstract public function getConfig(); 70 | 71 | 72 | /*--------------------------------------------------------* 73 | * Fully declared methods that don't need to be abstracts * 74 | *--------------------------------------------------------*/ 75 | 76 | 77 | /** 78 | * Get the current tunnel URL from the API. 79 | * @param string $site The site 80 | * 81 | * @return string The current tunnel URL 82 | */ 83 | public function currentTunnelUrl(string $site) { 84 | info("Trying to retrieve tunnel URL..."); 85 | 86 | foreach ($this->tunnelsEndpoints as $endpoint) { 87 | try { 88 | $response = retry(5, function () use ($endpoint, $site) { 89 | // Create a GuzzleHttp get request to the ngrok tunnels API and 90 | // get the body of the API response and decode the json. 91 | $body = json_decode((new Client())->get($endpoint)->getBody()); 92 | 93 | // If tunnels is set in the body AND has more than 0 tunnels... 94 | if (isset($body->tunnels) && count($body->tunnels) > 0) { 95 | // If the tunnel URL is NOT null, return the URL. 96 | if ($tunnelUrl = $this->findHttpTunnelUrl($body->tunnels, $site)) { 97 | // Use | clip to copy the URL to the clipboard. 98 | $this->cli->passthru("echo $tunnelUrl | clip"); 99 | 100 | return $tunnelUrl; 101 | } 102 | } 103 | 104 | throw new DomainException('Failed to retrieve tunnel URL.'); 105 | }, 250); 106 | 107 | // If response is NOT empty, return the response. 108 | if (! empty($response)) { 109 | return $response; 110 | } 111 | } 112 | catch (Exception $e) { 113 | // Do nothing, suppress the exception to check the other port 114 | } 115 | } 116 | 117 | throw new DomainException('Tunnel not established.'); 118 | } 119 | 120 | /** 121 | * Find the HTTP tunnel URL from the list of tunnels. 122 | * 123 | * @param array $tunnels 124 | * 125 | * @return string|null 126 | */ 127 | public function findHttpTunnelUrl(array $tunnels, ?string $site = null) { 128 | // If there are active tunnels on the ngrok instance we will spin through them and 129 | // find the one responding on HTTP. Each tunnel has an HTTP and a HTTPS address 130 | // but for local dev purposes we just desire the plain HTTP URL endpoint. 131 | foreach ($tunnels as $tunnel) { 132 | if (($tunnel->proto === 'http' || $tunnel->proto === 'https') && strpos($tunnel->config->addr, $site)) { 133 | return $tunnel->public_url; 134 | } 135 | } 136 | return null; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /cli/Valet/Upgrader.php: -------------------------------------------------------------------------------- 1 | files = $files; 23 | $this->config = $config; 24 | $this->site = $site; 25 | } 26 | 27 | /** 28 | * Run all the upgrades that should be run every time Valet commands are run. 29 | */ 30 | public function onEveryRun() { 31 | $this->prunePathsFromConfig(); 32 | $this->pruneSymbolicLinks(); 33 | $this->upgradeSymbolicLinks(); 34 | $this->lintNginxConfigs(); 35 | $this->upgradeNginxSiteConfigs(); 36 | } 37 | 38 | /** 39 | * Prune all non-existent paths from the configuration. 40 | */ 41 | private function prunePathsFromConfig() { 42 | try { 43 | $this->config->prune(); 44 | } 45 | catch (\JsonException $e) { 46 | warning('Invalid configuration file at ' . $this->config->path() . '.'); 47 | exit; 48 | } 49 | } 50 | 51 | /** 52 | * Prune all symbolic links that no longer point to a valid site. 53 | */ 54 | private function pruneSymbolicLinks() { 55 | $this->site->pruneLinks(); 56 | } 57 | 58 | /** 59 | * Upgrade and convert all Windows junction links to real symbolic links. 60 | * 61 | * This is a one-time upgrade that will be run when Valet is first installed. 62 | */ 63 | private function upgradeSymbolicLinks() { 64 | // Check if the symlinks have already been upgraded, by checking if a key exists in 65 | // the config. If not, then upgrade them. 66 | if ($this->config->get("symlinks_upgraded", false) === false) { 67 | info("Upgrading your linked sites from the old junction links to symbolic links..."); 68 | 69 | $this->files->convertJunctionsToSymlinks($this->site->sitesPath()); 70 | // Add a new key to the config file to indicate that the symlinks have been upgraded. 71 | // This will prevent the upgrade from running again, since it is a one-time upgrade. 72 | $this->config->updateKey("symlinks_upgraded", true); 73 | 74 | info("Successfully upgraded junction links to symbolic links."); 75 | } 76 | } 77 | 78 | /** 79 | * Lint the Nginx configuration files. 80 | * 81 | * @param boolean $returnOutput 82 | * 83 | * @return string|null 84 | */ 85 | private function lintNginxConfigs($returnOutput = false) { 86 | if (resolve(Packages\Nginx::class)->isInstalled()) { 87 | return \Nginx::lint($returnOutput); 88 | } 89 | } 90 | 91 | /** 92 | * Upgrade Nginx site configurations. 93 | * 94 | * This method checks the Nginx configuration files for deprecated `http2` param 95 | * and `http2_push_preload` directive, then upgrades the site configurations accordingly. 96 | */ 97 | private function upgradeNginxSiteConfigs() { 98 | $output = $this->lintNginxConfigs(true); 99 | 100 | // If output is not empty... 101 | if (!empty($output)) { 102 | $stringsToCheck = ['the "listen ... http2" directive is deprecated', '"http2_push_preload" directive is obsolete']; 103 | 104 | $outputArray = explode("\r\n", $output); 105 | 106 | // For each line in the output... 107 | foreach ($outputArray as $line) { 108 | // If the line contains any of the strings in the array... 109 | if (str_contains_any($line, $stringsToCheck)) { 110 | // Check if the line contains "in [path]:[line number]" 111 | // eg. "in C:/path/to/file.conf:123"; and if it does add the 112 | // matched string to a variable. 113 | if (preg_match('/in (.*):\d+$/', $line, $matches)) { 114 | // Get the site name from file path in the matched string, 115 | // ie. gets the filename (site.conf) and removes the extension. 116 | $site = basename($matches[1], ".conf"); 117 | 118 | // If the site is isolated... 119 | if ($this->site->isIsolated($site)) { 120 | // Get the PHP version for the site. 121 | $phpVersion = $this->site->customPhpVersion($site); 122 | 123 | // Unisolate the site and re-isolate it using the phpVersion to 124 | // upgrade the Nginx config file. 125 | $this->site->unisolate($site); 126 | $this->site->isolate($phpVersion, $site); 127 | } 128 | // If the site is secured... 129 | elseif ($this->site->isSecured($site)) { 130 | // Unsecure the site and re-secure it to upgrade 131 | // the Nginx config file. 132 | $this->site->unsecure($site); 133 | $this->site->secure($site); 134 | } 135 | } 136 | } 137 | } 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /cli/Valet/Valet.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 21 | $this->files = $files; 22 | } 23 | 24 | /** 25 | * Get the paths to all of the Valet extensions. 26 | * 27 | * @return array 28 | */ 29 | public function extensions(): array { 30 | $path = static::homePath('Extensions'); 31 | 32 | if (!$this->files->isDir($path)) { 33 | return []; 34 | } 35 | 36 | return collect($this->files->scandir($path))->reject(function ($file) { 37 | return is_dir($file); 38 | })->map(function ($file) use ($path) { 39 | return "$path/$file"; 40 | })->values()->all(); 41 | } 42 | 43 | /** 44 | * Get the installed Valet services. 45 | * 46 | * @param bool $disable Don't show the progressbar. 47 | * Used in `install` command to query if the services are running. 48 | * 49 | * @return array 50 | */ 51 | public function services($disable = false): array { 52 | $phps = \Configuration::get('php', []); 53 | 54 | $phpCGIs = collect([]); 55 | $phpXdebugCGIs = collect([]); 56 | foreach ($phps as $php) { 57 | $phpCGIs->put("php {$php['version']}", \PhpCgi::getPhpCgiName($php['version'])); 58 | $phpXdebugCGIs->put("php-xdebug {$php['version']}", \PhpCgiXdebug::getPhpCgiName($php['version'])); 59 | } 60 | 61 | $services = collect([ 62 | 'acrylic' => 'AcrylicDNSProxySvc', 63 | 'nginx' => 'valet_nginx' 64 | ])->merge($phpCGIs)->merge($phpXdebugCGIs); 65 | 66 | // Set empty variable first, prevents errors when $disable is true. 67 | $progressBar = ""; 68 | 69 | if (!$disable) { 70 | $progressBar = progressbar($services->count(), "Checking"); 71 | } 72 | 73 | return $services->map(function ($id, $service) use ($progressBar, $disable) { 74 | $output = $this->cli->run('powershell -command "Get-Service -Name ' . $id . '"'); 75 | 76 | if (!$disable) { 77 | $progressBar->setMessage(ucfirst($service), "placeholder"); 78 | $progressBar->advance(); 79 | } 80 | 81 | if (strpos($output, 'Running') > -1) { 82 | $status = 'running'; 83 | } 84 | elseif (strpos($output, 'Stopped') > -1) { 85 | $status = 'stopped'; 86 | } 87 | else { 88 | $status = 'missing'; 89 | } 90 | 91 | if (strpos($status, "missing") && strpos($service, "xdebug")) { 92 | $status = 'not installed'; 93 | } 94 | 95 | return [ 96 | 'service' => $service, 97 | 'winname' => $id, 98 | 'status' => $status 99 | ]; 100 | })->values()->all(); 101 | } 102 | 103 | /** 104 | * Determine if this is the latest version of Valet. 105 | * 106 | * @param string $currentVersion 107 | * @return bool 108 | * 109 | * @throws \GuzzleHttp\Exception\GuzzleException 110 | */ 111 | public function onLatestVersion($currentVersion): bool { 112 | /** 113 | * Set a new GuzzleHttp client and use the Composer\CaBundle package 114 | * to find and use the TLS CA bundle in order to verify the TLS/SSL 115 | * certificate of the requesting website/API. 116 | * Otherwise, Guzzle errors out with a curl error. 117 | * 118 | * Code from StackOverflow answer: https://stackoverflow.com/a/53823135/2358222 119 | */ 120 | $client = new Client([ 121 | \GuzzleHttp\RequestOptions::VERIFY => CaBundle::getSystemCaRootBundlePath() 122 | ]); 123 | 124 | // Create a GuzzleHttp get request to the github API. 125 | $get = $client->request( 126 | "GET", 127 | 'https://api.github.com/repos/ycodetech/valet-windows/releases/latest' 128 | ); 129 | $response = json_decode($get->getBody()->getContents()); 130 | 131 | return version_compare($currentVersion, trim($response->tag_name, 'v'), '>='); 132 | } 133 | 134 | /** 135 | * Get a calculation of the percentage of parity completion against Laravel Valet for macOS 136 | * @param string $url The URL to the raw code in Github of `app.php` of Laravel Valet on a released version. 137 | * eg. https://raw.githubusercontent.com/laravel/valet/v4.3.0/cli/app.php 138 | * @return void 139 | */ 140 | public function parity($url) { 141 | 142 | // Get the contents of the URL. 143 | $contents = $this->files->get($url); 144 | 145 | // Split the string by the strings "$app->" and "function" and collect them. 146 | $collection = collect(preg_split('/(\$app->|, function)+/', $contents)); 147 | 148 | // Filter the collection to get only the commands in the form of command(command-name. 149 | $macOScommands = $collection->filter(function ($item, $key) { 150 | if (str_contains($item, "command(")) { 151 | return $item; 152 | } 153 | // Then rearrange the values into a new collection with their indexes reset. 154 | // Then split the items by the strings "'" and whitespace to get the command name only, 155 | // and remap them to a new collection and output as an array. 156 | })->values()->map(function ($item, $key) { 157 | $item = preg_split("/('|\s)/", $item); 158 | return $item[1]; 159 | })->toArray(); 160 | 161 | // Define commands in the macOS version that will not be available to make parity. 162 | $commandsNotApplicableForParity = [ 163 | "composer", 164 | "loopback", 165 | "trust" 166 | ]; 167 | 168 | // Define Valet 3.0 commands that are new only to this Windows version. 169 | $newValetCommands = [ 170 | "ngrok", 171 | "parity", 172 | "php:add", 173 | "php:install", 174 | "php:remove", 175 | "php:uninstall", 176 | "php:list", 177 | "sudo", 178 | "sites", 179 | "xdebug:install", 180 | "xdebug:uninstall" 181 | ]; 182 | 183 | // Run symfony's `valet list --raw` command and collect the un-styled raw output 184 | // of all commands. 185 | $valetCommands = collect(explode("\n", $this->cli->run("valet list --raw")->__toString())); 186 | 187 | /** 188 | * Find the commands that have been made parity. 189 | */ 190 | 191 | // Remap and split at the point of multiple whitespace and return the first item. 192 | // This will strip out the descriptions of the commands, therefore only returning the 193 | // command name. 194 | $valetCommands = $valetCommands->map(function ($item, $key) { 195 | $item = preg_split("/\s\s\s/", $item); 196 | return $item[0]; 197 | // Filter the collection and discard any commands that symfony adds automatically, 198 | // and also discard any new Valet 3.0 only commands. 199 | })->filter(function ($item, $key) use ($newValetCommands) { 200 | 201 | // Define symfony commands to discard. 202 | $discardElements = [ 203 | "completion", 204 | "help", 205 | "list" 206 | ]; 207 | 208 | if (!in_array($item, $discardElements) && !in_array($item, $newValetCommands)) { 209 | return $item; 210 | } 211 | })->values()->all(); 212 | 213 | // Total mac commands. 214 | $total_Commands = count($macOScommands); 215 | // Total commands that can be made parity. 216 | $total_CommandsForParity = $total_Commands - count($commandsNotApplicableForParity); 217 | // Total commands parity complete. 218 | $total_CommandsCompleted = count($valetCommands); 219 | // Total Valet 3.0 only commands. 220 | $total_NewValetCommands = count($newValetCommands); 221 | 222 | // Calculate the parity percentage. 223 | $parityPercentage = round($total_CommandsCompleted / $total_Commands * 100); 224 | // Calculate the total percentage it is possible to make parity. 225 | $total_PossibleParityPercentage = round($total_CommandsForParity / $total_Commands * 100); 226 | 227 | info("Out of a total $total_Commands commands, $total_CommandsForParity are possible for parity, with $total_CommandsCompleted complete, and $total_NewValetCommands brand new commands."); 228 | 229 | // Find the version from the URL. 230 | preg_match("/v[0-9]\.[0-9]\.[0-9]+/", $url, $macVersion); 231 | $macVersion = implode("", $macVersion); 232 | 233 | info("Parity at $parityPercentage% out of a total $total_PossibleParityPercentage% possible parity with the Laravel Valet (macOS) $macVersion"); 234 | } 235 | 236 | /** 237 | * Run composer global diagnose. 238 | */ 239 | public function composerGlobalDiagnose() { 240 | $this->cli->runAsUser('composer global diagnose'); 241 | } 242 | 243 | /** 244 | * Run composer global update. 245 | */ 246 | public function composerGlobalUpdate() { 247 | $this->cli->runAsUser('composer global update'); 248 | } 249 | 250 | /** 251 | * Get the path to the home directory of composer global. 252 | * 253 | * While the default is "~/AppData/Roaming/Composer", 254 | * composer does allow the user to change where global packages are installed. 255 | * So we need to essentially ask composer where the home directory is. 256 | * 257 | * This is used by the `Diagnose` class. 258 | * 259 | * @return string The path to the global composer directory. 260 | */ 261 | public function getComposerGlobalPath() { 262 | return $this->cli->runAsUser('composer -n config --global home'); 263 | } 264 | 265 | /** 266 | * Get the Valet home path (VALET_HOME_PATH = ~/.config/valet). 267 | * 268 | * @param string $path 269 | * @return string 270 | */ 271 | public static function homePath(string $path = ''): string { 272 | return VALET_HOME_PATH . ($path ? "/$path" : $path); 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /cli/Valet/ValetException.php: -------------------------------------------------------------------------------- 1 | getMessage(); 15 | $errorTypeName = $this->getErrorTypeName($this->getCode()); 16 | $constructTrace = $this->constructTrace(); 17 | 18 | return "$errorTypeName: $errorMsg\n\n$constructTrace"; 19 | } 20 | 21 | /** 22 | * Get the error type name. 23 | * Eg.: Inputs error code `0`, outputs error name `"FATAL"` 24 | * 25 | * @param mixed $code The numeric error type/code 26 | * @return string The error type name 27 | */ 28 | private function getErrorTypeName($code) { 29 | return $code == 0 ? "FATAL" : array_search($code, get_defined_constants(true)['Core']); 30 | } 31 | 32 | /** 33 | * Construct a better-formatted error trace. 34 | * 35 | * @return string 36 | */ 37 | private function constructTrace() { 38 | $constructTrace = []; 39 | $count = 0; 40 | foreach ($this->getTrace() as $key => $value) { 41 | $count_num = $count++ . ") "; 42 | $class = $value["class"] ?? ""; 43 | $type = $value["type"] ?? ""; 44 | $func = $value["function"] ?? ""; 45 | 46 | $file_n_line = isset($value["file"]) ? 47 | " ------ " . $value["file"] . ":" . $value["line"] : ""; 48 | 49 | $constructTrace[] = $count_num . $class . $type . $func . $file_n_line; 50 | } 51 | return implode("\n", $constructTrace); 52 | } 53 | 54 | /** 55 | * If the GitHub API rate limit has exceeded, we need to handle it. 56 | * 57 | * The API rate limit is 60 requests per hour for unauthenticated requests. 58 | * 59 | * @param array $headers The headers from the response. 60 | * @param string $responseMsg The error message from the response. 61 | * 62 | * @return array `[$ip, $rateLimit, $timeLeftToReset]` An array containing the IP address, rate limit, and time left to reset. 63 | */ 64 | public function githubApiRateLimitExceededError($headers, $responseMsg) { 65 | // Get the rate limit. 66 | $rateLimit = $headers["X-RateLimit-Limit"][0]; 67 | // Get the reset time (UTC epoch seconds). 68 | $resetTime = $headers["X-RateLimit-Reset"][0]; 69 | 70 | $timeLeftToReset = $this->calculateTimeToGithubApiLimitReset($resetTime); 71 | 72 | // Get the IP address from the original error response message using regex. 73 | // The regex pattern matches an IPv4 address. 74 | // The pattern looks for 1 to 3 digits followed by a dot, repeated 3 times, 75 | // and then 1 to 3 digits. 76 | preg_match('/\b(?:\d{1,3}\.){3}\d{1,3}\b/', $responseMsg, $matches); 77 | 78 | // Assign the IP address to a variable if the regex matched, otherwise set it to "unknown". 79 | $ip = $matches[0] ?: "unknown"; 80 | 81 | return [$ip, $rateLimit, $timeLeftToReset]; 82 | } 83 | 84 | /** 85 | * Calculate the time left to reset the GitHub API rate limit. 86 | * 87 | * @param string $resetTime The reset time in UTC epoch seconds. 88 | * 89 | * @return string The time left to reset the rate limit in a human-readable format. 90 | */ 91 | private function calculateTimeToGithubApiLimitReset($resetTime) { 92 | // Create new DateTime objects for the reset time and the current time. 93 | $reset_time = new \DateTime("@$resetTime"); 94 | $current_time = new \DateTime("now"); 95 | 96 | // Get the difference between the 2 times. 97 | $timeDifference = $reset_time->diff($current_time); 98 | 99 | // Get the difference in minutes and seconds. 100 | // The DateInterval object has many properties, including minutes and seconds, 101 | // which we can directly access. 102 | $mins = $timeDifference->i; 103 | $secs = $timeDifference->s; 104 | 105 | // Format the minutes and seconds into a human-readable string. 106 | // If the minutes or seconds equals 1, we need to use the singular form 107 | // of "minute" or "second". 108 | $minsTxt = $mins === 1 ? "$mins minute" : "$mins minutes"; 109 | $secsTxt = $secs === 1 ? "$secs second" : "$secs seconds"; 110 | 111 | return "$minsTxt and $secsTxt"; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /cli/Valet/WinSW.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 35 | $this->files = $files; 36 | $this->service = $service; 37 | $this->serviceId = $serviceId; 38 | } 39 | 40 | /** 41 | * Install the service. 42 | * 43 | * @param array $args 44 | * @return void 45 | */ 46 | public function install(array $args = []) { 47 | $this->createConfiguration($args); 48 | 49 | $command = 'cmd /C cd "' . $this->servicesPath() . '" && "' . $this->servicesPath($this->service) . '" install'; 50 | 51 | $this->cli->runOrExit($command, function ($code, $output) { 52 | error("Failed to install service [$this->service]. Check ~/.config/valet/Log for errors.\n$output"); 53 | }); 54 | } 55 | 56 | /** 57 | * Create the .exe and .xml files. 58 | * 59 | * @param array $args 60 | * @return void 61 | */ 62 | protected function createConfiguration(array $args = []) { 63 | $args['VALET_HOME_PATH'] = Valet::homePath(); 64 | 65 | $this->files->copy( 66 | realpath(valetBinPath() . 'winsw/winsw.exe'), 67 | $this->binaryPath() 68 | ); 69 | 70 | $config = $this->files->getStub("$this->service.xml"); 71 | 72 | $this->files->put( 73 | $this->configPath(), 74 | str_replace(array_keys($args), array_values($args), $config ?: '') 75 | ); 76 | } 77 | 78 | /** 79 | * Uninstall the service. 80 | * 81 | * @return void 82 | */ 83 | public function uninstall() { 84 | if ($this->isRunning()) { 85 | $this->stop(); 86 | } 87 | 88 | $this->cli->run('cmd /C cd "' . $this->servicesPath() . '" && "' . $this->servicesPath($this->service) . '" uninstall'); 89 | 90 | sleep(1); 91 | 92 | $this->files->unlink($this->binaryPath()); 93 | $this->files->unlink($this->configPath()); 94 | } 95 | 96 | /** 97 | * Determine if the service is installed. 98 | * 99 | * @return bool 100 | */ 101 | public function installed(): bool { 102 | return $this->cli->powershell("Get-Service -Name \"$this->serviceId\"")->isSuccessful(); 103 | } 104 | 105 | /** 106 | * Restart the service. 107 | * 108 | * @return void 109 | */ 110 | public function restart() { 111 | $command = 'cmd /C cd "' . $this->servicesPath() . '" && "' . $this->servicesPath($this->service) . '" restart'; 112 | 113 | $this->cli->run($command, function () use ($command) { 114 | sleep(2); 115 | 116 | $this->cli->runOrExit($command, function ($code, $output) { 117 | error("Failed to restart service [$this->service]. Check ~/.config/valet/Log for errors.\n$output"); 118 | }); 119 | }); 120 | } 121 | 122 | /** 123 | * Stop the service. 124 | * 125 | * @return void 126 | */ 127 | public function stop() { 128 | $command = 'cmd /C cd "' . $this->servicesPath() . '" && "' . $this->servicesPath($this->service) . '" stop'; 129 | 130 | $this->cli->run($command, function ($code, $output) { 131 | warning("Failed to stop service [$this->service].\n$output"); 132 | }); 133 | } 134 | 135 | /** 136 | * Is a service running? 137 | * 138 | * @return boolean 139 | */ 140 | public function isRunning() { 141 | $output = $this->cli->powershell('Get-Service -Name ' . $this->serviceId)->__toString(); 142 | 143 | if (str_contains($output, "Running")) { 144 | return true; 145 | } 146 | return false; 147 | } 148 | 149 | /** 150 | * Get the config path. 151 | * 152 | * @return string 153 | */ 154 | protected function configPath(): string { 155 | return $this->servicesPath("$this->service.xml"); 156 | } 157 | 158 | /** 159 | * Get the binary path. 160 | * 161 | * @return string 162 | */ 163 | protected function binaryPath(): string { 164 | return $this->servicesPath("$this->service.exe"); 165 | } 166 | 167 | /** 168 | * Get the services path. 169 | * 170 | * @param string $path 171 | * @return string 172 | */ 173 | protected function servicesPath(string $path = ''): string { 174 | return Valet::homePath('Services' . ($path ? "/$path" : $path)); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /cli/Valet/WinSwFactory.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 25 | $this->files = $files; 26 | 27 | // Download and install the WinSW package if it's not already installed. 28 | (new \Valet\Packages\WinSW($cli, $files))->install(); 29 | } 30 | 31 | /** 32 | * Make a new WinSW instance. 33 | * 34 | * @param string $service 35 | * @param string $serviceId 36 | * @return WinSW 37 | */ 38 | public function make(string $service, string $serviceId) { 39 | return new WinSW( 40 | $service, 41 | $serviceId, 42 | $this->cli, 43 | $this->files 44 | ); 45 | } 46 | } -------------------------------------------------------------------------------- /cli/drivers/BasicValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($staticFilePath = $sitePath . $uri)) { 35 | return $staticFilePath; 36 | } 37 | 38 | return false; 39 | } 40 | 41 | /** 42 | * Get the fully resolved path to the application's front controller. 43 | * 44 | * @param string $sitePath 45 | * @param string $siteName 46 | * @param string $uri 47 | * @return string 48 | */ 49 | public function frontControllerPath($sitePath, $siteName, $uri) { 50 | $_SERVER['PHP_SELF'] = $uri; 51 | $_SERVER['SERVER_ADDR'] = '127.0.0.1'; 52 | $_SERVER['SERVER_NAME'] = $_SERVER['HTTP_HOST']; 53 | 54 | $dynamicCandidates = [ 55 | $this->asActualFile($sitePath, $uri), 56 | $this->asPhpIndexFileInDirectory($sitePath, $uri), 57 | $this->asHtmlIndexFileInDirectory($sitePath, $uri) 58 | ]; 59 | 60 | foreach ($dynamicCandidates as $candidate) { 61 | if ($this->isActualFile($candidate)) { 62 | $_SERVER['SCRIPT_FILENAME'] = $candidate; 63 | $_SERVER['SCRIPT_NAME'] = str_replace($sitePath, '', $candidate); 64 | $_SERVER['DOCUMENT_ROOT'] = $sitePath; 65 | 66 | return $candidate; 67 | } 68 | } 69 | 70 | $fixedCandidatesAndDocroots = [ 71 | $this->asRootPhpIndexFile($sitePath) => $sitePath, 72 | $this->asPublicPhpIndexFile($sitePath) => $sitePath . '/public', 73 | $this->asPublicHtmlIndexFile($sitePath) => $sitePath . '/public' 74 | ]; 75 | 76 | foreach ($fixedCandidatesAndDocroots as $candidate => $docroot) { 77 | if ($this->isActualFile($candidate)) { 78 | $_SERVER['SCRIPT_FILENAME'] = $candidate; 79 | $_SERVER['SCRIPT_NAME'] = '/index.php'; 80 | $_SERVER['DOCUMENT_ROOT'] = $docroot; 81 | 82 | return $candidate; 83 | } 84 | } 85 | } 86 | 87 | /** 88 | * Concatenate the site path and URI as a single file name. 89 | * 90 | * @param string $sitePath 91 | * @param string $uri 92 | * @return string 93 | */ 94 | protected function asActualFile($sitePath, $uri) { 95 | return $sitePath . $uri; 96 | } 97 | 98 | /** 99 | * Format the site path and URI with a trailing "index.php". 100 | * 101 | * @param string $sitePath 102 | * @param string $uri 103 | * @return string 104 | */ 105 | protected function asPhpIndexFileInDirectory($sitePath, $uri) { 106 | return $sitePath . rtrim($uri, '/') . '/index.php'; 107 | } 108 | 109 | /** 110 | * Format the site path and URI with a trailing "index.html". 111 | * 112 | * @param string $sitePath 113 | * @param string $uri 114 | * @return string 115 | */ 116 | protected function asHtmlIndexFileInDirectory($sitePath, $uri) { 117 | return $sitePath . rtrim($uri, '/') . '/index.html'; 118 | } 119 | 120 | /** 121 | * Format the incoming site path as root "index.php" file path. 122 | * 123 | * @param string $sitePath 124 | * @return string 125 | */ 126 | protected function asRootPhpIndexFile($sitePath) { 127 | return $sitePath . '/index.php'; 128 | } 129 | 130 | /** 131 | * Format the incoming site path as a "public/index.php" file path. 132 | * 133 | * @param string $sitePath 134 | * @return string 135 | */ 136 | protected function asPublicPhpIndexFile($sitePath) { 137 | return $sitePath . '/public/index.php'; 138 | } 139 | 140 | /** 141 | * Format the incoming site path as a "public/index.php" file path. 142 | * 143 | * @param string $sitePath 144 | * @return string 145 | */ 146 | protected function asPublicHtmlIndexFile($sitePath) { 147 | return $sitePath . '/public/index.html'; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /cli/drivers/BedrockValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($staticFilePath)) { 28 | return $staticFilePath; 29 | } 30 | 31 | return false; 32 | } 33 | 34 | /** 35 | * Get the fully resolved path to the application's front controller. 36 | * 37 | * @param string $sitePath 38 | * @param string $siteName 39 | * @param string $uri 40 | * @return string 41 | */ 42 | public function frontControllerPath($sitePath, $siteName, $uri) { 43 | $_SERVER['PHP_SELF'] = $uri; 44 | $_SERVER['SERVER_NAME'] = $_SERVER['HTTP_HOST']; 45 | 46 | if (strpos($uri, '/wp/') === 0) { 47 | return is_dir($sitePath . '/web' . $uri) 48 | ? $sitePath . '/web' . $this->forceTrailingSlash($uri) . '/index.php' 49 | : $sitePath . '/web' . $uri; 50 | } 51 | 52 | return $sitePath . '/web/index.php'; 53 | } 54 | 55 | /** 56 | * Redirect to uri with trailing slash. 57 | * 58 | * @param string $uri 59 | * @return string 60 | */ 61 | private function forceTrailingSlash($uri) { 62 | if (substr($uri, -1 * strlen('/wp/wp-admin')) == '/wp/wp-admin') { 63 | header('Location: ' . $uri . '/'); 64 | exit; 65 | } 66 | 67 | return $uri; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /cli/drivers/CakeValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($staticFilePath = $sitePath . '/webroot/' . $uri)) { 26 | return $staticFilePath; 27 | } 28 | 29 | return false; 30 | } 31 | 32 | /** 33 | * Get the fully resolved path to the application's front controller. 34 | * 35 | * @param string $sitePath 36 | * @param string $siteName 37 | * @param string $uri 38 | * @return string 39 | */ 40 | public function frontControllerPath($sitePath, $siteName, $uri) { 41 | $_SERVER['DOCUMENT_ROOT'] = $sitePath . '/webroot'; 42 | $_SERVER['SCRIPT_FILENAME'] = $sitePath . '/webroot/index.php'; 43 | $_SERVER['SCRIPT_NAME'] = '/index.php'; 44 | $_SERVER['PHP_SELF'] = '/index.php'; 45 | 46 | return $sitePath . '/webroot/index.php'; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /cli/drivers/Concrete5ValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($staticFilePath = $sitePath . '/web' . $uri)) { 26 | return $staticFilePath; 27 | } 28 | 29 | return false; 30 | } 31 | 32 | /** 33 | * Get the fully resolved path to the application's front controller. 34 | * 35 | * @param string $sitePath 36 | * @param string $siteName 37 | * @param string $uri 38 | * @return string 39 | */ 40 | public function frontControllerPath($sitePath, $siteName, $uri) { 41 | if ($uri === '/install.php') { 42 | return $sitePath . '/web/install.php'; 43 | } 44 | 45 | if (0 === strncmp($uri, '/app_dev.php', 12)) { 46 | $_SERVER['SCRIPT_NAME'] = '/app_dev.php'; 47 | $_SERVER['SCRIPT_FILENAME'] = $sitePath . '/app_dev.php'; 48 | 49 | return $sitePath . '/web/app_dev.php'; 50 | } 51 | 52 | return $sitePath . '/web/app.php'; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /cli/drivers/CraftValetDriver.php: -------------------------------------------------------------------------------- 1 | frontControllerDirectory($sitePath); 44 | 45 | if ($this->isActualFile($staticFilePath = $sitePath . '/' . $frontControllerDirectory . $uri)) { 46 | return $staticFilePath; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Get the fully resolved path to the application's front controller. 54 | * 55 | * @param string $sitePath 56 | * @param string $siteName 57 | * @param string $uri 58 | * @return string 59 | */ 60 | public function frontControllerPath($sitePath, $siteName, $uri) { 61 | $frontControllerDirectory = $this->frontControllerDirectory($sitePath); 62 | 63 | // Default index path 64 | $indexPath = $sitePath . '/' . $frontControllerDirectory . '/index.php'; 65 | $scriptName = '/index.php'; 66 | 67 | // Check if the first URL segment matches any of the defined locales 68 | $locales = [ 69 | 'ar', 70 | 'ar_sa', 71 | 'bg', 72 | 'bg_bg', 73 | 'ca_es', 74 | 'cs', 75 | 'cy_gb', 76 | 'da', 77 | 'da_dk', 78 | 'de', 79 | 'de_at', 80 | 'de_ch', 81 | 'de_de', 82 | 'el', 83 | 'el_gr', 84 | 'en', 85 | 'en_as', 86 | 'en_au', 87 | 'en_bb', 88 | 'en_be', 89 | 'en_bm', 90 | 'en_bw', 91 | 'en_bz', 92 | 'en_ca', 93 | 'en_dsrt', 94 | 'en_dsrt_us', 95 | 'en_gb', 96 | 'en_gu', 97 | 'en_gy', 98 | 'en_hk', 99 | 'en_ie', 100 | 'en_in', 101 | 'en_jm', 102 | 'en_mh', 103 | 'en_mp', 104 | 'en_mt', 105 | 'en_mu', 106 | 'en_na', 107 | 'en_nz', 108 | 'en_ph', 109 | 'en_pk', 110 | 'en_sg', 111 | 'en_shaw', 112 | 'en_tt', 113 | 'en_um', 114 | 'en_us', 115 | 'en_us_posix', 116 | 'en_vi', 117 | 'en_za', 118 | 'en_zw', 119 | 'en_zz', 120 | 'es', 121 | 'es_cl', 122 | 'es_es', 123 | 'es_mx', 124 | 'es_us', 125 | 'es_ve', 126 | 'et', 127 | 'fi', 128 | 'fi_fi', 129 | 'fil', 130 | 'fr', 131 | 'fr_be', 132 | 'fr_ca', 133 | 'fr_ch', 134 | 'fr_fr', 135 | 'fr_ma', 136 | 'he', 137 | 'hr', 138 | 'hr_hr', 139 | 'hu', 140 | 'hu_hu', 141 | 'id', 142 | 'id_id', 143 | 'it', 144 | 'it_ch', 145 | 'it_it', 146 | 'ja', 147 | 'ja_jp', 148 | 'ko', 149 | 'ko_kr', 150 | 'lt', 151 | 'lv', 152 | 'ms', 153 | 'ms_my', 154 | 'nb', 155 | 'nb_no', 156 | 'nl', 157 | 'nl_be', 158 | 'nl_nl', 159 | 'nn', 160 | 'nn_no', 161 | 'no', 162 | 'pl', 163 | 'pl_pl', 164 | 'pt', 165 | 'pt_br', 166 | 'pt_pt', 167 | 'ro', 168 | 'ro_ro', 169 | 'ru', 170 | 'ru_ru', 171 | 'sk', 172 | 'sl', 173 | 'sr', 174 | 'sv', 175 | 'sv_se', 176 | 'th', 177 | 'th_th', 178 | 'tr', 179 | 'tr_tr', 180 | 'uk', 181 | 'vi', 182 | 'zh', 183 | 'zh_cn', 184 | 'zh_tw' 185 | ]; 186 | $parts = explode('/', $uri); 187 | 188 | if (count($parts) > 1 && in_array($parts[1], $locales)) { 189 | $indexLocalizedPath = $sitePath . '/' . $frontControllerDirectory . '/' . $parts[1] . '/index.php'; 190 | 191 | // Check if index.php exists in the localized folder, this is optional in Craft 3 192 | if (file_exists($indexLocalizedPath)) { 193 | $indexPath = $indexLocalizedPath; 194 | $scriptName = '/' . $parts[1] . '/index.php'; 195 | } 196 | } 197 | 198 | $_SERVER['SCRIPT_FILENAME'] = $indexPath; 199 | $_SERVER['SERVER_NAME'] = $_SERVER['HTTP_HOST']; 200 | $_SERVER['SCRIPT_NAME'] = $scriptName; 201 | $_SERVER['PHP_SELF'] = $scriptName; 202 | $_SERVER['DOCUMENT_ROOT'] = $sitePath . '/' . $frontControllerDirectory; 203 | 204 | return $indexPath; 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /cli/drivers/DrupalValetDriver.php: -------------------------------------------------------------------------------- 1 | addSubdirectory($sitePath); 14 | 15 | /** 16 | * /misc/drupal.js = Drupal 7 17 | * /core/lib/Drupal.php = Drupal 8. 18 | */ 19 | if (file_exists($sitePath . '/misc/drupal.js') || file_exists($sitePath . '/core/lib/Drupal.php')) { 20 | return true; 21 | } 22 | return false; 23 | } 24 | 25 | /** 26 | * Determine if the incoming request is for a static file. 27 | * 28 | * @param string $sitePath 29 | * @param string $siteName 30 | * @param string $uri 31 | * @return string|false 32 | */ 33 | public function isStaticFile($sitePath, $siteName, $uri) { 34 | $sitePath = $this->addSubdirectory($sitePath); 35 | 36 | if (file_exists($sitePath . $uri) && !is_dir($sitePath . $uri) && pathinfo($sitePath . $uri)['extension'] != 'php') { 37 | return $sitePath . $uri; 38 | } 39 | 40 | return false; 41 | } 42 | 43 | /** 44 | * Get the fully resolved path to the application's front controller. 45 | * 46 | * @param string $sitePath 47 | * @param string $siteName 48 | * @param string $uri 49 | * @return string 50 | */ 51 | public function frontControllerPath($sitePath, $siteName, $uri) { 52 | $sitePath = $this->addSubdirectory($sitePath); 53 | 54 | if (!isset($_GET['q']) && !empty($uri) && $uri !== '/' && strpos($uri, '/jsonapi/') === false) { 55 | $_GET['q'] = $uri; 56 | } 57 | 58 | $matches = []; 59 | if (preg_match('/^\/(.*?)\.php/', $uri, $matches)) { 60 | $filename = $matches[0]; 61 | if (file_exists($sitePath . $filename) && !is_dir($sitePath . $filename)) { 62 | $_SERVER['SCRIPT_FILENAME'] = $sitePath . $filename; 63 | $_SERVER['SCRIPT_NAME'] = $filename; 64 | 65 | return $sitePath . $filename; 66 | } 67 | } 68 | 69 | // Fallback 70 | $_SERVER['SCRIPT_FILENAME'] = $sitePath . '/index.php'; 71 | $_SERVER['SCRIPT_NAME'] = '/index.php'; 72 | 73 | return $sitePath . '/index.php'; 74 | } 75 | 76 | /** 77 | * Add any matching subdirectory to the site path. 78 | */ 79 | public function addSubdirectory($sitePath) { 80 | $paths = array_map(function ($subDir) use ($sitePath) { 81 | return "$sitePath/$subDir"; 82 | }, $this->possibleSubdirectories()); 83 | 84 | $foundPaths = array_filter($paths, function ($path) { 85 | return file_exists($path); 86 | }); 87 | 88 | // If paths are found, return the first one. 89 | if (!empty($foundPaths)) { 90 | return array_shift($foundPaths); 91 | } 92 | 93 | // If there are no matches, return the original path. 94 | return $sitePath; 95 | } 96 | 97 | /** 98 | * Return an array of possible subdirectories. 99 | * 100 | * @return array 101 | */ 102 | private function possibleSubdirectories() { 103 | return ['docroot', 'public', 'web']; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /cli/drivers/JigsawValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($staticFilePath = $sitePath . $uri)) { 26 | return $staticFilePath; 27 | } 28 | elseif ($this->isActualFile($staticFilePath = $sitePath . '/public' . $uri)) { 29 | return $staticFilePath; 30 | } 31 | 32 | return false; 33 | } 34 | 35 | /** 36 | * Get the fully resolved path to the application's front controller. 37 | * 38 | * @param string $sitePath 39 | * @param string $siteName 40 | * @param string $uri 41 | * @return string 42 | */ 43 | public function frontControllerPath($sitePath, $siteName, $uri) { 44 | $scriptName = '/index.php'; 45 | 46 | if ($this->isActualFile($sitePath . '/index.php')) { 47 | $indexPath = $sitePath . '/index.php'; 48 | } 49 | 50 | if ($isAboveWebroot = $this->isActualFile($sitePath . '/public/index.php')) { 51 | $indexPath = $sitePath . '/public/index.php'; 52 | } 53 | 54 | if (preg_match('/^\/panel/', $uri) && $this->isActualFile($sitePath . '/panel/index.php')) { 55 | $scriptName = '/panel/index.php'; 56 | $indexPath = $sitePath . '/panel/index.php'; 57 | } 58 | 59 | $sitePathPrefix = ($isAboveWebroot) ? $sitePath . '/public' : $sitePath; 60 | 61 | $_SERVER['SERVER_NAME'] = $_SERVER['HTTP_HOST']; 62 | $_SERVER['SCRIPT_NAME'] = $scriptName; 63 | $_SERVER['SCRIPT_FILENAME'] = $sitePathPrefix . $scriptName; 64 | 65 | return $indexPath; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /cli/drivers/LaravelValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($storagePath = $sitePath . '/storage/app/public' . $storageUri)) { 36 | return $storagePath; 37 | } 38 | 39 | return false; 40 | } 41 | 42 | /** 43 | * Get the fully resolved path to the application's front controller. 44 | * 45 | * @param string $sitePath 46 | * @param string $siteName 47 | * @param string $uri 48 | * @return string 49 | */ 50 | public function frontControllerPath($sitePath, $siteName, $uri) { 51 | // Shortcut for getting the "local" hostname as the HTTP_HOST 52 | if (isset($_SERVER['HTTP_X_ORIGINAL_HOST'], $_SERVER['HTTP_X_FORWARDED_HOST'])) { 53 | $_SERVER['HTTP_HOST'] = $_SERVER['HTTP_X_FORWARDED_HOST']; 54 | } 55 | 56 | return $sitePath . '/public/index.php'; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /cli/drivers/Magento2ValetDriver.php: -------------------------------------------------------------------------------- 1 | checkMageMode($sitePath); 33 | 34 | $uri = $this->handleForVersions($uri); 35 | $route = parse_url(substr($uri, 1))['path']; 36 | 37 | $pub = ''; 38 | if ('developer' === $this->mageMode) { 39 | $pub = 'pub/'; 40 | } 41 | 42 | if (!$this->isPubDirectory($sitePath, $route, $pub)) { 43 | return false; 44 | } 45 | 46 | $magentoPackagePubDir = $sitePath; 47 | if ('developer' !== $this->mageMode) { 48 | $magentoPackagePubDir .= '/pub'; 49 | } 50 | 51 | $file = $magentoPackagePubDir . '/' . $route; 52 | 53 | if (file_exists($file)) { 54 | return $magentoPackagePubDir . $uri; 55 | } 56 | 57 | if (strpos($route, $pub . 'static/') === 0) { 58 | $route = preg_replace('#' . $pub . 'static/#', '', $route, 1); 59 | $_GET['resource'] = $route; 60 | include $magentoPackagePubDir . '/' . $pub . 'static.php'; 61 | exit; 62 | } 63 | 64 | if (strpos($route, $pub . 'media/') === 0) { 65 | include $magentoPackagePubDir . '/' . $pub . 'get.php'; 66 | exit; 67 | } 68 | 69 | return false; 70 | } 71 | 72 | /** 73 | * Rewrite URLs that look like "versions12345/" to remove 74 | * the versions12345/ part. 75 | * 76 | * @param string $route 77 | * @return string 78 | */ 79 | private function handleForVersions($route) { 80 | return preg_replace('/version\d*\//', '', $route); 81 | } 82 | 83 | /** 84 | * Determine the current MAGE_MODE. 85 | * 86 | * @param string $sitePath 87 | */ 88 | private function checkMageMode($sitePath) { 89 | if (null !== $this->mageMode) { 90 | // We have already figure out mode, no need to check it again 91 | return; 92 | } 93 | 94 | if (!file_exists($sitePath . '/index.php')) { 95 | $this->mageMode = 'production'; // Can't use developer mode without index.php in project root 96 | 97 | return; 98 | } 99 | 100 | $mageConfig = []; 101 | 102 | if (file_exists($sitePath . '/app/etc/env.php')) { 103 | $mageConfig = require $sitePath . '/app/etc/env.php'; 104 | } 105 | 106 | if (array_key_exists('MAGE_MODE', $mageConfig)) { 107 | $this->mageMode = $mageConfig['MAGE_MODE']; 108 | } 109 | } 110 | 111 | /** 112 | * Checks to see if route is referencing any directory inside pub. This is a dynamic check so that if any new 113 | * directories are added to pub this driver will not need to be updated. 114 | * 115 | * @param string $sitePath 116 | * @param string $route 117 | * @param string $pub 118 | * @return bool 119 | */ 120 | private function isPubDirectory($sitePath, $route, $pub = '') { 121 | $sitePath .= '/pub/'; 122 | $dirs = glob($sitePath . '*', GLOB_ONLYDIR); 123 | 124 | $dirs = str_replace($sitePath, '', $dirs); 125 | foreach ($dirs as $dir) { 126 | if (strpos($route, $pub . $dir . '/') === 0) { 127 | return true; 128 | } 129 | } 130 | 131 | return false; 132 | } 133 | 134 | /** 135 | * Get the fully resolved path to the application's front controller. 136 | * 137 | * @param string $sitePath 138 | * @param string $siteName 139 | * @param string $uri 140 | * @return string 141 | */ 142 | public function frontControllerPath($sitePath, $siteName, $uri) { 143 | $this->checkMageMode($sitePath); 144 | 145 | if ('developer' === $this->mageMode) { 146 | $_SERVER['DOCUMENT_ROOT'] = $sitePath; 147 | 148 | return $sitePath . '/index.php'; 149 | } 150 | 151 | $_SERVER['DOCUMENT_ROOT'] = $sitePath . '/pub'; 152 | 153 | return $sitePath . '/pub/index.php'; 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /cli/drivers/NeosValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($staticFilePath = $sitePath . '/Web' . $uri)) { 26 | return $staticFilePath; 27 | } 28 | 29 | return false; 30 | } 31 | 32 | /** 33 | * Get the fully resolved path to the application's front controller. 34 | * 35 | * @param string $sitePath 36 | * @param string $siteName 37 | * @param string $uri 38 | * @return string 39 | */ 40 | public function frontControllerPath($sitePath, $siteName, $uri) { 41 | putenv('FLOW_CONTEXT=Development'); 42 | putenv('FLOW_REWRITEURLS=1'); 43 | $_SERVER['SCRIPT_FILENAME'] = $sitePath . '/Web/index.php'; 44 | $_SERVER['SCRIPT_NAME'] = '/index.php'; 45 | 46 | return $sitePath . '/Web/index.php'; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /cli/drivers/SculpinValetDriver.php: -------------------------------------------------------------------------------- 1 | isModernSculpinProject($sitePath) || $this->isLegacySculpinProject($sitePath); 14 | } 15 | 16 | private function isModernSculpinProject($sitePath) { 17 | return is_dir($sitePath . '/source') && is_dir($sitePath . '/output_dev') && $this->composerRequiresSculpin($sitePath); 18 | } 19 | 20 | private function isLegacySculpinProject($sitePath) { 21 | return is_dir($sitePath . '/.sculpin'); 22 | } 23 | 24 | private function composerRequiresSculpin($sitePath) { 25 | if (!file_exists($sitePath . '/composer.json')) { 26 | return false; 27 | } 28 | 29 | $composer_json_source = file_get_contents($sitePath . '/composer.json'); 30 | $composer_json = json_decode($composer_json_source, true); 31 | 32 | if (json_last_error() !== JSON_ERROR_NONE) { 33 | return false; 34 | } 35 | 36 | return isset($composer_json['require']['sculpin/sculpin']); 37 | } 38 | 39 | /** 40 | * Mutate the incoming URI. 41 | * 42 | * @param string $uri 43 | * @return string 44 | */ 45 | public function mutateUri($uri) { 46 | return rtrim('/output_dev' . $uri, '/'); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /cli/drivers/StatamicV1ValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($staticFilePath = $sitePath . $uri)) { 30 | return $staticFilePath; 31 | } 32 | 33 | return false; 34 | } 35 | 36 | /** 37 | * Get the fully resolved path to the application's front controller. 38 | * 39 | * @param string $sitePath 40 | * @param string $siteName 41 | * @param string $uri 42 | * @return string 43 | */ 44 | public function frontControllerPath($sitePath, $siteName, $uri) { 45 | if (strpos($uri, '/admin.php') === 0) { 46 | $_SERVER['SCRIPT_NAME'] = '/admin.php'; 47 | 48 | return $sitePath . '/admin.php'; 49 | } 50 | 51 | if ($uri === '/admin') { 52 | $_SERVER['SCRIPT_NAME'] = '/admin/index.php'; 53 | 54 | return $sitePath . '/admin/index.php'; 55 | } 56 | 57 | $_SERVER['SCRIPT_NAME'] = '/index.php'; 58 | 59 | return $sitePath . '/index.php'; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /cli/drivers/StatamicValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($staticFilePath = $sitePath . $uri)) { 32 | return $staticFilePath; 33 | } 34 | elseif ($this->isActualFile($staticFilePath = $sitePath . '/public' . $uri)) { 35 | return $staticFilePath; 36 | } 37 | 38 | return false; 39 | } 40 | 41 | /** 42 | * Get the fully resolved path to the application's front controller. 43 | * 44 | * @param string $sitePath 45 | * @param string $siteName 46 | * @param string $uri 47 | * @return string 48 | */ 49 | public function frontControllerPath($sitePath, $siteName, $uri) { 50 | if ($_SERVER['REQUEST_METHOD'] === 'GET' && $this->isActualFile($staticPath = $this->getStaticPath($sitePath))) { 51 | return $staticPath; 52 | } 53 | 54 | if ($uri === '/installer.php') { 55 | return $sitePath . '/installer.php'; 56 | } 57 | 58 | $scriptName = '/index.php'; 59 | 60 | if ($this->isActualFile($sitePath . '/index.php')) { 61 | $indexPath = $sitePath . '/index.php'; 62 | } 63 | 64 | if ($isAboveWebroot = $this->isActualFile($sitePath . '/public/index.php')) { 65 | $indexPath = $sitePath . '/public/index.php'; 66 | } 67 | 68 | $sitePathPrefix = ($isAboveWebroot) ? $sitePath . '/public' : $sitePath; 69 | 70 | if ($locale = $this->getUriLocale($uri)) { 71 | if ($this->isActualFile($localeIndexPath = $sitePathPrefix . '/' . $locale . '/index.php')) { 72 | // Force trailing slashes on locale roots. 73 | if ($uri === '/' . $locale) { 74 | header('Location: ' . $uri . '/'); 75 | exit; 76 | } 77 | 78 | $indexPath = $localeIndexPath; 79 | $scriptName = '/' . $locale . '/index.php'; 80 | } 81 | } 82 | 83 | $_SERVER['SCRIPT_NAME'] = $scriptName; 84 | $_SERVER['SCRIPT_FILENAME'] = $sitePathPrefix . $scriptName; 85 | 86 | return $indexPath; 87 | } 88 | 89 | /** 90 | * Get the locale from this URI. 91 | * 92 | * @param string $uri 93 | * @return string|null 94 | */ 95 | public function getUriLocale($uri) { 96 | $parts = explode('/', $uri); 97 | $locale = $parts[1]; 98 | 99 | if (count($parts) < 2 || !in_array($locale, $this->getLocales())) { 100 | return; 101 | } 102 | 103 | return $locale; 104 | } 105 | 106 | /** 107 | * Get the list of possible locales used in the first segment of a URI. 108 | * 109 | * @return array 110 | */ 111 | public function getLocales() { 112 | return [ 113 | 'af', 114 | 'ax', 115 | 'al', 116 | 'dz', 117 | 'as', 118 | 'ad', 119 | 'ao', 120 | 'ai', 121 | 'aq', 122 | 'ag', 123 | 'ar', 124 | 'am', 125 | 'aw', 126 | 'au', 127 | 'at', 128 | 'az', 129 | 'bs', 130 | 'bh', 131 | 'bd', 132 | 'bb', 133 | 'by', 134 | 'be', 135 | 'bz', 136 | 'bj', 137 | 'bm', 138 | 'bt', 139 | 'bo', 140 | 'bq', 141 | 'ba', 142 | 'bw', 143 | 'bv', 144 | 'br', 145 | 'io', 146 | 'bn', 147 | 'bg', 148 | 'bf', 149 | 'bi', 150 | 'cv', 151 | 'kh', 152 | 'cm', 153 | 'ca', 154 | 'ky', 155 | 'cf', 156 | 'td', 157 | 'cl', 158 | 'cn', 159 | 'cx', 160 | 'cc', 161 | 'co', 162 | 'km', 163 | 'cg', 164 | 'cd', 165 | 'ck', 166 | 'cr', 167 | 'ci', 168 | 'hr', 169 | 'cu', 170 | 'cw', 171 | 'cy', 172 | 'cz', 173 | 'dk', 174 | 'dj', 175 | 'dm', 176 | 'do', 177 | 'ec', 178 | 'eg', 179 | 'sv', 180 | 'gq', 181 | 'er', 182 | 'ee', 183 | 'et', 184 | 'fk', 185 | 'fo', 186 | 'fj', 187 | 'fi', 188 | 'fr', 189 | 'gf', 190 | 'pf', 191 | 'tf', 192 | 'ga', 193 | 'gm', 194 | 'ge', 195 | 'de', 196 | 'gh', 197 | 'gi', 198 | 'gr', 199 | 'gl', 200 | 'gd', 201 | 'gp', 202 | 'gu', 203 | 'gt', 204 | 'gg', 205 | 'gn', 206 | 'gw', 207 | 'gy', 208 | 'ht', 209 | 'hm', 210 | 'va', 211 | 'hn', 212 | 'hk', 213 | 'hu', 214 | 'is', 215 | 'in', 216 | 'id', 217 | 'ir', 218 | 'iq', 219 | 'ie', 220 | 'im', 221 | 'il', 222 | 'it', 223 | 'jm', 224 | 'jp', 225 | 'je', 226 | 'jo', 227 | 'kz', 228 | 'ke', 229 | 'ki', 230 | 'kp', 231 | 'kr', 232 | 'kw', 233 | 'kg', 234 | 'la', 235 | 'lv', 236 | 'lb', 237 | 'ls', 238 | 'lr', 239 | 'ly', 240 | 'li', 241 | 'lt', 242 | 'lu', 243 | 'mo', 244 | 'mk', 245 | 'mg', 246 | 'mw', 247 | 'my', 248 | 'mv', 249 | 'ml', 250 | 'mt', 251 | 'mh', 252 | 'mq', 253 | 'mr', 254 | 'mu', 255 | 'yt', 256 | 'mx', 257 | 'fm', 258 | 'md', 259 | 'mc', 260 | 'mn', 261 | 'me', 262 | 'ms', 263 | 'ma', 264 | 'mz', 265 | 'mm', 266 | 'na', 267 | 'nr', 268 | 'np', 269 | 'nl', 270 | 'nc', 271 | 'nz', 272 | 'ni', 273 | 'ne', 274 | 'ng', 275 | 'nu', 276 | 'nf', 277 | 'mp', 278 | 'no', 279 | 'om', 280 | 'pk', 281 | 'pw', 282 | 'ps', 283 | 'pa', 284 | 'pg', 285 | 'py', 286 | 'pe', 287 | 'ph', 288 | 'pn', 289 | 'pl', 290 | 'pt', 291 | 'pr', 292 | 'qa', 293 | 're', 294 | 'ro', 295 | 'ru', 296 | 'rw', 297 | 'bl', 298 | 'sh', 299 | 'kn', 300 | 'lc', 301 | 'mf', 302 | 'pm', 303 | 'vc', 304 | 'ws', 305 | 'sm', 306 | 'st', 307 | 'sa', 308 | 'sn', 309 | 'rs', 310 | 'sc', 311 | 'sl', 312 | 'sg', 313 | 'sx', 314 | 'sk', 315 | 'si', 316 | 'sb', 317 | 'so', 318 | 'za', 319 | 'gs', 320 | 'ss', 321 | 'es', 322 | 'lk', 323 | 'sd', 324 | 'sr', 325 | 'sj', 326 | 'sz', 327 | 'se', 328 | 'ch', 329 | 'sy', 330 | 'tw', 331 | 'tj', 332 | 'tz', 333 | 'th', 334 | 'tl', 335 | 'tg', 336 | 'tk', 337 | 'to', 338 | 'tt', 339 | 'tn', 340 | 'tr', 341 | 'tm', 342 | 'tc', 343 | 'tv', 344 | 'ug', 345 | 'ua', 346 | 'ae', 347 | 'gb', 348 | 'us', 349 | 'um', 350 | 'uy', 351 | 'uz', 352 | 'vu', 353 | 've', 354 | 'vn', 355 | 'vg', 356 | 'vi', 357 | 'wf', 358 | 'eh', 359 | 'ye', 360 | 'zm', 361 | 'zw', 362 | 'en', 363 | 'zh' 364 | ]; 365 | } 366 | 367 | /** 368 | * Get the path to a statically cached page. 369 | * 370 | * @param string $sitePath 371 | * @return string 372 | */ 373 | protected function getStaticPath($sitePath) { 374 | $parts = parse_url($_SERVER['REQUEST_URI']); 375 | $query = isset($parts['query']) ? $parts['query'] : ''; 376 | 377 | return $sitePath . '/static' . $parts['path'] . '_' . $query . '.html'; 378 | } 379 | } 380 | -------------------------------------------------------------------------------- /cli/drivers/SymfonyValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($staticFilePath = $sitePath . '/web/' . $uri)) { 26 | return $staticFilePath; 27 | } 28 | elseif ($this->isActualFile($staticFilePath = $sitePath . '/public/' . $uri)) { 29 | return $staticFilePath; 30 | } 31 | 32 | return false; 33 | } 34 | 35 | /** 36 | * Get the fully resolved path to the application's front controller. 37 | * 38 | * @param string $sitePath 39 | * @param string $siteName 40 | * @param string $uri 41 | * @return string 42 | */ 43 | public function frontControllerPath($sitePath, $siteName, $uri) { 44 | if (file_exists($frontControllerPath = $sitePath . '/web/app_dev.php')) { 45 | return $frontControllerPath; 46 | } 47 | elseif (file_exists($frontControllerPath = $sitePath . '/web/app.php')) { 48 | return $frontControllerPath; 49 | } 50 | elseif (file_exists($frontControllerPath = $sitePath . '/public/index.php')) { 51 | return $frontControllerPath; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /cli/drivers/Typo3ValetDriver.php: -------------------------------------------------------------------------------- 1 | documentRoot . '/typo3'; 50 | 51 | return file_exists($typo3Dir) && is_dir($typo3Dir); 52 | } 53 | 54 | /** 55 | * Determine if the incoming request is for a static file. That is, it is 56 | * no PHP script file and the URI points to a valid file (no folder) on 57 | * the disk. Access to those static files will be authorized. 58 | * 59 | * @param string $sitePath 60 | * @param string $siteName 61 | * @param string $uri 62 | * @return string|false 63 | */ 64 | public function isStaticFile($sitePath, $siteName, $uri) { 65 | // May the file contains a cache busting version string like filename.12345678.css 66 | // If that is the case, the file cannot be found on disk, so remove the version 67 | // identifier before retrying below. 68 | if (!$this->isActualFile($filePath = $sitePath . $this->documentRoot . $uri)) { 69 | $uri = preg_replace("@^(.+)\.(\d+)\.(js|css|png|jpg|gif|gzip)$@", '$1.$3', $uri); 70 | } 71 | 72 | // Now that any possible version string is cleared from the filename, the resulting 73 | // URI should be a valid file on disc. So assemble the absolute file name with the 74 | // same schema as above and if it exists, authorize access and return its path. 75 | if ($this->isActualFile($filePath = $sitePath . $this->documentRoot . $uri)) { 76 | return $this->isAccessAuthorized($uri) ? $filePath : false; 77 | } 78 | 79 | // This file cannot be found in the current project and thus cannot be served. 80 | return false; 81 | } 82 | 83 | /** 84 | * Determines if the given URI is blacklisted so that access is prevented. 85 | * 86 | * @param string $uri 87 | * @return bool 88 | */ 89 | private function isAccessAuthorized($uri) { 90 | foreach ($this->forbiddenUriPatterns as $forbiddenUriPattern) { 91 | if (preg_match("@$forbiddenUriPattern@", $uri)) { 92 | return false; 93 | } 94 | } 95 | 96 | return true; 97 | } 98 | 99 | /** 100 | * Get the fully resolved path to the application's front controller. 101 | * This can be the currently requested PHP script, a folder that 102 | * contains an index.php or the global index.php otherwise. 103 | * 104 | * @param string $sitePath 105 | * @param string $siteName 106 | * @param string $uri 107 | * @return string 108 | */ 109 | public function frontControllerPath($sitePath, $siteName, $uri) { 110 | // without modifying the URI, redirect if necessary 111 | $this->handleRedirectBackendShorthandUris($uri); 112 | 113 | // from now on, remove trailing / for convenience for all the following join operations 114 | $uri = rtrim($uri, '/'); 115 | 116 | // try to find the responsible script file for the requested folder / script URI 117 | if (file_exists($absoluteFilePath = $sitePath . $this->documentRoot . $uri)) { 118 | if (is_dir($absoluteFilePath)) { 119 | if (file_exists($absoluteFilePath . '/index.php')) { 120 | // this folder can be served by index.php 121 | return $this->serveScript($sitePath, $siteName, $uri . '/index.php'); 122 | } 123 | 124 | if (file_exists($absoluteFilePath . '/index.html')) { 125 | // this folder can be served by index.html 126 | return $absoluteFilePath . '/index.html'; 127 | } 128 | } 129 | elseif (pathinfo($absoluteFilePath, PATHINFO_EXTENSION) === 'php') { 130 | // this file can be served directly 131 | return $this->serveScript($sitePath, $siteName, $uri); 132 | } 133 | } 134 | 135 | // the global index.php will handle all other cases 136 | return $this->serveScript($sitePath, $siteName, '/index.php'); 137 | } 138 | 139 | /** 140 | * Direct access to installtool via domain.dev/typo3/install/ will be redirected to 141 | * sysext install script. domain.dev/typo3 will be redirected to /typo3/, because 142 | * the generated JavaScript URIs on the login screen would be broken on /typo3. 143 | * 144 | * @param string $uri 145 | */ 146 | private function handleRedirectBackendShorthandUris($uri) { 147 | if (rtrim($uri, '/') === '/typo3/install') { 148 | header('Location: /typo3/sysext/install/Start/Install.php'); 149 | exit(); 150 | } 151 | 152 | if ($uri === '/typo3') { 153 | header('Location: /typo3/'); 154 | exit(); 155 | } 156 | } 157 | 158 | /** 159 | * Configures the $_SERVER globals for serving the script at 160 | * the specified URI and returns it absolute file path. 161 | * 162 | * @param string $sitePath 163 | * @param string $siteName 164 | * @param string $uri 165 | * @param string $script 166 | * @return string 167 | */ 168 | private function serveScript($sitePath, $siteName, $uri) { 169 | $docroot = $sitePath . $this->documentRoot; 170 | $abspath = $docroot . $uri; 171 | 172 | $_SERVER['SERVER_NAME'] = $siteName . '.dev'; 173 | $_SERVER['DOCUMENT_ROOT'] = $docroot; 174 | $_SERVER['DOCUMENT_URI'] = $uri; 175 | $_SERVER['SCRIPT_FILENAME'] = $abspath; 176 | $_SERVER['SCRIPT_NAME'] = $uri; 177 | $_SERVER['PHP_SELF'] = $uri; 178 | 179 | return $abspath; 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /cli/drivers/ValetDriver.php: -------------------------------------------------------------------------------- 1 | serves($sitePath, $siteName, $driver->mutateUri($uri))) { 80 | return $driver; 81 | } 82 | } 83 | } 84 | 85 | /** 86 | * Get the custom driver class from the site path, if one exists. 87 | * 88 | * @param string $sitePath 89 | * @return void|string 90 | */ 91 | public static function customSiteDriver($sitePath) { 92 | if (!file_exists($sitePath . '/LocalValetDriver.php')) { 93 | return; 94 | } 95 | 96 | require_once $sitePath . '/LocalValetDriver.php'; 97 | 98 | return 'LocalValetDriver'; 99 | } 100 | 101 | /** 102 | * Get all of the driver classes in a given path. 103 | * 104 | * @param string $path 105 | * @return array 106 | */ 107 | public static function driversIn($path) { 108 | if (!is_dir($path)) { 109 | return []; 110 | } 111 | 112 | $drivers = []; 113 | 114 | $dir = new RecursiveDirectoryIterator($path); 115 | $iterator = new RecursiveIteratorIterator($dir); 116 | $regex = new RegexIterator($iterator, '/^.+ValetDriver\.php$/i', RecursiveRegexIterator::GET_MATCH); 117 | 118 | foreach ($regex as $file) { 119 | require_once $file[0]; 120 | 121 | $drivers[] = basename($file[0], '.php'); 122 | } 123 | 124 | return $drivers; 125 | } 126 | 127 | /** 128 | * Mutate the incoming URI. 129 | * 130 | * @param string $uri 131 | * @return string 132 | */ 133 | public function mutateUri($uri) { 134 | return $uri; 135 | } 136 | 137 | /** 138 | * Serve the static file at the given path. 139 | * 140 | * @param string $staticFilePath 141 | * @param string $sitePath 142 | * @param string $siteName 143 | * @param string $uri 144 | * @return void 145 | */ 146 | public function serveStaticFile($staticFilePath, $sitePath, $siteName, $uri) { 147 | /** 148 | * Back story... 149 | * 150 | * PHP docs *claim* you can set default_mimetype = "" to disable the default 151 | * Content-Type header. This works in PHP 7+, but in PHP 5.* it sends an 152 | * *empty* Content-Type header, which is significantly different than 153 | * sending *no* Content-Type header. 154 | * 155 | * However, if you explicitly set a Content-Type header, then explicitly 156 | * remove that Content-Type header, PHP seems to not re-add the default. 157 | * 158 | * I have a hard time believing this is by design and not coincidence. 159 | * 160 | * Burn. it. all. 161 | */ 162 | header('Content-Type: text/html'); 163 | header_remove('Content-Type'); 164 | 165 | header('X-Accel-Redirect: /' . VALET_STATIC_PREFIX . '/' . $staticFilePath); 166 | } 167 | 168 | /** 169 | * Determine if the path is a file and not a directory. 170 | * 171 | * @param string $path 172 | * @return bool 173 | */ 174 | protected function isActualFile($path) { 175 | return !is_dir($path) && file_exists($path); 176 | } 177 | 178 | /** 179 | * Load server environment variables if available. 180 | * Processes any '*' entries first, and then adds site-specific entries. 181 | * 182 | * @param string $sitePath 183 | * @param string $siteName 184 | * @return void 185 | */ 186 | public function loadServerEnvironmentVariables($sitePath, $siteName) { 187 | $varFilePath = $sitePath . '/.valet-env.php'; 188 | if (!file_exists($varFilePath)) { 189 | return; 190 | } 191 | 192 | $variables = include $varFilePath; 193 | 194 | $variablesToSet = isset($variables['*']) ? $variables['*'] : []; 195 | 196 | if (isset($variables[$siteName])) { 197 | $variablesToSet = array_merge($variablesToSet, $variables[$siteName]); 198 | } 199 | 200 | foreach ($variablesToSet as $key => $value) { 201 | if (!is_string($key)) { 202 | continue; 203 | } 204 | $_SERVER[$key] = $value; 205 | $_ENV[$key] = $value; 206 | putenv($key . '=' . $value); 207 | } 208 | } 209 | } -------------------------------------------------------------------------------- /cli/drivers/WordPressValetDriver.php: -------------------------------------------------------------------------------- 1 | forceTrailingSlash($uri) 33 | ); 34 | } 35 | 36 | /** 37 | * Redirect to uri with trailing slash. 38 | * 39 | * @param string $uri 40 | * @return string 41 | */ 42 | private function forceTrailingSlash($uri) { 43 | if (substr($uri, -1 * strlen('/wp-admin')) == '/wp-admin') { 44 | header('Location: ' . $uri . '/'); 45 | exit; 46 | } 47 | 48 | return $uri; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /cli/drivers/require.php: -------------------------------------------------------------------------------- 1 | make(static::containerKey()); 38 | 39 | return call_user_func_array([$resolvedInstance, $method], $parameters); 40 | } 41 | } 42 | 43 | class Acrylic extends Facade {} 44 | class Ansicon extends Facade {} 45 | class CommandLine extends Facade {} 46 | class Configuration extends Facade {} 47 | class Diagnose extends Facade {} 48 | class Filesystem extends Facade {} 49 | class Gsudo extends Facade {} 50 | class Nginx extends Facade {} 51 | class Ngrok extends Facade {} 52 | class PhpCgi extends Facade {} 53 | class PhpCgiXdebug extends Facade {} 54 | class Share extends Facade {} 55 | class Site extends Facade {} 56 | class Upgrader extends Facade {} 57 | class Valet extends Facade {} 58 | class ValetException extends Facade {} 59 | class WinSW extends Facade {} -------------------------------------------------------------------------------- /cli/includes/helpers.php: -------------------------------------------------------------------------------- 1 | ' . $output . ''); 36 | } 37 | 38 | /** 39 | * Debugging only. 40 | * Output the given array to the console using var_dump. 41 | * 42 | * @param string $output 43 | * @return void 44 | */ 45 | function info_dump($output) { 46 | output('' . var_dump($output) . ''); 47 | } 48 | 49 | /** 50 | * Output the given text to the console. 51 | * 52 | * @param string $output 53 | * @return void 54 | */ 55 | function warning($output) { 56 | if (isset($_ENV['APP_ENV']) && $_ENV['APP_ENV'] === 'testing') { 57 | throw new RuntimeException($output); 58 | } 59 | 60 | output('' . $output . ''); 61 | } 62 | 63 | /** 64 | * Output errors to the console. 65 | * 66 | * @param string $output 67 | * @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`] 68 | * 69 | * @throws RuntimeException 70 | * @throws ValetException 71 | * 72 | * @return void 73 | */ 74 | function error(string $output, $exception = false) { 75 | if (isset($_ENV['APP_ENV']) && $_ENV['APP_ENV'] === 'testing') { 76 | throw new RuntimeException($output); 77 | } 78 | if ($exception === true) { 79 | $errors = (new ValetException($output))->getError(); 80 | 81 | // Wait 1 microsecond, to make sure all output before the error call has reached 82 | // the terminal. 83 | usleep(1); 84 | 85 | // Print the error message to the console. 86 | (new ConsoleOutput())->getErrorOutput()->writeln("\n\n$errors"); 87 | 88 | exit(); 89 | } 90 | else { 91 | (new ConsoleOutput())->getErrorOutput()->writeln("$output"); 92 | } 93 | } 94 | 95 | /** 96 | * Output the given text to the console. 97 | * 98 | * @param string $output 99 | * @return void 100 | */ 101 | function output($output) { 102 | if (isset($_ENV['APP_ENV']) && $_ENV['APP_ENV'] === 'testing') { 103 | return; 104 | } 105 | (new ConsoleOutput())->writeln($output); 106 | } 107 | 108 | /** 109 | * Output a table to the console. 110 | * 111 | * @param array $headers 112 | * @param array $rows 113 | * @return void 114 | */ 115 | function table(array $headers = [], array $rows = [], $setHorizontal = false, $title = null) { 116 | $table = new Table(new ConsoleOutput()); 117 | 118 | // Symfony Console component from 6.1 added support for a vertical table. 119 | // But older versions won't reconise the function and will 120 | // spit out errors. So to avoid this, we need to check 121 | // whether the method exists and if it does, we can use it. 122 | if (method_exists(Table::class, 'setVertical') && !$setHorizontal) { 123 | $table->setVertical(); 124 | } 125 | 126 | if ($title) { 127 | $table->setHeaderTitle($title); 128 | } 129 | if ($setHorizontal) { 130 | $rows = addTableSeparator($rows); 131 | } 132 | 133 | $table->setHeaders($headers)->setRows($rows); 134 | 135 | if (count($headers) > 1) { 136 | changeColumnMaxWidth($table, $headers, ["URL", "Path"], 30); 137 | } 138 | 139 | $table->setStyle('box'); 140 | 141 | $table->render(); 142 | } 143 | 144 | /** 145 | * Defines the default table headers. 146 | * 147 | * @return array ['Site', 'Alias', 'Secured', 'PHP', 'URL', 'Alias URL', 'Path'] 148 | */ 149 | function default_table_headers() { 150 | return ['Site', 'Alias', 'Secured', 'PHP', 'URL', 'Alias URL', 'Path']; 151 | } 152 | 153 | /** 154 | * Change the max width of specified table columns. 155 | * 156 | * @param Table $table The table instance 157 | * @param array $headers Table headers 158 | * @param array $columns The column names to change the width of 159 | * @param int $maxWidth The maximum width of the columns 160 | */ 161 | function changeColumnMaxWidth($table, $headers, $columns, $maxWidth) { 162 | foreach ($columns as $column) { 163 | $index = array_search($column, $headers); 164 | // (column, width) - column is zero based. 165 | $table->setColumnMaxWidth($index, $maxWidth); 166 | } 167 | } 168 | 169 | /** 170 | * Add a table separator inbetween all the rows. 171 | * 172 | * @param array $rows The array of rows 173 | */ 174 | function addTableSeparator($rows) { 175 | /** 176 | * Create a new laravel collection and add the table separator 177 | * inbetween all the rows. 178 | * Code from https://laracasts.com/discuss/channels/site-improvements/php-array-insert-between-each-item 179 | */ 180 | $separatedRows = collect($rows)->flatMap(function ($item) { 181 | return [$item, new TableSeparator()]; 182 | })->slice(0, -1)->toArray(); 183 | 184 | return $separatedRows; 185 | } 186 | 187 | if (!function_exists('resolve')) { 188 | /** 189 | * Resolve the given class from the container. 190 | * 191 | * https://laravel.com/docs/12.x/helpers#method-resolve 192 | * 193 | * @param string $class 194 | * @return mixed 195 | */ 196 | function resolve($class) { 197 | return Container::getInstance()->make($class); 198 | } 199 | } 200 | 201 | /** 202 | * Swap the given class implementation in the container. 203 | * 204 | * @param string $class 205 | * @param mixed $instance 206 | * @return void 207 | */ 208 | function swap($class, $instance) { 209 | Container::getInstance()->instance($class, $instance); 210 | } 211 | 212 | if (!function_exists('retry')) { 213 | /** 214 | * Retry the given function N times. 215 | * 216 | * https://laravel.com/docs/12.x/helpers#method-retry 217 | * 218 | * @param int $retries 219 | * @param callable $fn 220 | * @param int $sleep 221 | * @return mixed 222 | */ 223 | function retry($retries, $fn, $sleep = 0) { 224 | beginning: 225 | try { 226 | return $fn(); 227 | } 228 | catch (Exception $e) { 229 | if (!$retries) { 230 | throw $e; 231 | } 232 | 233 | $retries--; 234 | 235 | if ($sleep > 0) { 236 | usleep($sleep * 1000); 237 | } 238 | 239 | goto beginning; 240 | } 241 | } 242 | } 243 | 244 | if (!function_exists('tap')) { 245 | /** 246 | * Tap the given value. 247 | * 248 | * https://laravel.com/docs/12.x/helpers#method-tap 249 | * 250 | * @param mixed $value 251 | * @param callable $callback 252 | * @return mixed 253 | */ 254 | function tap($value, callable $callback) { 255 | $callback($value); 256 | 257 | return $value; 258 | } 259 | } 260 | 261 | /** 262 | * Get the user. 263 | * 264 | * @return string 265 | */ 266 | function user() { 267 | if (isset($_SERVER['USER'])) { 268 | return $_SERVER['USER']; 269 | } 270 | 271 | return $_SERVER['USERNAME']; 272 | } 273 | 274 | /** 275 | * Get the bin path. 276 | * @return string `"c:\Users\Username\AppData\Roaming\Composer\vendor\ycodetech\valet-windows\bin\"` 277 | */ 278 | function valetBinPath() { 279 | $DIR = pathFilter(__DIR__); 280 | return $DIR . '/../../bin/'; 281 | } 282 | 283 | /** 284 | * Alternative Naming Convention for Directories or Paths containing spaces 285 | * 286 | * Renames directories with spaces to the alternative Windows shortened name 287 | * such as `John Doe` to `JOHNDO~1`. 288 | * This is to prevent errors when installing Valet components like 'Ansicon' and commands like `diagnose`. 289 | * 290 | * @param string $path The path 291 | * 292 | * @return string `"c:\Users\USERNA~1\......"` 293 | */ 294 | function pathFilter($path) { 295 | 296 | $path = str_replace('/', DIRECTORY_SEPARATOR, $path); 297 | 298 | $path = explode(DIRECTORY_SEPARATOR, $path); 299 | foreach ($path as $key => $value) { 300 | if (strpos($value, ' ')) { 301 | $value = strtoupper(substr(str_replace(' ', '', $value), 0, 6)) . "~1"; 302 | $path[$key] = $value; 303 | } 304 | } 305 | 306 | $path = implode(DIRECTORY_SEPARATOR, $path); 307 | 308 | return str_replace('\\', '/', $path); 309 | } 310 | 311 | /** 312 | * #### Prefix options/flags 313 | * 314 | * Create a new options array with `--` prefixed to each option 315 | * and implode the array into a single space-delimited string. 316 | * 317 | * @param array $options The options/flags. 318 | * 319 | * @return string The new prefixed options as a string. 320 | */ 321 | function prefixOptions($options) { 322 | return (new \Illuminate\Support\Collection($options))->map(function ($value) { 323 | // If value has length of 1, ie. has 1 character, then its a shortcut option, 324 | // so apply the single "-". 325 | if (strlen($value) === 1) { 326 | return "-$value"; 327 | } 328 | 329 | // Prefix the option with "--". 330 | return "--$value"; 331 | })->implode(' '); 332 | } 333 | 334 | /** 335 | * Display a progress bar 336 | * 337 | * @param int $maxItems Max items/steps 338 | * @param string $message The message 339 | * @param string $startingTxt The text for the `%placeholder%` at the start of the progressbar, which is placed after `$message`. Default: `"services"` 340 | * 341 | * @return ProgressBar The ProgressBar object that holds various methods of the class. Including: 342 | * 343 | * - `setMessage($string, $placeholderName)` To set the message of the `%placeholder%` during progress. 344 | * - `advance([$num])` To advance the progress by 1 (if option omitted), optionally specify a number to progress by. 345 | */ 346 | function progressbar($maxItems, $message, $startingTxt = "services") { 347 | ProgressBar::setFormatDefinition('custom', " %current%/%max% %bar% %percent%% %message% %placeholder%..."); 348 | 349 | $progressBar = new ProgressBar(new ConsoleOutput(), $maxItems); 350 | 351 | $progressBar->setFormat('custom'); 352 | // the finished part of the bar 353 | $progressBar->setBarCharacter('█'); 354 | // the unfinished part of the bar 355 | $progressBar->setEmptyBarCharacter(' '); 356 | // the progress character 357 | $progressBar->setProgressCharacter('█'); 358 | 359 | $progressBar->setMessage($message, "message"); 360 | $progressBar->setMessage($startingTxt, "placeholder"); 361 | $progressBar->start(); 362 | 363 | return $progressBar; 364 | } 365 | 366 | /** 367 | * Get the path to the `tar` command executable from the `C:\Windows\System32` directory. 368 | * 369 | * @link https://ss64.com/nt/tar.html 370 | * 371 | * @return string 372 | */ 373 | function getTarExecutable() { 374 | return 'C:\Windows\System32\tar.exe'; 375 | } 376 | 377 | /** 378 | * Check if a string contains any of the needles in an array. 379 | * Code based on this StackOverflow answer: 380 | * https://stackoverflow.com/a/74876203/2358222 381 | * 382 | * @param string $haystack 383 | * @param array $needles 384 | * @return bool 385 | */ 386 | function str_contains_any($haystack, $needles) { 387 | foreach ($needles as $needle) { 388 | if (str_contains($haystack, $needle)) { 389 | return true; 390 | } 391 | } 392 | return false; 393 | } 394 | -------------------------------------------------------------------------------- /cli/stubs/AcrylicHosts.txt: -------------------------------------------------------------------------------- 1 | 127.0.0.1 *.VALET_TLD 2 | ::1 *.VALET_TLD 3 | 4 | @VALET_HOME_PATH/AcrylicHosts.txt 5 | -------------------------------------------------------------------------------- /cli/stubs/SampleValetDriver.php: -------------------------------------------------------------------------------- 1 | 2 | valet_nginx 3 | valet_nginx 4 | Valet Nginx 5 | NGINX_PATH\nginx.exe 6 | -p 7 | NGINX_PATH 8 | NGINX_PATH\nginx.exe 9 | -p 10 | NGINX_PATH 11 | -s 12 | stop 13 | VALET_HOME_PATH\Log\ 14 | 15 | 16 | -------------------------------------------------------------------------------- /cli/stubs/phpcgiservice.xml: -------------------------------------------------------------------------------- 1 | 2 | PHPCGINAME 3 | PHPCGINAME 4 | Valet PHP-CGI 5 | 6 | PHP_PATH\php-cgi.exe 7 | -b 127.0.0.1:PHP_PORT 8 | VALET_HOME_PATH\Log\ 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /cli/stubs/phpcgixdebugservice.xml: -------------------------------------------------------------------------------- 1 | 2 | PHPCGINAME 3 | PHPCGINAME 4 | Valet PHP-CGI Xdebug 5 | 6 | PHP_PATH\php-cgi.exe 7 | -b 127.0.0.1:PHP_XDEBUG_PORT -d zend_extension=xdebug 8 | VALET_HOME_PATH\Log\ 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /cli/stubs/proxy.valet.conf: -------------------------------------------------------------------------------- 1 | # valet stub: proxy.valet.conf 2 | 3 | server { 4 | listen 127.0.0.1:80; 5 | server_name VALET_SITE www.VALET_SITE *.VALET_SITE; 6 | root /; 7 | charset utf-8; 8 | client_max_body_size 128M; 9 | 10 | location /VALET_STATIC_PREFIX/ { 11 | internal; 12 | alias /; 13 | try_files $uri $uri/; 14 | } 15 | 16 | access_log off; 17 | error_log "VALET_HOME_PATH/Log/VALET_SITE-error.log"; 18 | 19 | error_page 404 "VALET_SERVER_PATH"; 20 | 21 | location / { 22 | proxy_pass VALET_PROXY_HOST; 23 | proxy_set_header Host $host; 24 | proxy_set_header X-Real-IP $remote_addr; 25 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 26 | proxy_set_header X-Forwarded-Proto $scheme; 27 | proxy_set_header X-Client-Verify SUCCESS; 28 | proxy_set_header X-Client-DN $ssl_client_s_dn; 29 | proxy_set_header X-SSL-Subject $ssl_client_s_dn; 30 | proxy_set_header X-SSL-Issuer $ssl_client_i_dn; 31 | proxy_set_header X-NginX-Proxy true; 32 | proxy_set_header Upgrade $http_upgrade; 33 | proxy_set_header Connection "upgrade"; 34 | proxy_http_version 1.1; 35 | proxy_read_timeout 1800; 36 | proxy_connect_timeout 1800; 37 | chunked_transfer_encoding on; 38 | proxy_redirect off; 39 | proxy_buffering off; 40 | 41 | # Prevent being cached... 42 | # Code from https://ubiq.co/tech-blog/disable-nginx-cache/ 43 | 44 | # Kill cache 45 | add_header Last-Modified $date_gmt; 46 | add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; 47 | if_modified_since off; 48 | expires off; 49 | etag off; 50 | # Don't cache it 51 | proxy_no_cache 1; 52 | # Even if cached, don't try to use it 53 | proxy_cache_bypass 1; 54 | } 55 | 56 | location ~ /\.ht { 57 | deny all; 58 | } 59 | } 60 | 61 | server { 62 | listen 127.0.0.1:60; 63 | server_name VALET_SITE www.VALET_SITE *.VALET_SITE; 64 | root /; 65 | charset utf-8; 66 | client_max_body_size 128M; 67 | 68 | add_header X-Robots-Tag 'noindex, nofollow, nosnippet, noarchive'; 69 | 70 | location /VALET_STATIC_PREFIX/ { 71 | internal; 72 | alias /; 73 | try_files $uri $uri/; 74 | } 75 | 76 | access_log off; 77 | error_log "VALET_HOME_PATH/Log/VALET_SITE-error.log"; 78 | 79 | error_page 404 "VALET_SERVER_PATH"; 80 | 81 | location / { 82 | proxy_pass VALET_PROXY_HOST; 83 | proxy_set_header Host $host; 84 | proxy_set_header X-Real-IP $remote_addr; 85 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 86 | proxy_set_header X-Forwarded-Proto $scheme; 87 | } 88 | 89 | location ~ /\.ht { 90 | deny all; 91 | } 92 | } 93 | 94 | -------------------------------------------------------------------------------- /cli/stubs/secure.proxy.valet.conf: -------------------------------------------------------------------------------- 1 | # valet stub: secure.proxy.valet.conf 2 | 3 | server { 4 | listen 127.0.0.1:80; 5 | server_name VALET_SITE www.VALET_SITE *.VALET_SITE; 6 | return 301 https://$host$request_uri; 7 | } 8 | 9 | server { 10 | listen 127.0.0.1:443 ssl; 11 | server_name VALET_SITE www.VALET_SITE *.VALET_SITE; 12 | root /; 13 | charset utf-8; 14 | client_max_body_size 128M; 15 | http2 on; 16 | 17 | location /VALET_STATIC_PREFIX/ { 18 | internal; 19 | alias /; 20 | try_files $uri $uri/; 21 | } 22 | 23 | ssl_certificate "VALET_CERT"; 24 | ssl_certificate_key "VALET_KEY"; 25 | 26 | access_log off; 27 | error_log "VALET_HOME_PATH/Log/VALET_SITE-error.log"; 28 | 29 | error_page 404 "VALET_SERVER_PATH"; 30 | 31 | location / { 32 | proxy_pass VALET_PROXY_HOST; 33 | proxy_set_header Host $host; 34 | proxy_set_header X-Real-IP $remote_addr; 35 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 36 | proxy_set_header X-Forwarded-Proto $scheme; 37 | proxy_set_header X-Client-Verify SUCCESS; 38 | proxy_set_header X-Client-DN $ssl_client_s_dn; 39 | proxy_set_header X-SSL-Subject $ssl_client_s_dn; 40 | proxy_set_header X-SSL-Issuer $ssl_client_i_dn; 41 | proxy_set_header X-NginX-Proxy true; 42 | proxy_set_header Upgrade $http_upgrade; 43 | proxy_set_header Connection "upgrade"; 44 | proxy_http_version 1.1; 45 | proxy_read_timeout 1800; 46 | proxy_connect_timeout 1800; 47 | chunked_transfer_encoding on; 48 | proxy_redirect off; 49 | proxy_buffering off; 50 | 51 | # Prevent being cached... 52 | # Code from https://ubiq.co/tech-blog/disable-nginx-cache/ 53 | 54 | # Kill cache 55 | add_header Last-Modified $date_gmt; 56 | add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; 57 | if_modified_since off; 58 | expires off; 59 | etag off; 60 | # Don't cache it 61 | proxy_no_cache 1; 62 | # Even if cached, don't try to use it 63 | proxy_cache_bypass 1; 64 | } 65 | 66 | location ~ /\.ht { 67 | deny all; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /cli/stubs/secure.valet.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 127.0.0.1:80; 3 | server_name VALET_SITE www.VALET_SITE *.VALET_SITE; 4 | return 302 https://$host$request_uri; 5 | } 6 | 7 | server { 8 | listen 127.0.0.1:443 ssl; 9 | server_name VALET_SITE www.VALET_SITE *.VALET_SITE; 10 | root /; 11 | charset utf-8; 12 | client_max_body_size 512M; 13 | http2 on; 14 | 15 | location ~* /VALET_STATIC_PREFIX/([A-Z]+:)(.*) { 16 | internal; 17 | alias $1; 18 | try_files $2 $2/; 19 | } 20 | 21 | ssl_certificate "VALET_CERT"; 22 | ssl_certificate_key "VALET_KEY"; 23 | 24 | location / { 25 | # Prevent being cached... 26 | # Code from https://ubiq.co/tech-blog/disable-nginx-cache/ 27 | 28 | # Kill cache 29 | add_header Last-Modified $date_gmt; 30 | add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; 31 | if_modified_since off; 32 | expires off; 33 | etag off; 34 | # Don't cache it 35 | proxy_no_cache 1; 36 | # Even if cached, don't try to use it 37 | proxy_cache_bypass 1; 38 | 39 | rewrite ^ "VALET_SERVER_PATH" last; 40 | } 41 | 42 | # location = /favicon.ico { access_log off; log_not_found off; } 43 | # location = /robots.txt { access_log off; log_not_found off; } 44 | 45 | access_log off; 46 | error_log "VALET_HOME_PATH/Log/nginx-error.log"; 47 | 48 | error_page 404 "VALET_SERVER_PATH"; 49 | 50 | location ~ [^/]\.php(/|$) { 51 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 52 | fastcgi_pass 127.0.0.1:$valet_php_port; 53 | fastcgi_index "VALET_SERVER_PATH"; 54 | include fastcgi_params; 55 | fastcgi_param SCRIPT_FILENAME "VALET_SERVER_PATH"; 56 | fastcgi_param HOME 'HOME_PATH'; 57 | fastcgi_param PATH_INFO $fastcgi_path_info; 58 | } 59 | 60 | location ~ /\.ht { 61 | deny all; 62 | } 63 | } 64 | 65 | server { 66 | listen 127.0.0.1:60; 67 | server_name VALET_SITE www.VALET_SITE *.VALET_SITE; 68 | root /; 69 | charset utf-8; 70 | client_max_body_size 128M; 71 | 72 | add_header X-Robots-Tag 'noindex, nofollow, nosnippet, noarchive'; 73 | 74 | location /VALET_STATIC_PREFIX/ { 75 | internal; 76 | alias /; 77 | try_files $uri $uri/; 78 | } 79 | 80 | location / { 81 | rewrite ^ "VALET_SERVER_PATH" last; 82 | } 83 | 84 | # location = /favicon.ico { access_log off; log_not_found off; } 85 | # location = /robots.txt { access_log off; log_not_found off; } 86 | 87 | access_log off; 88 | error_log "VALET_HOME_PATH/Log/nginx-error.log"; 89 | 90 | error_page 404 "VALET_SERVER_PATH"; 91 | 92 | location ~ [^/]\.php(/|$) { 93 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 94 | fastcgi_pass 127.0.0.1:$valet_php_port; 95 | fastcgi_index "VALET_SERVER_PATH"; 96 | include fastcgi_params; 97 | fastcgi_param SCRIPT_FILENAME "VALET_SERVER_PATH"; 98 | fastcgi_param HOME 'HOME_PATH'; 99 | fastcgi_param PATH_INFO $fastcgi_path_info; 100 | } 101 | 102 | location ~ /\.ht { 103 | deny all; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /cli/stubs/unsecure.valet.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 127.0.0.1:80; 3 | server_name VALET_SITE www.VALET_SITE *.VALET_SITE; 4 | root /; 5 | charset utf-8; 6 | client_max_body_size 512M; 7 | 8 | location ~* /VALET_STATIC_PREFIX/([A-Z]+:)(.*) { 9 | internal; 10 | alias $1; 11 | try_files $2 $2/; 12 | } 13 | 14 | location / { 15 | # Prevent being cached... 16 | # Code from https://ubiq.co/tech-blog/disable-nginx-cache/ 17 | 18 | # Kill cache 19 | add_header Last-Modified $date_gmt; 20 | add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; 21 | if_modified_since off; 22 | expires off; 23 | etag off; 24 | # Don't cache it 25 | proxy_no_cache 1; 26 | # Even if cached, don't try to use it 27 | proxy_cache_bypass 1; 28 | 29 | rewrite ^ "VALET_SERVER_PATH" last; 30 | } 31 | 32 | # location = /favicon.ico { access_log off; log_not_found off; } 33 | # location = /robots.txt { access_log off; log_not_found off; } 34 | 35 | access_log off; 36 | error_log "VALET_HOME_PATH/Log/nginx-error.log"; 37 | 38 | error_page 404 "VALET_SERVER_PATH"; 39 | 40 | location ~ [^/]\.php(/|$) { 41 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 42 | fastcgi_pass 127.0.0.1:$valet_php_port; 43 | fastcgi_index "VALET_SERVER_PATH"; 44 | include fastcgi_params; 45 | fastcgi_param SCRIPT_FILENAME "VALET_SERVER_PATH"; 46 | fastcgi_param HOME 'HOME_PATH'; 47 | fastcgi_param PATH_INFO $fastcgi_path_info; 48 | } 49 | 50 | location ~ /\.ht { 51 | deny all; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /cli/stubs/valet.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 127.0.0.1:80 default_server; 3 | root /; 4 | charset utf-8; 5 | client_max_body_size 128M; 6 | 7 | location ~* /VALET_STATIC_PREFIX/([A-Z]+:)(.*) { 8 | internal; 9 | alias $1; 10 | try_files $2 $2/; 11 | } 12 | 13 | location / { 14 | # Prevent being cached... 15 | # Code from https://ubiq.co/tech-blog/disable-nginx-cache/ 16 | 17 | # Kill cache 18 | add_header Last-Modified $date_gmt; 19 | add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; 20 | if_modified_since off; 21 | expires off; 22 | etag off; 23 | # Don't cache it 24 | proxy_no_cache 1; 25 | # Even if cached, don't try to use it 26 | proxy_cache_bypass 1; 27 | 28 | rewrite ^ "VALET_SERVER_PATH" last; 29 | } 30 | 31 | # location = /favicon.ico { access_log off; log_not_found off; } 32 | # location = /robots.txt { access_log off; log_not_found off; } 33 | 34 | access_log off; 35 | error_log "VALET_HOME_PATH/Log/nginx-error.log"; 36 | 37 | error_page 404 "VALET_SERVER_PATH"; 38 | 39 | location ~ [^/]\.php(/|$) { 40 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 41 | fastcgi_pass 127.0.0.1:VALET_PHP_PORT; 42 | fastcgi_index "VALET_SERVER_PATH"; 43 | include fastcgi_params; 44 | fastcgi_param SCRIPT_FILENAME "VALET_SERVER_PATH"; 45 | fastcgi_param HOME "HOME_PATH"; 46 | fastcgi_param PATH_INFO $fastcgi_path_info; 47 | } 48 | 49 | location ~ /\.ht { 50 | deny all; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /cli/templates/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Valet - Not Found 4 | 5 | 10 | 11 | 12 | 13 |
14 | 15 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 |

404

44 |

Not Found

45 |
46 | 47 | 48 | -------------------------------------------------------------------------------- /cli/version.php: -------------------------------------------------------------------------------- 1 | =8.0", 40 | "composer/ca-bundle": "^1.5", 41 | "guzzlehttp/guzzle": "^7.9", 42 | "illuminate/collections": "^8.0|^9.0|^10.0|^11.0", 43 | "illuminate/container": "^8.0|^9.0|^10.0|^11.0", 44 | "mnapoli/silly": "^1.9", 45 | "phpseclib/phpseclib": "^3.0", 46 | "symfony/polyfill-php80": "^1.31", 47 | "symfony/process": "^4.0|^5.0|^6.0|^7.0", 48 | "symfony/yaml": "^5.0|^6.0|^7.0" 49 | }, 50 | "conflict": { 51 | "cretueusebiu/valet-windows": "*" 52 | }, 53 | "bin": [ 54 | "valet" 55 | ], 56 | "config": { 57 | "sort-packages": true 58 | }, 59 | "extra": { 60 | "branch-alias": { 61 | "dev-master": "3.x-dev" 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /emergency_uninstall_services.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | echo "Stopping and uninstalling all services..." 4 | 5 | @REM Stop and uninstall Acrylic process 6 | echo Stopping AcrylicDNSProxySvc process... 7 | taskkill /fi "Services eq AcrylicDNSProxySvc" /F 8 | echo Uninstalling AcrylicDNSProxySvc process... 9 | sc delete AcrylicDNSProxySvc 10 | 11 | @REM Stop nginx process 12 | echo Stopping nginx process... 13 | taskkill /IM nginx.exe /F 14 | 15 | for /f %%f in ('dir /b /s "%UserProfile%\.config\valet\Services\*.exe"') do ( 16 | @REM %%~dpnf is the path to the executable without the extension 17 | echo Stopping %%~dpnf... 18 | %%~dpnf stop 19 | 20 | echo Uninstalling %%~dpnf... 21 | %%~dpnf uninstall 22 | ) 23 | 24 | echo "All services stopped and uninstalled." 25 | -------------------------------------------------------------------------------- /phpcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | yCodeTech's Coding Standards 4 | 5 | 6 | . 7 | 8 | 9 | 10 | 11 | 12 | web/wp 13 | web/app/plugins 14 | web/app/mu-plugins 15 | vendor/ 16 | web/app/uploads/ 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 26 | 27 | *facades.php 28 | 29 | 32 | 33 | *facades.php 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /server.php: -------------------------------------------------------------------------------- 1 | uriFromRequestUri($_SERVER['REQUEST_URI']); 29 | $siteName = $server->siteNameFromHttpHost($_SERVER['HTTP_HOST']); 30 | $valetSitePath = $server->sitePath($siteName); 31 | 32 | /** 33 | * Show 404 if the site path is not found. 34 | */ 35 | if (is_null($valetSitePath) && is_null($valetSitePath = $server->defaultSitePath())) { 36 | $server->show404(); 37 | } 38 | 39 | // Resolve the site path to an absolute path. 40 | $valetSitePath = realpath($valetSitePath); 41 | 42 | /** 43 | * Find the appropriate Valet driver for the request. 44 | */ 45 | $valetDriver = null; 46 | 47 | $valetDriver = ValetDriver::assign($valetSitePath, $siteName, $uri); 48 | 49 | // Show 404 if no driver is found. 50 | if (!$valetDriver) { 51 | $server->show404(); 52 | } 53 | 54 | // Set the ngrok server forwarded host 55 | $server->setNgrokServerForwardedHost(); 56 | 57 | /** 58 | * Attempt to load server environment variables. 59 | */ 60 | $valetDriver->loadServerEnvironmentVariables($valetSitePath, $siteName); 61 | 62 | /** 63 | * Allow driver to mutate incoming URL. 64 | */ 65 | $uri = $valetDriver->mutateUri($uri); 66 | 67 | /** 68 | * Determine if the incoming request is for a static file. 69 | */ 70 | $staticFilePath = $server->isRequestStaticFile($uri, $valetSitePath, $siteName, $valetDriver); 71 | 72 | if ($staticFilePath) { 73 | return $valetDriver->serveStaticFile($staticFilePath, $valetSitePath, $siteName, $uri); 74 | } 75 | 76 | /** 77 | * Attempt to dispatch to a front controller. 78 | */ 79 | $frontControllerPath = $valetDriver->frontControllerPath($valetSitePath, $siteName, $uri); 80 | 81 | if (!$frontControllerPath) { 82 | $server->showDirectoryListingOr404($valetSitePath, $uri); 83 | } 84 | 85 | /** 86 | * Change the working directory and require the front controller. 87 | */ 88 | 89 | // Change the working directory to the front controller's directory. 90 | $server->changeDir($frontControllerPath); 91 | 92 | require $frontControllerPath; -------------------------------------------------------------------------------- /valet: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | 0 && count($versionMatches) > 0) { 79 | 80 | $phpVersion = $versionMatches[0]; 81 | 82 | // Escape the PHP executable path for shell execution 83 | $phpPath = escapeshellcmd($pathMatches[0]); 84 | 85 | echo PHP_EOL . "Proxying the command to $phpVersion at $phpPath" . PHP_EOL . PHP_EOL; 86 | 87 | // Construct the proxy command with the PHP executable and arguments 88 | $phpProxyCommand = "$phpPath $phpArgs"; 89 | 90 | echo $phpProxyCommand . PHP_EOL; 91 | 92 | // Execute the proxy command 93 | passthru($phpProxyCommand); 94 | echo PHP_EOL; 95 | } 96 | else { 97 | echo "Something went wrong finding the php executable." . PHP_EOL; 98 | } 99 | 100 | exit; 101 | } 102 | 103 | require $valetCli; -------------------------------------------------------------------------------- /valet.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | setlocal DISABLEDELAYEDEXPANSION 3 | SET BIN_TARGET=%~dp0/valet 4 | php "%BIN_TARGET%" %* 5 | --------------------------------------------------------------------------------