From cde48a9b35ed8f923af7f9d9a197d96c65bf8238 Mon Sep 17 00:00:00 2001 From: lukinovec Date: Mon, 29 Dec 2025 13:18:42 +0100 Subject: [PATCH 1/5] Add failling test for determining if string is subdomain --- tests/SubdomainTest.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/SubdomainTest.php b/tests/SubdomainTest.php index a7cc58aef..7d69bb20e 100644 --- a/tests/SubdomainTest.php +++ b/tests/SubdomainTest.php @@ -7,6 +7,7 @@ use Stancl\Tenancy\Exceptions\NotASubdomainException; use Stancl\Tenancy\Middleware\InitializeTenancyBySubdomain; use Stancl\Tenancy\Database\Models; +use Stancl\Tenancy\Resolvers\DomainTenantResolver; use function Stancl\Tenancy\Tests\pest; beforeEach(function () { @@ -108,6 +109,13 @@ ->get('http://foo.localhost/foo/abc/xyz'); }); +test('domain resolver correctly determines if string is a subdomain', function() { + config(['tenancy.identification.central_domains' => ['app.test']]); + + expect(DomainTenantResolver::isSubdomain('foo.app.test'))->toBeTrue(); + expect(DomainTenantResolver::isSubdomain('fooapp.test'))->toBeFalse(); +}); + class SubdomainTenant extends Models\Tenant { use HasDomains; From 4287f2a8a0edb9e327d0d70d9d85ee98ce7e7527 Mon Sep 17 00:00:00 2001 From: lukinovec Date: Mon, 29 Dec 2025 14:14:59 +0100 Subject: [PATCH 2/5] Fix `DomainTenantResolver::isSubdomain()` Previously, the method returned `true` even for central domains, or tenant domains that ended with a central domain (e.g. tenant-app.test when app.test was central domain). This fix is basically the same as in #1423. --- src/Resolvers/DomainTenantResolver.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Resolvers/DomainTenantResolver.php b/src/Resolvers/DomainTenantResolver.php index 9535cdf27..1a46b6ed5 100644 --- a/src/Resolvers/DomainTenantResolver.php +++ b/src/Resolvers/DomainTenantResolver.php @@ -13,6 +13,7 @@ use Stancl\Tenancy\Contracts\Tenant; use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedOnDomainException; use Stancl\Tenancy\Tenancy; +use Illuminate\Support\Arr; class DomainTenantResolver extends Contracts\CachedTenantResolver { @@ -58,7 +59,19 @@ public static function findTenantByDomain(string $domain): (Tenant&Model)|null public static function isSubdomain(string $domain): bool { - return Str::endsWith($domain, config('tenancy.identification.central_domains')); + $centralDomains = Arr::wrap(config('tenancy.identification.central_domains')); + + foreach ($centralDomains as $centralDomain) { + if ($domain === $centralDomain) { + return false; + } + + if (Str::endsWith($domain, '.' . $centralDomain)) { + return true; + } + } + + return false; } public function resolved(Tenant $tenant, mixed ...$args): void From e37d4b67bdaa186547982674ead0aef17bf71c8f Mon Sep 17 00:00:00 2001 From: lukinovec Date: Mon, 29 Dec 2025 14:21:15 +0100 Subject: [PATCH 3/5] Fix assertion for domain or subdomain ID exception The test asserted that NotASubdomainException was thrown when visiting localhost/central-route. Because this exception is only thrown after the host is identified as a subdomain, while calling `makeSubdomain()`, NotASubdomainException is not thrown in this case anymore. --- tests/EarlyIdentificationTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/EarlyIdentificationTest.php b/tests/EarlyIdentificationTest.php index e6c08d260..4521b6139 100644 --- a/tests/EarlyIdentificationTest.php +++ b/tests/EarlyIdentificationTest.php @@ -300,7 +300,7 @@ $exception = match ($middleware) { InitializeTenancyByDomain::class => TenantCouldNotBeIdentifiedOnDomainException::class, InitializeTenancyBySubdomain::class => NotASubdomainException::class, - InitializeTenancyByDomainOrSubdomain::class => NotASubdomainException::class, + InitializeTenancyByDomainOrSubdomain::class => TenantCouldNotBeIdentifiedOnDomainException::class, }; expect(fn () => $this->withoutExceptionHandling()->get('http://localhost/central-route'))->toThrow($exception); From a5a243469ff130b25db8e35a7f19927549dd481d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 29 Dec 2025 13:21:36 +0000 Subject: [PATCH 4/5] Fix code style (php-cs-fixer) --- src/Resolvers/DomainTenantResolver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Resolvers/DomainTenantResolver.php b/src/Resolvers/DomainTenantResolver.php index 1a46b6ed5..77477087c 100644 --- a/src/Resolvers/DomainTenantResolver.php +++ b/src/Resolvers/DomainTenantResolver.php @@ -7,13 +7,13 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\Relation; +use Illuminate\Support\Arr; use Illuminate\Support\Str; use Stancl\Tenancy\Contracts\Domain; use Stancl\Tenancy\Contracts\SingleDomainTenant; use Stancl\Tenancy\Contracts\Tenant; use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedOnDomainException; use Stancl\Tenancy\Tenancy; -use Illuminate\Support\Arr; class DomainTenantResolver extends Contracts\CachedTenantResolver { From f4ee0a79433694c84cf4cdd51e34060a9e77ccdb Mon Sep 17 00:00:00 2001 From: lukinovec Date: Mon, 13 Apr 2026 17:59:57 +0200 Subject: [PATCH 5/5] Make central domain order not matter in `isSubdomain()` --- src/Resolvers/DomainTenantResolver.php | 8 ++++---- tests/SubdomainTest.php | 7 ++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Resolvers/DomainTenantResolver.php b/src/Resolvers/DomainTenantResolver.php index 77477087c..59ebb81fc 100644 --- a/src/Resolvers/DomainTenantResolver.php +++ b/src/Resolvers/DomainTenantResolver.php @@ -61,11 +61,11 @@ public static function isSubdomain(string $domain): bool { $centralDomains = Arr::wrap(config('tenancy.identification.central_domains')); - foreach ($centralDomains as $centralDomain) { - if ($domain === $centralDomain) { - return false; - } + if (in_array($domain, $centralDomains, true)) { + return false; + } + foreach ($centralDomains as $centralDomain) { if (Str::endsWith($domain, '.' . $centralDomain)) { return true; } diff --git a/tests/SubdomainTest.php b/tests/SubdomainTest.php index 7d69bb20e..62e002f29 100644 --- a/tests/SubdomainTest.php +++ b/tests/SubdomainTest.php @@ -110,10 +110,11 @@ }); test('domain resolver correctly determines if string is a subdomain', function() { - config(['tenancy.identification.central_domains' => ['app.test']]); + config(['tenancy.identification.central_domains' => ['site.com', 'blog.site.com']]); - expect(DomainTenantResolver::isSubdomain('foo.app.test'))->toBeTrue(); - expect(DomainTenantResolver::isSubdomain('fooapp.test'))->toBeFalse(); + expect(DomainTenantResolver::isSubdomain('blog.site.com'))->toBeFalse(); + expect(DomainTenantResolver::isSubdomain('tenant.site.com'))->toBeTrue(); + expect(DomainTenantResolver::isSubdomain('tenantsite.com'))->toBeFalse(); }); class SubdomainTenant extends Models\Tenant