php实现punycode
PHP 实现 Punycode
Punycode 是一种用于将 Unicode 字符串转换为 ASCII 字符串的编码方式,常用于国际化域名(IDN)的处理。以下是几种在 PHP 中实现 Punycode 转换的方法。
使用内置函数 idn_to_ascii 和 idn_to_utf8
PHP 提供了内置函数 idn_to_ascii 和 idn_to_utf8,可以方便地进行 Punycode 编码和解码。这些函数需要 PHP 安装 intl 扩展。
// 编码为 Punycode
$domain = "例子.测试";
$punycode = idn_to_ascii($domain, IDNA_DEFAULT, INTL_IDNA_VARIANT_UTS46);
echo $punycode; // 输出: xn--fsq.xn--0zwm56d
// 解码为 Unicode
$original = idn_to_utf8($punycode, IDNA_DEFAULT, INTL_IDNA_VARIANT_UTS46);
echo $original; // 输出: 例子.测试
使用第三方库
如果无法使用 intl 扩展,可以使用第三方库如 true/punycode。
安装库:
composer require true/punycode
使用示例:
use True\Punycode;
$punycode = new Punycode();
// 编码为 Punycode
$encoded = $punycode->encode('例子.测试');
echo $encoded; // 输出: xn--fsq.xn--0zwm56d
// 解码为 Unicode
$decoded = $punycode->decode($encoded);
echo $decoded; // 输出: 例子.测试
手动实现 Punycode 算法
如果需要手动实现 Punycode 算法,可以参考以下简化代码(仅用于学习,实际项目中建议使用内置函数或第三方库)。
function punycode_encode($input) {
$output = '';
$n = 128;
$delta = 0;
$bias = 72;
$basic_length = 0;
// 处理基本字符
for ($i = 0; $i < strlen($input); $i++) {
if (ord($input[$i]) < 128) {
$output .= $input[$i];
$basic_length++;
}
}
if ($basic_length > 0) {
$output .= '-';
}
$h = $b = $basic_length;
while ($h < strlen($input)) {
$m = 0x10FFFF;
for ($i = 0; $i < strlen($input); $i++) {
$c = ord($input[$i]);
if ($c >= $n && $c < $m) {
$m = $c;
}
}
$delta += ($m - $n) * ($h + 1);
$n = $m;
for ($i = 0; $i < strlen($input); $i++) {
$c = ord($input[$i]);
if ($c < $n) {
$delta++;
} elseif ($c == $n) {
$q = $delta;
for ($k = 36;; $k += 36) {
$t = ($k <= $bias) ? 1 : (($k >= $bias + 26) ? 26 : ($k - $bias));
if ($q < $t) break;
$output .= chr($t + ($q - $t) % (36 - $t) + 97);
$q = floor(($q - $t) / (36 - $t));
}
$output .= chr($q + 97);
$bias = adapt($delta, $h + 1, $h == $b);
$delta = 0;
$h++;
}
}
$delta++;
$n++;
}
return 'xn--' . $output;
}
function adapt($delta, $numpoints, $firsttime) {
$delta = $firsttime ? floor($delta / 700) : floor($delta / 2);
$delta += floor($delta / $numpoints);
$k = 0;
while ($delta > 455) {
$delta = floor($delta / 35);
$k += 36;
}
return $k + floor(36 * $delta / ($delta + 38));
}
注意事项
- 使用
idn_to_ascii和idn_to_utf8时,确保intl扩展已安装并启用。 - 第三方库如
true/punycode提供了更简单的接口,适合没有intl扩展的环境。 - 手动实现 Punycode 算法较为复杂,建议仅在特殊需求时使用。







