JsonFormatter.php 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. <?php
  2. /*
  3. * This file is part of Composer.
  4. *
  5. * (c) Nils Adermann <naderman@naderman.de>
  6. * Jordi Boggiano <j.boggiano@seld.be>
  7. *
  8. * For the full copyright and license information, please view the LICENSE
  9. * file that was distributed with this source code.
  10. */
  11. namespace Composer\Json;
  12. /**
  13. * Formats json strings used for php < 5.4 because the json_encode doesn't
  14. * supports the flags JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE
  15. * in these versions
  16. *
  17. * @author Konstantin Kudryashiv <ever.zet@gmail.com>
  18. * @author Jordi Boggiano <j.boggiano@seld.be>
  19. */
  20. class JsonFormatter
  21. {
  22. /**
  23. * This code is based on the function found at:
  24. * http://recursive-design.com/blog/2008/03/11/format-json-with-php/
  25. *
  26. * Originally licensed under MIT by Dave Perrett <mail@recursive-design.com>
  27. *
  28. *
  29. * @param string $json
  30. * @param bool $unescapeUnicode Un escape unicode
  31. * @param bool $unescapeSlashes Un escape slashes
  32. * @return string
  33. */
  34. public static function format($json, $unescapeUnicode, $unescapeSlashes)
  35. {
  36. $result = '';
  37. $pos = 0;
  38. $strLen = strlen($json);
  39. $indentStr = ' ';
  40. $newLine = "\n";
  41. $outOfQuotes = true;
  42. $buffer = '';
  43. $noescape = true;
  44. for ($i = 0; $i < $strLen; $i++) {
  45. // Grab the next character in the string
  46. $char = substr($json, $i, 1);
  47. // Are we inside a quoted string?
  48. if ('"' === $char && $noescape) {
  49. $outOfQuotes = !$outOfQuotes;
  50. }
  51. if (!$outOfQuotes) {
  52. $buffer .= $char;
  53. $noescape = '\\' === $char ? !$noescape : true;
  54. continue;
  55. } elseif ('' !== $buffer) {
  56. if ($unescapeSlashes) {
  57. $buffer = str_replace('\\/', '/', $buffer);
  58. }
  59. if ($unescapeUnicode && function_exists('mb_convert_encoding')) {
  60. // https://stackoverflow.com/questions/2934563/how-to-decode-unicode-escape-sequences-like-u00ed-to-proper-utf-8-encoded-cha
  61. $buffer = preg_replace_callback('/(\\\\+)u([0-9a-f]{4})/i', function ($match) {
  62. $l = strlen($match[1]);
  63. if ($l % 2) {
  64. $code = hexdec($match[2]);
  65. // 0xD800..0xDFFF denotes UTF-16 surrogate pair which won't be unescaped
  66. // see https://github.com/composer/composer/issues/7510
  67. if (0xD800 <= $code && 0xDFFF >= $code) {
  68. return $match[0];
  69. }
  70. return str_repeat('\\', $l - 1) . mb_convert_encoding(
  71. pack('H*', $match[2]),
  72. 'UTF-8',
  73. 'UCS-2BE'
  74. );
  75. }
  76. return $match[0];
  77. }, $buffer);
  78. }
  79. $result .= $buffer.$char;
  80. $buffer = '';
  81. continue;
  82. }
  83. if (':' === $char) {
  84. // Add a space after the : character
  85. $char .= ' ';
  86. } elseif ('}' === $char || ']' === $char) {
  87. $pos--;
  88. $prevChar = substr($json, $i - 1, 1);
  89. if ('{' !== $prevChar && '[' !== $prevChar) {
  90. // If this character is the end of an element,
  91. // output a new line and indent the next line
  92. $result .= $newLine;
  93. for ($j = 0; $j < $pos; $j++) {
  94. $result .= $indentStr;
  95. }
  96. } else {
  97. // Collapse empty {} and []
  98. $result = rtrim($result);
  99. }
  100. }
  101. $result .= $char;
  102. // If the last character was the beginning of an element,
  103. // output a new line and indent the next line
  104. if (',' === $char || '{' === $char || '[' === $char) {
  105. $result .= $newLine;
  106. if ('{' === $char || '[' === $char) {
  107. $pos++;
  108. }
  109. for ($j = 0; $j < $pos; $j++) {
  110. $result .= $indentStr;
  111. }
  112. }
  113. }
  114. return $result;
  115. }
  116. }