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

Idna Convert Laravel Package

algo26-matthias/idna-convert

PHP library for converting internationalized domain names between Unicode and Punycode. Implements IDNA 2003 and UTS #46 (IDNA 2008) standards with configurable encoding and decoding options. A mature, standalone package compatible with PHP 5.3 through 8.x and used by phpMyAdmin.

Deep Wiki
Context7

Getting Started

Install with Composer; there is no service provider or config file to publish:

composer require algo26-matthias/idna-convert

Check composer.json for the PHP constraint before adopting — the 3.x line targets modern PHP (7.4/8.x). If you are pinned to an older runtime, pin the matching major.

First place to look is the README's two-class example, because the whole package is essentially two entry points:

use Algo26\IdnaConvert\ToIdn;
use Algo26\IdnaConvert\ToUnicode;

$ascii   = (new ToIdn())->convert('münchen.example');      // xn--mnchen-3ya.example
$unicode = (new ToUnicode())->convert('xn--mnchen-3ya.example'); // münchen.example

Everything else in src/ (Punycode\ToPunycode, Punycode\FromPunycode, the legacy IdnaConvert wrapper) is a lower-level or backward-compatible view of the same operation.

The first real use case is a single choke point: take whatever host a user typed (registration form, tenant subdomain, webhook config, email address) and convert it to ASCII before you validate it, resolve it, store it in a unique index, or hand it to an HTTP client. Do the reverse conversion only at the display edge.

Implementation Patterns

Wrap it once, inject everywhere. Bind a thin service in the container rather than new ToIdn() at call sites. It gives you one place to set the IDNA2003/2008 flag, one place to add caching, and one place to mock in tests.

namespace App\Support;

use Algo26\IdnaConvert\ToIdn;
use Algo26\IdnaConvert\ToUnicode;

class Idn
{
    public function __construct(
        private ToIdn $toIdn,
        private ToUnicode $toUnicode,
    ) {}

    public function toAscii(string $host): string
    {
        $host = mb_strtolower(rtrim(trim($host), '.'), 'UTF-8');

        return str_contains($host, 'xn--') ? $host : $this->toIdn->convert($host);
    }

    public function toUnicode(string $host): string
    {
        return str_contains($host, 'xn--') ? $this->toUnicode->convert($host) : $host;
    }
}

Canonicalize on write, decorate on read. Store the xn-- form as the canonical key — that is what DNS, TLS/SNI, cookies, curl, Guzzle, and your database unique index all agree on. Keep the original Unicode input in a separate display_domain column (or re-derive it with ToUnicode) and expose it through an accessor. Two rows for münchen.de and xn--mnchen-3ya.de is the classic duplicate-tenant bug.

Convert the host only — never the whole URL. Use parse_url(), convert ['host'], reassemble. This automatically sidesteps schemes, ports, userinfo, and paths that the converter will otherwise reject.

Email: split before converting. Convert the domain after the last @; leave the local part untouched unless you are deliberately building an SMTPUTF8 system. Converting the full address mangles quoted local parts and + tags.

[$local, $domain] = explode('@', $email, 2);
$canonical = $local . '@' . $this->idn->toAscii($domain);

Custom validation rule. Laravel's email and url/active_url rules are unreliable on raw Unicode hosts. Normalize first, then validate the ASCII form:

Validator::extend('idn_domain', function ($attribute, $value) {
    $ascii = app(Idn::class)->toAscii($value);
    return strlen($ascii) <= 253
        && filter_var($ascii, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) !== false;
});

Length checks belong after conversion. Punycode expands labels: the 63-octet label limit and 253-character domain limit must be measured on the ASCII output, not the UTF-8 string.

Wildcards and multi-label hosts. Split on ., skip *, convert label by label when you need to handle wildcard DNS or _acme-challenge-style records where the full-string converter may not behave as you expect. Use Punycode\ToPunycode when each label is already validated and you only want the encoding step.

Cache hot paths. Conversion is pure and deterministic, so Cache::remember('idn:'.$host, ...) is safe if you are normalizing thousands of hosts per request (log ingestion, sitemap generation). For typical form traffic it is not worth it.

Round-trip test everything. Add a fixture test asserting toUnicode(toAscii($x)) === NFC($x) for the vectors you care about — münchen.de, 例え.テスト, faß.de (where 2003 and 2008 disagree), and your own production domains. This catches accidental IDNA-version drift after a dependency bump.

Gotchas and Tips

  • IDNA2003 vs IDNA2008 is a real fork in behaviour, not a cosmetic flag. ß, ς, and ZWJ-containing labels resolve differently. Whichever flag you pick in the constructor, pick it once in your wrapper and be consistent across validation, storage, and outbound requests. A registrar using 2003 and an app using 2008 will disagree about faß.de.
  • Normalize to NFC yourself. The converter does case/IDNA mapping, but composed vs decomposed Unicode (ü as one codepoint vs u + combining diaeresis) can produce two visually identical stored values. Run Normalizer::normalize($host, Normalizer::FORM_C) (or the polyfill) before conversion, and add a DB unique constraint on the ASCII column as the real backstop.
  • Check return-vs-exception behaviour for your installed version. Older releases returned false on invalid input; newer ones throw. Wrap the call in your service and translate both into a domain-specific exception so callers never get a surprise false written into a varchar.
  • Strip the trailing dot and any port before converting. example.com. and example.com:8080 are common inputs and are not valid converter arguments.
  • Do not convert twice. Guard on the presence of xn--. Re-encoding ASCII is usually harmless, but a guard makes the code readable and protects you if the library's leniency ever changes.
  • filter_var will reject your Unicode domain. That is expected — always validate the ASCII output. The same applies to FILTER_VALIDATE_EMAIL.
  • Outbound HTTP needs ASCII. Guzzle, curl, and some DNS resolvers will fail or silently mangle a Unicode host. Convert before setting the URL, and use the ASCII form for cookie domains, Host headers, and TLS certificate matching.
  • Punycode is longer than what the user typed. Size columns for 253 characters, and remember MySQL's utf8mb4 index byte limits if you index the display column too.
  • IDNA does not stop homograph attacks. xn-- conversion is a transport encoding, not a security control. If users see domains from other tenants, add your own mixed-script detection or show the ASCII form on hover — the converter will happily produce a Cyrillic lookalike.
  • Extension points are constructor arguments, not config. There is no publishable config and no facade. If you need per-tenant IDNA versions, instantiate both converters and select in your wrapper rather than adding branching at call sites.
  • License note for the record: LGPL-2.1. Using it as an unmodified Composer dependency is fine; if you fork and redistribute modifications, plan to publish those changes.
  • Debugging checklist: if a host "doesn't resolve," log the ASCII output at the boundary and compare it against idn_to_ascii() from ext-intl (if installed) — a mismatch almost always means a normalization or IDNA-version difference, not a networking problem.
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.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast