共享主機/受限環境禁用 iconv、mbstring 擴展,需在純 PHP 中實現 **UTF-8 ⇄ Windows-1251 (CP1251)** 互轉(常見於俄語/西里爾字符處理)。
<?php
/**
* UTF-8 → CP1251
* @param string $utf8 合法 UTF-8 字符串
* @return string CP1251 字節流
*/
function utf8_to_cp1251(string $utf8): string {
static $map = null;
if ($map === null) {
// 建立 Unicode 碼點 → CP1251 字節 映射表(僅西里爾區塊 + ASCII)
$map = [];
// ASCII 0-127 直接通過
for ($i = 0; $i < 128; $i++) $map[$i] = chr($i);
// 西里爾大寫 U+0410–U+042F → 0xC0–0xDF
for ($i = 0x410; $i <= 0x42F; $i++) $map[$i] = chr(0xC0 + ($i - 0x410));
// 西里爾小寫 U+0430–U+044F → 0xE0–0xFF
for ($i = 0x430; $i <= 0x44F; $i++) $map[$i] = chr(0xE0 + ($i - 0x430));
// 特殊字符
$map[0x401] = chr(0xA8); // Ё
$map[0x451] = chr(0xB8); // ё
$map[0x402] = chr(0xAF); // Ђ
$map[0x452] = chr(0xBF); // ђ
$map[0x403] = chr(0xAA); // Ѓ
$map[0x453] = chr(0xBA); // ѓ
$map[0x404] = chr(0xAC); // Є
$map[0x454] = chr(0xBC); // є
$map[0x405] = chr(0xAD); // Ѕ
$map[0x455] = chr(0xBD); // ѕ
$map[0x406] = chr(0xAE); // І
$map[0x456] = chr(0xBE); // і
$map[0x407] = chr(0xA2); // Ї
$map[0x457] = chr(0xB2); // ї
$map[0x408] = chr(0xA3); // Ј
$map[0x458] = chr(0xB3); // ј
$map[0x409] = chr(0xA4); // Љ
$map[0x459] = chr(0xB4); // љ
$map[0x40A] = chr(0xA5); // Њ
$map[0x45A] = chr(0xB5); // њ
$map[0x40B] = chr(0xA6); // Ћ
$map[0x45B] = chr(0xB6); // ћ
$map[0x40C] = chr(0xA7); // Ќ
$map[0x45C] = chr(0xB7); // ќ
$map[0x40E] = chr(0xA9); // Ў
$map[0x45E] = chr(0xB9); // ў
$map[0x40F] = chr(0xAB); // Џ
$map[0x45F] = chr(0xBB); // џ
// 其餘字符映射為 '?' (0x3F)
}
$len = strlen($utf8);
$out = '';
for ($i = 0; $i < $len; ) {
$byte = ord($utf8[$i]);
if ($byte < 128) { // ASCII
$out .= chr($byte);
$i++;
} elseif (($byte & 0xE0) === 0xC0) { // 2 字節序列
if ($i + 1 >= $len) { $out .= '?'; break; }
$byte2 = ord($utf8[$i+1]);
if (($byte2 & 0xC0) !== 0x80) { $out .= '?'; $i += 2; continue; }
$codepoint = (($byte & 0x1F) << 6) | ($byte2 & 0x3F);
$out .= $map[$codepoint] ?? '?';
$i += 2;
} elseif (($byte & 0xF0) === 0xE0) { // 3 字節(西里爾不在這裡,直接 ?)
$i += 3; $out .= '?';
} elseif (($byte & 0xF8) === 0xF0) { // 4 字節
$i += 4; $out .= '?';
} else {
$i++; $out .= '?';
}
}
return $out;
}
/**
* CP1251 → UTF-8
* @param string $cp1251 CP1251 字節流
* @return string UTF-8 字符串
*/
function cp1251_to_utf8(string $cp1251): string {
static $map = null;
if ($map === null) {
$map = [];
// ASCII
for ($i = 0; $i < 128; $i++) $map[chr($i)] = chr($i);
// 0xC0-0xDF → U+0410-U+042F
for ($i = 0xC0; $i <= 0xDF; $i++) $map[chr($i)] = "\xD0".chr(0x90 + ($i - 0xC0));
// 0xE0-0xFF → U+0430-U+044F
for ($i = 0xE0; $i <= 0xFF; $i++) $map[chr($i)] = "\xD1".chr(0x80 + ($i - 0xE0));
// 特殊
$map[chr(0xA8)] = "\xD0\x81"; // Ё
$map[chr(0xB8)] = "\xD1\x91"; // ё
$map[chr(0xAF)] = "\xD0\x82"; // Ђ
$map[chr(0xBF)] = "\xD1\x92"; // ђ
$map[chr(0xAA)] = "\xD0\x83"; // Ѓ
$map[chr(0xBA)] = "\xD1\x93"; // ѓ
$map[chr(0xAC)] = "\xD0\x84"; // Є
$map[chr(0xBC)] = "\xD1\x94"; // є
$map[chr(0xAD)] = "\xD0\x85"; // Ѕ
$map[chr(0xBD)] = "\xD1\x95"; // ѕ
$map[chr(0xAE)] = "\xD0\x86"; // І
$map[chr(0xBE)] = "\xD1\x96"; // і
$map[chr(0xA2)] = "\xD0\x87"; // Ї
$map[chr(0xB2)] = "\xD1\x97"; // ї
$map[chr(0xA3)] = "\xD0\x88"; // Ј
$map[chr(0xB3)] = "\xD1\x98"; // ј
$map[chr(0xA4)] = "\xD0\x89"; // Љ
$map[chr(0xB4)] = "\xD1\x99"; // љ
$map[chr(0xA5)] = "\xD0\x8A"; // Њ
$map[chr(0xB5)] = "\xD1\x9A"; // њ
$map[chr(0xA6)] = "\xD0\x8B"; // Ћ
$map[chr(0xB6)] = "\xD1\x9B"; // ћ
$map[chr(0xA7)] = "\xD0\x8C"; // Ќ
$map[chr(0xB7)] = "\xD1\x9C"; // ќ
$map[chr(0xA9)] = "\xD0\x8E"; // Ў
$map[chr(0xB9)] = "\xD1\x9E"; // ў
$map[chr(0xAB)] = "\xD0\x8F"; // Џ
$map[chr(0xBB)] = "\xD1\x9F"; // џ
}
$len = strlen($cp1251);
$out = '';
for ($i = 0; $i < $len; $i++) {
$ch = $cp1251[$i];
$out .= $map[$ch] ?? '?';
}
return $out;
}
// 測試
$utf8 = "Привет, мир! Hello";
$cp1251 = utf8_to_cp1251($utf8);
$back = cp1251_to_utf8($cp1251);
echo "原始: $utf8\n";
echo "CP1251(hex): " . bin2hex($cp1251) . "\n";
echo "回轉: $back\n";
echo "一致: " . ($utf8 === $back ? '是' : '否') . "\n";
$map 僅首次調用構建,後續直接複用,O(1) 查找。iconv 或 mb_convert_encoding**(C 級擴展,速度快 10-50 倍、支持完整 Unicode)。同理可構建 UTF-8 ⇄ CP1252 (Latin1)、UTF-8 ⇄ GBK(雙字節,需雙字節查找表)等純 PHP 轉換器。但 **GBK/Big5 等雙字節編碼映射表極大(萬級條目),純 PHP 實現極慢,強烈建議啟用 mbstring**。
在無 iconv/mbstring 環境下,通過 **Unicode 碼點 ↔ 目標編碼字節** 的靜態映射表,可高效實現 **UTF-8 ⇄ CP1251** 互轉。代碼已封裝為無依賴函數,可直接集成至項目。生產環境若可能,**務必啟用 mbstring 擴展**。