Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Tld Database Laravel Package

layershifter/tld-database

A PHP package providing an up-to-date database of top-level domains, with tools to parse domain names, extract and validate TLDs, and refresh the TLD list from IANA. Useful for domain validation, normalization, and routing logic.

View on GitHub
Deep Wiki
Context7

Getting Started

Install (with eyes open):

composer require layershifter/tld-database

Composer will likely flag it as abandoned. That is accurate — the repo is archived, last tagged 2019-08-04, and the bundled Public Suffix List snapshot is frozen at roughly that date. Verify it installs cleanly on your PHP version (it predates PHP 8; expect deprecations on 8.1+).

There is no Laravel service provider. The package is a plain PHP library. You instantiate it yourself:

use LayerShifter\TLDDatabase\Store;

$store = new Store();            // loads the bundled data file
$store->isExists('com');         // TLD is in the list
$store->isValid('co.uk');        // suffix is a valid public suffix
$store->getRules('uk');          // ['uk', 'co.uk', 'me.uk', ...]

Where to look first:

  1. src/Store.php — the entire public surface area (three or four methods).
  2. The bundled data file — this is what you will eventually need to regenerate.
  3. tests/ — confirms exact semantics of isValid vs isExists and whether wildcard/exception rules are included.

First use case: a registration or "add your domain" form where you want to reject [email protected] or a tenant subdomain on a bogus suffix. Wrap it in a custom validation rule and you have the package earning its keep in ten minutes.

Implementation Patterns

Bind it once as a singleton. The data file is parsed into an array on construction; you do not want that per request.

// AppServiceProvider::register()
$this->app->singleton(Store::class, fn () => new Store(
    config('tld.path') // null = bundled file; override if you vendor a fresher PSL
));

Custom validation rule.

class ValidTld implements ValidationRule
{
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        $normalized = Str::lower(rtrim(trim($value), '.'));
        if (! app(Store::class)->isExists($normalized)) {
            $fail("The {$attribute} has an unrecognised top-level domain.");
        }
    }
}

Use it in a FormRequest: 'domain' => ['required', 'string', new ValidTld]. Pair with dns only if you actually want a blocking DNS lookup — PSL membership says nothing about whether the domain resolves or is registered.

Registrable domain / tenant resolution. This is the highest-value day-to-day pattern for a Laravel app: deriving eTLD+1 so acme.co.uk and blog.acme.co.uk share a tenant, cookie scope, and cache namespace.

$suffixes = $store->getRules($lastLabel);      // ['uk', 'co.uk', ...]
usort($suffixes, fn ($a, $b) => substr_count($b, '.') <=> substr_count($a, '.'));
$suffix = collect($suffixes)->first(fn ($s) => Str::endsWith($host, ".$s"));
$registrable = implode('.', array_slice(explode('.', $host), -1 - substr_count($suffix, '.')));

Push this into a DomainService class, not a middleware closure — see the migration note in Gotchas.

Normalize before every call. Strip scheme/path with parse_url, lowercase, trim the trailing dot, convert IDN with idn_to_ascii() (ext-intl) — the store will not do this for you.

Bulk work belongs in a command or job. Importing 50k customer domains? Resolve the store once, loop, and chunk. Never new Store() inside a loop.

Test with a fake. Because Store is a concrete class, bind your own DomainService interface in the container and swap it in tests. Otherwise your test suite inherits the 2019 PSL snapshot as an implicit fixture.

Gotchas and Tips

  • It is a data package first, a library second. The code is trivial; the value is the snapshot. An archived snapshot means .dev, .app, .zip, and every TLD delegated since mid-2019 may be missing or wrong. Decide explicitly: accept staleness, regenerate the JSON from publicsuffix.org with the repo's generator, or replace the dependency.
  • isValid ≠ domain is valid. It answers "is this a public suffix under PSL rules," not "does this domain exist." Do not conflate it with the dns rule or with egulias/email-validator (email:rfc already handles syntax; PSL adds the TLD check).
  • Wildcards and exceptions. PSL contains *.ck and !www.ck. Check whether this package's isValid honors both before relying on it for cookie-domain scoping — a mis-scoped cookie domain is a security bug, not a UX bug.
  • Private vs ICANN sections. The PSL has a private-domains section (*.github.io, *.herokuapp.com). Whether those are included materially changes tenant resolution and cookie scoping for apps hosted on shared suffixes. Test your fixture explicitly.
  • IDN/punycode. A user typing münchen.de will fail a naive lookup. idn_to_ascii() before the check, and store the punycode form as canonical.
  • PHP 8 compatibility. Pre-8 era code can emit deprecations (dynamic properties, implicit nullable params). If you're on 8.2+, run the suite and consider suppressing or patching rather than discovering it in production logs.
  • Octane. A singleton is fine as long as you never mutate the store. Keep it read-only and you avoid cross-request leakage.
  • Do not leak it through your codebase. Wrap it in App\Support\Domain\DomainService with an interface today. When you migrate to jeremykendall/php-domain-parser (maintained, IDN-aware, gives you eTLD+1 directly), the swap is one binding instead of a grep across forty controllers.
  • Config, not constants. Put the data path in config/tld.php and point it at storage_path('app/tld.json') if you vendor your own snapshot. That makes refreshing data a deploy artifact rather than a dependency bump you cannot make.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource