├── .github └── ISSUE_TEMPLATE │ └── bug_report.md ├── LICENSE.txt ├── SECURITY.md ├── bin └── ngrok ├── cli ├── Valet │ ├── CommandLine.php │ ├── Configuration.php │ ├── Contracts │ │ ├── PackageManager.php │ │ └── ServiceManager.php │ ├── DnsMasq.php │ ├── Filesystem.php │ ├── Nginx.php │ ├── Ngrok.php │ ├── PackageManagers │ │ ├── Apt.php │ │ ├── Dnf.php │ │ ├── Eopkg.php │ │ ├── Homebrew.php │ │ ├── PackageKit.php │ │ ├── Pacman.php │ │ └── Yum.php │ ├── PhpFpm.php │ ├── Requirements.php │ ├── ServiceManagers │ │ ├── Homebrew.php │ │ ├── LinuxService.php │ │ └── Systemd.php │ ├── Site.php │ └── Valet.php ├── drivers │ ├── BasicValetDriver.php │ ├── BedrockValetDriver.php │ ├── Cake2ValetDriver.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 ├── scripts │ ├── fetch-share-url.sh │ ├── install.sh │ └── update.sh ├── stubs │ ├── SampleValetDriver.php │ ├── dnsmasq.conf │ ├── dnsmasq_options │ ├── etc-phpfpm-valet.conf │ ├── fastcgi_params │ ├── fpm.conf │ ├── init │ │ ├── systemd │ │ └── sysvinit │ ├── isolated.valet.conf │ ├── networkmanager.conf │ ├── nginx.conf │ ├── openssl.conf │ ├── php-fpm.service.d │ │ └── valet.conf │ ├── proxy.valet.conf │ ├── resolved.conf │ ├── secure.proxy.valet.conf │ ├── secure.valet.conf │ ├── valet-dns │ └── valet.conf ├── templates │ └── 404.html └── valet.php ├── composer.json ├── develop ├── readme.md ├── server.php └── valet /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a bug report to improve valet-linux 4 | title: 'Bug: ' 5 | labels: Bug, Needs review 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Basic info** 11 | 12 | | Distro (Name and version) | PHP Version | Valet version | 13 | | ------------------------- | ----------- | ------------- | 14 | | Ex: Manjaro 18.3 | 7.3.1 | 2.2.3 | 15 | 16 | - [ ] I've checked the issue queue and could not find anything similar to my bug. 17 | - [ ] I'm on the latest version of valet-linux (`valet --version`): `` 18 | - [ ] I've run `valet fix` and `valet install` after updating and before submitting my issue/feature. 19 | 20 | **What is the problem?** 21 | A description of what you think the problem is. 22 | 23 | **What was supposed to happen?** 24 | A description of what you think was supposed to happen. 25 | 26 | **What actually happened?** 27 | A description of what actually happened. 28 | 29 | **How to reproduce this?** 30 | A step by step guide on how to reproduce this issue. 31 | 32 | **What is the solution?** 33 | A description of the proposed solution. 34 | 35 | **Sources** 36 | All sources related to the bug. If the bug uses external tools like PHP extensions it should at 37 | least contain a link to the tool. Any other media which proves helpful can be included here. 38 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Here a list of supported versions 6 | 7 | | Version | Supported | Support until | 8 | | ------- | ------------------ | ---- | 9 | | 2.1.x | :white_check_mark: | Currently under support | 10 | | 2.0.x | :white_check_mark: | until 1/1/2020 | 11 | | < 2.0 | :x: | - | 12 | 13 | ## Reporting a Vulnerability 14 | 15 | Just open a new issue here on GitHub. 16 | -------------------------------------------------------------------------------- /bin/ngrok: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpriego/valet-linux/30072af0f53aefae4f3137bec5590a7226b568c8/bin/ngrok -------------------------------------------------------------------------------- /cli/Valet/CommandLine.php: -------------------------------------------------------------------------------- 1 | runCommand($command . ' > /dev/null 2>&1'); 18 | } 19 | 20 | /** 21 | * Simple global function to run commands. 22 | * 23 | * @param string $command 24 | * @return void 25 | */ 26 | public function quietlyAsUser($command) 27 | { 28 | $this->quietly('sudo -u ' . user() . ' ' . $command . ' > /dev/null 2>&1'); 29 | } 30 | 31 | /** 32 | * Pass the command to the command line and display the output. 33 | * 34 | * @param string $command 35 | * @return void 36 | */ 37 | public function passthru($command) 38 | { 39 | passthru($command); 40 | } 41 | 42 | /** 43 | * Run the given command as the non-root user. 44 | * 45 | * @param string $command 46 | * @param callable $onError 47 | * @return string 48 | */ 49 | public function run($command, callable $onError = null) 50 | { 51 | return $this->runCommand($command, $onError); 52 | } 53 | 54 | /** 55 | * Run the given command. 56 | * 57 | * @param string $command 58 | * @param callable $onError 59 | * @return string 60 | */ 61 | public function runAsUser($command, callable $onError = null) 62 | { 63 | return $this->runCommand('sudo -u ' . user() . ' ' . $command, $onError); 64 | } 65 | 66 | /** 67 | * Run the given command. 68 | * 69 | * @param string $command 70 | * @param callable $onError 71 | * @return string 72 | */ 73 | protected function runCommand($command, callable $onError = null) 74 | { 75 | $onError = $onError ?: function () { 76 | }; 77 | 78 | // Symfony's 4.x Process component has deprecated passing a command string 79 | // to the constructor, but older versions (which Valet's Composer 80 | // constraints allow) don't have the fromShellCommandLine method. 81 | // For more information, see: https://github.com/laravel/valet/pull/761 82 | if (method_exists(Process::class, 'fromShellCommandline')) { 83 | $process = Process::fromShellCommandline($command); 84 | } else { 85 | $process = new Process($command); 86 | } 87 | 88 | $processOutput = ''; 89 | $process->setTimeout(null)->run(function ($type, $line) use (&$processOutput) { 90 | $processOutput .= $line; 91 | }); 92 | 93 | if ($process->getExitCode() > 0) { 94 | $onError($process->getExitCode(), $processOutput); 95 | } 96 | 97 | return $processOutput; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /cli/Valet/Configuration.php: -------------------------------------------------------------------------------- 1 | files = $files; 17 | } 18 | 19 | /** 20 | * Install the Valet configuration file. 21 | * 22 | * @return void 23 | */ 24 | public function install() 25 | { 26 | $this->createConfigurationDirectory(); 27 | $this->createDriversDirectory(); 28 | $this->createSitesDirectory(); 29 | $this->createExtensionsDirectory(); 30 | $this->createLogDirectory(); 31 | $this->createCertificatesDirectory(); 32 | $this->writeBaseConfiguration(); 33 | 34 | $this->files->chown($this->path(), user()); 35 | } 36 | 37 | /** 38 | * Uninstall the Valet configuration folder. 39 | * 40 | * @return void 41 | */ 42 | public function uninstall() 43 | { 44 | if ($this->files->isDir(VALET_HOME_PATH, user())) { 45 | $this->files->remove(VALET_HOME_PATH); 46 | } 47 | } 48 | 49 | /** 50 | * Create the Valet configuration directory. 51 | * 52 | * @return void 53 | */ 54 | public function createConfigurationDirectory() 55 | { 56 | $this->files->ensureDirExists(VALET_HOME_PATH, user()); 57 | } 58 | 59 | /** 60 | * Create the Valet drivers directory. 61 | * 62 | * @return void 63 | */ 64 | public function createDriversDirectory() 65 | { 66 | if ($this->files->isDir($driversDirectory = VALET_HOME_PATH . '/Drivers')) { 67 | return; 68 | } 69 | 70 | $this->files->mkdirAsUser($driversDirectory); 71 | 72 | $this->files->putAsUser( 73 | $driversDirectory . '/SampleValetDriver.php', 74 | $this->files->get(__DIR__ . '/../stubs/SampleValetDriver.php') 75 | ); 76 | } 77 | 78 | /** 79 | * Create the Valet sites directory. 80 | * 81 | * @return void 82 | */ 83 | public function createSitesDirectory() 84 | { 85 | $this->files->ensureDirExists(VALET_HOME_PATH . '/Sites', user()); 86 | } 87 | 88 | /** 89 | * Create the directory for the Valet extensions. 90 | * 91 | * @return void 92 | */ 93 | public function createExtensionsDirectory() 94 | { 95 | $this->files->ensureDirExists(VALET_HOME_PATH . '/Extensions', user()); 96 | } 97 | 98 | /** 99 | * Create the directory for Nginx logs. 100 | * 101 | * @return void 102 | */ 103 | public function createLogDirectory() 104 | { 105 | $this->files->ensureDirExists(VALET_HOME_PATH . '/Log', user()); 106 | 107 | $this->files->touch(VALET_HOME_PATH . '/Log/nginx-error.log'); 108 | } 109 | 110 | /** 111 | * Create the directory for SSL certificates. 112 | * 113 | * @return void 114 | */ 115 | public function createCertificatesDirectory() 116 | { 117 | $this->files->ensureDirExists(VALET_HOME_PATH . '/Certificates', user()); 118 | } 119 | 120 | /** 121 | * Write the base, initial configuration for Valet. 122 | */ 123 | public function writeBaseConfiguration() 124 | { 125 | if (!$this->files->exists($this->path())) { 126 | $this->write(['domain' => 'test', 'paths' => [], 'port' => '80']); 127 | } 128 | } 129 | 130 | /** 131 | * Add the given path to the configuration. 132 | * 133 | * @param string $path 134 | * @param bool $prepend 135 | * @return void 136 | */ 137 | public function addPath($path, $prepend = false) 138 | { 139 | $this->write(tap($this->read(), function (&$config) use ($path, $prepend) { 140 | $method = $prepend ? 'prepend' : 'push'; 141 | 142 | $config['paths'] = collect($config['paths'])->{$method}($path)->unique()->all(); 143 | })); 144 | } 145 | 146 | /** 147 | * Prepend the given path to the configuration. 148 | * 149 | * @param string $path 150 | * @return void 151 | */ 152 | public function prependPath($path) 153 | { 154 | $this->addPath($path, true); 155 | } 156 | 157 | /** 158 | * Add the given path to the configuration. 159 | * 160 | * @param string $path 161 | * @return void 162 | */ 163 | public function removePath($path) 164 | { 165 | $this->write(tap($this->read(), function (&$config) use ($path) { 166 | $config['paths'] = collect($config['paths'])->reject(function ($value) use ($path) { 167 | return $value === $path; 168 | })->values()->all(); 169 | })); 170 | } 171 | 172 | /** 173 | * Prune all non-existent paths from the configuration. 174 | * 175 | * @return void 176 | */ 177 | public function prune() 178 | { 179 | if (!$this->files->exists($this->path())) { 180 | return; 181 | } 182 | 183 | $this->write(tap($this->read(), function (&$config) { 184 | $config['paths'] = collect($config['paths'])->filter(function ($path) { 185 | return $this->files->isDir($path); 186 | })->values()->all(); 187 | })); 188 | } 189 | 190 | /** 191 | * Read the configuration file as JSON. 192 | * 193 | * @return array 194 | */ 195 | public function read() 196 | { 197 | if (!$this->files->exists($this->path())){ 198 | // Create default configuration 199 | $this->install(); 200 | } 201 | return json_decode($this->files->get($this->path()), true); 202 | } 203 | 204 | /** 205 | * Get a configuration value. 206 | * 207 | * @param string $key 208 | * @param mixed $default 209 | * @return mixed 210 | */ 211 | public function get($key, $default = null) 212 | { 213 | $config = $this->read(); 214 | 215 | return array_key_exists($key, $config) ? $config[$key] : $default; 216 | } 217 | 218 | /** 219 | * Update a specific key in the configuration file. 220 | * 221 | * @param string $key 222 | * @param mixed $value 223 | * @return array 224 | */ 225 | public function updateKey($key, $value) 226 | { 227 | return tap($this->read(), function (&$config) use ($key, $value) { 228 | $config[$key] = $value; 229 | $this->write($config); 230 | }); 231 | } 232 | 233 | /** 234 | * Write the given configuration to disk. 235 | * 236 | * @param array $config 237 | * @return void 238 | */ 239 | public function write(array $config) 240 | { 241 | $this->files->putAsUser($this->path(), json_encode( 242 | $config, 243 | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES 244 | ) . PHP_EOL); 245 | } 246 | 247 | /** 248 | * Get the configuration file path. 249 | * 250 | * @return string 251 | */ 252 | public function path() 253 | { 254 | return VALET_HOME_PATH . '/config.json'; 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /cli/Valet/Contracts/PackageManager.php: -------------------------------------------------------------------------------- 1 | pm = $pm; 32 | $this->sm = $sm; 33 | $this->cli = $cli; 34 | $this->files = $files; 35 | $this->rclocal = '/etc/rc.local'; 36 | $this->resolvconf = '/etc/resolv.conf'; 37 | $this->dnsmasqconf = '/etc/dnsmasq.conf'; 38 | $this->configPath = '/etc/dnsmasq.d/valet'; 39 | $this->dnsmasqOpts = '/etc/dnsmasq.d/options'; 40 | $this->nmConfigPath = '/etc/NetworkManager/conf.d/valet.conf'; 41 | $this->resolvedConfigPath = '/etc/systemd/resolved.conf'; 42 | } 43 | 44 | /** 45 | * Install and configure DnsMasq. 46 | * 47 | * @param bool $lock Lock or Unlock the file 48 | * 49 | * @return void 50 | */ 51 | private function _lockResolvConf($lock = true) 52 | { 53 | $arg = $lock ? '+i' : '-i'; 54 | 55 | if (!$this->files->isLink($this->resolvconf)) { 56 | $this->cli->run( 57 | "chattr {$arg} {$this->resolvconf}", 58 | function ($code, $msg) { 59 | warning($msg); 60 | } 61 | ); 62 | } 63 | } 64 | 65 | /** 66 | * Enable nameserver merging 67 | * 68 | * @return void 69 | */ 70 | private function _mergeDns() 71 | { 72 | $optDir = '/opt/valet-linux'; 73 | $script = $optDir . '/valet-dns'; 74 | 75 | $this->pm->ensureInstalled('inotify-tools'); 76 | //$this->files->remove($optDir); 77 | $this->files->ensureDirExists($optDir); 78 | $this->files->put($script, $this->files->get(__DIR__ . '/../stubs/valet-dns')); 79 | $this->cli->run("chmod +x $script"); 80 | $this->sm->installValetDns($this->files); 81 | 82 | if ($this->files->exists($this->rclocal)) { 83 | $this->files->restore($this->rclocal); 84 | } 85 | 86 | $this->files->backup($this->resolvconf); 87 | $this->files->unlink($this->resolvconf); 88 | 89 | return true; 90 | } 91 | 92 | /** 93 | * Install and configure DnsMasq. 94 | * 95 | * @param string $domain Domain TLD to use 96 | * 97 | * @return void 98 | */ 99 | public function install($domain = 'test') 100 | { 101 | $this->dnsmasqSetup(); 102 | $this->fixResolved(); 103 | $this->createCustomConfigFile($domain); 104 | $this->pm->nmRestart($this->sm); 105 | $this->sm->restart('dnsmasq'); 106 | $this->sm->start('valet-dns'); 107 | } 108 | 109 | /** 110 | * Append the custom DnsMasq configuration file to the main configuration file. 111 | * 112 | * @param string $domain Domain TLD to use 113 | * 114 | * @return void 115 | */ 116 | public function createCustomConfigFile($domain) 117 | { 118 | $this->files->putAsUser($this->configPath, 'address=/.' . $domain . '/127.0.0.1' . PHP_EOL); 119 | } 120 | 121 | /** 122 | * Fix systemd-resolved configuration. 123 | * 124 | * @return void 125 | */ 126 | public function fixResolved() 127 | { 128 | // $resolved = $this->resolvedConfigPath; 129 | 130 | // $this->files->backup($resolved); 131 | // $this->files->putAsUser($resolved, $this->files->get(__DIR__.'/../stubs/resolved.conf')); 132 | 133 | $this->sm->disable('systemd-resolved'); 134 | $this->sm->stop('systemd-resolved'); 135 | } 136 | 137 | /** 138 | * Setup dnsmasq with Network Manager. 139 | * 140 | * @return void 141 | */ 142 | public function dnsmasqSetup() 143 | { 144 | $this->pm->ensureInstalled('dnsmasq'); 145 | $this->sm->enable('dnsmasq'); 146 | 147 | $this->files->ensureDirExists('/etc/NetworkManager/conf.d'); 148 | $this->files->ensureDirExists('/etc/dnsmasq.d'); 149 | 150 | $this->files->uncommentLine('IGNORE_RESOLVCONF', '/etc/default/dnsmasq'); 151 | 152 | $this->_lockResolvConf(false); 153 | $this->_mergeDns(); 154 | 155 | $this->files->unlink('/etc/dnsmasq.d/network-manager'); 156 | $this->files->backup($this->dnsmasqconf); 157 | 158 | $this->files->putAsUser($this->dnsmasqconf, $this->files->get(__DIR__ . '/../stubs/dnsmasq.conf')); 159 | $this->files->putAsUser($this->dnsmasqOpts, $this->files->get(__DIR__ . '/../stubs/dnsmasq_options')); 160 | $this->files->putAsUser($this->nmConfigPath, $this->files->get(__DIR__ . '/../stubs/networkmanager.conf')); 161 | } 162 | 163 | /** 164 | * Update the domain used by DnsMasq. 165 | * 166 | * @param string $oldDomain Old TLD 167 | * @param string $newDomain New TLD 168 | * 169 | * @return void 170 | */ 171 | public function updateDomain($oldDomain, $newDomain) 172 | { 173 | $this->createCustomConfigFile($newDomain); 174 | $this->sm->restart('dnsmasq'); 175 | } 176 | 177 | /** 178 | * Start the DnsMasq service. 179 | * 180 | * @return void 181 | */ 182 | public function start() 183 | { 184 | $this->sm->start('dnsmasq'); 185 | } 186 | 187 | /** 188 | * Restart the DnsMasq service. 189 | * 190 | * @return void 191 | */ 192 | public function restart() 193 | { 194 | $this->sm->restart('dnsmasq'); 195 | } 196 | 197 | /** 198 | * Stop the DnsMasq service. 199 | * 200 | * @return void 201 | */ 202 | public function stop() 203 | { 204 | $this->sm->stop('dnsmasq'); 205 | } 206 | 207 | /** 208 | * DnsMasq service status. 209 | * 210 | * @return void 211 | */ 212 | public function status() 213 | { 214 | $this->sm->printStatus('dnsmasq'); 215 | } 216 | 217 | /** 218 | * Delete the DnsMasq config file. 219 | * 220 | * @return void 221 | */ 222 | public function uninstall() 223 | { 224 | $this->sm->stop('valet-dns'); 225 | $this->sm->disable('valet-dns'); 226 | 227 | $this->cli->passthru('rm -rf /opt/valet-linux'); 228 | $this->files->unlink($this->configPath); 229 | $this->files->unlink($this->dnsmasqOpts); 230 | $this->files->unlink($this->nmConfigPath); 231 | $this->files->restore($this->resolvedConfigPath); 232 | 233 | $this->_lockResolvConf(false); 234 | $this->files->restore($this->rclocal); 235 | //$this->files->restore($this->resolvconf); 236 | 237 | $this->cli->passthru('rm -f /etc/resolv.conf'); 238 | $this->sm->stop('systemd-resolved'); 239 | $this->sm->start('systemd-resolved'); 240 | $this->files->symlink('/run/systemd/resolve/resolv.conf', $this->resolvconf); 241 | 242 | $this->files->restore($this->dnsmasqconf); 243 | $this->files->commentLine('IGNORE_RESOLVCONF', '/etc/default/dnsmasq'); 244 | 245 | $this->pm->nmRestart($this->sm); 246 | $this->sm->restart('dnsmasq'); 247 | 248 | info('Valet DNS changes have been rolled back'); 249 | warning('If your system depended on systemd-resolved (like Ubuntu 17.04), please enable it manually'); 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /cli/Valet/Filesystem.php: -------------------------------------------------------------------------------- 1 | toIterator($files)); 27 | $files = array_reverse($files); 28 | foreach ($files as $file) { 29 | if (!file_exists($file) && !is_link($file)) { 30 | continue; 31 | } 32 | 33 | if (is_dir($file) && !is_link($file)) { 34 | $this->remove(new \FilesystemIterator($file)); 35 | 36 | if (true !== @rmdir($file)) { 37 | throw new \Exception(sprintf('Failed to remove directory "%s".', $file), 0, null); 38 | } 39 | } else { 40 | // https://bugs.php.net/bug.php?id=52176 41 | if ('\\' === DIRECTORY_SEPARATOR && is_dir($file)) { 42 | if (true !== @rmdir($file)) { 43 | throw new \Exception(sprintf('Failed to remove file "%s".', $file), 0, null); 44 | } 45 | } else { 46 | if (true !== @unlink($file)) { 47 | throw new \Exception(sprintf('Failed to remove file "%s".', $file), 0, null); 48 | } 49 | } 50 | } 51 | } 52 | } 53 | 54 | /** 55 | * Determine if the given path is a directory. 56 | * 57 | * @param string $path 58 | * @return bool 59 | */ 60 | public function isDir($path) 61 | { 62 | return is_dir($path); 63 | } 64 | 65 | /** 66 | * Create a directory. 67 | * 68 | * @param string $path 69 | * @param string|null $owner 70 | * @param int $mode 71 | * @return void 72 | */ 73 | public function mkdir($path, $owner = null, $mode = 0755) 74 | { 75 | if (!mkdir($path, $mode, true) && !is_dir($path)) { 76 | throw new \RuntimeException(sprintf('Directory "%s" was not created', $path)); 77 | } 78 | 79 | if ($owner) { 80 | $this->chown($path, $owner); 81 | } 82 | } 83 | 84 | /** 85 | * Ensure that the given directory exists. 86 | * 87 | * @param string $path 88 | * @param string|null $owner 89 | * @param int $mode 90 | * @return void 91 | */ 92 | public function ensureDirExists($path, $owner = null, $mode = 0755) 93 | { 94 | if (!$this->isDir($path)) { 95 | $this->mkdir($path, $owner, $mode); 96 | } 97 | } 98 | 99 | /** 100 | * Create a directory as the non-root user. 101 | * 102 | * @param string $path 103 | * @param int $mode 104 | * @return void 105 | */ 106 | public function mkdirAsUser($path, $mode = 0755) 107 | { 108 | return $this->mkdir($path, user(), $mode); 109 | } 110 | 111 | /** 112 | * Touch the given path. 113 | * 114 | * @param string $path 115 | * @param string|null $owner 116 | * @return string 117 | */ 118 | public function touch($path, $owner = null) 119 | { 120 | touch($path); 121 | 122 | if ($owner) { 123 | $this->chown($path, $owner); 124 | } 125 | 126 | return $path; 127 | } 128 | 129 | /** 130 | * Touch the given path as the non-root user. 131 | * 132 | * @param string $path 133 | * @return void 134 | */ 135 | public function touchAsUser($path) 136 | { 137 | return $this->touch($path, user()); 138 | } 139 | 140 | /** 141 | * Determine if the given file exists. 142 | * 143 | * @param string $path 144 | * @return bool 145 | */ 146 | public function exists($files) 147 | { 148 | foreach ($this->toIterator($files) as $file) { 149 | if (!file_exists($file)) { 150 | return false; 151 | } 152 | } 153 | 154 | return true; 155 | } 156 | 157 | /** 158 | * Read the contents of the given file. 159 | * 160 | * @param string $path 161 | * @return string 162 | */ 163 | public function get($path) 164 | { 165 | if (!file_exists($path)) return null; 166 | return file_get_contents($path); 167 | } 168 | 169 | /** 170 | * Write to the given file. 171 | * 172 | * @param string $path 173 | * @param string $contents 174 | * @param string|null $owner 175 | * @return string 176 | */ 177 | public function put($path, $contents, $owner = null) 178 | { 179 | $status = file_put_contents($path, $contents); 180 | 181 | if ($owner) { 182 | $this->chown($path, $owner); 183 | } 184 | 185 | return $status; 186 | } 187 | 188 | /** 189 | * Write to the given file as the non-root user. 190 | * 191 | * @param string $path 192 | * @param string $contents 193 | * @return string 194 | */ 195 | public function putAsUser($path, $contents) 196 | { 197 | return $this->put($path, $contents, user()); 198 | } 199 | 200 | /** 201 | * Append the contents to the given file. 202 | * 203 | * @param string $path 204 | * @param string $contents 205 | * @param string|null $owner 206 | * @return void 207 | */ 208 | public function append($path, $contents, $owner = null) 209 | { 210 | file_put_contents($path, $contents, FILE_APPEND); 211 | 212 | if ($owner) { 213 | $this->chown($path, $owner); 214 | } 215 | } 216 | 217 | /** 218 | * Append the contents to the given file as the non-root user. 219 | * 220 | * @param string $path 221 | * @param string $contents 222 | * @return void 223 | */ 224 | public function appendAsUser($path, $contents) 225 | { 226 | $this->append($path, $contents, user()); 227 | } 228 | 229 | /** 230 | * Copy the given file to a new location. 231 | * 232 | * @param string $from 233 | * @param string $to 234 | * @return void 235 | */ 236 | public function copy($from, $to) 237 | { 238 | copy($from, $to); 239 | } 240 | 241 | /** 242 | * Backup the given file. 243 | * 244 | * @param string $file 245 | * @return bool 246 | */ 247 | public function backup($file) 248 | { 249 | $to = $file . '.bak'; 250 | 251 | if (!$this->exists($to)) { 252 | if ($this->exists($file)) { 253 | return rename($file, $to); 254 | } 255 | } 256 | 257 | return false; 258 | } 259 | 260 | /** 261 | * Restore a backed up file. 262 | * 263 | * @param string $file 264 | * @return bool 265 | */ 266 | public function restore($file) 267 | { 268 | $from = $file . '.bak'; 269 | 270 | if ($this->exists($from)) { 271 | return rename($from, $file); 272 | } 273 | 274 | return false; 275 | } 276 | 277 | /** 278 | * Copy the given file to a new location for the non-root user. 279 | * 280 | * @param string $from 281 | * @param string $to 282 | * @return void 283 | */ 284 | public function copyAsUser($from, $to) 285 | { 286 | copy($from, $to); 287 | 288 | $this->chown($to, user()); 289 | } 290 | 291 | /** 292 | * Create a symlink to the given target. 293 | * 294 | * @param string $target 295 | * @param string $link 296 | * @return void 297 | */ 298 | public function symlink($target, $link) 299 | { 300 | if ($this->exists($link)) { 301 | $this->unlink($link); 302 | } 303 | 304 | symlink($target, $link); 305 | } 306 | 307 | /** 308 | * Create a symlink to the given target for the non-root user. 309 | * 310 | * This uses the command line as PHP can't change symlink permissions. 311 | * 312 | * @param string $target 313 | * @param string $link 314 | * @return void 315 | */ 316 | public function symlinkAsUser($target, $link) 317 | { 318 | if (is_link($link)) { 319 | $this->unlink($link); 320 | } 321 | 322 | CommandLineFacade::runAsUser('ln -s ' . escapeshellarg($target) . ' ' . escapeshellarg($link)); 323 | } 324 | 325 | /** 326 | * Comment a line in a file. 327 | * 328 | * @param string $line 329 | * @param string $file 330 | * @return void 331 | */ 332 | public function commentLine($line, $file) 333 | { 334 | if ($this->exists($file)) { 335 | $command = "sed -i '/{$line}/ s/^/# /' {$file}"; 336 | CommandLineFacade::run($command); 337 | } 338 | } 339 | 340 | /** 341 | * Uncomment a line in a file. 342 | * 343 | * @param string $line 344 | * @param string $file 345 | * @return void 346 | */ 347 | public function uncommentLine($line, $file) 348 | { 349 | if ($this->exists($file)) { 350 | $command = "sed -i '/{$line}/ s/# *//' {$file}"; 351 | CommandLineFacade::run($command); 352 | } 353 | } 354 | 355 | /** 356 | * Delete the file at the given path. 357 | * 358 | * @param string $path 359 | * @return void 360 | */ 361 | public function unlink($path) 362 | { 363 | if (file_exists($path) || is_link($path)) { 364 | @unlink($path); 365 | } 366 | } 367 | 368 | /** 369 | * Change the owner of the given path. 370 | * 371 | * @param string $path 372 | * @param string $user 373 | */ 374 | public function chown($path, $user) 375 | { 376 | chown($path, $user); 377 | } 378 | 379 | /** 380 | * Change the group of the given path. 381 | * 382 | * @param string $path 383 | * @param string $group 384 | */ 385 | public function chgrp($path, $group) 386 | { 387 | chgrp($path, $group); 388 | } 389 | 390 | /** 391 | * Resolve the given path. 392 | * 393 | * @param string $path 394 | * @return string 395 | */ 396 | public function realpath($path) 397 | { 398 | return realpath($path); 399 | } 400 | 401 | /** 402 | * Determine if the given path is a symbolic link. 403 | * 404 | * @param string $path 405 | * @return bool 406 | */ 407 | public function isLink($path) 408 | { 409 | return is_link($path); 410 | } 411 | 412 | /** 413 | * Resolve the given symbolic link. 414 | * 415 | * @param string $path 416 | * @return string 417 | */ 418 | public function readLink($path) 419 | { 420 | $link = $path; 421 | 422 | while (is_link($link)) { 423 | $link = readlink($link); 424 | } 425 | 426 | return $link; 427 | } 428 | 429 | /** 430 | * Remove all of the broken symbolic links at the given path. 431 | * 432 | * @param string $path 433 | * @return void 434 | */ 435 | public function removeBrokenLinksAt($path) 436 | { 437 | collect($this->scandir($path)) 438 | ->filter(function ($file) use ($path) { 439 | return $this->isBrokenLink($path . '/' . $file); 440 | }) 441 | ->each(function ($file) use ($path) { 442 | $this->unlink($path . '/' . $file); 443 | }); 444 | } 445 | 446 | /** 447 | * Determine if the given path is a broken symbolic link. 448 | * 449 | * @param string $path 450 | * @return bool 451 | */ 452 | public function isBrokenLink($path) 453 | { 454 | return is_link($path) && !file_exists($path); 455 | } 456 | 457 | /** 458 | * Scan the given directory path. 459 | * 460 | * @param string $path 461 | * @return array 462 | */ 463 | public function scandir($path) 464 | { 465 | return collect(scandir($path)) 466 | ->reject(function ($file) { 467 | return in_array($file, ['.', '..']); 468 | })->values()->all(); 469 | } 470 | } 471 | -------------------------------------------------------------------------------- /cli/Valet/Nginx.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 34 | $this->pm = $pm; 35 | $this->sm = $sm; 36 | $this->site = $site; 37 | $this->files = $files; 38 | $this->configuration = $configuration; 39 | $this->nginx_conf = '/etc/nginx/nginx.conf'; 40 | $this->sites_available_conf = '/etc/nginx/sites-available/valet.conf'; 41 | $this->sites_enabled_conf = '/etc/nginx/sites-enabled/valet.conf'; 42 | } 43 | 44 | /** 45 | * Install the configuration files for Nginx. 46 | * 47 | * @return void 48 | */ 49 | public function install() 50 | { 51 | $this->pm->ensureInstalled('nginx'); 52 | $this->sm->enable('nginx'); 53 | $this->files->ensureDirExists('/etc/nginx/sites-available'); 54 | $this->files->ensureDirExists('/etc/nginx/sites-enabled'); 55 | 56 | $this->stop(); 57 | $this->installConfiguration(); 58 | $this->installServer(); 59 | $this->installNginxDirectory(); 60 | } 61 | 62 | /** 63 | * Install the Nginx configuration file. 64 | * 65 | * @return void 66 | */ 67 | public function installConfiguration() 68 | { 69 | $contents = $this->files->get(__DIR__ . '/../stubs/nginx.conf'); 70 | $nginx = $this->nginx_conf; 71 | 72 | $pid_string = 'pid /run/nginx.pid'; 73 | $hasPIDoption = strpos($this->cli->run('cat /lib/systemd/system/nginx.service'), 'pid /'); 74 | 75 | if ($hasPIDoption) { 76 | $pid_string = '# pid /run/nginx.pid'; 77 | } 78 | 79 | $this->files->backup($nginx); 80 | 81 | $this->files->putAsUser( 82 | $nginx, 83 | str_array_replace([ 84 | 'VALET_USER' => user(), 85 | 'VALET_GROUP' => group(), 86 | 'VALET_HOME_PATH' => VALET_HOME_PATH, 87 | 'VALET_PID' => $pid_string, 88 | ], $contents) 89 | ); 90 | } 91 | 92 | /** 93 | * Install the Valet Nginx server configuration file. 94 | * 95 | * @return void 96 | */ 97 | public function installServer() 98 | { 99 | $this->files->putAsUser( 100 | $this->sites_available_conf, 101 | str_replace( 102 | ['VALET_HOME_PATH', 'VALET_SERVER_PATH', 'VALET_STATIC_PREFIX', 'VALET_PORT'], 103 | [VALET_HOME_PATH, VALET_SERVER_PATH, VALET_STATIC_PREFIX, $this->configuration->read()['port']], 104 | $this->files->get(__DIR__ . '/../stubs/valet.conf') 105 | ) 106 | ); 107 | 108 | if ($this->files->exists('/etc/nginx/sites-enabled/default')) { 109 | $this->files->unlink('/etc/nginx/sites-enabled/default'); 110 | } 111 | 112 | $this->cli->run("ln -snf {$this->sites_available_conf} {$this->sites_enabled_conf}"); 113 | $this->files->backup('/etc/nginx/fastcgi_params'); 114 | 115 | $this->files->putAsUser( 116 | '/etc/nginx/fastcgi_params', 117 | $this->files->get(__DIR__ . '/../stubs/fastcgi_params') 118 | ); 119 | } 120 | 121 | /** 122 | * Install the Nginx configuration directory to the ~/.valet directory. 123 | * 124 | * This directory contains all site-specific Nginx servers. 125 | * 126 | * @return void 127 | */ 128 | public function installNginxDirectory() 129 | { 130 | if (!$this->files->isDir($nginxDirectory = VALET_HOME_PATH . '/Nginx')) { 131 | $this->files->mkdirAsUser($nginxDirectory); 132 | } 133 | 134 | $this->files->putAsUser($nginxDirectory . '/.keep', "\n"); 135 | 136 | $this->rewriteSecureNginxFiles(); 137 | } 138 | 139 | /** 140 | * Update the port used by Nginx. 141 | * 142 | * @param string $newPort 143 | * @return void 144 | */ 145 | public function updatePort($newPort) 146 | { 147 | $this->files->putAsUser( 148 | $this->sites_available_conf, 149 | str_replace( 150 | ['VALET_HOME_PATH', 'VALET_SERVER_PATH', 'VALET_STATIC_PREFIX', 'VALET_PORT'], 151 | [VALET_HOME_PATH, VALET_SERVER_PATH, VALET_STATIC_PREFIX, $newPort], 152 | $this->files->get(__DIR__ . '/../stubs/valet.conf') 153 | ) 154 | ); 155 | } 156 | 157 | /** 158 | * Generate fresh Nginx servers for existing secure sites. 159 | * 160 | * @return void 161 | */ 162 | public function rewriteSecureNginxFiles() 163 | { 164 | $domain = $this->configuration->read()['domain']; 165 | 166 | $this->site->resecureForNewDomain($domain, $domain); 167 | } 168 | 169 | /** 170 | * Restart the Nginx service. 171 | * 172 | * @return void 173 | */ 174 | public function restart() 175 | { 176 | $this->sm->restart('nginx'); 177 | } 178 | 179 | /** 180 | * Stop the Nginx service. 181 | * 182 | * @return void 183 | */ 184 | public function stop() 185 | { 186 | $this->sm->stop('nginx'); 187 | } 188 | 189 | /** 190 | * Nginx service status. 191 | * 192 | * @return void 193 | */ 194 | public function status() 195 | { 196 | $this->sm->printStatus('nginx'); 197 | } 198 | 199 | /** 200 | * Prepare Nginx for uninstallation. 201 | * 202 | * @return void 203 | */ 204 | public function uninstall() 205 | { 206 | $this->stop(); 207 | $this->files->restore($this->nginx_conf); 208 | $this->files->restore('/etc/nginx/fastcgi_params'); 209 | $this->files->unlink($this->sites_enabled_conf); 210 | $this->files->unlink($this->sites_available_conf); 211 | 212 | if ($this->files->exists('/etc/nginx/sites-available/default')) { 213 | $this->files->symlink('/etc/nginx/sites-available/default', '/etc/nginx/sites-enabled/default'); 214 | } 215 | } 216 | 217 | 218 | /** 219 | * Return a list of all sites with explicit Nginx configurations. 220 | * 221 | * @return \Illuminate\Support\Collection 222 | */ 223 | public function configuredSites() 224 | { 225 | return collect($this->files->scandir(VALET_HOME_PATH.'/Nginx')) 226 | ->reject(function ($file) { 227 | return starts_with($file, '.'); 228 | }); 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /cli/Valet/Ngrok.php: -------------------------------------------------------------------------------- 1 | tunnelsEndpoint)->send()->body; 21 | 22 | // If there are active tunnels on the Ngrok instance we will spin through them and 23 | // find the one responding on HTTP. Each tunnel has an HTTP and a HTTPS address 24 | // but for local testing purposes we just desire the plain HTTP URL endpoint. 25 | if (isset($body->tunnels) && count($body->tunnels) > 0) { 26 | return $this->findHttpTunnelUrl($body->tunnels); 27 | } else { 28 | throw new DomainException("Tunnel not established."); 29 | } 30 | }, 250); 31 | } 32 | 33 | /** 34 | * Find the HTTP tunnel URL from the list of tunnels. 35 | * 36 | * @param array $tunnels 37 | * @return string|null 38 | */ 39 | public function findHttpTunnelUrl(array $tunnels) 40 | { 41 | foreach ($tunnels as $tunnel) { 42 | if ($tunnel->proto === 'http') { 43 | return $tunnel->public_url; 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /cli/Valet/PackageManagers/Apt.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 35 | } 36 | 37 | /** 38 | * Get array of installed packages 39 | * 40 | * @param string $package 41 | * @return array 42 | */ 43 | public function packages($package) 44 | { 45 | $query = "dpkg -l {$package} | grep '^ii' | sed 's/\s\+/ /g' | cut -d' ' -f2"; 46 | 47 | return explode(PHP_EOL, $this->cli->run($query)); 48 | } 49 | 50 | /** 51 | * Determine if the given package is installed. 52 | * 53 | * @param string $package 54 | * @return bool 55 | */ 56 | public function installed($package) 57 | { 58 | return in_array($package, $this->packages($package)); 59 | } 60 | 61 | /** 62 | * Ensure that the given package is installed. 63 | * 64 | * @param string $package 65 | * @return void 66 | */ 67 | public function ensureInstalled($package) 68 | { 69 | if (!$this->installed($package)) { 70 | $this->installOrFail($package); 71 | } 72 | } 73 | 74 | /** 75 | * Install the given package and throw an exception on failure. 76 | * 77 | * @param string $package 78 | * @return void 79 | */ 80 | public function installOrFail($package) 81 | { 82 | output('[' . $package . '] is not installed, installing it now via Apt... 🍻'); 83 | 84 | $this->cli->run(trim('apt-get install -y ' . $package), function ($exitCode, $errorOutput) use ($package) { 85 | output($errorOutput); 86 | 87 | throw new DomainException('Apt was unable to install [' . $package . '].'); 88 | }); 89 | } 90 | 91 | /** 92 | * Configure package manager on valet install. 93 | * 94 | * @return void 95 | */ 96 | public function setup() 97 | { 98 | // Nothing to do 99 | } 100 | 101 | /** 102 | * Restart dnsmasq in Ubuntu. 103 | */ 104 | public function nmRestart($sm) 105 | { 106 | $sm->restart(['network-manager']); 107 | } 108 | 109 | /** 110 | * Determine if package manager is available on the system. 111 | * 112 | * @return bool 113 | */ 114 | public function isAvailable() 115 | { 116 | try { 117 | $output = $this->cli->run('which apt-get', function ($exitCode, $output) { 118 | throw new DomainException('Apt not available'); 119 | }); 120 | 121 | return $output != ''; 122 | } catch (DomainException $e) { 123 | return false; 124 | } 125 | } 126 | 127 | public function supportedPhpVersions() 128 | { 129 | return collect(static::SUPPORTED_PHP_VERSIONS); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /cli/Valet/PackageManagers/Dnf.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 27 | } 28 | 29 | /** 30 | * Determine if the given package is installed. 31 | * 32 | * @param string $package 33 | * @return bool 34 | */ 35 | public function installed($package) 36 | { 37 | $query = "dnf list installed {$package} | grep {$package} | sed 's_ _\\t_g' | sed 's_\\._\\t_g' | cut -f 1"; 38 | 39 | $packages = explode(PHP_EOL, $this->cli->run($query)); 40 | 41 | return in_array($package, $packages); 42 | } 43 | 44 | /** 45 | * Ensure that the given package is installed. 46 | * 47 | * @param string $package 48 | * @return void 49 | */ 50 | public function ensureInstalled($package) 51 | { 52 | if (!$this->installed($package)) { 53 | $this->installOrFail($package); 54 | } 55 | } 56 | 57 | /** 58 | * Install the given package and throw an exception on failure. 59 | * 60 | * @param string $package 61 | * @return void 62 | */ 63 | public function installOrFail($package) 64 | { 65 | output('[' . $package . '] is not installed, installing it now via Dnf... 🍻'); 66 | 67 | $this->cli->run(trim('dnf install -y ' . $package), function ($exitCode, $errorOutput) use ($package) { 68 | output($errorOutput); 69 | 70 | throw new DomainException('Dnf was unable to install [' . $package . '].'); 71 | }); 72 | } 73 | 74 | /** 75 | * Configure package manager on valet install. 76 | * 77 | * @return void 78 | */ 79 | public function setup() 80 | { 81 | // Nothing to do 82 | } 83 | 84 | /** 85 | * Restart dnsmasq in Fedora. 86 | */ 87 | public function nmRestart($sm) 88 | { 89 | $sm->restart('NetworkManager'); 90 | } 91 | 92 | /** 93 | * Determine if package manager is available on the system. 94 | * 95 | * @return bool 96 | */ 97 | public function isAvailable() 98 | { 99 | try { 100 | $output = $this->cli->run('which dnf', function ($exitCode, $output) { 101 | throw new DomainException('Dnf not available'); 102 | }); 103 | 104 | return $output != ''; 105 | } catch (DomainException $e) { 106 | return false; 107 | } 108 | } 109 | 110 | public function supportedPhpVersions() 111 | { 112 | return collect(static::SUPPORTED_PHP_VERSIONS); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /cli/Valet/PackageManagers/Eopkg.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 27 | } 28 | 29 | /** 30 | * Get array of installed packages 31 | * 32 | * @param string $package 33 | * @return array 34 | */ 35 | public function packages($package) 36 | { 37 | $query = "eopkg li | cut -d ' ' -f 1"; 38 | 39 | return explode(PHP_EOL, $this->cli->run($query)); 40 | } 41 | 42 | /** 43 | * Determine if the given package is installed. 44 | * 45 | * @param string $package 46 | * @return bool 47 | */ 48 | public function installed($package) 49 | { 50 | return in_array($package, $this->packages($package)); 51 | } 52 | 53 | /** 54 | * Ensure that the given package is installed. 55 | * 56 | * @param string $package 57 | * @return void 58 | */ 59 | public function ensureInstalled($package) 60 | { 61 | if (!$this->installed($package)) { 62 | $this->installOrFail($package); 63 | } 64 | } 65 | 66 | /** 67 | * Install the given package and throw an exception on failure. 68 | * 69 | * @param string $package 70 | * @return void 71 | */ 72 | public function installOrFail($package) 73 | { 74 | output('[' . $package . '] is not installed, installing it now via Eopkg... 🍻'); 75 | 76 | $this->cli->run(trim('eopkg install -y ' . $package), function ($exitCode, $errorOutput) use ($package) { 77 | output($errorOutput); 78 | 79 | throw new DomainException('Eopkg was unable to install [' . $package . '].'); 80 | }); 81 | } 82 | 83 | /** 84 | * Configure package manager on valet install. 85 | * 86 | * @return void 87 | */ 88 | public function setup() 89 | { 90 | // Nothing to do 91 | } 92 | 93 | /** 94 | * Restart dnsmasq in Ubuntu. 95 | */ 96 | public function nmRestart($sm) 97 | { 98 | $sm->restart(['NetworkManager']); 99 | } 100 | 101 | /** 102 | * Determine if package manager is available on the system. 103 | * 104 | * @return bool 105 | */ 106 | public function isAvailable() 107 | { 108 | try { 109 | $output = $this->cli->run('which eopkg', function ($exitCode, $output) { 110 | throw new DomainException('Eopkg not available'); 111 | }); 112 | 113 | return $output != ''; 114 | } catch (DomainException $e) { 115 | return false; 116 | } 117 | } 118 | 119 | public function supportedPhpVersions() 120 | { 121 | return collect(static::SUPPORTED_PHP_VERSIONS); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /cli/Valet/PackageManagers/Homebrew.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 38 | } 39 | 40 | /** 41 | * Get array of installed packages 42 | * 43 | * @param string $package 44 | * @return array 45 | */ 46 | public function packages($package) 47 | { 48 | $query = "brew list --formula | grep {$package}"; 49 | 50 | return explode(PHP_EOL, $this->cli->runAsUser($query)); 51 | } 52 | 53 | /** 54 | * Determine if the given package is installed. 55 | * 56 | * @param string $package 57 | * @return bool 58 | */ 59 | public function installed($package) 60 | { 61 | // For php-fpm we need to tim the -fpm out of the string as 62 | // php-fpm gets installed among php 63 | $package = str_replace('-fpm', null, $package); 64 | return in_array($package, $this->packages($package)); 65 | } 66 | 67 | /** 68 | * Ensure that the given package is installed. 69 | * 70 | * @param string $package 71 | * @return void 72 | */ 73 | public function ensureInstalled($package) 74 | { 75 | if (!$this->installed($package)) { 76 | $this->installOrFail($package); 77 | } 78 | } 79 | 80 | /** 81 | * Install the given package and throw an exception on failure. 82 | * 83 | * @param string $package 84 | * @return void 85 | */ 86 | public function installOrFail($package) 87 | { 88 | output('[' . $package . '] is not installed, installing it now via Brew... 🍻'); 89 | 90 | $this->cli->runAsUser(trim('brew install ' . $package), function ($exitCode, $errorOutput) use ($package) { 91 | output($errorOutput); 92 | 93 | throw new DomainException('Brew was unable to install [' . $package . '].'); 94 | }); 95 | } 96 | 97 | /** 98 | * Configure package manager on valet install. 99 | * 100 | * @return void 101 | */ 102 | public function setup() 103 | { 104 | // Nothing to do 105 | } 106 | 107 | /** 108 | * Restart dnsmasq in Ubuntu. 109 | */ 110 | public function nmRestart($sm) 111 | { 112 | $sm->restart('NetworkManager'); 113 | } 114 | 115 | /** 116 | * Determine if package manager is available on the system. 117 | * 118 | * @return bool 119 | */ 120 | public function isAvailable() 121 | { 122 | try { 123 | $output = $this->cli->runAsUser('which brew', function ($exitCode, $output) { 124 | throw new DomainException('Brew not available'); 125 | }); 126 | 127 | return $output != ''; 128 | } catch (DomainException $e) { 129 | return false; 130 | } 131 | } 132 | 133 | public function supportedPhpVersions() 134 | { 135 | return collect(static::SUPPORTED_PHP_VERSIONS); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /cli/Valet/PackageManagers/PackageKit.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 27 | } 28 | 29 | /** 30 | * Get array of installed packages 31 | * 32 | * @param string $package 33 | * @return array 34 | */ 35 | public function packages($package) 36 | { 37 | $query = "pkcon search {$package} | grep '^In' | sed 's/\s\+/ /g' | cut -d' ' -f2 | sed 's/-[0-9].*//'"; 38 | 39 | return explode(PHP_EOL, $this->cli->run($query)); 40 | } 41 | 42 | /** 43 | * Determine if the given package is installed. 44 | * 45 | * @param string $package 46 | * @return bool 47 | */ 48 | public function installed($package) 49 | { 50 | return in_array($package, $this->packages($package)); 51 | } 52 | 53 | /** 54 | * Ensure that the given package is installed. 55 | * 56 | * @param string $package 57 | * @return void 58 | */ 59 | public function ensureInstalled($package) 60 | { 61 | if (!$this->installed($package)) { 62 | $this->installOrFail($package); 63 | } 64 | } 65 | 66 | /** 67 | * Install the given package and throw an exception on failure. 68 | * 69 | * @param string $package 70 | * @return void 71 | */ 72 | public function installOrFail($package) 73 | { 74 | output('[' . $package . '] is not installed, installing it now via PackageKit... 🍻'); 75 | 76 | $this->cli->run(trim('pkcon install -y ' . $package), function ($exitCode, $errorOutput) use ($package) { 77 | output($errorOutput); 78 | 79 | throw new DomainException('PackageKit was unable to install [' . $package . '].'); 80 | }); 81 | } 82 | 83 | /** 84 | * Configure package manager on valet install. 85 | * 86 | * @return void 87 | */ 88 | public function setup() 89 | { 90 | // Nothing to do 91 | } 92 | 93 | /** 94 | * Restart dnsmasq in Ubuntu. 95 | */ 96 | public function nmRestart($sm) 97 | { 98 | $sm->restart(['network-manager']); 99 | 100 | $version = trim($this->cli->run('cat /etc/*release | grep DISTRIB_RELEASE | cut -d\= -f2')); 101 | 102 | if ($version === '17.04') { 103 | $sm->enable('systemd-resolved'); 104 | $sm->restart('systemd-resolved'); 105 | } 106 | } 107 | 108 | /** 109 | * Determine if package manager is available on the system. 110 | * 111 | * @return bool 112 | */ 113 | public function isAvailable() 114 | { 115 | try { 116 | $output = $this->cli->run('which pkcon', function ($exitCode, $output) { 117 | throw new DomainException('PackageKit not available'); 118 | }); 119 | 120 | return $output != ''; 121 | } catch (DomainException $e) { 122 | return false; 123 | } 124 | } 125 | 126 | public function supportedPhpVersions() 127 | { 128 | return collect(static::SUPPORTED_PHP_VERSIONS); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /cli/Valet/PackageManagers/Pacman.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 27 | } 28 | 29 | /** 30 | * Get array of installed packages 31 | * 32 | * @param string $package 33 | * @return array 34 | */ 35 | public function packages($package) 36 | { 37 | $query = "pacman -Qqs {$package}"; 38 | 39 | return explode(PHP_EOL, $this->cli->run($query)); 40 | } 41 | 42 | /** 43 | * Determine if the given package is installed. 44 | * 45 | * @param string $package 46 | * @return bool 47 | */ 48 | public function installed($package) 49 | { 50 | return in_array($package, $this->packages($package)); 51 | } 52 | 53 | /** 54 | * Ensure that the given package is installed. 55 | * 56 | * @param string $package 57 | * @return void 58 | */ 59 | public function ensureInstalled($package) 60 | { 61 | if (!$this->installed($package)) { 62 | $this->installOrFail($package); 63 | } 64 | } 65 | 66 | /** 67 | * Install the given package and throw an exception on failure. 68 | * 69 | * @param string $package 70 | * @return void 71 | */ 72 | public function installOrFail($package) 73 | { 74 | output('[' . $package . '] is not installed, installing it now via Pacman... 🍻'); 75 | 76 | $this->cli->run(trim('pacman --noconfirm --needed -S ' . $package), function ($exitCode, $errorOutput) use ($package) { 77 | output($errorOutput); 78 | 79 | throw new DomainException('Pacman was unable to install [' . $package . '].'); 80 | }); 81 | } 82 | 83 | /** 84 | * Configure package manager on valet install. 85 | * 86 | * @return void 87 | */ 88 | public function setup() 89 | { 90 | // Nothing to do 91 | } 92 | 93 | /** 94 | * Restart dnsmasq in Ubuntu. 95 | */ 96 | public function nmRestart($sm) 97 | { 98 | $sm->restart('NetworkManager'); 99 | } 100 | 101 | /** 102 | * Determine if package manager is available on the system. 103 | * 104 | * @return bool 105 | */ 106 | public function isAvailable() 107 | { 108 | try { 109 | $output = $this->cli->run('which pacman', function ($exitCode, $output) { 110 | throw new DomainException('Pacman not available'); 111 | }); 112 | 113 | return $output != ''; 114 | } catch (DomainException $e) { 115 | return false; 116 | } 117 | } 118 | 119 | public function supportedPhpVersions() 120 | { 121 | return collect(static::SUPPORTED_PHP_VERSIONS); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /cli/Valet/PackageManagers/Yum.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 27 | } 28 | 29 | /** 30 | * Determine if the given package is installed. 31 | * 32 | * @param string $package 33 | * @return bool 34 | */ 35 | public function installed($package) 36 | { 37 | $query = "yum list installed {$package} | grep {$package} | sed 's_ _\\t_g' | sed 's_\\._\\t_g' | cut -f 1"; 38 | 39 | $packages = explode(PHP_EOL, $this->cli->run($query)); 40 | 41 | return in_array($package, $packages); 42 | } 43 | 44 | /** 45 | * Ensure that the given package is installed. 46 | * 47 | * @param string $package 48 | * @return void 49 | */ 50 | public function ensureInstalled($package) 51 | { 52 | if (!$this->installed($package)) { 53 | $this->installOrFail($package); 54 | } 55 | } 56 | 57 | /** 58 | * Install the given package and throw an exception on failure. 59 | * 60 | * @param string $package 61 | * @return void 62 | */ 63 | public function installOrFail($package) 64 | { 65 | output('[' . $package . '] is not installed, installing it now via Yum... 🍻'); 66 | 67 | $this->cli->run(trim('yum install -y ' . $package), function ($exitCode, $errorOutput) use ($package) { 68 | output($errorOutput); 69 | 70 | throw new DomainException('Yum was unable to install [' . $package . '].'); 71 | }); 72 | } 73 | 74 | /** 75 | * Configure package manager on valet install. 76 | * 77 | * @return void 78 | */ 79 | public function setup() 80 | { 81 | // Nothing to do 82 | } 83 | 84 | /** 85 | * Restart dnsmasq in Fedora. 86 | */ 87 | public function nmRestart($sm) 88 | { 89 | $sm->restart('NetworkManager'); 90 | } 91 | 92 | /** 93 | * Determine if package manager is available on the system. 94 | * 95 | * @return bool 96 | */ 97 | public function isAvailable() 98 | { 99 | try { 100 | $output = $this->cli->run('which yum', function ($exitCode, $output) { 101 | throw new DomainException('Yum not available'); 102 | }); 103 | 104 | return $output != ''; 105 | } catch (DomainException $e) { 106 | return false; 107 | } 108 | } 109 | 110 | public function supportedPhpVersions() 111 | { 112 | return collect(static::SUPPORTED_PHP_VERSIONS); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /cli/Valet/Requirements.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 20 | } 21 | 22 | /** 23 | * Determine if SELinux check should be skipped 24 | * 25 | * @param bool $ignore 26 | * @return $this 27 | */ 28 | public function setIgnoreSELinux($ignore = true) 29 | { 30 | $this->ignoreSELinux = $ignore; 31 | 32 | return $this; 33 | } 34 | 35 | /** 36 | * Run all checks and output warnings. 37 | */ 38 | public function check() 39 | { 40 | $this->homePathIsInsideRoot(); 41 | $this->seLinuxIsEnabled(); 42 | } 43 | 44 | /** 45 | * Verify if valet home is inside /root directory. 46 | * 47 | * This usually means the HOME parameters has not been 48 | * kept using sudo. 49 | */ 50 | public function homePathIsInsideRoot() 51 | { 52 | if (strpos(VALET_HOME_PATH, '/root/') === 0) { 53 | throw new RuntimeException("Valet home directory is inside /root"); 54 | } 55 | } 56 | 57 | /** 58 | * Verify is SELinux is enabled and in enforcing mode. 59 | */ 60 | public function seLinuxIsEnabled() 61 | { 62 | if ($this->ignoreSELinux) { 63 | return; 64 | } 65 | 66 | $output = $this->cli->run('sestatus'); 67 | 68 | if (preg_match('@SELinux status:(\s+)enabled@', $output) 69 | && preg_match('@Current mode:(\s+)enforcing@', $output) 70 | ) { 71 | throw new RuntimeException("SELinux is in enforcing mode"); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /cli/Valet/ServiceManagers/Homebrew.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 21 | } 22 | 23 | /** 24 | * Start the given services. 25 | * 26 | * @param mixed $services Service name 27 | * 28 | * @return void 29 | */ 30 | public function start($services) 31 | { 32 | $services = is_array($services) ? $services : func_get_args(); 33 | 34 | foreach ($services as $service) { 35 | info("Starting $service..."); 36 | $this->cli->quietlyAsUser('brew services start ' . $this->getRealService($service)); 37 | } 38 | } 39 | 40 | /** 41 | * Stop the given services. 42 | * 43 | * @param mixed $services Service name 44 | * 45 | * @return void 46 | */ 47 | public function stop($services) 48 | { 49 | $services = is_array($services) ? $services : func_get_args(); 50 | 51 | foreach ($services as $service) { 52 | info("Stopping $service..."); 53 | $this->cli->quietlyAsUser('brew services stop ' . $this->getRealService($service)); 54 | $this->cli->quietlyAsUser('sudo brew services stop ' . $this->getRealService($service)); 55 | } 56 | } 57 | 58 | /** 59 | * Restart the given services. 60 | * 61 | * @param mixed $services Service name 62 | * 63 | * @return void 64 | */ 65 | public function restart($services) 66 | { 67 | $services = is_array($services) ? $services : func_get_args(); 68 | 69 | foreach ($services as $service) { 70 | info("Restarting $service..."); 71 | $this->cli->quietlyAsUser('brew services stop ' . $this->getRealService($service)); 72 | $this->cli->quietly('sudo brew services stop ' . $this->getRealService($service)); 73 | $this->cli->quietly('sudo brew services start ' . $this->getRealService($service)); 74 | } 75 | } 76 | 77 | /** 78 | * Status of the given services. 79 | * 80 | * @param mixed $services Service name 81 | * 82 | * @return void 83 | */ 84 | public function printStatus($services) 85 | { 86 | $services = is_array($services) ? $services : func_get_args(); 87 | 88 | foreach ($services as $service) { 89 | $status = $this->cli->runAsUser('brew services info ' . $this->getRealService($service) . ' --json'); 90 | $statusObject = json_decode($status)[0]; 91 | $running = $statusObject['running']; 92 | 93 | if ($running) { 94 | info(ucfirst($service) . ' is running...'); 95 | } else { 96 | warning(ucfirst($service) . ' is stopped...'); 97 | } 98 | } 99 | } 100 | 101 | /** 102 | * Status of the given services. 103 | * 104 | * @param mixed $service Service name 105 | * 106 | * @return void 107 | */ 108 | public function status($service) 109 | { 110 | return $this->cli->run('brew services info ' . $this->getRealService($service)); 111 | } 112 | 113 | /** 114 | * Check if service is disabled. 115 | * 116 | * @param mixed $service Service name 117 | * 118 | * @return void 119 | */ 120 | public function disabled($service) 121 | { 122 | return false; 123 | $service = $this->getRealService($service); 124 | 125 | return (strpos(trim($this->cli->run("systemctl is-enabled {$service}")), 'enabled')) === false; 126 | } 127 | 128 | /** 129 | * Enable services. 130 | * 131 | * @param mixed $services Service name 132 | * 133 | * @return void 134 | */ 135 | public function enable($services) 136 | { 137 | $services = is_array($services) ? $services : func_get_args(); 138 | 139 | foreach ($services as $service) { 140 | try { 141 | $service = $this->getRealService($service); 142 | 143 | if ($this->disabled($service)) { 144 | $this->cli->quietly('sudo systemctl enable ' . $service); 145 | info(ucfirst($service) . ' has been enabled'); 146 | 147 | return true; 148 | } 149 | 150 | info(ucfirst($service) . ' was already enabled'); 151 | 152 | return true; 153 | } catch (DomainException $e) { 154 | warning(ucfirst($service) . ' unavailable.'); 155 | 156 | return false; 157 | } 158 | } 159 | } 160 | 161 | /** 162 | * Disable services. 163 | * 164 | * @param mixed $services Service name 165 | * 166 | * @return void 167 | */ 168 | public function disable($services) 169 | { 170 | $services = is_array($services) ? $services : func_get_args(); 171 | 172 | foreach ($services as $service) { 173 | try { 174 | $service = $this->getRealService($service); 175 | 176 | if (!$this->disabled($service)) { 177 | $this->cli->quietly('sudo systemctl disable ' . $service); 178 | info(ucfirst($service) . ' has been disabled'); 179 | 180 | return true; 181 | } 182 | 183 | info(ucfirst($service) . ' was already disabled'); 184 | 185 | return true; 186 | } catch (DomainException $e) { 187 | warning(ucfirst($service) . ' unavailable.'); 188 | 189 | return false; 190 | } 191 | } 192 | } 193 | 194 | /** 195 | * Determine if service manager is available on the system. 196 | * 197 | * @return bool 198 | */ 199 | public function isAvailable() 200 | { 201 | try { 202 | $output = $this->cli->runAsUser( 203 | 'brew services', 204 | function ($exitCode, $output) { 205 | throw new DomainException('Systemd not available'); 206 | } 207 | ); 208 | 209 | return $output != ''; 210 | } catch (DomainException $e) { 211 | return false; 212 | } 213 | } 214 | 215 | /** 216 | * Determine real service name 217 | * 218 | * @param mixed $service Service name 219 | * 220 | * @return string 221 | */ 222 | public function getRealService($service) 223 | { 224 | return collect($service)->first( 225 | function ($service) { 226 | return strpos($this->cli->run("systemctl status {$service} | grep Loaded"), 'Loaded: loaded') >= 0; 227 | }, 228 | function () { 229 | throw new DomainException("Unable to determine service name."); 230 | } 231 | ); 232 | } 233 | 234 | /** 235 | * Install Valet DNS services. 236 | * 237 | * @param Filesystem $files Filesystem object 238 | * 239 | * @return void 240 | */ 241 | public function installValetDns($files) 242 | { 243 | info("Installing Valet DNS service..."); 244 | 245 | $files->put( 246 | '/etc/systemd/system/valet-dns.service', 247 | $files->get(__DIR__ . '/../../stubs/init/systemd') 248 | ); 249 | 250 | $this->enable('valet-dns'); 251 | } 252 | 253 | public function getRunningServices() 254 | { 255 | // TODO: Implement getRunningServices() method. 256 | } 257 | 258 | public function getAllRunningServices() 259 | { 260 | $command = 'brew services list | grep started | awk \'{ print $1; }\' '; 261 | $onError = function ($exitCode, $errorOutput) { 262 | output($errorOutput); 263 | 264 | throw new DomainException('Brew was unable to check which services are running.'); 265 | }; 266 | 267 | return collect(array_filter(explode(PHP_EOL, $this->cli->run($command, $onError) 268 | ))); 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /cli/Valet/ServiceManagers/LinuxService.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 21 | } 22 | 23 | /** 24 | * Start the given services. 25 | * 26 | * @param mixed $services Service name 27 | * 28 | * @return void 29 | */ 30 | public function start($services) 31 | { 32 | $services = is_array($services) ? $services : func_get_args(); 33 | 34 | foreach ($services as $service) { 35 | info("Starting $service..."); 36 | $this->cli->quietly('sudo service ' . $this->getRealService($service) . ' start'); 37 | } 38 | } 39 | 40 | /** 41 | * Stop the given services. 42 | * 43 | * @param mixed $services Service name 44 | * 45 | * @return void 46 | */ 47 | public function stop($services) 48 | { 49 | $services = is_array($services) ? $services : func_get_args(); 50 | 51 | foreach ($services as $service) { 52 | info("Stopping $service..."); 53 | $this->cli->quietly('sudo service ' . $this->getRealService($service) . ' stop'); 54 | } 55 | } 56 | 57 | /** 58 | * Restart the given services. 59 | * 60 | * @param mixed $services Service name 61 | * 62 | * @return void 63 | */ 64 | public function restart($services) 65 | { 66 | $services = is_array($services) ? $services : func_get_args(); 67 | 68 | foreach ($services as $service) { 69 | info("Restarting $service..."); 70 | $this->cli->quietly('sudo service ' . $this->getRealService($service) . ' restart'); 71 | } 72 | } 73 | 74 | /** 75 | * Status of the given services. 76 | * 77 | * @param mixed $services Service name 78 | * 79 | * @return void 80 | */ 81 | public function printStatus($services) 82 | { 83 | $services = is_array($services) ? $services : func_get_args(); 84 | 85 | foreach ($services as $service) { 86 | if ($this->_hasSystemd()) { 87 | $status = $this->cli->run( 88 | 'systemctl status ' . $this->getRealService($service) . ' | grep "Active:"' 89 | ); 90 | 91 | $running = strpos(trim($status), 'running'); 92 | 93 | if ($running) { 94 | return info(ucfirst($service) . ' is running...'); 95 | } else { 96 | return warning(ucfirst($service) . ' is stopped...'); 97 | } 98 | } 99 | 100 | return info($this->cli->run('service ' . $this->getRealService($service))); 101 | } 102 | } 103 | 104 | /** 105 | * Status of the given services. 106 | * 107 | * @param mixed $service Service to get status from 108 | * 109 | * @return void 110 | */ 111 | public function status($service) 112 | { 113 | return $this->cli->run('service ' . $this->getRealService($service) . ' status'); 114 | } 115 | 116 | /** 117 | * Check if service is disabled. 118 | * 119 | * @param mixed $service Service name 120 | * 121 | * @return boolean 122 | */ 123 | public function disabled($service) 124 | { 125 | $service = $this->getRealService($service); 126 | 127 | return (strpos(trim($this->cli->run("systemctl is-enabled {$service}")), 'enabled')) === false; 128 | } 129 | 130 | /** 131 | * Disable services. 132 | * 133 | * @param mixed $services Service or services to disable 134 | * 135 | * @return void 136 | */ 137 | public function disable($services) 138 | { 139 | if ($this->_hasSystemd()) { 140 | $services = is_array($services) ? $services : func_get_args(); 141 | 142 | foreach ($services as $service) { 143 | try { 144 | $service = $this->getRealService($service); 145 | 146 | if (!$this->disabled($service)) { 147 | $this->cli->quietly('sudo systemctl disable ' . $service); 148 | info(ucfirst($service) . ' has been disabled'); 149 | } 150 | 151 | info(ucfirst($service) . ' was already disabled'); 152 | } catch (DomainException $e) { 153 | warning(ucfirst($service) . ' not available.'); 154 | } 155 | } 156 | } else { 157 | $services = is_array($services) ? $services : func_get_args(); 158 | 159 | foreach ($services as $service) { 160 | try { 161 | $service = $this->getRealService($service); 162 | $this->cli->quietly("sudo chmod -x /etc/init.d/{$service}"); 163 | $this->cli->quietly("sudo update-rc.d $service defaults"); 164 | } catch (DomainException $e) { 165 | warning(ucfirst($service) . ' not available.'); 166 | } 167 | } 168 | } 169 | } 170 | 171 | /** 172 | * Enable services. 173 | * 174 | * @param mixed $services Service or services to enable 175 | * 176 | * @return void 177 | */ 178 | public function enable($services) 179 | { 180 | if ($this->_hasSystemd()) { 181 | $services = is_array($services) ? $services : func_get_args(); 182 | 183 | foreach ($services as $service) { 184 | try { 185 | $service = $this->getRealService($service); 186 | 187 | if ($this->disabled($service)) { 188 | $this->cli->quietly('sudo systemctl enable ' . $service); 189 | info(ucfirst($service) . ' has been enabled'); 190 | 191 | return true; 192 | } 193 | 194 | info(ucfirst($service) . ' was already enabled'); 195 | 196 | return true; 197 | } catch (DomainException $e) { 198 | warning(ucfirst($service) . ' not available.'); 199 | 200 | return false; 201 | } 202 | } 203 | } else { 204 | $services = is_array($services) ? $services : func_get_args(); 205 | 206 | foreach ($services as $service) { 207 | try { 208 | $service = $this->getRealService($service); 209 | $this->cli->quietly("sudo update-rc.d $service defaults"); 210 | info(ucfirst($service) . ' has been enabled'); 211 | 212 | return true; 213 | } catch (DomainException $e) { 214 | warning(ucfirst($service) . ' not available.'); 215 | 216 | return false; 217 | } 218 | } 219 | } 220 | } 221 | 222 | /** 223 | * Determine if service manager is available on the system. 224 | * 225 | * @return bool 226 | */ 227 | public function isAvailable() 228 | { 229 | try { 230 | $output = $this->cli->run( 231 | 'which service', 232 | function ($exitCode, $output) { 233 | throw new DomainException('Service not available'); 234 | } 235 | ); 236 | 237 | return $output != ''; 238 | } catch (DomainException $e) { 239 | return false; 240 | } 241 | } 242 | 243 | /** 244 | * Determine real service name 245 | * 246 | * @param mixed $service Service name 247 | * 248 | * @return string 249 | */ 250 | public function getRealService($service) 251 | { 252 | return collect($service)->first( 253 | function ($service) { 254 | return !strpos( 255 | $this->cli->run('service ' . $service . ' status'), 256 | 'not-found' 257 | ); 258 | }, 259 | function () { 260 | throw new DomainException("Unable to determine service name."); 261 | } 262 | ); 263 | } 264 | 265 | /** 266 | * Determine if systemd is available on the system. 267 | * 268 | * @return bool 269 | */ 270 | private function _hasSystemd() 271 | { 272 | try { 273 | $this->cli->run( 274 | 'which systemctl', 275 | function ($exitCode, $output) { 276 | throw new DomainException('Systemd not available'); 277 | } 278 | ); 279 | 280 | return true; 281 | } catch (DomainException $e) { 282 | return false; 283 | } 284 | } 285 | 286 | /** 287 | * Install Valet DNS services. 288 | * 289 | * @param Filesystem $files Filesystem object 290 | * 291 | * @return void 292 | */ 293 | public function installValetDns($files) 294 | { 295 | info("Installing Valet DNS service..."); 296 | 297 | $servicePath = '/etc/init.d/valet-dns'; 298 | $serviceFile = __DIR__ . '/../../stubs/init/sysvinit'; 299 | $hasSystemd = $this->_hasSystemd(); 300 | 301 | if ($hasSystemd) { 302 | $servicePath = '/etc/systemd/system/valet-dns.service'; 303 | $serviceFile = __DIR__ . '/../../stubs/init/systemd'; 304 | } 305 | 306 | $files->put( 307 | $servicePath, 308 | $files->get($serviceFile) 309 | ); 310 | 311 | if (!$hasSystemd) { 312 | $this->cli->run("chmod +x $servicePath"); 313 | } 314 | 315 | $this->enable('valet-dns'); 316 | } 317 | 318 | public function getRunningServices() 319 | { 320 | // TODO: Implement getRunningServices() method. 321 | } 322 | 323 | public function getAllRunningServices() 324 | { 325 | // TODO: Implement getAllRunningServices() method. 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /cli/Valet/ServiceManagers/Systemd.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 21 | } 22 | 23 | /** 24 | * Start the given services. 25 | * 26 | * @param mixed $services Service name 27 | * 28 | * @return void 29 | */ 30 | public function start($services) 31 | { 32 | $services = is_array($services) ? $services : func_get_args(); 33 | 34 | foreach ($services as $service) { 35 | info("Starting $service..."); 36 | $this->cli->quietly('sudo systemctl start ' . $this->getRealService($service)); 37 | } 38 | } 39 | 40 | /** 41 | * Stop the given services. 42 | * 43 | * @param mixed $services Service name 44 | * 45 | * @return void 46 | */ 47 | public function stop($services) 48 | { 49 | $services = is_array($services) ? $services : func_get_args(); 50 | 51 | foreach ($services as $service) { 52 | info("Stopping $service..."); 53 | $this->cli->quietly('sudo systemctl stop ' . $this->getRealService($service)); 54 | } 55 | } 56 | 57 | /** 58 | * Restart the given services. 59 | * 60 | * @param mixed $services Service name 61 | * 62 | * @return void 63 | */ 64 | public function restart($services) 65 | { 66 | $services = is_array($services) ? $services : func_get_args(); 67 | 68 | foreach ($services as $service) { 69 | info("Restarting $service..."); 70 | $this->cli->quietly('sudo systemctl restart ' . $this->getRealService($service)); 71 | } 72 | } 73 | 74 | /** 75 | * Status of the given services. 76 | * 77 | * @param mixed $services Service name 78 | * 79 | * @return void 80 | */ 81 | public function printStatus($services) 82 | { 83 | $services = is_array($services) ? $services : func_get_args(); 84 | 85 | foreach ($services as $service) { 86 | $status = $this->cli->run('systemctl status ' . $this->getRealService($service) . ' | grep "Active:"'); 87 | $running = strpos(trim($status), 'running'); 88 | 89 | if ($running) { 90 | info(ucfirst($service) . ' is running...'); 91 | } else { 92 | warning(ucfirst($service) . ' is stopped...'); 93 | } 94 | } 95 | } 96 | 97 | /** 98 | * Status of the given services. 99 | * 100 | * @param mixed $service Service name 101 | * 102 | * @return string 103 | */ 104 | public function status($service) 105 | { 106 | return $this->cli->run('systemctl status ' . $this->getRealService($service)); 107 | } 108 | 109 | /** 110 | * Check if service is disabled. 111 | * 112 | * @param mixed $service Service name 113 | * 114 | * @return void 115 | */ 116 | public function disabled($service) 117 | { 118 | $service = $this->getRealService($service); 119 | 120 | return (strpos(trim($this->cli->run("systemctl is-enabled {$service}")), 'enabled')) === false; 121 | } 122 | 123 | /** 124 | * Enable services. 125 | * 126 | * @param mixed $services Service name 127 | * 128 | * @return void 129 | */ 130 | public function enable($services) 131 | { 132 | $services = is_array($services) ? $services : func_get_args(); 133 | 134 | foreach ($services as $service) { 135 | try { 136 | $service = $this->getRealService($service); 137 | 138 | if ($this->disabled($service)) { 139 | $this->cli->quietly('sudo systemctl enable ' . $service); 140 | info(ucfirst($service) . ' has been enabled'); 141 | 142 | return true; 143 | } 144 | 145 | info(ucfirst($service) . ' was already enabled'); 146 | 147 | return true; 148 | } catch (DomainException $e) { 149 | warning(ucfirst($service) . ' unavailable.'); 150 | 151 | return false; 152 | } 153 | } 154 | } 155 | 156 | /** 157 | * Disable services. 158 | * 159 | * @param mixed $services Service name 160 | * 161 | * @return void 162 | */ 163 | public function disable($services) 164 | { 165 | $services = is_array($services) ? $services : func_get_args(); 166 | 167 | foreach ($services as $service) { 168 | try { 169 | $service = $this->getRealService($service); 170 | 171 | if (!$this->disabled($service)) { 172 | $this->cli->quietly('sudo systemctl disable ' . $service); 173 | info(ucfirst($service) . ' has been disabled'); 174 | 175 | return true; 176 | } 177 | 178 | info(ucfirst($service) . ' was already disabled'); 179 | 180 | return true; 181 | } catch (DomainException $e) { 182 | warning(ucfirst($service) . ' unavailable.'); 183 | 184 | return false; 185 | } 186 | } 187 | } 188 | 189 | /** 190 | * Determine if service manager is available on the system. 191 | * 192 | * @return bool 193 | */ 194 | public function isAvailable() 195 | { 196 | try { 197 | $output = $this->cli->run( 198 | 'which systemctl', 199 | function ($exitCode, $output) { 200 | throw new DomainException('Systemd not available'); 201 | } 202 | ); 203 | 204 | return $output != ''; 205 | } catch (DomainException $e) { 206 | return false; 207 | } 208 | } 209 | 210 | /** 211 | * Determine real service name 212 | * 213 | * @param mixed $service Service name 214 | * 215 | * @return string 216 | */ 217 | public function getRealService($service) 218 | { 219 | return collect($service)->first( 220 | function ($service) { 221 | return strpos($this->cli->run("systemctl status {$service} | grep Loaded"), 'Loaded: loaded') >= 0; 222 | }, 223 | function () { 224 | throw new DomainException("Unable to determine service name."); 225 | } 226 | ); 227 | } 228 | 229 | /** 230 | * Install Valet DNS services. 231 | * 232 | * @param Filesystem $files Filesystem object 233 | * 234 | * @return void 235 | */ 236 | public function installValetDns($files) 237 | { 238 | info("Installing Valet DNS service..."); 239 | 240 | $files->put( 241 | '/etc/systemd/system/valet-dns.service', 242 | $files->get(__DIR__ . '/../../stubs/init/systemd') 243 | ); 244 | 245 | $this->enable('valet-dns'); 246 | } 247 | 248 | public function getRunningServices() 249 | { 250 | return $this->getAllRunningServices(); 251 | } 252 | 253 | public function getAllRunningServices() 254 | { 255 | $list = $this->cli->run('systemctl list-units --type=service --state=active | grep \.service | awk \'{ print $1; }\''); 256 | return collect(explode(PHP_EOL, $list)); 257 | 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /cli/Valet/Valet.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 37 | $this->files = $files; 38 | } 39 | 40 | /** 41 | * Symlink the Valet Bash script into the user's local bin. 42 | * 43 | * @return void 44 | */ 45 | public function symlinkToUsersBin() 46 | { 47 | $this->cli->run('ln -snf ' . dirname(__DIR__, 2) . '/valet' . ' ' . $this->valetBin); 48 | } 49 | 50 | /** 51 | * Unlink the Valet Bash script from the user's local bin 52 | * and the sudoers.d entry 53 | * 54 | * @return void 55 | */ 56 | public function uninstall() 57 | { 58 | $this->files->unlink($this->valetBin); 59 | $this->files->unlink($this->sudoers); 60 | } 61 | 62 | /** 63 | * Get the paths to all of the Valet extensions. 64 | * 65 | * @return array 66 | */ 67 | public function extensions() 68 | { 69 | if (!$this->files->isDir(VALET_HOME_PATH . '/Extensions')) { 70 | return []; 71 | } 72 | 73 | return collect($this->files->scandir(VALET_HOME_PATH . '/Extensions')) 74 | ->reject(static function ($file) { 75 | return is_dir($file); 76 | }) 77 | ->map(static function ($file) { 78 | return VALET_HOME_PATH . '/Extensions/' . $file; 79 | }) 80 | ->values()->all(); 81 | } 82 | 83 | /** 84 | * Determine if this is the latest version of Valet. 85 | * 86 | * @param string $currentVersion 87 | * @return bool 88 | * @throws \Httpful\Exception\ConnectionErrorException 89 | */ 90 | public function onLatestVersion($currentVersion): bool 91 | { 92 | $response = \Httpful\Request::get($this->github)->send(); 93 | 94 | return version_compare($currentVersion, trim($response->body->tag_name), '>='); 95 | } 96 | 97 | /** 98 | * Determine current environment 99 | * 100 | * @return void 101 | */ 102 | public function environmentSetup() 103 | { 104 | $this->packageManagerSetup(); 105 | $this->serviceManagerSetup(); 106 | } 107 | 108 | /** 109 | * Configure package manager 110 | * 111 | * @return void 112 | */ 113 | public function packageManagerSetup() 114 | { 115 | Container::getInstance()->bind(PackageManager::class, $this->getAvailablePackageManager()); 116 | } 117 | 118 | /** 119 | * Determine the first available package manager 120 | * 121 | * @return string 122 | */ 123 | public function getAvailablePackageManager(): string 124 | { 125 | return collect([ 126 | Homebrew::class, 127 | Apt::class, 128 | Dnf::class, 129 | Pacman::class, 130 | Yum::class, 131 | PackageKit::class, 132 | Eopkg::class, 133 | ])->first(static function ($pm) { 134 | return resolve($pm)->isAvailable(); 135 | }, static function () { 136 | throw new DomainException("No compatible package manager found."); 137 | }); 138 | } 139 | 140 | /** 141 | * Configure service manager 142 | * 143 | * @return void 144 | */ 145 | public function serviceManagerSetup() 146 | { 147 | Container::getInstance()->bind(ServiceManager::class, $this->getAvailableServiceManager()); 148 | } 149 | 150 | /** 151 | * Determine the first available service manager 152 | * 153 | * @return string 154 | */ 155 | public function getAvailableServiceManager() 156 | { 157 | return collect([ 158 | LinuxService::class, 159 | Systemd::class, 160 | ])->first(static function ($pm) { 161 | return resolve($pm)->isAvailable(); 162 | }, static function () { 163 | throw new DomainException("No compatible service manager found."); 164 | }); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /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 | { 51 | $_SERVER['PHP_SELF'] = $uri; 52 | $_SERVER['SERVER_ADDR'] = '127.0.0.1'; 53 | $_SERVER['SERVER_NAME'] = $_SERVER['HTTP_HOST']; 54 | 55 | $dynamicCandidates = [ 56 | $this->asActualFile($sitePath, $uri), 57 | $this->asPhpIndexFileInDirectory($sitePath, $uri), 58 | $this->asHtmlIndexFileInDirectory($sitePath, $uri), 59 | ]; 60 | 61 | foreach ($dynamicCandidates as $candidate) { 62 | if ($this->isActualFile($candidate)) { 63 | $_SERVER['SCRIPT_FILENAME'] = $candidate; 64 | $_SERVER['SCRIPT_NAME'] = str_replace($sitePath, '', $candidate); 65 | $_SERVER['DOCUMENT_ROOT'] = $sitePath; 66 | 67 | return $candidate; 68 | } 69 | } 70 | 71 | $fixedCandidatesAndDocroots = [ 72 | $this->asRootPhpIndexFile($sitePath) => $sitePath, 73 | $this->asPublicPhpIndexFile($sitePath) => $sitePath . '/public', 74 | $this->asPublicHtmlIndexFile($sitePath) => $sitePath . '/public', 75 | ]; 76 | 77 | foreach ($fixedCandidatesAndDocroots as $candidate => $docroot) { 78 | if ($this->isActualFile($candidate)) { 79 | $_SERVER['SCRIPT_FILENAME'] = $candidate; 80 | $_SERVER['SCRIPT_NAME'] = '/index.php'; 81 | $_SERVER['DOCUMENT_ROOT'] = $docroot; 82 | 83 | return $candidate; 84 | } 85 | } 86 | } 87 | 88 | /** 89 | * Concatenate the site path and URI as a single file name. 90 | * 91 | * @param string $sitePath 92 | * @param string $uri 93 | * @return string 94 | */ 95 | protected function asActualFile($sitePath, $uri) 96 | { 97 | return $sitePath . $uri; 98 | } 99 | 100 | /** 101 | * Format the site path and URI with a trailing "index.php". 102 | * 103 | * @param string $sitePath 104 | * @param string $uri 105 | * @return string 106 | */ 107 | protected function asPhpIndexFileInDirectory($sitePath, $uri) 108 | { 109 | return $sitePath . rtrim($uri, '/') . '/index.php'; 110 | } 111 | 112 | /** 113 | * Format the site path and URI with a trailing "index.html". 114 | * 115 | * @param string $sitePath 116 | * @param string $uri 117 | * @return string 118 | */ 119 | protected function asHtmlIndexFileInDirectory($sitePath, $uri) 120 | { 121 | return $sitePath . rtrim($uri, '/') . '/index.html'; 122 | } 123 | 124 | /** 125 | * Format the incoming site path as root "index.php" file path. 126 | * 127 | * @param string $sitePath 128 | * @return string 129 | */ 130 | protected function asRootPhpIndexFile($sitePath) 131 | { 132 | return $sitePath . '/index.php'; 133 | } 134 | 135 | /** 136 | * Format the incoming site path as a "public/index.php" file path. 137 | * 138 | * @param string $sitePath 139 | * @return string 140 | */ 141 | protected function asPublicPhpIndexFile($sitePath) 142 | { 143 | return $sitePath . '/public/index.php'; 144 | } 145 | 146 | /** 147 | * Format the incoming site path as a "public/index.php" file path. 148 | * 149 | * @param string $sitePath 150 | * @return string 151 | */ 152 | protected function asPublicHtmlIndexFile($sitePath) 153 | { 154 | return $sitePath . '/public/index.html'; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /cli/drivers/BedrockValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($staticFilePath)) { 34 | return $staticFilePath; 35 | } 36 | 37 | return false; 38 | } 39 | 40 | /** 41 | * Get the fully resolved path to the application's front controller. 42 | * 43 | * @param string $sitePath 44 | * @param string $siteName 45 | * @param string $uri 46 | * @return string 47 | */ 48 | public function frontControllerPath($sitePath, $siteName, $uri) 49 | { 50 | $_SERVER['PHP_SELF'] = $uri; 51 | $_SERVER['SERVER_NAME'] = $_SERVER['HTTP_HOST']; 52 | 53 | if (strpos($uri, '/wp/') === 0) { 54 | return is_dir($sitePath . '/web' . $uri) 55 | ? $sitePath . '/web' . $this->forceTrailingSlash($uri) . '/index.php' 56 | : $sitePath . '/web' . $uri; 57 | } 58 | 59 | return $sitePath . '/web/index.php'; 60 | } 61 | 62 | /** 63 | * Redirect to uri with trailing slash. 64 | * 65 | * @param string $uri 66 | * @return string 67 | */ 68 | private function forceTrailingSlash($uri) 69 | { 70 | if (substr($uri, -1 * strlen('/wp/wp-admin')) == '/wp/wp-admin') { 71 | header('Location: ' . $uri . '/'); 72 | die; 73 | } 74 | 75 | return $uri; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /cli/drivers/Cake2ValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($staticFilePath = $sitePath . '/webroot/' . $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 | { 46 | $_SERVER['DOCUMENT_ROOT'] = $sitePath . '/webroot'; 47 | $_SERVER['SCRIPT_FILENAME'] = $sitePath . '/webroot/index.php'; 48 | $_SERVER['SCRIPT_NAME'] = '/index.php'; 49 | $_SERVER['PHP_SELF'] = '/index.php'; 50 | 51 | return $sitePath . '/webroot/index.php'; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /cli/drivers/CakeValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($staticFilePath = $sitePath . '/webroot/' . $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 | { 45 | $_SERVER['DOCUMENT_ROOT'] = $sitePath . '/webroot'; 46 | $_SERVER['SCRIPT_FILENAME'] = $sitePath . '/webroot/index.php'; 47 | $_SERVER['SCRIPT_NAME'] = '/index.php'; 48 | $_SERVER['PHP_SELF'] = '/index.php'; 49 | 50 | return $sitePath . '/webroot/index.php'; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /cli/drivers/Concrete5ValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($staticFilePath = $sitePath . '/web' . $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 | { 45 | if ($uri === '/install.php') { 46 | return $sitePath . '/web/install.php'; 47 | } 48 | 49 | if (0 === strncmp($uri, '/app_dev.php', 12)) { 50 | $_SERVER['SCRIPT_NAME'] = '/app_dev.php'; 51 | $_SERVER['SCRIPT_FILENAME'] = $sitePath . '/app_dev.php'; 52 | 53 | return $sitePath . '/web/app_dev.php'; 54 | } 55 | 56 | return $sitePath . '/web/app.php'; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /cli/drivers/CraftValetDriver.php: -------------------------------------------------------------------------------- 1 | frontControllerDirectory($sitePath); 49 | 50 | if ($this->isActualFile($staticFilePath = $sitePath . '/' . $frontControllerDirectory . $uri)) { 51 | return $staticFilePath; 52 | } 53 | 54 | return false; 55 | } 56 | 57 | /** 58 | * Get the fully resolved path to the application's front controller. 59 | * 60 | * @param string $sitePath 61 | * @param string $siteName 62 | * @param string $uri 63 | * @return string 64 | */ 65 | public function frontControllerPath($sitePath, $siteName, $uri) 66 | { 67 | $frontControllerDirectory = $this->frontControllerDirectory($sitePath); 68 | 69 | // Default index path 70 | $indexPath = $sitePath . '/' . $frontControllerDirectory . '/index.php'; 71 | $scriptName = '/index.php'; 72 | 73 | // Check if the first URL segment matches any of the defined locales 74 | $locales = [ 75 | 'ar', 76 | 'ar_sa', 77 | 'bg', 78 | 'bg_bg', 79 | 'ca_es', 80 | 'cs', 81 | 'cy_gb', 82 | 'da', 83 | 'da_dk', 84 | 'de', 85 | 'de_at', 86 | 'de_ch', 87 | 'de_de', 88 | 'el', 89 | 'el_gr', 90 | 'en', 91 | 'en_as', 92 | 'en_au', 93 | 'en_bb', 94 | 'en_be', 95 | 'en_bm', 96 | 'en_bw', 97 | 'en_bz', 98 | 'en_ca', 99 | 'en_dsrt', 100 | 'en_dsrt_us', 101 | 'en_gb', 102 | 'en_gu', 103 | 'en_gy', 104 | 'en_hk', 105 | 'en_ie', 106 | 'en_in', 107 | 'en_jm', 108 | 'en_mh', 109 | 'en_mp', 110 | 'en_mt', 111 | 'en_mu', 112 | 'en_na', 113 | 'en_nz', 114 | 'en_ph', 115 | 'en_pk', 116 | 'en_sg', 117 | 'en_shaw', 118 | 'en_tt', 119 | 'en_um', 120 | 'en_us', 121 | 'en_us_posix', 122 | 'en_vi', 123 | 'en_za', 124 | 'en_zw', 125 | 'en_zz', 126 | 'es', 127 | 'es_cl', 128 | 'es_es', 129 | 'es_mx', 130 | 'es_us', 131 | 'es_ve', 132 | 'et', 133 | 'fi', 134 | 'fi_fi', 135 | 'fil', 136 | 'fr', 137 | 'fr_be', 138 | 'fr_ca', 139 | 'fr_ch', 140 | 'fr_fr', 141 | 'fr_ma', 142 | 'he', 143 | 'hr', 144 | 'hr_hr', 145 | 'hu', 146 | 'hu_hu', 147 | 'id', 148 | 'id_id', 149 | 'it', 150 | 'it_ch', 151 | 'it_it', 152 | 'ja', 153 | 'ja_jp', 154 | 'ko', 155 | 'ko_kr', 156 | 'lt', 157 | 'lv', 158 | 'ms', 159 | 'ms_my', 160 | 'nb', 161 | 'nb_no', 162 | 'nl', 163 | 'nl_be', 164 | 'nl_nl', 165 | 'nn', 166 | 'nn_no', 167 | 'no', 168 | 'pl', 169 | 'pl_pl', 170 | 'pt', 171 | 'pt_br', 172 | 'pt_pt', 173 | 'ro', 174 | 'ro_ro', 175 | 'ru', 176 | 'ru_ru', 177 | 'sk', 178 | 'sl', 179 | 'sr', 180 | 'sv', 181 | 'sv_se', 182 | 'th', 183 | 'th_th', 184 | 'tr', 185 | 'tr_tr', 186 | 'uk', 187 | 'vi', 188 | 'zh', 189 | 'zh_cn', 190 | 'zh_tw', 191 | ]; 192 | $parts = explode('/', $uri); 193 | 194 | if (count($parts) > 1 && in_array($parts[1], $locales)) { 195 | $indexLocalizedPath = $sitePath . '/' . $frontControllerDirectory . '/' . $parts[1] . '/index.php'; 196 | 197 | // Check if index.php exists in the localized folder, this is optional in Craft 3 198 | if (file_exists($indexLocalizedPath)) { 199 | $indexPath = $indexLocalizedPath; 200 | $scriptName = '/' . $parts[1] . '/index.php'; 201 | } 202 | } 203 | 204 | $_SERVER['SCRIPT_FILENAME'] = $indexPath; 205 | $_SERVER['SERVER_NAME'] = $_SERVER['HTTP_HOST']; 206 | $_SERVER['SCRIPT_NAME'] = $scriptName; 207 | $_SERVER['PHP_SELF'] = $scriptName; 208 | $_SERVER['DOCUMENT_ROOT'] = $sitePath . '/' . $frontControllerDirectory; 209 | 210 | return $indexPath; 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /cli/drivers/DrupalValetDriver.php: -------------------------------------------------------------------------------- 1 | addSubdirectory($sitePath); 16 | 17 | /** 18 | * /misc/drupal.js = Drupal 7 19 | * /core/lib/Drupal.php = Drupal 8 20 | */ 21 | if (file_exists($sitePath . '/misc/drupal.js') || 22 | file_exists($sitePath . '/core/lib/Drupal.php')) { 23 | return true; 24 | } 25 | } 26 | 27 | /** 28 | * Determine if the incoming request is for a static file. 29 | * 30 | * @param string $sitePath 31 | * @param string $siteName 32 | * @param string $uri 33 | * @return string|false 34 | */ 35 | public function isStaticFile($sitePath, $siteName, $uri) 36 | { 37 | $sitePath = $this->addSubdirectory($sitePath); 38 | 39 | if (file_exists($sitePath . $uri) && 40 | !is_dir($sitePath . $uri) && 41 | pathinfo($sitePath . $uri)['extension'] != 'php') { 42 | return $sitePath . $uri; 43 | } 44 | 45 | return false; 46 | } 47 | 48 | /** 49 | * Get the fully resolved path to the application's front controller. 50 | * 51 | * @param string $sitePath 52 | * @param string $siteName 53 | * @param string $uri 54 | * @return string 55 | */ 56 | public function frontControllerPath($sitePath, $siteName, $uri) 57 | { 58 | $sitePath = $this->addSubdirectory($sitePath); 59 | 60 | if (!isset($_GET['q']) && !empty($uri) && $uri !== '/') { 61 | $_GET['q'] = $uri; 62 | } 63 | 64 | $matches = []; 65 | if (preg_match('/^\/(.*?)\.php/', $uri, $matches)) { 66 | $filename = $matches[0]; 67 | if (file_exists($sitePath . $filename) && !is_dir($sitePath . $filename)) { 68 | $_SERVER['SCRIPT_FILENAME'] = $sitePath . $filename; 69 | $_SERVER['SCRIPT_NAME'] = $filename; 70 | 71 | return $sitePath . $filename; 72 | } 73 | } 74 | 75 | // Fallback 76 | $_SERVER['SCRIPT_FILENAME'] = $sitePath . '/index.php'; 77 | $_SERVER['SCRIPT_NAME'] = '/index.php'; 78 | 79 | return $sitePath . '/index.php'; 80 | } 81 | 82 | /** 83 | * Add any matching subdirectory to the site path. 84 | */ 85 | public function addSubdirectory($sitePath) 86 | { 87 | $paths = array_map(function ($subDir) use ($sitePath) { 88 | return "$sitePath/$subDir"; 89 | }, $this->possibleSubdirectories()); 90 | 91 | $foundPaths = array_filter($paths, function ($path) { 92 | return file_exists($path); 93 | }); 94 | 95 | // If paths are found, return the first one. 96 | if (!empty($foundPaths)) { 97 | return array_shift($foundPaths); 98 | } 99 | 100 | // If there are no matches, return the original path. 101 | return $sitePath; 102 | } 103 | 104 | /** 105 | * Return an array of possible subdirectories. 106 | * 107 | * @return array 108 | */ 109 | private function possibleSubdirectories() 110 | { 111 | return ['docroot', 'public', 'web']; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /cli/drivers/JigsawValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($staticFilePath = $sitePath . $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 | { 45 | // Needed to force Kirby to use *.test to generate its URLs... 46 | $_SERVER['SERVER_NAME'] = $_SERVER['HTTP_HOST']; 47 | 48 | if (preg_match('/^\/panel/', $uri) && file_exists($sitePath . '/panel/index.php')) { 49 | $_SERVER['SCRIPT_NAME'] = '/panel/index.php'; 50 | 51 | return $sitePath . '/panel/index.php'; 52 | } 53 | 54 | if (file_exists($indexPath = $sitePath . '/index.php')) { 55 | $_SERVER['SCRIPT_NAME'] = '/index.php'; 56 | 57 | return $indexPath; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /cli/drivers/LaravelValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($storagePath = $sitePath . '/storage/app/public' . $storageUri)) { 41 | return $storagePath; 42 | } 43 | 44 | return false; 45 | } 46 | 47 | /** 48 | * Get the fully resolved path to the application's front controller. 49 | * 50 | * @param string $sitePath 51 | * @param string $siteName 52 | * @param string $uri 53 | * @return string 54 | */ 55 | public function frontControllerPath($sitePath, $siteName, $uri) 56 | { 57 | // Shortcut for getting the "local" hostname as the HTTP_HOST 58 | if (isset($_SERVER['HTTP_X_ORIGINAL_HOST'], $_SERVER['HTTP_X_FORWARDED_HOST'])) { 59 | $_SERVER['HTTP_HOST'] = $_SERVER['HTTP_X_FORWARDED_HOST']; 60 | } 61 | 62 | return $sitePath . '/public/index.php'; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /cli/drivers/Magento2ValetDriver.php: -------------------------------------------------------------------------------- 1 | checkMageMode($sitePath); 37 | 38 | $uri = $this->handleForVersions($uri); 39 | $route = parse_url(substr($uri, 1))['path']; 40 | 41 | $pub = ''; 42 | if ('developer' === $this->mageMode) { 43 | $pub = 'pub/'; 44 | } 45 | 46 | if (!$this->isPubDirectory($sitePath, $route, $pub)) { 47 | return false; 48 | } 49 | 50 | $magentoPackagePubDir = $sitePath; 51 | if ('developer' !== $this->mageMode) { 52 | $magentoPackagePubDir .= '/pub'; 53 | } 54 | 55 | $file = $magentoPackagePubDir . '/' . $route; 56 | 57 | if (file_exists($file)) { 58 | return $magentoPackagePubDir . $uri; 59 | } 60 | 61 | if (strpos($route, $pub . 'static/') === 0) { 62 | $route = preg_replace('#' . $pub . 'static/#', '', $route, 1); 63 | $_GET['resource'] = $route; 64 | include $magentoPackagePubDir . '/' . $pub . 'static.php'; 65 | exit; 66 | } 67 | 68 | if (strpos($route, $pub . 'media/') === 0) { 69 | include $magentoPackagePubDir . '/' . $pub . 'get.php'; 70 | exit; 71 | } 72 | 73 | return false; 74 | } 75 | 76 | /** 77 | * Rewrite URLs that look like "versions12345/" to remove 78 | * the versions12345/ part 79 | * 80 | * @param string $route 81 | */ 82 | private function handleForVersions($route) 83 | { 84 | return preg_replace('/version\d*\//', '', $route); 85 | } 86 | 87 | /** 88 | * Determine the current MAGE_MODE 89 | * 90 | * @param string $sitePath 91 | */ 92 | private function checkMageMode($sitePath) 93 | { 94 | if (null !== $this->mageMode) { 95 | // We have already figure out mode, no need to check it again 96 | return; 97 | } 98 | if (!file_exists($sitePath . '/index.php')) { 99 | $this->mageMode = 'production'; // Can't use developer mode without index.php in project root 100 | 101 | return; 102 | } 103 | $mageConfig = []; 104 | if (file_exists($sitePath . '/app/etc/env.php')) { 105 | $mageConfig = require $sitePath . '/app/etc/env.php'; 106 | } 107 | if (array_key_exists('MAGE_MODE', $mageConfig)) { 108 | $this->mageMode = $mageConfig['MAGE_MODE']; 109 | } 110 | } 111 | 112 | /** 113 | * Checks to see if route is referencing any directory inside pub. This is a dynamic check so that if any new 114 | * directories are added to pub this driver will not need to be updated. 115 | * 116 | * @param string $sitePath 117 | * @param string $route 118 | * @param string $pub 119 | * @return bool 120 | */ 121 | private function isPubDirectory($sitePath, $route, $pub = '') 122 | { 123 | $sitePath .= '/pub/'; 124 | $dirs = glob($sitePath . '*', GLOB_ONLYDIR); 125 | 126 | $dirs = str_replace($sitePath, '', $dirs); 127 | foreach ($dirs as $dir) { 128 | if (strpos($route, $pub . $dir . '/') === 0) { 129 | return true; 130 | } 131 | } 132 | 133 | return false; 134 | } 135 | 136 | /** 137 | * Get the fully resolved path to the application's front controller. 138 | * 139 | * @param string $sitePath 140 | * @param string $siteName 141 | * @param string $uri 142 | * @return string 143 | */ 144 | public function frontControllerPath($sitePath, $siteName, $uri) 145 | { 146 | $this->checkMageMode($sitePath); 147 | 148 | if ('developer' === $this->mageMode) { 149 | $_SERVER['DOCUMENT_ROOT'] = $sitePath; 150 | 151 | return $sitePath . '/index.php'; 152 | } 153 | $_SERVER['DOCUMENT_ROOT'] = $sitePath . '/pub'; 154 | 155 | return $sitePath . '/pub/index.php'; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /cli/drivers/NeosValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($staticFilePath = $sitePath . '/Web' . $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 | { 45 | putenv('FLOW_CONTEXT=Development'); 46 | putenv('FLOW_REWRITEURLS=1'); 47 | $_SERVER['SCRIPT_FILENAME'] = $sitePath . '/Web/index.php'; 48 | $_SERVER['SCRIPT_NAME'] = '/index.php'; 49 | 50 | return $sitePath . '/Web/index.php'; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /cli/drivers/SculpinValetDriver.php: -------------------------------------------------------------------------------- 1 | isModernSculpinProject($sitePath) || 16 | $this->isLegacySculpinProject($sitePath); 17 | } 18 | 19 | private function isModernSculpinProject($sitePath) 20 | { 21 | return is_dir($sitePath . '/source') && 22 | is_dir($sitePath . '/output_dev') && 23 | $this->composerRequiresSculpin($sitePath); 24 | } 25 | 26 | private function isLegacySculpinProject($sitePath) 27 | { 28 | return is_dir($sitePath . '/.sculpin'); 29 | } 30 | 31 | private function composerRequiresSculpin($sitePath) 32 | { 33 | if (!file_exists($sitePath . '/composer.json')) { 34 | return false; 35 | } 36 | 37 | $composer_json_source = file_get_contents($sitePath . '/composer.json'); 38 | $composer_json = json_decode($composer_json_source, true); 39 | 40 | if (json_last_error() !== JSON_ERROR_NONE) { 41 | return false; 42 | } 43 | 44 | return isset($composer_json['require']['sculpin/sculpin']); 45 | } 46 | 47 | /** 48 | * Mutate the incoming URI. 49 | * 50 | * @param string $uri 51 | * @return string 52 | */ 53 | public function mutateUri($uri) 54 | { 55 | return rtrim('/output_dev' . $uri, '/'); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /cli/drivers/StatamicV1ValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($staticFilePath = $sitePath . $uri)) { 36 | return $staticFilePath; 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 | { 52 | if (strpos($uri, '/admin.php') === 0) { 53 | $_SERVER['SCRIPT_NAME'] = '/admin.php'; 54 | 55 | return $sitePath . '/admin.php'; 56 | } 57 | 58 | if ($uri === '/admin') { 59 | $_SERVER['SCRIPT_NAME'] = '/admin/index.php'; 60 | 61 | return $sitePath . '/admin/index.php'; 62 | } 63 | 64 | $_SERVER['SCRIPT_NAME'] = '/index.php'; 65 | 66 | return $sitePath . '/index.php'; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /cli/drivers/StatamicValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($staticFilePath = $sitePath . $uri)) { 33 | return $staticFilePath; 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 | { 51 | if ($_SERVER['REQUEST_METHOD'] === 'GET' && $this->isActualFile($staticPath = $this->getStaticPath($sitePath))) { 52 | return $staticPath; 53 | } 54 | 55 | if ($uri === '/installer.php') { 56 | return $sitePath . '/installer.php'; 57 | } 58 | 59 | $scriptName = '/index.php'; 60 | 61 | if ($this->isActualFile($sitePath . '/index.php')) { 62 | $indexPath = $sitePath . '/index.php'; 63 | } 64 | 65 | if ($isAboveWebroot = $this->isActualFile($sitePath . '/public/index.php')) { 66 | $indexPath = $sitePath . '/public/index.php'; 67 | } 68 | 69 | $sitePathPrefix = ($isAboveWebroot) ? $sitePath . '/public' : $sitePath; 70 | 71 | if ($locale = $this->getUriLocale($uri)) { 72 | if ($this->isActualFile($localeIndexPath = $sitePathPrefix . '/' . $locale . '/index.php')) { 73 | // Force trailing slashes on locale roots. 74 | if ($uri === '/' . $locale) { 75 | header('Location: ' . $uri . '/'); 76 | die; 77 | } 78 | 79 | $indexPath = $localeIndexPath; 80 | $scriptName = '/' . $locale . '/index.php'; 81 | } 82 | } 83 | 84 | $_SERVER['SCRIPT_NAME'] = $scriptName; 85 | $_SERVER['SCRIPT_FILENAME'] = $sitePathPrefix . $scriptName; 86 | 87 | return $indexPath; 88 | } 89 | 90 | /** 91 | * Get the locale from this URI 92 | * 93 | * @param string $uri 94 | * @return string|null 95 | */ 96 | public function getUriLocale($uri) 97 | { 98 | $parts = explode('/', $uri); 99 | $locale = $parts[1]; 100 | 101 | if (count($parts) < 2 || !in_array($locale, $this->getLocales())) { 102 | return; 103 | } 104 | 105 | return $locale; 106 | } 107 | 108 | /** 109 | * Get the list of possible locales used in the first segment of a URI 110 | * 111 | * @return array 112 | */ 113 | public function getLocales() 114 | { 115 | return [ 116 | 'af', 'ax', 'al', 'dz', 'as', 'ad', 'ao', 'ai', 'aq', 'ag', 'ar', 'am', 'aw', 'au', 'at', 'az', 'bs', 'bh', 117 | 'bd', 'bb', 'by', 'be', 'bz', 'bj', 'bm', 'bt', 'bo', 'bq', 'ba', 'bw', 'bv', 'br', 'io', 'bn', 'bg', 'bf', 118 | 'bi', 'cv', 'kh', 'cm', 'ca', 'ky', 'cf', 'td', 'cl', 'cn', 'cx', 'cc', 'co', 'km', 'cg', 'cd', 'ck', 'cr', 119 | 'ci', 'hr', 'cu', 'cw', 'cy', 'cz', 'dk', 'dj', 'dm', 'do', 'ec', 'eg', 'sv', 'gq', 'er', 'ee', 'et', 'fk', 120 | 'fo', 'fj', 'fi', 'fr', 'gf', 'pf', 'tf', 'ga', 'gm', 'ge', 'de', 'gh', 'gi', 'gr', 'gl', 'gd', 'gp', 'gu', 121 | 'gt', 'gg', 'gn', 'gw', 'gy', 'ht', 'hm', 'va', 'hn', 'hk', 'hu', 'is', 'in', 'id', 'ir', 'iq', 'ie', 'im', 122 | 'il', 'it', 'jm', 'jp', 'je', 'jo', 'kz', 'ke', 'ki', 'kp', 'kr', 'kw', 'kg', 'la', 'lv', 'lb', 'ls', 'lr', 123 | 'ly', 'li', 'lt', 'lu', 'mo', 'mk', 'mg', 'mw', 'my', 'mv', 'ml', 'mt', 'mh', 'mq', 'mr', 'mu', 'yt', 'mx', 124 | 'fm', 'md', 'mc', 'mn', 'me', 'ms', 'ma', 'mz', 'mm', 'na', 'nr', 'np', 'nl', 'nc', 'nz', 'ni', 'ne', 'ng', 125 | 'nu', 'nf', 'mp', 'no', 'om', 'pk', 'pw', 'ps', 'pa', 'pg', 'py', 'pe', 'ph', 'pn', 'pl', 'pt', 'pr', 'qa', 126 | 're', 'ro', 'ru', 'rw', 'bl', 'sh', 'kn', 'lc', 'mf', 'pm', 'vc', 'ws', 'sm', 'st', 'sa', 'sn', 'rs', 'sc', 127 | 'sl', 'sg', 'sx', 'sk', 'si', 'sb', 'so', 'za', 'gs', 'ss', 'es', 'lk', 'sd', 'sr', 'sj', 'sz', 'se', 'ch', 128 | 'sy', 'tw', 'tj', 'tz', 'th', 'tl', 'tg', 'tk', 'to', 'tt', 'tn', 'tr', 'tm', 'tc', 'tv', 'ug', 'ua', 'ae', 129 | 'gb', 'us', 'um', 'uy', 'uz', 'vu', 've', 'vn', 'vg', 'vi', 'wf', 'eh', 'ye', 'zm', 'zw', 'en', 130 | ]; 131 | } 132 | 133 | /** 134 | * Get the path to a statically cached page 135 | * 136 | * @param string $sitePath 137 | * @return string 138 | */ 139 | protected function getStaticPath($sitePath) 140 | { 141 | $parts = parse_url($_SERVER['REQUEST_URI']); 142 | $query = isset($parts['query']) ? $parts['query'] : ''; 143 | 144 | return $sitePath . '/static' . $parts['path'] . '_' . $query . '.html'; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /cli/drivers/SymfonyValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($staticFilePath = $sitePath . '/web/' . $uri)) { 31 | return $staticFilePath; 32 | } elseif ($this->isActualFile($staticFilePath = $sitePath . '/public/' . $uri)) { 33 | return $staticFilePath; 34 | } 35 | 36 | return false; 37 | } 38 | 39 | /** 40 | * Get the fully resolved path to the application's front controller. 41 | * 42 | * @param string $sitePath 43 | * @param string $siteName 44 | * @param string $uri 45 | * @return string 46 | */ 47 | public function frontControllerPath($sitePath, $siteName, $uri) 48 | { 49 | if (file_exists($frontControllerPath = $sitePath . '/web/app_dev.php')) { 50 | return $frontControllerPath; 51 | } elseif (file_exists($frontControllerPath = $sitePath . '/web/app.php')) { 52 | return $frontControllerPath; 53 | } elseif (file_exists($frontControllerPath = $sitePath . '/public/index.php')) { 54 | // enable support for Symfony 5.3 with Runtime component 55 | $_SERVER['SCRIPT_FILENAME'] = $frontControllerPath; 56 | 57 | return $frontControllerPath; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /cli/drivers/Typo3ValetDriver.php: -------------------------------------------------------------------------------- 1 | documentRoot . '/typo3'; 52 | 53 | return file_exists($typo3Dir) && is_dir($typo3Dir); 54 | } 55 | 56 | /** 57 | * Determine if the incoming request is for a static file. That is, it is 58 | * no PHP script file and the URI points to a valid file (no folder) on 59 | * the disk. Access to those static files will be authorized. 60 | * 61 | * @param string $sitePath 62 | * @param string $siteName 63 | * @param string $uri 64 | * @return string|false 65 | */ 66 | public function isStaticFile($sitePath, $siteName, $uri) 67 | { 68 | // May the file contains a cache busting version string like filename.12345678.css 69 | // If that is the case, the file cannot be found on disk, so remove the version 70 | // identifier before retrying below. 71 | if (!$this->isActualFile($filePath = $sitePath . $this->documentRoot . $uri)) { 72 | $uri = preg_replace("@^(.+)\.(\d+)\.(js|css|png|jpg|gif|gzip)$@", "$1.$3", $uri); 73 | } 74 | 75 | // Now that any possible version string is cleared from the filename, the resulting 76 | // URI should be a valid file on disc. So assemble the absolut file name with the 77 | // same schema as above and if it exists, authorize access and return its path. 78 | if ($this->isActualFile($filePath = $sitePath . $this->documentRoot . $uri)) { 79 | return $this->isAccessAuthorized($uri) ? $filePath : false; 80 | } 81 | 82 | // This file cannot be found in the current project and thus cannot be served. 83 | return false; 84 | } 85 | 86 | /** 87 | * Determines if the given URI is blacklisted so that access is prevented. 88 | * 89 | * @param string $uri 90 | * @return boolean 91 | */ 92 | private function isAccessAuthorized($uri) 93 | { 94 | foreach ($this->forbiddenUriPatterns as $forbiddenUriPattern) { 95 | if (preg_match("@$forbiddenUriPattern@", $uri)) { 96 | return false; 97 | } 98 | } 99 | 100 | return true; 101 | } 102 | 103 | /** 104 | * Get the fully resolved path to the application's front controller. 105 | * This can be the currently requested PHP script, a folder that 106 | * contains an index.php or the global index.php otherwise. 107 | * 108 | * @param string $sitePath 109 | * @param string $siteName 110 | * @param string $uri 111 | * @return string 112 | */ 113 | public function frontControllerPath($sitePath, $siteName, $uri) 114 | { 115 | // without modifying the URI, redirect if necessary 116 | $this->handleRedirectBackendShorthandUris($uri); 117 | 118 | // from now on, remove trailing / for convenience for all the following join operations 119 | $uri = rtrim($uri, '/'); 120 | 121 | // try to find the responsible script file for the requested folder / script URI 122 | if (file_exists($absoluteFilePath = $sitePath . $this->documentRoot . $uri)) { 123 | if (is_dir($absoluteFilePath)) { 124 | if (file_exists($absoluteFilePath . '/index.php')) { 125 | // this folder can be served by index.php 126 | return $this->serveScript($sitePath, $siteName, $uri . '/index.php'); 127 | } 128 | 129 | if (file_exists($absoluteFilePath . '/index.html')) { 130 | // this folder can be served by index.html 131 | return $absoluteFilePath . '/index.html'; 132 | } 133 | } elseif (pathinfo($absoluteFilePath, PATHINFO_EXTENSION) === 'php') { 134 | // this file can be served directly 135 | return $this->serveScript($sitePath, $siteName, $uri); 136 | } 137 | } 138 | 139 | // the global index.php will handle all other cases 140 | return $this->serveScript($sitePath, $siteName, '/index.php'); 141 | } 142 | 143 | /** 144 | * Direct access to installtool via domain.test/typo3/install/ will be redirected to 145 | * sysext install script. domain.test/typo3 will be redirected to /typo3/, because 146 | * the generated JavaScript URIs on the login screen would be broken on /typo3. 147 | * 148 | * @param string $uri 149 | */ 150 | private function handleRedirectBackendShorthandUris($uri) 151 | { 152 | if (rtrim($uri, '/') === '/typo3/install') { 153 | header('Location: /typo3/sysext/install/Start/Install.php'); 154 | die(); 155 | } 156 | 157 | if ($uri === '/typo3') { 158 | header('Location: /typo3/'); 159 | die(); 160 | } 161 | } 162 | 163 | /** 164 | * Configures the $_SERVER globals for serving the script at 165 | * the specified URI and returns it absolute file path. 166 | * 167 | * @param string $sitePath 168 | * @param string $siteName 169 | * @param string $uri 170 | * @param string $script 171 | * @return string 172 | */ 173 | private function serveScript($sitePath, $siteName, $uri) 174 | { 175 | $docroot = $sitePath . $this->documentRoot; 176 | $abspath = $docroot . $uri; 177 | 178 | $_SERVER['SERVER_NAME'] = $siteName . '.test'; 179 | $_SERVER['DOCUMENT_ROOT'] = $docroot; 180 | $_SERVER['DOCUMENT_URI'] = $uri; 181 | $_SERVER['SCRIPT_FILENAME'] = $abspath; 182 | $_SERVER['SCRIPT_NAME'] = $uri; 183 | $_SERVER['PHP_SELF'] = $uri; 184 | 185 | return $abspath; 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /cli/drivers/ValetDriver.php: -------------------------------------------------------------------------------- 1 | serves($sitePath, $siteName, $driver->mutateUri($uri))) { 81 | return $driver; 82 | } 83 | } 84 | } 85 | 86 | /** 87 | * Get the custom driver class from the site path, if one exists. 88 | * 89 | * @param string $sitePath 90 | * @return string 91 | */ 92 | public static function customSiteDriver($sitePath) 93 | { 94 | if (!file_exists($sitePath . '/LocalValetDriver.php')) { 95 | return; 96 | } 97 | 98 | require_once $sitePath . '/LocalValetDriver.php'; 99 | 100 | return 'LocalValetDriver'; 101 | } 102 | 103 | /** 104 | * Get all of the driver classes in a given path. 105 | * 106 | * @param string $path 107 | * @return array 108 | */ 109 | public static function driversIn($path) 110 | { 111 | if (!is_dir($path)) { 112 | return []; 113 | } 114 | 115 | $drivers = []; 116 | 117 | foreach (scandir($path) as $file) { 118 | if ($file !== 'ValetDriver.php' && strpos($file, 'ValetDriver') !== false) { 119 | require_once $path . '/' . $file; 120 | 121 | $drivers[] = basename($file, '.php'); 122 | } 123 | } 124 | 125 | return $drivers; 126 | } 127 | 128 | /** 129 | * Mutate the incoming URI. 130 | * 131 | * @param string $uri 132 | * @return string 133 | */ 134 | public function mutateUri($uri) 135 | { 136 | return $uri; 137 | } 138 | 139 | /** 140 | * Serve the static file at the given path. 141 | * 142 | * @param string $staticFilePath 143 | * @param string $sitePath 144 | * @param string $siteName 145 | * @param string $uri 146 | * @return void 147 | */ 148 | public function serveStaticFile($staticFilePath, $sitePath, $siteName, $uri) 149 | { 150 | /** 151 | * Back story... 152 | * 153 | * PHP docs *claim* you can set default_mimetype = "" to disable the default 154 | * Content-Type header. This works in PHP 7+, but in PHP 5.* it sends an 155 | * *empty* Content-Type header, which is significantly different than 156 | * sending *no* Content-Type header. 157 | * 158 | * However, if you explicitly set a Content-Type header, then explicitly 159 | * remove that Content-Type header, PHP seems to not re-add the default. 160 | * 161 | * I have a hard time believing this is by design and not coincidence. 162 | * 163 | * Burn. it. all. 164 | */ 165 | header('Content-Type: text/html'); 166 | header_remove('Content-Type'); 167 | 168 | header('X-Accel-Redirect: /' . VALET_STATIC_PREFIX . $staticFilePath); 169 | } 170 | 171 | /** 172 | * Determine if the path is a file and not a directory. 173 | * 174 | * @param string $path 175 | * @return bool 176 | */ 177 | protected function isActualFile($path) 178 | { 179 | return !is_dir($path) && file_exists($path); 180 | } 181 | 182 | /** 183 | * Load server environment variables if available. 184 | * Processes any '*' entries first, and then adds site-specific entries 185 | * 186 | * @param string $sitePath 187 | * @param string $siteName 188 | * @return void 189 | */ 190 | public function loadServerEnvironmentVariables($sitePath, $siteName) 191 | { 192 | $varFilePath = $sitePath . '/.valet-env.php'; 193 | if (!file_exists($varFilePath)) { 194 | return; 195 | } 196 | 197 | $variables = include $varFilePath; 198 | 199 | $variablesToSet = isset($variables['*']) ? $variables['*'] : []; 200 | 201 | if (isset($variables[$siteName])) { 202 | $variablesToSet = array_merge($variablesToSet, $variables[$siteName]); 203 | } 204 | 205 | foreach ($variablesToSet as $key => $value) { 206 | if (!is_string($key)) continue; 207 | $_SERVER[$key] = $value; 208 | $_ENV[$key] = $value; 209 | putenv($key . '=' . $value); 210 | } 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /cli/drivers/WordPressValetDriver.php: -------------------------------------------------------------------------------- 1 | forceTrailingSlash($uri) 34 | ); 35 | } 36 | 37 | /** 38 | * Redirect to uri with trailing slash. 39 | * 40 | * @param string $uri 41 | * @return string 42 | */ 43 | private function forceTrailingSlash($uri) 44 | { 45 | if (substr($uri, -1 * strlen('/wp-admin')) == '/wp-admin') { 46 | header('Location: ' . $uri . '/'); 47 | die; 48 | } 49 | 50 | return $uri; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /cli/drivers/require.php: -------------------------------------------------------------------------------- 1 | make(static::containerKey()); 27 | 28 | return call_user_func_array([$resolvedInstance, $method], $parameters); 29 | } 30 | } 31 | 32 | class Nginx extends Facade 33 | { 34 | } 35 | 36 | class PackageManager extends Facade 37 | { 38 | } 39 | 40 | class Apt extends Facade 41 | { 42 | } 43 | 44 | class Dnf extends Facade 45 | { 46 | } 47 | 48 | class Pacman extends Facade 49 | { 50 | } 51 | 52 | class ServiceManager extends Facade 53 | { 54 | } 55 | 56 | class LinuxService extends Facade 57 | { 58 | } 59 | 60 | class Systemd extends Facade 61 | { 62 | } 63 | 64 | class CommandLine extends Facade 65 | { 66 | } 67 | 68 | class Configuration extends Facade 69 | { 70 | } 71 | 72 | class DnsMasq extends Facade 73 | { 74 | } 75 | 76 | class Filesystem extends Facade 77 | { 78 | } 79 | 80 | class Ngrok extends Facade 81 | { 82 | } 83 | 84 | class PhpFpm extends Facade 85 | { 86 | } 87 | 88 | class Site extends Facade 89 | { 90 | } 91 | 92 | class Valet extends Facade 93 | { 94 | } 95 | 96 | class Requirements extends Facade 97 | { 98 | } 99 | -------------------------------------------------------------------------------- /cli/includes/helpers.php: -------------------------------------------------------------------------------- 1 | ' . $output . ''); 27 | } 28 | 29 | /** 30 | * Output the given text to the console. 31 | * 32 | * @param string $output 33 | * @return void 34 | */ 35 | function warning($output) 36 | { 37 | output('' . $output . ''); 38 | } 39 | 40 | /** 41 | * Output a table to the console. 42 | * 43 | * @param array $headers 44 | * @param array $rows 45 | * @return void 46 | */ 47 | function table(array $headers = [], array $rows = []) 48 | { 49 | $table = new Table(new ConsoleOutput); 50 | 51 | $table->setHeaders($headers)->setRows($rows); 52 | 53 | $table->render(); 54 | } 55 | 56 | /** 57 | * Output the given text to the console. 58 | * 59 | * @param string $output 60 | * @return void 61 | */ 62 | function output($output) 63 | { 64 | if (isset($_ENV['APP_ENV']) && $_ENV['APP_ENV'] === 'testing') { 65 | return; 66 | } 67 | 68 | (new ConsoleOutput)->writeln($output); 69 | } 70 | 71 | if (!function_exists('resolve')) { 72 | /** 73 | * Resolve the given class from the container. 74 | * 75 | * @param string $class 76 | * @return mixed 77 | */ 78 | function resolve($class) 79 | { 80 | return Container::getInstance()->make($class); 81 | } 82 | } 83 | 84 | 85 | if (! function_exists('ends_with')) { 86 | /** 87 | * Determine if a given string ends with a given substring. 88 | * 89 | * @param string $haystack 90 | * @param string|array $needles 91 | * @return bool 92 | */ 93 | function ends_with($haystack, $needles) 94 | { 95 | foreach ((array) $needles as $needle) { 96 | if (substr($haystack, -strlen($needle)) === (string) $needle) { 97 | return true; 98 | } 99 | } 100 | 101 | return false; 102 | } 103 | } 104 | 105 | if (! function_exists('starts_with')) { 106 | /** 107 | * Determine if a given string starts with a given substring. 108 | * 109 | * @param string $haystack 110 | * @param string|string[] $needles 111 | * @return bool 112 | */ 113 | function starts_with($haystack, $needles) 114 | { 115 | foreach ((array) $needles as $needle) { 116 | if ((string) $needle !== '' && strncmp($haystack, $needle, strlen($needle)) === 0) { 117 | return true; 118 | } 119 | } 120 | 121 | return false; 122 | } 123 | } 124 | 125 | 126 | /** 127 | * Swap the given class implementation in the container. 128 | * 129 | * @param string $class 130 | * @param mixed $instance 131 | * @return void 132 | */ 133 | function swap($class, $instance) 134 | { 135 | Container::getInstance()->instance($class, $instance); 136 | } 137 | 138 | if (!function_exists('retry')) { 139 | /** 140 | * Retry the given function N times. 141 | * 142 | * @param int $retries 143 | * @param callable $retries 144 | * @param int $sleep 145 | * @return mixed 146 | */ 147 | function retry($retries, $fn, $sleep = 0) 148 | { 149 | beginning: 150 | try { 151 | return $fn(); 152 | } catch (Exception $e) { 153 | if (!$retries) { 154 | throw $e; 155 | } 156 | 157 | $retries--; 158 | 159 | if ($sleep > 0) { 160 | usleep($sleep * 1000); 161 | } 162 | 163 | goto beginning; 164 | } 165 | } 166 | } 167 | 168 | /** 169 | * Verify that the script is currently running as "sudo". 170 | * 171 | * @return void 172 | */ 173 | function should_be_sudo() 174 | { 175 | if (!isset($_SERVER['SUDO_USER'])) { 176 | throw new Exception('This command must be run with sudo.'); 177 | } 178 | } 179 | 180 | if (!function_exists('tap')) { 181 | /** 182 | * Tap the given value. 183 | * 184 | * @param mixed $value 185 | * @param callable $callback 186 | * @return mixed 187 | */ 188 | function tap($value, callable $callback) 189 | { 190 | $callback($value); 191 | 192 | return $value; 193 | } 194 | } 195 | 196 | if (!function_exists('ends_with')) { 197 | /** 198 | * Determine if a given string ends with a given substring. 199 | * 200 | * @param string $haystack 201 | * @param string|array $needles 202 | * @return bool 203 | */ 204 | function ends_with($haystack, $needles) 205 | { 206 | foreach ((array)$needles as $needle) { 207 | if (substr($haystack, -strlen($needle)) === (string)$needle) { 208 | return true; 209 | } 210 | } 211 | 212 | return false; 213 | } 214 | } 215 | 216 | /** 217 | * Get the user 218 | */ 219 | function user() 220 | { 221 | if (!isset($_SERVER['SUDO_USER'])) { 222 | return $_SERVER['USER']; 223 | } 224 | 225 | return $_SERVER['SUDO_USER']; 226 | } 227 | 228 | /** 229 | * Get the user's group 230 | */ 231 | function group() 232 | { 233 | if (!isset($_SERVER['SUDO_USER'])) { 234 | return exec('id -gn ' . $_SERVER['USER']); 235 | } 236 | 237 | return exec('id -gn ' . $_SERVER['SUDO_USER']); 238 | } 239 | 240 | /** 241 | * Search and replace using associative array 242 | * 243 | * @param array $searchAndReplace 244 | * @param string $subject 245 | * @return string 246 | */ 247 | function str_array_replace($searchAndReplace, $subject) 248 | { 249 | return str_replace(array_keys($searchAndReplace), array_values($searchAndReplace), $subject); 250 | } 251 | -------------------------------------------------------------------------------- /cli/scripts/fetch-share-url.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | 5 | php $DIR/../valet.php fetch-share-url | xsel -b 6 | -------------------------------------------------------------------------------- /cli/scripts/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | apt-get install libnss3-tools jq xsel > /dev/null 2>&1 4 | 5 | # Install PHP 7.0 6 | if [[ ! $(apt-cache search php[5-7].[0-9]-cli) ]] 7 | then 8 | add-apt-repository -y ppa:ondrej/php && apt-get update 9 | fi 10 | 11 | $VERSION='7.0' 12 | # Install PHP $VERSION 13 | apt-get install -y "php$VERSION-cli php$VERSION-common php$VERSION-curl php$VERSION-json php$VERSION-mbstring php$VERSION-mcrypt php$VERSION-mysql php$VERSION-opcache php$VERSION-readline php$VERSION-xml php$VERSION-zip" 14 | 15 | # Install Composer to /usr/local/bin 16 | if [[ ! $(which -a composer) ]] 17 | then 18 | echo "Installing Composer..." 19 | /usr/bin/php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" 20 | /usr/bin/php -r "if (hash_file('SHA384', 'composer-setup.php') === '92102166af5abdb03f49ce52a40591073a7b859a86e8ff13338cf7db58a19f7844fbc0bb79b2773bf30791e935dbd938') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" > /dev/null 2>&1 21 | /usr/bin/php composer-setup.php > /dev/null 2>&1 22 | /usr/bin/php -r "unlink('composer-setup.php');" 23 | 24 | mv composer.phar /usr/local/bin/composer 25 | chmod +x /usr/local/bin/composer 26 | fi 27 | 28 | COMPOSER_PATH=$(which composer) 29 | 30 | # Download and unpack the latest Valet release 31 | rm -rf $HOME/.valet-cli 32 | mkdir $HOME/.valet-cli 33 | 34 | echo "Downloading Valet..." 35 | TARBALL=$(curl -s https://api.github.com/repos/cpriego/valet-ubuntu/releases/latest | jq ".tarball_url") 36 | TARBALL=$(echo $TARBALL | sed -e 's/^"//' -e 's/"$//') 37 | wget --max-redirect=10 $TARBALL -O $HOME/.valet-cli/valet.tar.gz > /dev/null 2>&1 38 | tar xvzf $HOME/.valet-cli/valet.tar.gz -C $HOME/.valet-cli --strip 1 > /dev/null 2>&1 39 | 40 | # Install Valet to /usr/local/bin 41 | ln -snf $HOME/.valet-cli/valet /usr/local/bin/valet 42 | chmod +x /usr/local/bin/valet 43 | 44 | # Install Valet's Composer dependencies 45 | echo "Installing Valet's Composer dependencies..." 46 | /usr/bin/php $COMPOSER_PATH install -d $HOME/.valet-cli > /dev/null 2>&1 47 | 48 | # Run the Valet server installation process 49 | /usr/local/bin/valet install 50 | -------------------------------------------------------------------------------- /cli/scripts/update.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | # Determine if the port config key exists, if not, create it 5 | function fix-config() { 6 | local CONFIG="$HOME/.valet/config.json" 7 | 8 | if [[ -f $CONFIG ]] 9 | then 10 | local PORT=$(jq -r ".port" "$CONFIG") 11 | 12 | if [[ "$PORT" = "null" ]] 13 | then 14 | echo "Fixing valet config file..." 15 | CONTENTS=$(jq '. + {port: "80"}' "$CONFIG") 16 | echo -n $CONTENTS >| "$CONFIG" 17 | fi 18 | fi 19 | } 20 | 21 | if [[ "$1" = "update" ]] 22 | then 23 | composer global update "cpriego/valet-linux" 24 | valet install 25 | fi 26 | 27 | fix-config 28 | -------------------------------------------------------------------------------- /cli/stubs/SampleValetDriver.php: -------------------------------------------------------------------------------- 1 | /dev/null 59 | 60 | cat "$DNSHEAD" | unique | tee "$DNSFILE" &>/dev/null 61 | cat "${FILES[@]}" | grep -i '^nameserver' | grep -vE '127\.(0\.){2}\d{1,3}' | unique | tee -a "$DNSFILE" &>/dev/null 62 | 63 | symlinkResolv 64 | 65 | cat "${FILES[@]}" | grep -v '^nameserver' | grep -v '^#' | unique | tee "$VRESOLV" &>/dev/null 66 | echo 'nameserver 127.0.0.1' >> "$VRESOLV" 67 | 68 | # Add "search" and "domain" directives to /etc/resolv.conf 69 | # chattr -i "$RESOLV" && \ 70 | # cat "${FILES[@]}" | grep -v '^nameserver' | grep -v '^#' | unique | tee "$VRESOLV" &>/dev/null && \ 71 | # echo 'nameserver 127.0.0.1' >> "$VRESOLV" && \ 72 | # chattr +i "$RESOLV" 73 | } 74 | 75 | function getFiles() { 76 | FILES=() 77 | local TARRAY=() 78 | 79 | for DIR in "${DIRS[@]}"; do 80 | readarray -t TARRAY <<< "$(find ${DIR} -path ! -readable -prune -o -name 'resolv.conf' -print)" 81 | 82 | # Find nameserver files in the /run/resolvconf/interface folder (as they do not have a standard name) 83 | if [[ "$DIR" = "/run/resolvconf/interface" ]]; then 84 | readarray -t TARRAY <<< "$(find ${DIR} ! -readable -prune -o -type f -print)" 85 | fi 86 | 87 | FILES=(${FILES[@]} ${TARRAY[@]}) 88 | done 89 | } 90 | 91 | function watchDirs() { 92 | local WATCHERS=(${DIRS[@]} "$DNSHEAD") 93 | 94 | # Log which directories are being watched 95 | echo "Watching the following directories for changes:" >> "$LOGFILE" 96 | 97 | for DIR in "${DIRS[@]}"; do 98 | echo " - $DIR" >> "$LOGFILE" 99 | done 100 | 101 | # Watch directories for changes in files 102 | inotifywait -q -m -e modify -e create -e delete --format "%w%f" "${WATCHERS[@]}" | while read change; do 103 | updateNameservers 104 | done & 105 | } 106 | 107 | function main() { 108 | # Create dns file in case it does not exists 109 | if [[ ! -f "$DNSHEAD" ]] 110 | then 111 | echo "Creating default $DNSHEAD file" 112 | echo nameserver 1.1.1.1 > $DNSHEAD 113 | fi 114 | 115 | touch "$DNSFILE" 116 | 117 | # Clear log file 118 | if [[ -f "$LOGFILE" ]]; then 119 | rm "$LOGFILE" 120 | fi 121 | 122 | touch "$LOGFILE" 123 | 124 | getDirs 125 | updateNameservers 126 | watchDirs 127 | } 128 | 129 | ################################################################################ 130 | 131 | function start { 132 | if [[ $(pgrep -f 'inotifywait -q -m -e modify') ]]; then 133 | echo -e "Valet DNS Watcher is already running..." 134 | else 135 | echo -e "Starting Valet DNS Watcher..." 136 | main 137 | sleep 2 && echo $(pgrep -f 'inotifywait -q -m -e modify') > "${WORKDIR}/watch.pid" 138 | echo -e "Valet DNS Watcher started succesfully." 139 | fi 140 | } 141 | 142 | function stop { 143 | echo -e "Stopping Valet DNS Watcher...\n" 144 | 145 | pkill -f "inotifywait -q -m -e modify" 146 | 147 | rm "$LOGFILE" && touch "$LOGFILE" 148 | 149 | if [[ ! $(pgrep -f 'inotifywait -q -m -e modify') ]]; then 150 | echo -e "\nValet DNS Watcher stopped succesfully." 151 | fi 152 | } 153 | 154 | function restart { 155 | echo -e "Stopping Valet DNS Watcher..." 156 | 157 | if [[ $(pgrep -f 'inotifywait -q -m -e modify') ]]; then 158 | pkill -f "inotifywait -q -m -e modify" 159 | fi 160 | 161 | echo -e "Starting Valet DNS Watcher..." 162 | 163 | main 164 | 165 | if [[ $(pgrep -f 'inotifywait -q -m -e modify') ]]; then 166 | echo -e "Valet DNS Watcher restarted succesfully." 167 | fi 168 | } 169 | 170 | function status { 171 | if [[ -f "$LOGFILE" ]]; then 172 | echo -e "Valet DNS service is running correctly.\n" 173 | cat '/opt/valet-linux/watch.log' 174 | else 175 | echo "Valet DNS service is not running." 176 | fi 177 | } 178 | 179 | case "$1" in 180 | start) 181 | start 182 | ;; 183 | stop) 184 | stop 185 | ;; 186 | restart) 187 | restart 188 | ;; 189 | status) 190 | status 191 | ;; 192 | *) 193 | echo "Usage: $0 {start|stop|restart|status}" 194 | esac 195 | 196 | exit 0 197 | -------------------------------------------------------------------------------- /cli/stubs/valet.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen VALET_PORT default_server; 3 | root /; 4 | charset utf-8; 5 | client_max_body_size 128M; 6 | 7 | location /VALET_STATIC_PREFIX/ { 8 | internal; 9 | alias /; 10 | try_files $uri $uri/; 11 | } 12 | 13 | location / { 14 | rewrite ^ VALET_SERVER_PATH last; 15 | } 16 | 17 | location = /favicon.ico { access_log off; log_not_found off; } 18 | location = /robots.txt { access_log off; log_not_found off; } 19 | 20 | access_log off; 21 | error_log VALET_HOME_PATH/Log/nginx-error.log; 22 | 23 | error_page 404 VALET_SERVER_PATH; 24 | 25 | location ~ \.php$ { 26 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 27 | fastcgi_pass unix:VALET_HOME_PATH/valet.sock; 28 | fastcgi_index VALET_SERVER_PATH; 29 | include fastcgi_params; 30 | fastcgi_param SCRIPT_FILENAME VALET_SERVER_PATH; 31 | } 32 | 33 | location ~ /\.ht { 34 | deny all; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /cli/templates/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Valet - Not Found 4 | 5 | 10 | 11 | 12 | 404 - Not Found 13 | 14 | 15 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cpriego/valet-linux", 3 | "description": "A more enjoyable local development experience for Linux.", 4 | "keywords": ["laravel", "zonda", "wwdhhd", "ubuntu", "fedora", "arch", "linux", "valet"], 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Taylor Otwell", 9 | "email": "taylorotwell@gmail.com" 10 | }, 11 | { 12 | "name": "Adam Wathan", 13 | "email": "adam.wathan@gmail.com" 14 | }, 15 | { 16 | "name": "Carlos Priego", 17 | "email": "carlos.enrique.priego@gmail.com" 18 | }, 19 | { 20 | "name": "Leonardo Nodari", 21 | "email": "nodarileonardo@gmail.com" 22 | }, 23 | { 24 | "name": "Joaquín Marcher", 25 | "email": "joaquin@marcher.com.uy" 26 | }, 27 | { 28 | "name": "Ludovic Lemarinel", 29 | "email": "ludovic@adesin.fr" 30 | } 31 | ], 32 | "autoload": { 33 | "files": [ 34 | "cli/includes/compatibility.php", 35 | "cli/includes/facades.php", 36 | "cli/includes/helpers.php" 37 | ], 38 | "psr-4": { 39 | "Valet\\": "cli/Valet/" 40 | } 41 | }, 42 | "autoload-dev": { 43 | "psr-4": { 44 | "Valet\\Tests\\": "tests/" 45 | } 46 | }, 47 | "require": { 48 | "php": ">=7.0", 49 | "illuminate/container": "~5.3|^6.0|^7.0|^8.0|^9.0|^10.0|^11.00|^12.00", 50 | "mnapoli/silly": "~1.1", 51 | "symfony/process": "~2.7|~3.0|~4.0|~5.0|^6.0|^7.0", 52 | "nategood/httpful": "~1.0", 53 | "tightenco/collect": "~5.3|^6.0|^7.0|^8.0|^9.0", 54 | "ext-posix": "*", 55 | "ext-json": "*", 56 | "outrightvision/api-model": "^1.0" 57 | }, 58 | "require-dev": { 59 | "mockery/mockery": "^1.2.3", 60 | "phpunit/phpunit": "~5.5|^9.0" 61 | }, 62 | "scripts": { 63 | "post-install-cmd": [ 64 | ], 65 | "post-update-cmd": [ 66 | ] 67 | }, 68 | "bin": [ 69 | "valet" 70 | ] 71 | } 72 | -------------------------------------------------------------------------------- /develop: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | VAGRANT_FOLDER="./tests/Acceptance/Vagrant" 4 | 5 | if [ $# -gt 0 ]; then 6 | 7 | # Spin up given development environment 8 | if [ "$1" == "up" ]; then 9 | cd "$VAGRANT_FOLDER/$2" && \ 10 | shift 2 && \ 11 | VALET_ENVIRONMENT=development vagrant up "$@" 12 | 13 | # SSH into the given development environment 14 | elif [ "$1" == "ssh" ]; then 15 | cd "$VAGRANT_FOLDER/$2" && \ 16 | shift 2 && \ 17 | vagrant ssh "$@" 18 | 19 | # Switch off given development environment 20 | elif [ "$1" == "down" ]; then 21 | cd "$VAGRANT_FOLDER/$2" && \ 22 | shift 2 && \ 23 | vagrant halt "$@" 24 | 25 | # Destroy given development environment 26 | elif [[ "$1" == "destroy" ]]; then 27 | cd "$VAGRANT_FOLDER/$2" && \ 28 | shift 2 && \ 29 | vagrant destroy "$@" 30 | 31 | # Destroy all development environments 32 | elif [[ "$1" == "destroy-all" ]]; then 33 | for DIRECTORY in `find $VAGRANT_FOLDER -maxdepth 1 -type d`; do 34 | if [[ "$DIRECTORY" != $VAGRANT_FOLDER ]]; then 35 | ./develop destroy ${DIRECTORY##*/} -f 36 | fi 37 | done 38 | 39 | # Run Acceptance tests against given environment 40 | elif [ "$1" == "test" ]; then 41 | ./develop up $2 && \ 42 | ./develop ssh $2 --command "bash ~/cpriego-valet-linux/tests/Acceptance/vagrant.sh" 43 | 44 | # Run Acceptance tests against ALL environments 45 | elif [ "$1" == "test-all" ]; then 46 | for DIRECTORY in `find $VAGRANT_FOLDER -maxdepth 1 -type d`; do 47 | if [[ "$DIRECTORY" != $VAGRANT_FOLDER ]]; then 48 | echo -e "\033[44m ${DIRECTORY##*/} \033[0m" 49 | 50 | ./develop test ${DIRECTORY##*/} && \ 51 | ./develop down ${DIRECTORY##*/} 52 | fi 53 | done 54 | 55 | # Run Acceptance tests against ALL environments in parallel 56 | elif [ "$1" == "test-all-parallel" ]; then 57 | for DIRECTORY in `find $VAGRANT_FOLDER -maxdepth 1 -type d`; do 58 | if [[ "$DIRECTORY" != $VAGRANT_FOLDER ]]; then 59 | gnome-terminal --tab \ 60 | --command="bash -c \"./develop test ${DIRECTORY##*/} && ./develop down ${DIRECTORY##*/}; read\"" 61 | fi 62 | done 63 | 64 | fi 65 | 66 | else 67 | # Display usage 68 | echo -e "Usage: ./develop [action] [arguments]\n" 69 | echo -e "Available actions:" 70 | 71 | # ./develop up {OS_NAME} 72 | echo -e "\tup {OS_NAME}" 73 | echo -e "\t\tSpin up a development environment using vagrant and SSH into it." 74 | echo -e -n "\t\tAvailable OSes: " 75 | for DIRECTORY in `find $VAGRANT_FOLDER -maxdepth 1 -type d`; do 76 | if [[ "$DIRECTORY" != $VAGRANT_FOLDER ]]; then 77 | echo -e -n "${DIRECTORY##*/} " 78 | fi 79 | done 80 | echo 81 | 82 | # ./develop ssh {OS_NAME} 83 | echo -e "\tssh {OS_NAME}" 84 | echo -e "\t\tSSH into the given development environment." 85 | 86 | # ./develop down {OS_NAME} 87 | echo -e "\tdown {OS_NAME}" 88 | echo -e "\t\tSwitch off given development environment. This does NOT destroy the box." 89 | 90 | # ./develop destroy {OS_NAME} 91 | echo -e "\tdestroy {OS_NAME}" 92 | echo -e "\t\tDestroy given development environment." 93 | 94 | # ./develop destroy-all 95 | echo -e "\tdestroy-all" 96 | echo -e "\t\tDestroy all development environments." 97 | 98 | # ./develop test {OS_NAME} 99 | echo -e "\ttest {OS_NAME}" 100 | echo -e "\t\tRun Acceptance tests against a given OS." 101 | 102 | # ./develop test-all 103 | echo -e "\ttest-all" 104 | echo -e "\t\tRun Acceptance tests against ALL OSes." 105 | 106 | # ./develop test-all-parallel 107 | echo -e "\ttest-all-parallel" 108 | echo -e "\t\tRun Acceptance tests against ALL OSes in parellel. Requires gnome-terminal." 109 | 110 | fi 111 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | Build Status 5 | Total Downloads 6 | Latest Stable Version 7 | Latest Unstable Version 8 | License 9 |

