PHP 5.2 前無原生 json_encode/decode,且許多虛擬主機仍運行舊版本。本文展示純 PHP 實現的 JSON 編解碼器,解碼器利用 eval() 技巧將 JSON 轉為 PHP 代碼執行,性能遠超同類純 PHP 庫。
<?php
function json_stringify($data) {
if (is_null($data)) return 'null';
if (is_scalar($data)) return json_stringify_scalar($data);
if (empty($data)) return '[]';
if (is_object($data)) {
$data = get_object_vars($data);
if (empty($data)) return '{}';
}
$keys = array_keys($data);
// 連續數字鍵視為數組
if ($keys === array_keys($keys)) {
return '[' . implode(',', array_map(__FUNCTION__, $data)) . ']';
}
// 關聯數組視為對象
$pairs = [];
foreach ($data as $k => $v) {
$pairs[] = json_stringify_scalar((string)$k) . ':' . json_stringify($v);
}
return '{' . implode(',', $pairs) . '}';
}
function json_stringify_scalar($v) {
if (is_bool($v)) return $v ? 'true' : 'false';
if (is_string($v)) {
$v = addcslashes($v, "\t\n\r\"\/\\");
// 非 ASCII 字符轉 Unicode 轉義
$v = '"' . preg_replace_callback('|[^\x00-\x7F]+|', '_unicode_escape', $v) . '"';
}
return (string)$v;
}
function _unicode_escape($matches) {
$s = iconv('UTF-8', 'UCS-2BE', $matches[0]);
$s = str_split($s, 2);
foreach ($s as $i => $c) {
$s[$i] = sprintf('\u%02x%02x', ord($c[0]), ord($c[1]));
}
return implode('', $s);
}
原理:JSON 是 JavaScript 子集,JS 用 eval() 直接解析。PHP 也有 eval(),若將 JSON 轉為等價 PHP 代碼即可直接執行。
轉換規則:
{ → (object)array(} → )[ → array(] → ): → =>null → NULL<?php
function json_parse($json, $assoc = false) {
static $strings = [], $count = 0;
if (is_string($json)) {
$json = trim($json);
$strings = [];
// 1. 提取並佔位所有字符串(處理轉義引號)
$json = preg_replace_callback('/"([\s\S]*?(?','NULL'],
$json
);
// 4. 還原字符串佔位符
$json = preg_replace_callback('/#\d+#/', function($m) use ($strings) {
$idx = (int)substr($m[0], 1, -1);
return $strings[$idx];
}, $json);
// 5. 執行並返回
@$result = eval("return $json;");
$strings = $count = 0; // GC
return $result;
}
// 內部回調處理...
}
function _unicode_unescape($data) {
if (is_string($data)) {
return preg_replace_callback(
'/(?
| 操作 | 本實現 | Services_JSON | PHP 內置 (C) |
|---|---|---|---|
| Encode | 基準 (1x) | ~3-4x 慢 | ~100x 快 |
| Decode | 基準 (1x) | ~10-15x 慢 | ~100x 快 |
iconv('UTF-8','UCS-2BE') 批量轉 Unicode,避免逐字符處理eval(),雖掃描多次但均為 C 級操作@eval 抑制錯誤<?php
$data = [
'name' => '測試',
'age' => 25,
'tags' => ['PHP', 'JSON', '性能'],
'active' => true,
'meta' => (object)['version' => '1.0']
];
$json = json_stringify($data);
echo $json . PHP_EOL;
// {"name":"\u6d4b\u8bd5","age":25,"tags":["PHP","JSON","\u6027\u80fd"],"active":true,"meta":{"version":"1.0"}}
$parsed = json_parse($json);
var_dump($parsed);
// object(stdClass)#1 (5) { ["name"]=> string(6) "測試" ... }
eval() 的環境不可用(需修改 php.ini)