centralnic-reseller/idn-converter
A PHP utility for CentralNic Reseller integrations that converts internationalized domain names between Unicode and Punycode/ASCII, making it easier to validate, register, and look up IDN domains safely.
Architecture fit. IDNA/UTS#46 conversion is a cross-cutting seam in registrar middleware, not a feature: every surface that accepts or emits a domain name — registration and transfer forms, EPP command construction, DNS zone publishing, WHOIS/RDAP lookups, TLS/ACME requests, availability checks — must agree on one canonical ASCII (A-label) representation. Today that conversion is typically duplicated per product (control panel, reseller API, WHMCS module, billing export) or delegated ad hoc to ext-intl. A single pure-PHP library that owns the transform is the correct architectural shape: a leaf dependency with no framework coupling, no I/O, and a small API, placed behind an internal facade so call sites depend on an interface rather than the vendor package.
The strongest argument for a vendored implementation over idn_to_ascii() is determinism, not feature breadth. The intl path delegates to the linked ICU build, so output for deviation characters, transitional processing, and bidi/contextual edge cases can shift with a distro upgrade or a PHP minor version. For a reseller platform, "same input produces the same <name> in EPP, the same registry hash, the same stored key" is a hard requirement. A package with its own pinned Unicode tables trades breadth for reproducibility — the right trade here.
Integration feasibility. Composer/Packagist distribution, MIT licence, PHP-only, no service dependency. Effort is low for new call sites and dominated by an audit of existing ones. Two factors determine real cost: (a) the PHP version constraint against the oldest runtime still in the fleet (panel and WHMCS-adjacent code frequently pins older 7.x/8.0 builds), and (b) how errors are surfaced. An exception-based API forces explicit handling; a null/pass-through fallback invites silent propagation of an invalid name into a registry call.
Technical risk.
IdnaTestV2.txt or equivalent) and the cadence tying releases to Unicode releases — the 2026-08-21 tag is recent, which helps, but recency is not the same as a process.ß, final sigma, ZWJ/ZWNJ and bidi are where implementations actually differ. The platform's policy (transitional vs non-transitional processing) must be an explicit, documented, tested configuration, not an inherited default.ext-intl for normalization. If the implementation shells out to Normalizer for NFC, part of the determinism argument evaporates. Confirm.Key questions.
toASCII/toUnicode), value objects, validation, or a full processing-options struct? Are errors exceptions or sentinels?ext-intl, ext-mbstring, or is it dependency-free PHP?symfony/polyfill-intl-idn / TrueBV punycode, and how does its output differ on the deviation-character corpus?Stack fit. A pure-PHP Composer package slots cleanly into a Laravel middleware stack: no service container bindings required, no config publishing, no migrations. The only integration decision is where it sits — wrap it in a small internal service (DomainNameNormalizer contract plus an adapter) and inject that. This keeps UTS#46 policy in one place, makes the vendor package replaceable, and gives a single point to assert behaviour in tests. Compare explicitly against the incumbent: ext-intl's idn_to_ascii(), and symfony/polyfill-intl-idn (broadly deployed, pure PHP, but a different table version and default processing mode). The selection criterion is byte-for-byte agreement with the platform's declared policy, not popularity.
Migration path. Do not rewrite call sites in place.
ß, ZWJ/ZWNJ, Arabic/Indic labels, mixed scripts, overlong labels).idn_to_ascii() calls; leave no fallback that silently accepts the raw input.Compatibility. Storage stays canonical A-label; U-label is a presentation concern in the UI and in outbound customer communication. That means no data migration if existing rows are already ASCII — verify, because a mixed-encoding table is the failure case that produces duplicate registrations and failed lookups. Check every place a domain is used as a key or compared for equality. Also verify the package's PHP constraint against the fleet before promising a rollout date.
Sequencing. Audit and baselining first (read-only, zero risk), then read paths (lookup, display, WHOIS), then write paths (registration, transfer, EPP) — writes are where a bad conversion becomes a customer-visible incident or a mis-registration. Table-version pinning and the conformance suite land in CI in the same change as the facade, so the guarantee is enforced from day one rather than retrofitted.
Maintenance. This is the crux of the decision. An unowned dependency that defines domain-name normalization is an organisational risk; an owned one is ordinary work. The existence of a dedicated JIRA component (IDNA-UTS46, 10244) under the Channel 3rd Party Software Integrations team indicates ownership, an escalation route, and a change trail — which materially offsets the zero-adoption signal. Treat the maintenance question as "is the Unicode table refresh on a recurring calendar with an owner," and get that in writing.
Support. Escalations should route internally through the owning team's backlog, not to the public repository — with zero stars there is effectively no external issue traffic and no community SLA. Document the escalation path and the pinned version per consuming service so that a support engineer diagnosing an IDN rejection can identify which converter and which table version produced the stored name.
Scaling. Cost per call is negligible; the concerns are volume and bootstrap. Bulk paths (zone file generation, portfolio-wide availability sweeps, RDAP refresh) multiply the per-call cost, so confirm the hot path is a pure function and not re-parsing data files per invocation. If the tables are several megabytes and loaded per request, that is a real PHP-FPM p95 and memory concern — the fix is a cached, opcache-friendly load, and it should be measured rather than assumed. Pure CPU, no shared state, so horizontal scaling is trivial.
Failure modes.
null-returning API that lets an unconvertible label reach the registry is the dangerous failure. At the registration boundary the converter must fail closed and surface a validation error to the user.Ramp-up. Shallow for consumers — the API is small and a competent PHP developer is productive in an afternoon. Deep for maintainers: UTS#46 processing options, bidi and contextual rules, and the table pipeline are specialist territory, and one named owner is the right target, not a team-wide skill. The highest-leverage onboarding artefact is a short written platform policy: transitional or non-transitional, which validations are mandatory, what is stored vs. displayed, and which errors are user-facing. Publishing that alongside the facade stops each product team from re-litigating encoding behaviour, which is where the real long-term cost of this package sits.
How can I help you explore Laravel packages today?