Lines 100.00% 27 / 27
Methods 100.00% 2 / 2
Classes 100.00% 1 / 1
Covered by tests of size
Name Lines Methods CRAP
 locateConfigurationDirectory 100.00% 15 / 15 100.00% 1 / 1 6
 extractTenantFromSubdomain 100.00% 12 / 12 100.00% 1 / 1 6
24class MultisiteConfigurationLocator
25{
26    public static function locateConfigurationDirectory(Request $request, string $configurationDirectory): ?string
27    {
28        $protocol = $request->isSecure() ? 'https' : 'http';
29        $host = $request->getHost();
30        $scriptName = $request->getScriptName();
31
32        $parsed = parse_url($protocol . '://' . $host . $scriptName);
33
34        $parsedHost = (string) ($parsed['host'] ?? '');
35        if ($parsedHost !== '') {
36            // 1. Try an exact hostname match (existing behavior)
37            $configDir = rtrim($configurationDirectory, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $parsedHost;
38
39            if (is_dir($configDir)) {
40                return $configDir;
41            }
42
43            // 2. Try subdomain-based tenant matching
44            $tenantName = self::extractTenantFromSubdomain($parsedHost);
45            if ($tenantName !== null) {
46                $configDir = rtrim($configurationDirectory, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $tenantName;
47
48                if (is_dir($configDir)) {
49                    return $configDir;
50                }
51            }
52        }
53
54        return null;
55    }
56
57    /**
58     * Extracts the tenant identifier from a subdomain pattern.
59     *
60     * Checks the PMF_MULTISITE_BASE_DOMAIN environment variable. If set,
61     * extracts the subdomain part from hostnames matching {tenant}.{baseDomain}.
62     *
63     * Example: With PMF_MULTISITE_BASE_DOMAIN=faq.example.com,
64     * the host "acme.faq.example.com" returns "acme".
65     */
66    public static function extractTenantFromSubdomain(string $host): ?string
67    {
68        $host = strtolower($host);
69        $baseDomain = getenv('PMF_MULTISITE_BASE_DOMAIN');
70        if ($baseDomain === false || $baseDomain === '') {
71            return null;
72        }
73
74        $baseDomain = strtolower(ltrim($baseDomain, characters: '.'));
75        $suffix = '.' . $baseDomain;
76
77        if (!str_ends_with($host, $suffix)) {
78            return null;
79        }
80
81        $tenant = strtolower(substr($host, offset: 0, length: -strlen($suffix)));
82        if ($tenant === '' || str_contains($tenant, '.')) {
83            return null;
84        }
85
86        return $tenant;
87    }
88}