123 lines
3.0 KiB
PHP
123 lines
3.0 KiB
PHP
<?php
|
|
|
|
namespace App\Domains\Tenant\Support;
|
|
|
|
class TenantDomainNormalizer
|
|
{
|
|
public static function hasValue(mixed $domain): bool
|
|
{
|
|
return is_string($domain) && trim(urldecode($domain)) !== '';
|
|
}
|
|
|
|
public static function normalize(mixed $domain): ?string
|
|
{
|
|
if (! is_string($domain)) {
|
|
return null;
|
|
}
|
|
|
|
$decodedDomain = trim(urldecode($domain));
|
|
|
|
if ($decodedDomain === '') {
|
|
return null;
|
|
}
|
|
|
|
$candidate = str_contains($decodedDomain, '://')
|
|
? $decodedDomain
|
|
: "//{$decodedDomain}";
|
|
|
|
$host = parse_url($candidate, PHP_URL_HOST);
|
|
|
|
if (! is_string($host) || $host === '') {
|
|
return null;
|
|
}
|
|
|
|
return strtolower($host);
|
|
}
|
|
|
|
public static function normalizeTenantKey(mixed $domain, mixed $path = null): ?string
|
|
{
|
|
$host = self::normalize($domain);
|
|
|
|
if ($host === null) {
|
|
return null;
|
|
}
|
|
|
|
if ($path === null && is_string($domain)) {
|
|
$decodedDomain = trim(urldecode($domain));
|
|
$candidate = str_contains($decodedDomain, '://')
|
|
? $decodedDomain
|
|
: "//{$decodedDomain}";
|
|
$path = parse_url($candidate, PHP_URL_PATH) ?: '/';
|
|
}
|
|
|
|
$normalizedPath = self::normalizePath($path);
|
|
|
|
if ($normalizedPath === null) {
|
|
return null;
|
|
}
|
|
|
|
return $host.($normalizedPath === '/' ? '' : $normalizedPath);
|
|
}
|
|
|
|
public static function normalizePath(mixed $path): ?string
|
|
{
|
|
if (! is_string($path)) {
|
|
return null;
|
|
}
|
|
|
|
$path = trim($path);
|
|
|
|
if ($path === '' || $path === '/') {
|
|
return '/';
|
|
}
|
|
|
|
$path = parse_url(str_starts_with($path, '/') ? $path : "/{$path}", PHP_URL_PATH);
|
|
|
|
if (! is_string($path)) {
|
|
return null;
|
|
}
|
|
|
|
$path = preg_replace('#/+#', '/', $path);
|
|
|
|
if (! is_string($path)) {
|
|
return null;
|
|
}
|
|
|
|
$segments = array_filter(explode('/', $path), static fn (string $segment): bool => $segment !== '');
|
|
|
|
foreach ($segments as $segment) {
|
|
if ($segment === '.' || $segment === '..') {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
return '/'.implode('/', $segments);
|
|
}
|
|
|
|
/** @return list<string> */
|
|
public static function tenantKeyCandidates(mixed $domain, mixed $path): array
|
|
{
|
|
$host = self::normalize($domain);
|
|
$normalizedPath = self::normalizePath($path);
|
|
|
|
if ($host === null || $normalizedPath === null) {
|
|
return [];
|
|
}
|
|
|
|
$segments = array_values(array_filter(
|
|
explode('/', $normalizedPath),
|
|
static fn (string $segment): bool => $segment !== '',
|
|
));
|
|
$candidates = [];
|
|
|
|
while ($segments !== []) {
|
|
$candidates[] = $host.'/'.implode('/', $segments);
|
|
array_pop($segments);
|
|
}
|
|
|
|
$candidates[] = $host;
|
|
|
|
return $candidates;
|
|
}
|
|
}
|