JsonParserTest.php 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. <?php
  2. /*
  3. * This file is part of the JSON Lint package.
  4. *
  5. * (c) Jordi Boggiano <j.boggiano@seld.be>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. use PHPUnit\Framework\TestCase;
  11. use Seld\JsonLint\JsonParser;
  12. use Seld\JsonLint\ParsingException;
  13. use Seld\JsonLint\DuplicateKeyException;
  14. class JsonParserTest extends TestCase
  15. {
  16. protected $json = array(
  17. '42', '42.3', '0.3', '-42', '-42.3', '-0.3',
  18. '2e1', '2E1', '-2e1', '-2E1', '2E+2', '2E-2', '-2E+2', '-2E-2',
  19. 'true', 'false', 'null', '""', '[]', '{}', '"string"',
  20. '["a", "sdfsd"]',
  21. '{"foo":"bar", "bar":"baz", "":"buz"}',
  22. '{"":"foo", "_empty_":"bar"}',
  23. '"\u00c9v\u00e9nement"',
  24. '"http:\/\/foo.com"',
  25. '"zo\\\\mg"',
  26. '{"test":"\u00c9v\u00e9nement"}',
  27. '["\u00c9v\u00e9nement"]',
  28. '"foo/bar"',
  29. '{"test":"http:\/\/foo\\\\zomg"}',
  30. '["http:\/\/foo\\\\zomg"]',
  31. '{"":"foo"}',
  32. '{"a":"b", "b":"c"}',
  33. '0',
  34. '""',
  35. );
  36. /**
  37. * @dataProvider provideValidStrings
  38. */
  39. public function testParsesValidStrings($input)
  40. {
  41. $parser = new JsonParser();
  42. $this->assertEquals(json_decode($input), $parser->parse($input));
  43. }
  44. public function provideValidStrings()
  45. {
  46. $strings = array();
  47. foreach ($this->json as $input) {
  48. $strings[] = array($input);
  49. }
  50. return $strings;
  51. }
  52. public function testErrorOnTrailingComma()
  53. {
  54. $parser = new JsonParser();
  55. try {
  56. $parser->parse('{
  57. "foo":"bar",
  58. }');
  59. $this->fail('Invalid trailing comma should be detected');
  60. } catch (ParsingException $e) {
  61. $this->assertContains('It appears you have an extra trailing comma', $e->getMessage());
  62. }
  63. }
  64. public function testErrorOnInvalidQuotes()
  65. {
  66. $parser = new JsonParser();
  67. try {
  68. $parser->parse('{
  69. "foo": \'bar\',
  70. }');
  71. $this->fail('Invalid quotes for string should be detected');
  72. } catch (ParsingException $e) {
  73. $this->assertContains('Invalid string, it appears you used single quotes instead of double quotes', $e->getMessage());
  74. }
  75. }
  76. public function testErrorOnUnescapedBackslash()
  77. {
  78. $parser = new JsonParser();
  79. try {
  80. $parser->parse('{
  81. "foo": "bar\z",
  82. }');
  83. $this->fail('Invalid unescaped string should be detected');
  84. } catch (ParsingException $e) {
  85. $this->assertContains('Invalid string, it appears you have an unescaped backslash at: \z', $e->getMessage());
  86. }
  87. }
  88. public function testErrorOnUnterminatedString()
  89. {
  90. $parser = new JsonParser();
  91. try {
  92. $parser->parse('{"bar": "foo}');
  93. $this->fail('Invalid unterminated string should be detected');
  94. } catch (ParsingException $e) {
  95. $this->assertContains('Invalid string, it appears you forgot to terminate a string, or attempted to write a multiline string which is invalid', $e->getMessage());
  96. }
  97. }
  98. public function testErrorOnMultilineString()
  99. {
  100. $parser = new JsonParser();
  101. try {
  102. $parser->parse('{"bar": "foo
  103. bar"}');
  104. $this->fail('Invalid multi-line string should be detected');
  105. } catch (ParsingException $e) {
  106. $this->assertContains('Invalid string, it appears you forgot to terminate a string, or attempted to write a multiline string which is invalid', $e->getMessage());
  107. }
  108. }
  109. public function testErrorAtBeginning()
  110. {
  111. $parser = new JsonParser();
  112. try {
  113. $parser->parse('
  114. ');
  115. $this->fail('Empty string should be invalid');
  116. } catch (ParsingException $e) {
  117. $this->assertContains("Parse error on line 1:\n\n^", $e->getMessage());
  118. }
  119. }
  120. public function testParsesMultiInARow()
  121. {
  122. $parser = new JsonParser();
  123. foreach ($this->json as $input) {
  124. $this->assertEquals(json_decode($input), $parser->parse($input));
  125. }
  126. }
  127. public function testDetectsKeyOverrides()
  128. {
  129. $parser = new JsonParser();
  130. try {
  131. $parser->parse('{"a":"b", "a":"c"}', JsonParser::DETECT_KEY_CONFLICTS);
  132. $this->fail('Duplicate keys should not be allowed');
  133. } catch (DuplicateKeyException $e) {
  134. $this->assertContains('Duplicate key: a', $e->getMessage());
  135. $this->assertSame('a', $e->getKey());
  136. $this->assertSame(array('line' => 1, 'key' => 'a'), $e->getDetails());
  137. }
  138. }
  139. public function testDetectsKeyOverridesWithEmpty()
  140. {
  141. $parser = new JsonParser();
  142. if (PHP_VERSION_ID >= 70100) {
  143. $this->markTestSkipped('Only for PHP < 7.1');
  144. }
  145. try {
  146. $parser->parse('{"":"b", "_empty_":"a"}', JsonParser::DETECT_KEY_CONFLICTS);
  147. $this->fail('Duplicate keys should not be allowed');
  148. } catch (DuplicateKeyException $e) {
  149. $this->assertContains('Duplicate key: _empty_', $e->getMessage());
  150. $this->assertSame('_empty_', $e->getKey());
  151. $this->assertSame(array('line' => 1, 'key' => '_empty_'), $e->getDetails());
  152. }
  153. }
  154. public function testDuplicateKeys()
  155. {
  156. $parser = new JsonParser();
  157. $result = $parser->parse('{"a":"b", "a":"c", "a":"d"}', JsonParser::ALLOW_DUPLICATE_KEYS);
  158. $this->assertThat($result,
  159. $this->logicalAnd(
  160. $this->objectHasAttribute('a'),
  161. $this->objectHasAttribute('a.1'),
  162. $this->objectHasAttribute('a.2')
  163. )
  164. );
  165. }
  166. public function testDuplicateKeysWithEmpty()
  167. {
  168. $parser = new JsonParser();
  169. if (PHP_VERSION_ID >= 70100) {
  170. $this->markTestSkipped('Only for PHP < 7.1');
  171. }
  172. $result = $parser->parse('{"":"a", "_empty_":"b"}', JsonParser::ALLOW_DUPLICATE_KEYS);
  173. $this->assertThat($result,
  174. $this->logicalAnd(
  175. $this->objectHasAttribute('_empty_'),
  176. $this->objectHasAttribute('_empty_.1')
  177. )
  178. );
  179. }
  180. public function testParseToArray()
  181. {
  182. $parser = new JsonParser();
  183. $json = '{"one":"a", "two":{"three": "four"}, "": "empty"}';
  184. $result = $parser->parse($json, JsonParser::PARSE_TO_ASSOC);
  185. $this->assertSame(json_decode($json, true), $result);
  186. }
  187. public function testFileWithBOM()
  188. {
  189. try {
  190. $parser = new JsonParser();
  191. $parser->parse(file_get_contents(dirname(__FILE__) .'/bom.json'));
  192. $this->fail('BOM should be detected');
  193. } catch (ParsingException $e) {
  194. $this->assertContains('BOM detected', $e->getMessage());
  195. }
  196. }
  197. public function testLongString()
  198. {
  199. $parser = new JsonParser();
  200. $json = '{"k":"' . str_repeat("a\\n",10000) . '"}';
  201. $this->assertEquals(json_decode($json), $parser->parse($json));
  202. }
  203. }