10 | 11 | ## Introduction 12 | 13 | Valet *Linux* is a Laravel development environment for Linux minimalists. No Vagrant, no `/etc/hosts` file. You can even share your sites publicly using local tunnels. _Yeah, we like it too._ 14 | 15 | Valet *Linux* configures your system to always run Nginx in the background when your machine starts. Then, using [DnsMasq](https://en.wikipedia.org/wiki/Dnsmasq), Valet proxies all requests on the `*.test` domain to point to sites installed on your local machine. 16 | 17 | In other words, a blazing fast Laravel development environment that uses roughly 7MB of RAM. Valet *Linux* isn't a complete replacement for Vagrant or Homestead, but provides a great alternative if you want flexible basics, prefer extreme speed, or are working on a machine with a limited amount of RAM. 18 | 19 | ## Official Documentation 20 | 21 | Documentation for Valet can be found on the [Valet Linux website](https://cpriego.github.io/valet-linux/). 22 | 23 | ## License 24 | 25 | Laravel Valet is an open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT) 26 | -------------------------------------------------------------------------------- /server.php: -------------------------------------------------------------------------------- 1 | 6) { 34 | $domain = implode('.', array_reverse(array_slice(array_reverse($domainPart), 6))); 35 | } 36 | } 37 | 38 | if (strpos($domain, ':') !== false) { 39 | $domain = explode(':', $domain)[0]; 40 | } 41 | 42 | return $domain; 43 | } 44 | } 45 | 46 | if (!function_exists('valet_path_to_slug')) { 47 | /** 48 | * Convert absolute path to slug. 49 | * 50 | * @param string $path 51 | * @return string Slug version of last folder name 52 | */ 53 | function valet_path_to_slug($path) 54 | { 55 | $replace = [ 56 | '<' => '', '>' => '', ''' => '', '&' => '', 57 | '"' => '', 'À' => 'A', 'Á' => 'A', 'Â' => 'A', 'Ã' => 'A', 'Ä' => 'Ae', 58 | 'Ä' => 'A', 'Å' => 'A', 'Ā' => 'A', 'Ą' => 'A', 'Ă' => 'A', 'Æ' => 'Ae', 59 | 'Ç' => 'C', 'Ć' => 'C', 'Č' => 'C', 'Ĉ' => 'C', 'Ċ' => 'C', 'Ď' => 'D', 'Đ' => 'D', 60 | 'Ð' => 'D', 'È' => 'E', 'É' => 'E', 'Ê' => 'E', 'Ë' => 'E', 'Ē' => 'E', 61 | 'Ę' => 'E', 'Ě' => 'E', 'Ĕ' => 'E', 'Ė' => 'E', 'Ĝ' => 'G', 'Ğ' => 'G', 62 | 'Ġ' => 'G', 'Ģ' => 'G', 'Ĥ' => 'H', 'Ħ' => 'H', 'Ì' => 'I', 'Í' => 'I', 63 | 'Î' => 'I', 'Ï' => 'I', 'Ī' => 'I', 'Ĩ' => 'I', 'Ĭ' => 'I', 'Į' => 'I', 64 | 'İ' => 'I', 'IJ' => 'IJ', 'Ĵ' => 'J', 'Ķ' => 'K', 'Ł' => 'K', 'Ľ' => 'K', 65 | 'Ĺ' => 'K', 'Ļ' => 'K', 'Ŀ' => 'K', 'Ñ' => 'N', 'Ń' => 'N', 'Ň' => 'N', 66 | 'Ņ' => 'N', 'Ŋ' => 'N', 'Ò' => 'O', 'Ó' => 'O', 'Ô' => 'O', 'Õ' => 'O', 67 | 'Ö' => 'Oe', 'Ö' => 'Oe', 'Ø' => 'O', 'Ō' => 'O', 'Ő' => 'O', 'Ŏ' => 'O', 68 | 'Œ' => 'OE', 'Ŕ' => 'R', 'Ř' => 'R', 'Ŗ' => 'R', 'Ś' => 'S', 'Š' => 'S', 69 | 'Ş' => 'S', 'Ŝ' => 'S', 'Ș' => 'S', 'Ť' => 'T', 'Ţ' => 'T', 'Ŧ' => 'T', 70 | 'Ț' => 'T', 'Ù' => 'U', 'Ú' => 'U', 'Û' => 'U', 'Ü' => 'Ue', 'Ū' => 'U', 71 | 'Ü' => 'Ue', 'Ů' => 'U', 'Ű' => 'U', 'Ŭ' => 'U', 'Ũ' => 'U', 'Ų' => 'U', 72 | 'Ŵ' => 'W', 'Ý' => 'Y', 'Ŷ' => 'Y', 'Ÿ' => 'Y', 'Ź' => 'Z', 'Ž' => 'Z', 73 | 'Ż' => 'Z', 'Þ' => 'T', 'à' => 'a', 'á' => 'a', 'â' => 'a', 'ã' => 'a', 74 | 'ä' => 'ae', 'ä' => 'ae', 'å' => 'a', 'ā' => 'a', 'ą' => 'a', 'ă' => 'a', 75 | 'æ' => 'ae', 'ç' => 'c', 'ć' => 'c', 'č' => 'c', 'ĉ' => 'c', 'ċ' => 'c', 76 | 'ď' => 'd', 'đ' => 'd', 'ð' => 'd', 'è' => 'e', 'é' => 'e', 'ê' => 'e', 77 | 'ë' => 'e', 'ē' => 'e', 'ę' => 'e', 'ě' => 'e', 'ĕ' => 'e', 'ė' => 'e', 78 | 'ƒ' => 'f', 'ĝ' => 'g', 'ğ' => 'g', 'ġ' => 'g', 'ģ' => 'g', 'ĥ' => 'h', 79 | 'ħ' => 'h', 'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i', 'ī' => 'i', 80 | 'ĩ' => 'i', 'ĭ' => 'i', 'į' => 'i', 'ı' => 'i', 'ij' => 'ij', 'ĵ' => 'j', 81 | 'ķ' => 'k', 'ĸ' => 'k', 'ł' => 'l', 'ľ' => 'l', 'ĺ' => 'l', 'ļ' => 'l', 82 | 'ŀ' => 'l', 'ñ' => 'n', 'ń' => 'n', 'ň' => 'n', 'ņ' => 'n', 'ʼn' => 'n', 83 | 'ŋ' => 'n', 'ò' => 'o', 'ó' => 'o', 'ô' => 'o', 'õ' => 'o', 'ö' => 'oe', 84 | 'ö' => 'oe', 'ø' => 'o', 'ō' => 'o', 'ő' => 'o', 'ŏ' => 'o', 'œ' => 'oe', 85 | 'ŕ' => 'r', 'ř' => 'r', 'ŗ' => 'r', 'š' => 's', 'ù' => 'u', 'ú' => 'u', 86 | 'û' => 'u', 'ü' => 'ue', 'ū' => 'u', 'ü' => 'ue', 'ů' => 'u', 'ű' => 'u', 87 | 'ŭ' => 'u', 'ũ' => 'u', 'ų' => 'u', 'ŵ' => 'w', 'ý' => 'y', 'ÿ' => 'y', 88 | 'ŷ' => 'y', 'ž' => 'z', 'ż' => 'z', 'ź' => 'z', 'þ' => 't', 'ß' => 'ss', 89 | 'ſ' => 'ss', 'ый' => 'iy', 'А' => 'A', 'Б' => 'B', 'В' => 'V', 'Г' => 'G', 90 | 'Д' => 'D', 'Е' => 'E', 'Ё' => 'YO', 'Ж' => 'ZH', 'З' => 'Z', 'И' => 'I', 91 | 'Й' => 'Y', 'К' => 'K', 'Л' => 'L', 'М' => 'M', 'Н' => 'N', 'О' => 'O', 92 | 'П' => 'P', 'Р' => 'R', 'С' => 'S', 'Т' => 'T', 'У' => 'U', 'Ф' => 'F', 93 | 'Х' => 'H', 'Ц' => 'C', 'Ч' => 'CH', 'Ш' => 'SH', 'Щ' => 'SCH', 'Ъ' => '', 94 | 'Ы' => 'Y', 'Ь' => '', 'Э' => 'E', 'Ю' => 'YU', 'Я' => 'YA', 'а' => 'a', 95 | 'б' => 'b', 'в' => 'v', 'г' => 'g', 'д' => 'd', 'е' => 'e', 'ё' => 'yo', 96 | 'ж' => 'zh', 'з' => 'z', 'и' => 'i', 'й' => 'y', 'к' => 'k', 'л' => 'l', 97 | 'м' => 'm', 'н' => 'n', 'о' => 'o', 'п' => 'p', 'р' => 'r', 'с' => 's', 98 | 'т' => 't', 'у' => 'u', 'ф' => 'f', 'х' => 'h', 'ц' => 'c', 'ч' => 'ch', 99 | 'ш' => 'sh', 'щ' => 'sch', 'ъ' => '', 'ы' => 'y', 'ь' => '', 'э' => 'e', 100 | 'ю' => 'yu', 'я' => 'ya', 101 | ]; 102 | 103 | // make a human readable string 104 | $slug = strtr(basename($path), $replace); 105 | 106 | // replace non letter or digits by - 107 | $slug = preg_replace('~[^\\pL\d.]+~u', '-', $slug); 108 | 109 | // trim 110 | $slug = trim($slug, '-'); 111 | 112 | // remove unwanted characters 113 | $slug = preg_replace('~[^-\w.]+~', '', $slug); 114 | 115 | return strtolower($slug); 116 | } 117 | } 118 | if (!function_exists('valet_default_site_path')) { 119 | /* 120 | * @param array $config Valet configuration array 121 | * 122 | * @return string|null If set, default site path for uncaught urls 123 | * */ 124 | function valet_default_site_path($config) 125 | { 126 | if (isset($config['default']) && is_string($config['default']) && is_dir($config['default'])) { 127 | return $config['default']; 128 | } 129 | 130 | return null; 131 | } 132 | } 133 | 134 | /** 135 | * Load the Valet configuration. 136 | */ 137 | $valetConfig = json_decode( 138 | file_get_contents(VALET_HOME_PATH . '/config.json'), true 139 | ); 140 | 141 | /** 142 | * Parse the URI and site / host for the incoming request. 143 | */ 144 | $uri = rawurldecode( 145 | explode("?", $_SERVER['REQUEST_URI'])[0] 146 | ); 147 | 148 | $siteName = basename( 149 | // Filter host to support xip.io feature 150 | valet_support_xip_io(explode(':', strtolower($_SERVER['HTTP_HOST']))[0]), 151 | '.' . $valetConfig['domain'] 152 | ); 153 | 154 | if (strpos($siteName, 'www.') === 0) { 155 | $siteName = substr($siteName, 4); 156 | } 157 | 158 | /** 159 | * Determine the fully qualified path to the site. 160 | */ 161 | $valetSitePath = null; 162 | 163 | foreach ($valetConfig['paths'] as $path) { 164 | $domain = ($pos = strrpos($siteName, '.')) !== false 165 | ? substr($siteName, $pos + 1) 166 | : null; 167 | 168 | foreach (glob($path . '/*', GLOB_ONLYDIR) as $dirPath) { 169 | $slug = valet_path_to_slug($dirPath); 170 | 171 | if ($slug == $siteName || $slug == $domain) { 172 | $valetSitePath = $dirPath; 173 | 174 | break 2; 175 | } 176 | } 177 | } 178 | 179 | if (is_null($valetSitePath) && is_null($valetSitePath = valet_default_site_path($valetConfig))) { 180 | show_valet_404(); 181 | } 182 | 183 | /** 184 | * Find the appropriate Valet driver for the request. 185 | */ 186 | $valetDriver = null; 187 | 188 | require __DIR__ . '/cli/drivers/require.php'; 189 | 190 | $valetDriver = ValetDriver::assign($valetSitePath, $siteName, $uri); 191 | 192 | if (!$valetDriver) { 193 | show_valet_404(); 194 | } 195 | 196 | /** 197 | * Overwrite the HTTP host for Ngrok. 198 | */ 199 | if (isset($_SERVER['HTTP_X_ORIGINAL_HOST'])) { 200 | $_SERVER['HTTP_HOST'] = $_SERVER['HTTP_X_ORIGINAL_HOST']; 201 | } 202 | 203 | /** 204 | * Attempt to load server environment variables. 205 | */ 206 | $valetDriver->loadServerEnvironmentVariables( 207 | $valetSitePath, $siteName 208 | ); 209 | 210 | /** 211 | * Allow driver to mutate incoming URL. 212 | */ 213 | $uri = $valetDriver->mutateUri($uri); 214 | 215 | /** 216 | * Determine if the incoming request is for a static file. 217 | */ 218 | $isPhpFile = pathinfo($uri, PATHINFO_EXTENSION) === 'php'; 219 | 220 | if ($uri !== '/' && !$isPhpFile && $staticFilePath = $valetDriver->isStaticFile($valetSitePath, $siteName, $uri)) { 221 | $valetDriver->serveStaticFile($staticFilePath, $valetSitePath, $siteName, $uri); 222 | 223 | return; 224 | } 225 | 226 | /** 227 | * Attempt to dispatch to a front controller. 228 | */ 229 | $frontControllerPath = $valetDriver->frontControllerPath( 230 | $valetSitePath, $siteName, $uri 231 | ); 232 | 233 | if (!$frontControllerPath) { 234 | show_valet_404(); 235 | } 236 | 237 | chdir(dirname($frontControllerPath)); 238 | 239 | require $frontControllerPath; 240 | -------------------------------------------------------------------------------- /valet: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | SOURCE="${BASH_SOURCE[0]}" 6 | SUDOCMDS="uninstall install start restart stop unsecure secure use isolate unisolate" 7 | HOMEPATH=$HOME 8 | 9 | function check_dependencies() { 10 | local RED='\033[1;31m' 11 | local NC='\033[0m' 12 | local msg='' 13 | 14 | for cmd in "jq" "xsel" "certutil"; do 15 | local str='' 16 | 17 | if ! [[ -x "$(command -v $cmd)" ]]; then 18 | printf -v str " - %s\n" "$cmd" 19 | local msg+="$str" 20 | fi 21 | done 22 | 23 | if [[ $msg != '' ]]; then 24 | printf "${RED}You have missing Valet dependiencies:\n" 25 | printf "$msg" 26 | printf "\nPlease refer to https://cpriego.github.io/valet-linux/requirements on how to install them.${NC}\n" 27 | exit 1 28 | fi 29 | } 30 | 31 | # If the current source is a symbolic link, we need to resolve it to an 32 | # actual directory name. We'll use PHP to do this easier than we can 33 | # do it in pure Bash. So, we'll call into PHP CLI here to resolve. 34 | if [[ -L $SOURCE ]] 35 | then 36 | DIR=$(php -r "echo dirname(realpath('$SOURCE'));") 37 | else 38 | DIR="$( cd "$( dirname "$SOURCE" )" && pwd )" 39 | fi 40 | 41 | # If we are in the global Composer "bin" directory, we need to bump our 42 | # current directory up two, so that we will correctly proxy into the 43 | # Valet CLI script which is written in PHP. Will use PHP to do it. 44 | if [[ ! -f "$DIR/cli/valet.php" ]] 45 | then 46 | DIR=$(php -r "echo realpath('$DIR/../laravel/valet');") 47 | fi 48 | 49 | # If the command is one of the commands that requires "sudo" privileges 50 | # then we'll proxy the incoming CLI request using sudo, which allows 51 | # us to never require the end users to manually specify each sudo. 52 | if [[ -n $1 && $SUDOCMDS =~ $1 ]] 53 | then 54 | check_dependencies 55 | 56 | if [[ "$EUID" -ne 0 ]] 57 | then 58 | sudo env HOME=$HOMEPATH $SOURCE "$@" 59 | exit 0 60 | fi 61 | fi 62 | 63 | if [[ -n $2 && "domain port" =~ $1 ]] 64 | then 65 | if [[ "$EUID" -ne 0 ]] 66 | then 67 | sudo env HOME=$HOMEPATH $SOURCE "$@" 68 | exit 0 69 | fi 70 | fi 71 | 72 | # If the command is the "share" command we will need to resolve out any 73 | # symbolic links for the site. Before starting Ngrok, we will fire a 74 | # process to retrieve the live Ngrok tunnel URL in the background. 75 | if [[ "$1" = "share" ]] 76 | then 77 | check_dependencies 78 | 79 | HOST="${PWD##*/}" 80 | DOMAIN=$(cat "$HOME/.valet/config.json" | jq -r ".domain") 81 | PORT=$(cat "$HOME/.valet/config.json" | jq -r ".port") 82 | 83 | for linkname in ~/.valet/Sites/*; do 84 | if [[ "$(readlink ${linkname})" = "$PWD" ]] 85 | then 86 | HOST="${linkname##*/}" 87 | fi 88 | done 89 | 90 | # Decide the correct PORT to use according if the site has a secure 91 | # config or not. 92 | if grep --no-messages --quiet 443 ~/.valet/Nginx/$HOST* 93 | then 94 | PORT=88 95 | fi 96 | 97 | # Fetch Ngrok URL In Background... 98 | bash "$DIR/cli/scripts/fetch-share-url.sh" & 99 | sudo -u $USER "$DIR/bin/ngrok" http "$HOST.$DOMAIN:$PORT" --host-header=rewrite ${*:2} 100 | exit 101 | 102 | # Finally, for every other command we will just proxy into the PHP tool 103 | # and let it handle the request. These are commands which can be run 104 | # without sudo and don't require taking over terminals like Ngrok. 105 | else 106 | php "$DIR/cli/valet.php" "$@" 107 | fi 108 | --------------------------------------------------------------------------------