2017-03-24 13 views
0

私はMariaDBのCOLUMN_JSON()関数を使用しています。 this bugが示すように、関数は二重引用符を適切にエスケープしますが、エンコード/エスケープする他の文字はエスケープしません。JSONコントロール文字のエンコード/エスケープ

ここでは、JSON列の作成方法を示すための愚かな例のクエリです。

SELECT CONCAT('[', GROUP_CONCAT(COLUMN_JSON(COLUMN_CREATE(
     'name', `name`, 
     'value', `value` 
    )) SEPARATOR ','), ']') AS `json` 
FROM `settings` 

nameまたはvalueが無効なJSON文字が含まれている場合、json_decodeは失敗します。

私は、クエリーから来る値をエスケープ/エンコードする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; 
} 

文字列ごとに文字列を調べると、このようにうまく機能しません。文字列の置換や正規表現の方がパフォーマンスがよいでしょうか?

+0

これはバグだとは限りません。 _Claudio Galdiolo_の問題は、彼の視聴者が '\ n'をエスケープしていないことを示しています。問題はMaria DBにありません。改行は '\ n'または' \ u000A'ですが、確かに '\\ n'ではありません。 – Halcyon

+0

PHPの 'json_decode()'は、値に制御文字がある場合、MariaDBの 'COLUMN_JSON()'関数から出てくる値を詰まらせます。私はその動作を検証し、解決策を探す際にバグレポートを見つけました。 – Sonny

+1

バグがあるとしましょう。 'str_replace'や' strpos'の実装が高速かどうか試してみてください。 'str_replace'は' value-> replacement'の配列を受け入れます。 strposの場合、32文字すべてをループする必要があります。 – Halcyon

答えて

0

Halcyonのコメントに基づいて、私はstr_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)); 
}