mivita/app/Domain/EarlyDomainParser.php
2025-10-20 17:42:08 +02:00

277 lines
8.2 KiB
PHP

<?php
namespace App\Domain;
/**
* Early Domain Parser Service
*
* Provides domain parsing functionality that can be used during
* bootstrap phase (RouteServiceProvider) and runtime (Middleware).
*
* This service caches parsing results per request to avoid duplicate work.
*/
class EarlyDomainParser
{
/**
* Cache for parsed domain information per request
* @var array|null
*/
private static ?array $cachedDomainInfo = null;
/**
* Cache key (host) for cache invalidation
* @var string|null
*/
private static ?string $cachedHost = null;
/**
* Parse domain information from config/domains.php
*
* Results are cached per request to avoid duplicate parsing.
*
* @param string|null $host If null, uses HTTP_HOST or SERVER_NAME
* @return array Domain information array
*/
public static function parseDomain(?string $host = null): array
{
// Get host from request if not provided
if ($host === null) {
$host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? 'localhost';
}
// Remove protocol if present
$host = preg_replace('/^https?:\/\//', '', $host);
// Return cached result if available for same host
if (self::$cachedHost === $host && self::$cachedDomainInfo !== null) {
return self::$cachedDomainInfo;
}
// Load domains configuration
$domains = self::getDomainsConfig();
$reservedSubdomains = self::getReservedSubdomains();
// Check exact matches first (main, shop, crm, portal, checkout)
foreach ($domains as $key => $domainConfig) {
if ($key === 'user-shop') {
continue; // Handle user-shop separately
}
if ($host === $domainConfig['host']) {
$domainInfo = [
'type' => $domainConfig['type'],
'host' => $host,
'subdomain' => null,
'config_key' => $key,
];
// Cache the result for this request
self::$cachedHost = $host;
self::$cachedDomainInfo = $domainInfo;
return $domainInfo;
}
}
// Check for user-shop pattern (dynamic subdomains)
if (isset($domains['user-shop'])) {
$userShopPattern = $domains['user-shop']['host'];
$baseDomain = str_replace('{subdomain}.', '', $userShopPattern);
if (str_ends_with($host, '.' . $baseDomain)) {
$subdomain = str_replace('.' . $baseDomain, '', $host);
// Check if subdomain is not reserved
if (!empty($subdomain) && !in_array($subdomain, $reservedSubdomains)) {
$domainInfo = [
'type' => 'user-shop',
'host' => $host,
'subdomain' => $subdomain,
'config_key' => 'user-shop',
];
// Cache the result for this request
self::$cachedHost = $host;
self::$cachedDomainInfo = $domainInfo;
return $domainInfo;
}
}
}
// Unknown domain
$domainInfo = [
'type' => 'unknown',
'host' => $host,
'subdomain' => null,
'config_key' => null,
];
// Cache the result for this request
self::$cachedHost = $host;
self::$cachedDomainInfo = $domainInfo;
return $domainInfo;
}
/**
* Get current domain type quickly (for RouteServiceProvider)
*/
public static function getCurrentDomainType(): string
{
$domainInfo = self::parseDomain();
return $domainInfo['type'];
}
/**
* Check if current domain is a user shop
*/
public static function isUserShop(): bool
{
return self::getCurrentDomainType() === 'user-shop';
}
/**
* Get subdomain for user shops
*/
public static function getSubdomain(): ?string
{
$domainInfo = self::parseDomain();
return $domainInfo['subdomain'] ?? null;
}
/**
* Get domains configuration (early bootstrap safe)
*/
private static function getDomainsConfig(): array
{
// Try Laravel config first (if available)
if (function_exists('config')) {
$config = config('domains.domains');
if ($config) {
return $config;
}
}
// Fallback: Read config file directly
$configPath = __DIR__ . '/../../../../config/domains.php';
if (file_exists($configPath)) {
$config = include $configPath;
return $config['domains'] ?? [];
}
// Last resort: Build from environment variables
return self::buildConfigFromEnv();
}
/**
* Get reserved subdomains configuration
*/
private static function getReservedSubdomains(): array
{
// Try Laravel config first (if available)
if (function_exists('config')) {
$reserved = config('domains.reserved_subdomains');
if ($reserved) {
return $reserved;
}
}
// Fallback: Read config file directly
$configPath = __DIR__ . '/../../../../config/domains.php';
if (file_exists($configPath)) {
$config = include $configPath;
return $config['reserved_subdomains'] ?? [];
}
// Default reserved subdomains
return ['my', 'in', 'checkout', 'www', 'api', 'mail'];
}
/**
* Build basic domain configuration from environment variables
* Used as fallback when config file is not available
*/
private static function buildConfigFromEnv(): array
{
$domain = $_ENV['APP_DOMAIN'] ?? 'mivita';
$tldCare = $_ENV['APP_TLD_CARE'] ?? '.care';
$tldShop = $_ENV['APP_TLD_SHOP'] ?? '.shop';
$crmPrefix = $_ENV['APP_PRE_URL_CRM'] ?? 'my.';
$portalPrefix = $_ENV['APP_PRE_URL_PORTAL'] ?? 'in.';
$checkoutPrefix = $_ENV['APP_URL_CHECKOUT'] ?? 'checkout.';
return [
'main' => [
'host' => $domain . $tldCare,
'type' => 'main',
],
'shop' => [
'host' => $domain . $tldShop,
'type' => 'main-shop',
'default_user_shop' => 'aloevera',
],
'crm' => [
'host' => $crmPrefix . $domain . $tldCare,
'type' => 'crm',
],
'portal' => [
'host' => $portalPrefix . $domain . $tldCare,
'type' => 'portal',
],
'checkout' => [
'host' => $checkoutPrefix . $domain . $tldCare,
'type' => 'checkout',
],
'user-shop' => [
'host' => '{subdomain}.' . $domain . $tldCare,
'type' => 'user-shop',
],
];
}
/**
* Get protocol from configuration
*/
public static function getProtocol(): string
{
if (function_exists('config')) {
return config('domains.protocol', 'https://');
}
return $_ENV['APP_PROTOCOL'] ?? 'https://';
}
/**
* Get main domain URL for redirects
*/
public static function getMainUrl(): string
{
$domains = self::getDomainsConfig();
$mainHost = $domains['main']['host'] ?? 'localhost';
$protocol = self::getProtocol();
return $protocol . $mainHost;
}
/**
* Clear the internal cache (useful for testing or special cases)
*/
public static function clearCache(): void
{
self::$cachedDomainInfo = null;
self::$cachedHost = null;
}
/**
* Check if result is cached for current/given host
*/
public static function isCached(?string $host = null): bool
{
if ($host === null) {
$host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? 'localhost';
$host = preg_replace('/^https?:\/\//', '', $host);
}
return self::$cachedHost === $host && self::$cachedDomainInfo !== null;
}
}