PHPackages                             rowbot/idna - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Localization &amp; i18n](/categories/localization)
4. /
5. rowbot/idna

ActiveLibrary[Localization &amp; i18n](/categories/localization)

rowbot/idna
===========

An implementation of UTS#46 Unicode IDNA Compatibility Processing.

0.3.0(8mo ago)2220.0k↑23.4%2MITPHPPHP &gt;=8.1CI passing

Since Jun 10Pushed 8mo ago1 watchersCompare

[ Source](https://github.com/TRowbotham/idna)[ Packagist](https://packagist.org/packages/rowbot/idna)[ RSS](/packages/rowbot-idna/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (8)Dependencies (9)Versions (11)Used By (2)

IDNA
====

[](#idna)

[![License](https://camo.githubusercontent.com/a7e20b27b51d39b1f654de71af2577605ab5984353e737e9be0c9f4efbd70858/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f54526f77626f7468616d2f69646e613f7374796c653d666c61742d737175617265)](https://github.com/TRowbotham/idna/blob/master/LICENSE)[![GitHub Actions Workflow Status](https://camo.githubusercontent.com/00d6cd5c34d882234f0a4dcb0731906bc708b35c3023ca590eef761d0bc6f09d/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f54526f77626f7468616d2f69646e612f74657374732e796d6c3f6272616e63683d6d6173746572267374796c653d666c61742d737175617265)](https://img.shields.io/github/workflow/status/TRowbotham/idna/actions)[![Code Coverage](https://camo.githubusercontent.com/b1866c3a8e8d355a9a214141444ad04f46cfdb533c8c81621c70af72ce25ab48/68747470733a2f2f696d672e736869656c64732e696f2f636f6465636f762f632f6769746875622f54526f77626f7468616d2f69646e612f6d61737465723f7374796c653d666c61742d737175617265)](https://codecov.io/gh/TRowbotham/idna)[![Version](https://camo.githubusercontent.com/32870d9f138912c0d76bc74fe6d45c4fbfe65be085edc512815c6be6fbb3d597/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f726f77626f742f69646e613f7374796c653d666c61742d737175617265)](https://packagist.org/packages/rowbot/idna)[![Downloads](https://camo.githubusercontent.com/da044d6a54f7d771f0480290be1e2be76a625debc1d370b25a38953a6a551a91/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f726f77626f742f69646e613f7374796c653d666c61742d737175617265)](https://packagist.org/packages/rowbot/idna)

A fully compliant implementation of [UTS#46](https://www.unicode.org/reports/tr46/), otherwise known as Unicode IDNA Compatibility Processing. You can read more about the differences between IDNA2003, IDNA2008, and UTS#46 in [Section 7. IDNA Comparison](https://www.unicode.org/reports/tr46/#IDNAComparison)of the specification.

This library currently ships with Unicode 17.0.0 support and implements Version 17.0.0, Revision 35 of IDNA Compatibility Processing.

- [Requirements](#requirements)
- [Installation](#installation)
- [API](#api)
- [Error Codes](#error-codes)
- [The WTFs of Unicode Support in PHP](#the-wtfs-of-unicode-support-in-php)
- [FAQs](#faqs)
- [Internals](#internals)

Requirements
------------

[](#requirements)

- PHP 8.1+
- `ext-mbstring`
- `rowbot/punycode`
- `symfony/polyfill-intl-normalizer`

Installation
------------

[](#installation)

```
composer require rowbot/idna
```

API
---

[](#api)

### Idna::UNICODE\_VERSION

[](#idnaunicode_version)

The Unicode version of the data files used, as a string.

### Idna::toAscii(string $domain, array $options = \[\]): IdnaResult

[](#idnatoasciistring-domain-array-options---idnaresult)

Converts a domain name to its ASCII form. Anytime an error is recorded while doing an ASCII transformation, the transformation is considered to have failed and whatever domain name string is returned is considered "garbage". What you do with that result is entirely up to you.

#### toAscii Parameters

[](#toascii-parameters)

- $domain - A domain name to convert to ASCII.
- $options - An array of options for customizing the behavior of the transformation. Possible options include:

    - `"CheckBidi"` - Checks the domain name string for errors with bi-directional characters. Defaults to true.
    - `"CheckHyphens"` - Checks the domain name string for the positioning of hypens. Defaults to true.
    - `"CheckJoiners"` - Checks the domain name string for errors with joining code points. Defaults to true.
    - `"UseSTD3ASCIIRules"` - Disallows the use of ASCII characters other than `[a-zA-Z0-9-]`. Defaults to true.
    - `"Transitional_Processing"` - Whether transitional or non-transitional processing is used. When enabled, processing behaves more like IDNA2003 and when disabled behaves like IDNA2008. Defaults to false, which means that non-transitional processing is used by default.
    - `"IgnoreInvalidPunycode"` - Allows for an all-ASCII fast-path.
    - `"VerifyDnsLength"` - Validates the length of the domain name string and it's individual labels. Defaults to true.

    **Note**: All options are case-sensitive.

    ```
    use Rowbot\Idna\Idna;

    $result = Idna::toAscii('x-.xn--nxa');

    // You must not use an ASCII domain that has errors.
    if ($result->hasErrors()) {
        throw new \Exception();
    }

    echo $result->getDomain(); // x-.xn--nxa
    ```

### Idna::toUnicode(string $domain, array $options = \[\]): IdnaResult

[](#idnatounicodestring-domain-array-options---idnaresult)

Converts the domain name to its Unicode form. Unlike the toAscii transformation, toUnicode does not have a failure concept. This means that you can always use the returned string. However, deciding what to do with the returned domain name string when an error is recorded is entirely up to you.

- $domain - A domain name to convert to UTF-8.
- $options - An array of options for customizing the behavior of the transformation. Possible options include:

    - `"CheckBidi"` - Checks the domain name string for errors with bi-directional characters. Defaults to true.
    - `"CheckHyphens"` - Checks the domain name string for the positioning of hypens. Defaults to true.
    - `"CheckJoiners"` - Checks the domain name string for errors with joining code points. Defaults to true.
    - `"UseSTD3ASCIIRules"` - Disallows the use of ASCII characters other than `[a-zA-Z0-9-]`. Defaults to true.
    - `"Transitional_Processing"` - Whether transitional or non-transitional processing is used. When enabled, processing behaves more like IDNA2003 and when disabled behaves like IDNA2008. Defaults to false, which means that non-transitional processing is used by default.

    **Note**: All options are case-sensitive.

    **Note**: `"VerifyDnsLength"` is not a valid option here.

    ```
    use Rowbot\Idna\Idna;

    $result = Idna::toUnicode('xn---with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n.com');
    echo $result->getDomain(); // 安室奈美恵-with-super-monkeys.com
    ```

### IdnaResult object

[](#idnaresult-object)

#### Members

[](#members)

##### IdnaResult::getDomain(): string

[](#idnaresultgetdomain-string)

Returns the transformed domain name string.

##### IdnaResult::getErrors(): int

[](#idnaresultgeterrors-int)

Returns a bitmask representing all errors that were recorded while processing the input domain name string.

##### IdnaResult::hasError(int $error): bool

[](#idnaresulthaserrorint-error-bool)

Returns whether or not a specific error was recorded.

##### IdnaResult::hasErrors(): bool

[](#idnaresulthaserrors-bool)

Returns whether or not an error was recorded while processing the input domain name string.

##### IdnaResult::isTransitionalDifferent(): bool

[](#idnaresultistransitionaldifferent-bool)

Returns `true` if the input domain name contains a code point that has a status of `"deviation"`. This status indicates that the code points are handled differently in IDNA2003 than they are in IDNA2008. At the time of writing, there are only 4 code points that have this status. They are U+00DF, U+03C2, U+200C, and U+200D.

Error Codes
-----------

[](#error-codes)

- `Idna::ERROR_EMPTY_LABEL`

    The domain name or one of it's labels are an empty string.
- `Idna::ERROR_LABEL_TOO_LONG`

    One of the domain's labels exceeds 63 bytes.
- `Idna::ERROR_DOMAIN_NAME_TOO_LONG`

    The length of the domain name exceeds 253 bytes.
- `Idna::ERROR_LEADING_HYPHEN`

    One of the domain name's labels starts with a hyphen-minus character (-).
- `Idna::ERROR_TRAILING_HYPHEN`

    One of the domain name's labels ends with a hyphen-minus character (-).
- `Idna::ERROR_HYPHEN_3_4`

    One of the domain name's labels contains a hyphen-minus character in the 3rd and 4th position.
- `Idna::ERROR_LEADING_COMBINING_MARK`

    One of the domain name's labels starts with a combining mark.
- `Idna::ERROR_DISALLOWED`

    The domain name contains characters that are disallowed.
- `Idna::ERROR_PUNYCODE`

    One of the domain name's labels starts with "xn--", but is not valid punycode.
- `Idna::ERROR_LABEL_HAS_DOT`

    One of the domain name's labels contains a full stop character (.).
- `Idna::ERROR_INVALID_ACE_LABEL`

    One of the domain name's labels is an invalid ACE label.
- `Idna::ERROR_BIDI`

    The domain name does not meet the BiDi requirements for IDNA.
- `Idna::ERROR_CONTEXTJ`

    One of the domain name's labels does not meet the CONTEXTJ requirements for IDNA.

The WTFs of Unicode Support in PHP
----------------------------------

[](#the-wtfs-of-unicode-support-in-php)

In any given version of PHP, there can be a multitude of different versions of Unicode in use. So... WTF?

- What does this mean?

    This means that if I ask the same question, each of the extensions listed below can give me a different answer. This is compounded by the fact that the versions of Unicode used in the below extensions can also be different given the same version of PHP. For example, the `intl` extension being used by my installation of PHP 7.2 could be using Unicode version 11, but the `intl`extension in your web hosts installation of PHP 7.2 could be using Unicode version 6.
- How does this happen?

    - The `mbstring` extension uses its own version of Unicode.
    - The `Onigurama` library, which is behind `mbstring`'s regular expression functions, uses its own version of Unicode.
    - The `PCRE` extension, which is the primary extension for working with regular extensions in PHP, uses its own version of Unicode.
    - The `intl` extension uses its own version of Unicode.
    - Any other extensions that add their own versions of Unicode.
    - Userland libraries use their own version of Unicode (including this library).
- This library

    Being able to use `mbstring` or `intl` extensions would be helpful, but we cannot depend on them being installed or them having a consistent version of Unicode when they are installed. Additionally, extensions like `PCRE` could be compiled without Unicode support entirely, though we do rely on `PCRE`'s `u` modifier. For this reason we have to include our own Unicode data.

FAQs
----

[](#faqs)

- **I'm confused! Is this IDNA2003 or IDNA2008?**

    The answer to this is somewhat convoluted. TL;DR; It is neither.

    Here is what the spec says:

    > To satisfy user expectations for mapping, and provide maximal compatibility with IDNA2003, this document specifies a mapping for use with IDNA2008. In addition, to transition more smoothly to IDNA2008, this document provides a Unicode algorithm for a standardized processing that allows conformant implementations to minimize the security and interoperability problems caused by the differences between IDNA2003 and IDNA2008. This Unicode IDNA Compatibility Processing is structured according to IDNA2003 principles, but extends those principles to Unicode 5.2 and later. It also incorporates the repertoire extensions provided by IDNA2008.

    More information can be found in [Section 2. Unicode IDNA Compatibility Processing](https://www.unicode.org/reports/tr46/#Compatibility_Processing)and [Section 7. IDNA Comparison](https://www.unicode.org/reports/tr46/#IDNAComparison).
- **What are the recommended options?**

    The default options are the recommended options, which are also the strictest.

    ```
    // Default options.
    [
      'CheckHyphens'            => true,
      'CheckBidi'               => true,
      'CheckJoiners'            => true,
      'UseSTD3ASCIIRules'       => true,
      'Transitional_Processing' => false,
      'IgnoreInvalidPunycode'   => false,
      'VerifyDnsLength'         => true, // Only for Idna::toAscii()
    ];
    ```
- **Do I have to provide all the options?**

    No. You only need to specifiy the options that you wish to change. Any option you specify will overwrite the default options.

    ```
    use Rowbot\Idna\Idna;

    $result = Idna::toAscii('x-.xn--nxa', ['CheckHyphens' => true]);
    $result->hasErrors(); // true
    $result->hasError(Idna::ERROR_TRAILING_HYPHEN); // true

    $result = Idna::toAscii('x-.xn--nxa', ['CheckHyphens' => false]);
    $result->hasErrors(); // false
    $result->hasError(Idna::ERROR_TRAILING_HYPHEN); // false
    ```
- **What is the difference between `Transitional` and `Non-transitional` processing?**

    `Transitional` processing is designed to mimic IDNA2003. It is highly recommended to use `Non-transitional` processing, which tries to mimic IDNA2008. You can always check if a domain name would be different between the two processing modes by checking `IdnaResult::isTransitionalDifferent()`.
- **Wouldn't it be neat if you also tested against the `idn_to_ascii()` and `idn_to_utf8()`functions from the `intl` extension?**

    Yes. Yes, it would be neat if we could do an additional check for parity with the ICU implementation, however, for the reasons outlined above in [The WTFs of Unicode Support in PHP](#the-wtfs-of-unicode-support-in-php), testing against these functions would be unreliable at best.
- **Why does the `intl` extension show weird characters that look like diamonds with question marks inside in invalid domains, but your implementation doesn't?**

    ```
    $input = '憡?Ⴔ.XN--1UG73GL146A';

    idn_to_utf8($input, 0, IDNA_INTL_VARIANT_UTS46, $info);
    echo $info['result']; // 憡��.xn--1ug73gl146a�
    echo ($info['errors'] & IDNA_ERROR_DISALLOWED) !== 0; // true

    $result = \Rowbot\Idna\Idna::toUnicode($input);
    echo $result->getDomain(); // 憡?Ⴔ.xn--1ug73gl146a
    echo $result->hasError(\Rowbot\Idna\Idna::ERROR_DISALLOWED); // true
    ```

    From [Section 4. Processing](https://www.unicode.org/reports/tr46/#Processing):

    > Implementations may make further modifications to the resulting Unicode string when showing it to the user. For example, it is recommended that disallowed characters be replaced by a U+FFFD to make them visible to the user. Similarly, labels that fail processing during steps 4 or 5 may be marked by the insertion of a U+FFFD or other visual device.

    This implementation currently does not make these recommended modifications.

Internals
---------

[](#internals)

### Building

[](#building)

Unicode data files are fetched from . Currently, Unicode version 16.0.0-17.0.0 are supported. To change the version of Unicode that the library is built with, you must first change the value of the `\Rowbot\Idna::UNICODE_VERSION` constant, like so:

```
class Idna
{
-     public const UNICODE_VERSION = '13.0.0';
+     public const UNICODE_VERSION = '14.0.0';
```

Then to generate the necessary data files, you execute the following command:

```
php bin/generateDataFiles.php
```

If no assertions or exceptions have occured, then you have successfully changed the Unicode version. You should now execute the tests to make sure everything is good to go. The tests will automatically fetch the version appropriate tests as the test files are not generated by the above command.

```
vendor/bin/phpunit
```

###  Health Score

46

—

FairBetter than 93% of packages

Maintenance62

Regular maintenance activity

Popularity36

Limited adoption so far

Community11

Small or concentrated contributor base

Maturity60

Established project with proven stability

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~275 days

Recently: every ~468 days

Total

8

Last Release

242d ago

PHP version history (2 changes)0.1.0PHP &gt;=7.1

0.2.0PHP &gt;=8.1

### Community

Maintainers

![](https://www.gravatar.com/avatar/534ea3958dd4732b3d4d4eee1fe459f93695be1bc28391e704be542592c96a89?d=identicon)[Rowbot](/maintainers/Rowbot)

---

Top Contributors

[![TRowbotham](https://avatars.githubusercontent.com/u/4984601?v=4)](https://github.com/TRowbotham "TRowbotham (90 commits)")

---

Tags

idnidnainternationalizationinternationalized-domainsiriphptr46uts46unicodeidnairiidninternational domain namestr46uts46

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP\_CodeSniffer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/rowbot-idna/health.svg)

```
[![Health](https://phpackages.com/badges/rowbot-idna/health.svg)](https://phpackages.com/packages/rowbot-idna)
```

###  Alternatives

[symfony/string

Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way

1.8k724.1M827](/packages/symfony-string)[symfony/polyfill-intl-idn

Symfony polyfill for intl's idn\_to\_ascii and idn\_to\_utf8 functions

3.4k774.6M90](/packages/symfony-polyfill-intl-idn)[rmccue/requests

A HTTP library written in PHP, for human beings.

3.6k34.5M258](/packages/rmccue-requests)[gettext/languages

gettext languages with plural rules

7530.3M11](/packages/gettext-languages)[voku/portable-utf8

Portable UTF-8 library - performance optimized (unicode) string functions for php.

52322.4M40](/packages/voku-portable-utf8)[punic/punic

PHP-Unicode CLDR

1542.9M29](/packages/punic-punic)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
