Эх сурвалжийг харах

Added support for JSON_UNESCAPED_UNICODE and fixed parsing string

Martin Hasoň 13 жил өмнө
parent
commit
ff0f833b3e

+ 40 - 14
src/Composer/Json/JsonFile.php

@@ -78,8 +78,9 @@ class JsonFile
      *
      * @param   array   $hash   writes hash into json file
      * @param Boolean $prettyPrint If true, output is pretty-printed
+     * @param Boolean $unescapeUnicode If true, unicode chars in output are unescaped
      */
-    public function write(array $hash, $prettyPrint = true)
+    public function write(array $hash, $prettyPrint = true, $unescapeUnicode = true)
     {
         $dir = dirname($this->path);
         if (!is_dir($dir)) {
@@ -94,7 +95,7 @@ class JsonFile
                 );
             }
         }
-        file_put_contents($this->path, static::encode($hash, $prettyPrint));
+        file_put_contents($this->path, static::encode($hash, $prettyPrint, $unescapeUnicode));
     }
 
     /**
@@ -105,17 +106,23 @@ class JsonFile
      *
      * @param array $hash Data to encode into a formatted JSON string
      * @param Boolean $prettyPrint If true, output is pretty-printed
+     * @param Boolean $unescapeUnicode If true, unicode chars in output are unescaped
      * @return string Indented version of the original JSON string
      */
-    static public function encode(array $hash, $prettyPrint = true)
+    static public function encode(array $hash, $prettyPrint = true, $unescapeUnicode = true)
     {
-        if ($prettyPrint && defined('JSON_PRETTY_PRINT')) {
-            return json_encode($hash, JSON_PRETTY_PRINT);
+        if (version_compare(PHP_VERSION, '5.4', '>=')) {
+            $options = $prettyPrint ? JSON_PRETTY_PRINT : 0;
+            if ($unescapeUnicode) {
+                $options |= JSON_UNESCAPED_UNICODE;
+            }
+
+            return json_encode($hash, $options);
         }
 
         $json = json_encode($hash);
 
-        if (!$prettyPrint) {
+        if (!$prettyPrint && !$unescapeUnicode) {
             return $json;
         }
 
@@ -124,21 +131,43 @@ class JsonFile
         $strLen = strlen($json);
         $indentStr = '    ';
         $newLine = "\n";
-        $prevChar = '';
         $outOfQuotes = true;
+        $buffer = '';
+        $noescape = true;
 
         for ($i = 0; $i <= $strLen; $i++) {
             // Grab the next character in the string
             $char = substr($json, $i, 1);
 
             // Are we inside a quoted string?
-            if ('"' === $char && ('\\' !== $prevChar || '\\\\' === substr($json, $i-2, 2))) {
+            if ('"' === $char && $noescape) {
                 $outOfQuotes = !$outOfQuotes;
-            } elseif (':' === $char && $outOfQuotes) {
+            }
+
+            if (!$outOfQuotes) {
+                $buffer .= $char;
+                $noescape = '\\' === $char ? !$noescape : true;
+                continue;
+            } elseif ('' !== $buffer) {
+                if ($unescapeUnicode && function_exists('mb_convert_encoding')) {
+                    // http://stackoverflow.com/questions/2934563/how-to-decode-unicode-escape-sequences-like-u00ed-to-proper-utf-8-encoded-cha
+                    $result .= preg_replace_callback('/\\\\u([0-9a-f]{4})/i', function($match) {
+                        return mb_convert_encoding(pack('H*', $match[1]), 'UTF-8', 'UCS-2BE');
+                    }, $buffer.$char);
+                } else {
+                    $result .= $buffer.$char;
+                }
+
+                $buffer = '';
+                continue;
+            }
+
+            if (':' === $char) {
                 // Add a space after the : character
                 $char .= ' ';
-            } elseif (('}' === $char || ']' === $char) && $outOfQuotes) {
+            } elseif (('}' === $char || ']' === $char)) {
                 $pos--;
+                $prevChar = substr($json, $i - 1, 1);
 
                 if ('{' !== $prevChar && '[' !== $prevChar) {
                     // If this character is the end of an element,
@@ -153,12 +182,11 @@ class JsonFile
                 }
             }
 
-            // Add the character to the result string
             $result .= $char;
 
             // If the last character was the beginning of an element,
             // output a new line and indent the next line
-            if ((',' === $char || '{' === $char || '[' === $char) && $outOfQuotes) {
+            if (',' === $char || '{' === $char || '[' === $char) {
                 $result .= $newLine;
 
                 if ('{' === $char || '[' === $char) {
@@ -169,8 +197,6 @@ class JsonFile
                     $result .= $indentStr;
                 }
             }
-
-            $prevChar = $char;
         }
 
         return $result;

+ 20 - 0
tests/Composer/Test/Json/JsonFileTest.php

@@ -121,6 +121,26 @@ class JsonFileTest extends \PHPUnit_Framework_TestCase
         $this->assertJsonFormat($json, $data);
     }
 
+    public function testEscape()
+    {
+        $data = array("Metadata\\\"" => 'src/');
+        $json = '{
+    "Metadata\\\\\\"": "src\/"
+}';
+
+        $this->assertJsonFormat($json, $data);
+    }
+
+    public function testUnicode()
+    {
+        $data = array("Žluťoučký \" kůň" => "úpěl ďábelské ódy za €");
+        $json = '{
+    "Žluťoučký \" kůň": "úpěl ďábelské ódy za €"
+}';
+
+        $this->assertJsonFormat($json, $data);
+    }
+
     private function expectParseException($text, $json)
     {
         try {