Php 编码/转义JSON控制字符

Php 编码/转义JSON控制字符,php,mysql,json,mariadb,Php,Mysql,Json,Mariadb,我正在使用MariaDB的函数。如图所示,该函数正确地转义双引号,但不转义其他应编码/转义的字符 下面是一个愚蠢的查询示例,演示如何创建JSON列 SELECT CONCAT('[', GROUP_CONCAT(COLUMN_JSON(COLUMN_CREATE( 'name', `name`, 'value', `value` )) SEPARATOR ','), ']') AS `json` FROM `settings` 如果名称或值包含无效的JS

我正在使用MariaDB的函数。如图所示,该函数正确地转义双引号,但不转义其他应编码/转义的字符

下面是一个愚蠢的查询示例,演示如何创建JSON列

SELECT CONCAT('[', GROUP_CONCAT(COLUMN_JSON(COLUMN_CREATE(
        'name', `name`,
        'value', `value`
    )) SEPARATOR ','), ']') AS `json`
FROM `settings`
如果
名称
包含无效的JSON字符,
JSON\u解码
将失败

我已经编写了一个PHP函数来转义/编码来自查询的值,但似乎应该有更好的方法

/**
 * Makes sure the JSON values built by COLUMN_JSON() in MariaDB are safe for json_decode()
 * Assumes that double quotes are already escaped
 *
 * @param string $mysql_json
 * @return string
 */
public static function jsonEscape($mysql_json)
{
    $rtn = '';
    for ($i = 0; $i < strlen($mysql_json); ++$i) {
        $char = $mysql_json[$i];
        if (($char === '\\') && ($mysql_json[$i + 1] !== '"')) {
            // escape a backslash, but leave escaped double quotes intact
            $rtn .= '\\\\';
        } elseif (($ord = ord($char)) && ($ord < 32)) {
            // hex encode control characters (below ASCII 32)
            $rtn .= '\\u' . str_pad(dechex($ord), 4, '0', STR_PAD_LEFT);
        } else {
            $rtn .= $char;
        }
    }
    return $rtn;
}
/**
*确保MariaDB中列_JSON()生成的JSON值对于JSON_decode()是安全的
*假设双引号已经转义
*
*@param string$mysql\u json
*@返回字符串
*/
公共静态函数jsonEscape($mysql\u json)
{
$rtn='';
对于($i=0;$i
像这样逐个字符检查字符串性能不好。也许有一个字符串替换或正则表达式性能更好?

根据的一条评论,我切换到了
stru replace()
解决方案,它的性能要好得多。
trim(json_encode(13),“”)之间的性能差异
“\\u”。str_-pad(dechex(13),4,'0',str_-pad_-LEFT)
只是稍微好一点,但它使意图更加明确

private static $json_replace_search;
private static $json_replace_replace;

/**
 * Makes sure the JSON values built by GROUP_CONCAT() and COLUMN_JSON() in MariaDB are safe for json_decode()
 * Assumes that double quotes are already escaped
 *
 * @param string $mysql_json
 * @return string
 */
public static function jsonEscape($mysql_json)
{
    if (is_null(self::$json_replace_search)) {
        // initialize
        self::$json_replace_search = [];
        self::$json_replace_replace = [];
        // set up all of the control characters (below ASCII 32)
        for ($i = 0; $i < 32; ++$i) {
            self::$json_replace_search[$i] = chr($i);
            self::$json_replace_replace[$i] = trim(json_encode(self::$json_replace_search[$i]), '"');
        }
    }
    // replace them
    return str_replace(self::$json_replace_search, self::$json_replace_replace, $mysql_json);
}

/**
 *
 * @param string $mysql_json
 * @return mixed
 */
public static function jsonDecode($mysql_json)
{
    return json_decode(self::jsonEscape($mysql_json));
}
private static$json\u replace\u search;
私有静态$json\u replace\u replace;
/**
*确保MariaDB中的组_CONCAT()和列_JSON()生成的JSON值对于JSON_decode()是安全的
*假设双引号已经转义
*
*@param string$mysql\u json
*@返回字符串
*/
公共静态函数jsonEscape($mysql\u json)
{
if(为null(self:$json\u replace\u search)){
//初始化
self::$json_replace_search=[];
self::$json_replace_replace=[];
//设置所有控制字符(低于ASCII 32)
对于($i=0;$i<32;++$i){
self::$json_replace_search[$i]=chr($i);
self:$json_replace_replace[$i]=trim(json_encode(self:$json_replace_search[$i]),“”);
}
}
//替换它们
返回str_replace(self:$json_replace_search,self:$json_replace_replace,$mysql_json);
}
/**
*
*@param string$mysql\u json
*@返回混合
*/
公共静态函数jsonDecode($mysql\u json)
{
返回json_decode(self::jsonEscape($mysql_json));
}

我不认为这是一个bug。正如Claudio Galdiolo所显示的问题是他的观众没有逃过
\n
,问题不在Maria DB中。换行符不是
\n
就是
\u000A
,但肯定不是
\\n
。PHP的
json\u decode()
会被MariaDB的
列的值阻塞()
函数,如果值具有控制字符。我已经验证了行为,并在寻找解决方案时找到了错误报告。让我们假设错误存在。尝试看看
str\u replace
strpos
实现是否更快。请注意
str\u replace
接受一个
value->replaces
数组或strpos您需要循环所有32个字符。@RyanVincent-没有“原始字符串”-COLUMN_JSON()正在使用数据库列中的值创建字符串。该列中的值不是JSON安全的。它们包含引号、回车等。@RyanVincent-突出点。添加了查询示例。