|
<?php |
|
|
|
/** |
|
* Locomotive Bedrock Valet Driver |
|
* |
|
* Supported Valet Version: 4 |
|
*/ |
|
|
|
namespace Valet\Drivers\Custom; |
|
|
|
use Illuminate\Support\Collection; |
|
use Valet\Drivers\BasicValetDriver; |
|
|
|
class LocomotiveBedrockValetDriver extends BasicValetDriver |
|
{ |
|
public const PUBLIC_PATHS = [ 'www', 'web' ]; |
|
public const WP_PATHS = [ 'wp', 'wordpress' ]; |
|
public const STATIC_FILE_TYPES = [ 'jpe?g', 'gif', 'pdf', 'png', 'svg', 'mp4' ]; |
|
|
|
/** @var ?array<string, mixed> */ |
|
public $valetSiteConfig = null; |
|
|
|
/** @var ?string */ |
|
public $valetSiteName = null; |
|
|
|
/** @var ?string */ |
|
public $valetSitePath = null; |
|
|
|
/** @var array<string, mixed> */ |
|
public $valetSiteOptions = [ |
|
'remote_assets_domain' => null, |
|
'allow_indexphp_files' => false, |
|
'redirect_trailing_slashes' => false, |
|
]; |
|
|
|
/** |
|
* Determine if the driver serves the request. |
|
*/ |
|
public function serves(string $sitePath, string $siteName, string $uri): bool |
|
{ |
|
$hasBedrockAutoloader = $this->composerRequires($sitePath, 'roots/bedrock-autoloader'); |
|
|
|
foreach (static::PUBLIC_PATHS as $publicPath) { |
|
if ( |
|
$hasBedrockAutoloader || |
|
file_exists("{$sitePath}/{$publicPath}/mu-plugins/bedrock-autoloader.php") || |
|
( |
|
is_dir("{$sitePath}/{$publicPath}/") && |
|
file_exists("{$sitePath}/{$publicPath}/wp-config.php") && |
|
file_exists("{$sitePath}/config/application.php") |
|
) |
|
) { |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
/** |
|
* Determine if the incoming request is for a static file. |
|
* |
|
* @return string|false |
|
*/ |
|
public function isStaticFile(string $sitePath, string $siteName, string $uri) |
|
{ |
|
foreach (static::PUBLIC_PATHS as $publicPath) { |
|
$staticFilePath = "{$sitePath}/{$publicPath}{$uri}"; |
|
|
|
if ($this->isActualFile($staticFilePath)) { |
|
return $staticFilePath; |
|
} |
|
} |
|
|
|
// Use remote assets as fallback |
|
$this->isRemoteFile($sitePath, $siteName, $uri); |
|
|
|
return false; |
|
} |
|
|
|
/** |
|
* Determine if the incoming request is for a remote static file. |
|
* |
|
* @return void |
|
*/ |
|
public function isRemoteFile(string $sitePath, string $siteName, string $uri): void |
|
{ |
|
$removeAssetHost = $this->getValetConfig('remote_assets_domain'); |
|
$regexpEligibleFiles = '/^(.*)\.('.implode('|', static::STATIC_FILE_TYPES).')$/i'; |
|
if ($removeAssetHost && preg_match($regexpEligibleFiles, $uri)) { |
|
header("Location: {$removeAssetHost}{$uri}"); |
|
exit; |
|
} |
|
} |
|
|
|
/** |
|
* Get the fully resolved path to the application's front controller. |
|
*/ |
|
public function frontControllerPath(string $sitePath, string $siteName, string $uri): string |
|
{ |
|
// Do not allow direct use of "index.php" |
|
if (!$this->getValetConfig('allow_indexphp_files') && preg_match('!/index.php$!', $uri)) { |
|
$uri = str_replace('index.php', '', $uri); |
|
header('Location: ' . $uri); |
|
exit; |
|
} |
|
|
|
$uri = ( |
|
$this->getValetConfig('redirect_trailing_slashes') |
|
? $uri |
|
: $this->forceTrailingSlash($uri) |
|
); |
|
|
|
foreach (static::PUBLIC_PATHS as $publicPath) { |
|
if (!is_dir("{$sitePath}/{$publicPath}/")) { |
|
continue; |
|
} |
|
|
|
$path = parent::frontControllerPath( |
|
"{$sitePath}/{$publicPath}", |
|
$siteName, |
|
$uri |
|
); |
|
|
|
foreach (static::WP_PATHS as $wpPath) { |
|
if (!is_dir("$sitePath/$publicPath/$wpPath")) { |
|
continue; |
|
} |
|
|
|
if (!str_contains($_SERVER['PHP_SELF'], "{$wpPath}/wp-admin")) { |
|
$_SERVER['PHP_SELF'] = '/index.php'; |
|
} |
|
} |
|
|
|
return $path; |
|
} |
|
} |
|
|
|
/** |
|
* Redirect to uri with trailing slash. |
|
* |
|
* @param string $uri |
|
* @return string |
|
*/ |
|
public function forceTrailingSlash($uri) |
|
{ |
|
foreach (static::WP_PATHS as $wpPath) { |
|
$path = "/{$wpPath}/wp-admin"; |
|
if (substr($uri, -1 * strlen($path)) === $path) { |
|
header("Location: {$uri}/"); |
|
exit; |
|
} |
|
} |
|
|
|
return $uri; |
|
} |
|
|
|
/** |
|
* Load server environment variables if available. |
|
* Processes any '*' entries first, and then adds site-specific entries. |
|
*/ |
|
public function loadServerEnvironmentVariables(string $sitePath, string $siteName): void |
|
{ |
|
parent::loadServerEnvironmentVariables($sitePath, $siteName); |
|
|
|
$this->valetSitePath = $sitePath; |
|
$this->valetSiteName = $siteName; |
|
|
|
$this->loadValetConfig($sitePath, $siteName); |
|
} |
|
|
|
/** |
|
* Determine if valet config file exists. |
|
* |
|
* @return bool |
|
*/ |
|
public function hasValetConfig() |
|
{ |
|
return $this->valetSitePath && file_exists($this->valetSitePath . '/' . static::VALET_CONFIG_FILE); |
|
} |
|
|
|
/** |
|
* Get the driver configuration options or a single option. |
|
* |
|
* @param string|null $key The single driver configuration option. |
|
* @return mixed|null Returns null when no matching option is found. |
|
*/ |
|
public function getValetConfig(?string $key = null) |
|
{ |
|
if (is_null($this->valetSiteConfig)) { |
|
$this->loadValetConfig($this->valetSitePath, $this->valetSiteName); |
|
} |
|
|
|
if ($key) { |
|
return $this->valetSiteConfig[$key] ?? null; |
|
} |
|
|
|
return $this->valetSiteConfig; |
|
} |
|
|
|
/** |
|
* Load driver configuration if available. |
|
* Processes any '*' entries first, and then adds site-specific entries. |
|
*/ |
|
public function loadValetConfig(string $sitePath, string $siteName): array |
|
{ |
|
$this->valetSiteConfig = $this->valetSiteOptions; |
|
|
|
$confFilePath = $sitePath.'/.valet-config.php'; |
|
if (file_exists($confFilePath)) { |
|
// Load .valet-config.php |
|
$config = include $confFilePath; |
|
} else { |
|
$confFilePath = $sitePath.'/valet-config.json'; |
|
if (file_exists($confFilePath)) { |
|
// Load valet-config.json |
|
$config = json_decode( |
|
file_get_contents($confFilePath), true |
|
); |
|
} else { |
|
// Fallback to global valet config.json |
|
$config = json_decode( |
|
file_get_contents(VALET_HOME_PATH.'/config.json'), true |
|
); |
|
} |
|
} |
|
|
|
if (! isset($config) || ! is_array($config)) { |
|
return $this->valetSiteConfig; |
|
} |
|
|
|
if (isset($config['*']) && is_array($config['*'])) { |
|
$this->valetSiteConfig = array_merge($this->valetSiteConfig, $config['*']); |
|
} |
|
|
|
if (isset($config[$siteName]) && is_array($config[$siteName])) { |
|
$this->valetSiteConfig = array_merge($this->valetSiteConfig, $config[$siteName]); |
|
} |
|
|
|
// Backwards compatibility with initial "valet-config.json" file. |
|
if ($config = array_intersect_key($config, $this->valetSiteOptions)) { |
|
$this->valetSiteConfig = array_merge($this->valetSiteConfig, $config); |
|
} |
|
|
|
return $this->valetSiteConfig; |
|
} |
|
} |
|
|
|
if (!function_exists('str_contains')) { |
|
function str_contains($haystack, $needle) { |
|
return $needle !== '' && mb_strpos($haystack, $needle) !== false; |
|
} |
|
} |