2013-02-21 02:30:45 +00:00
|
|
|
<?php
|
|
|
|
|
|
2013-10-24 19:35:04 +00:00
|
|
|
/**
|
|
|
|
|
* @covers FormatJson
|
|
|
|
|
*/
|
2013-02-21 02:30:45 +00:00
|
|
|
class FormatJsonTest extends MediaWikiTestCase {
|
|
|
|
|
|
2014-04-03 01:51:09 +00:00
|
|
|
public static function provideEncoderPrettyPrinting() {
|
|
|
|
|
return array(
|
|
|
|
|
// Four spaces
|
|
|
|
|
array( true, ' ' ),
|
|
|
|
|
array( ' ', ' ' ),
|
|
|
|
|
// Two spaces
|
|
|
|
|
array( ' ', ' ' ),
|
|
|
|
|
// One tab
|
|
|
|
|
array( "\t", "\t" ),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provideEncoderPrettyPrinting
|
|
|
|
|
*/
|
|
|
|
|
public function testEncoderPrettyPrinting( $pretty, $expectedIndent ) {
|
2013-02-21 02:30:45 +00:00
|
|
|
$obj = array(
|
|
|
|
|
'emptyObject' => new stdClass,
|
|
|
|
|
'emptyArray' => array(),
|
2013-03-28 02:04:58 +00:00
|
|
|
'string' => 'foobar\\',
|
2013-02-21 02:30:45 +00:00
|
|
|
'filledArray' => array(
|
|
|
|
|
array(
|
|
|
|
|
123,
|
|
|
|
|
456,
|
|
|
|
|
),
|
2013-10-08 21:23:06 +00:00
|
|
|
// Nested json works without problems
|
2013-02-21 02:30:45 +00:00
|
|
|
'"7":["8",{"9":"10"}]',
|
2013-10-08 21:23:06 +00:00
|
|
|
// Whitespace clean up doesn't touch strings that look alike
|
|
|
|
|
"{\n\t\"emptyObject\": {\n\t},\n\t\"emptyArray\": [ ]\n}",
|
2013-02-21 02:30:45 +00:00
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
|
2014-04-03 01:51:09 +00:00
|
|
|
// No trailing whitespace, no trailing linefeed
|
2013-02-21 02:30:45 +00:00
|
|
|
$json = '{
|
2014-04-03 01:51:09 +00:00
|
|
|
"emptyObject": {},
|
|
|
|
|
"emptyArray": [],
|
|
|
|
|
"string": "foobar\\\\",
|
|
|
|
|
"filledArray": [
|
|
|
|
|
[
|
|
|
|
|
123,
|
|
|
|
|
456
|
|
|
|
|
],
|
|
|
|
|
"\"7\":[\"8\",{\"9\":\"10\"}]",
|
|
|
|
|
"{\n\t\"emptyObject\": {\n\t},\n\t\"emptyArray\": [ ]\n}"
|
|
|
|
|
]
|
2013-02-21 02:30:45 +00:00
|
|
|
}';
|
|
|
|
|
|
|
|
|
|
$json = str_replace( "\r", '', $json ); // Windows compat
|
2014-04-03 01:51:09 +00:00
|
|
|
$json = str_replace( "\t", $expectedIndent, $json );
|
|
|
|
|
$this->assertSame( $json, FormatJson::encode( $obj, $pretty ) );
|
2013-02-21 02:30:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static function provideEncodeDefault() {
|
|
|
|
|
return self::getEncodeTestCases( array() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provideEncodeDefault
|
|
|
|
|
*/
|
|
|
|
|
public function testEncodeDefault( $from, $to ) {
|
|
|
|
|
$this->assertSame( $to, FormatJson::encode( $from ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static function provideEncodeUtf8() {
|
|
|
|
|
return self::getEncodeTestCases( array( 'unicode' ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provideEncodeUtf8
|
|
|
|
|
*/
|
|
|
|
|
public function testEncodeUtf8( $from, $to ) {
|
|
|
|
|
$this->assertSame( $to, FormatJson::encode( $from, false, FormatJson::UTF8_OK ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static function provideEncodeXmlMeta() {
|
|
|
|
|
return self::getEncodeTestCases( array( 'xmlmeta' ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provideEncodeXmlMeta
|
|
|
|
|
*/
|
|
|
|
|
public function testEncodeXmlMeta( $from, $to ) {
|
|
|
|
|
$this->assertSame( $to, FormatJson::encode( $from, false, FormatJson::XMLMETA_OK ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static function provideEncodeAllOk() {
|
|
|
|
|
return self::getEncodeTestCases( array( 'unicode', 'xmlmeta' ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provideEncodeAllOk
|
|
|
|
|
*/
|
|
|
|
|
public function testEncodeAllOk( $from, $to ) {
|
|
|
|
|
$this->assertSame( $to, FormatJson::encode( $from, false, FormatJson::ALL_OK ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testEncodePhpBug46944() {
|
|
|
|
|
$this->assertNotEquals(
|
|
|
|
|
'\ud840\udc00',
|
|
|
|
|
strtolower( FormatJson::encode( "\xf0\xa0\x80\x80" ) ),
|
|
|
|
|
'Test encoding an broken json_encode character (U+20000)'
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testDecodeReturnType() {
|
|
|
|
|
$this->assertInternalType(
|
|
|
|
|
'object',
|
|
|
|
|
FormatJson::decode( '{"Name": "Cheeso", "Rank": 7}' ),
|
|
|
|
|
'Default to object'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$this->assertInternalType(
|
|
|
|
|
'array',
|
|
|
|
|
FormatJson::decode( '{"Name": "Cheeso", "Rank": 7}', true ),
|
|
|
|
|
'Optional array'
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-15 23:53:49 +00:00
|
|
|
public static function provideParse() {
|
|
|
|
|
return array(
|
|
|
|
|
array( null ),
|
|
|
|
|
array( true ),
|
|
|
|
|
array( false ),
|
|
|
|
|
array( 0 ),
|
|
|
|
|
array( 1 ),
|
|
|
|
|
array( 1.2 ),
|
|
|
|
|
array( '' ),
|
|
|
|
|
array( 'str' ),
|
|
|
|
|
array( array( 0, 1, 2 ) ),
|
|
|
|
|
array( array( 'a' => 'b' ) ),
|
|
|
|
|
array( array( 'a' => 'b' ) ),
|
|
|
|
|
array( array( 'a' => 'b', 'x' => array( 'c' => 'd' ) ) ),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Recursively convert arrays into stdClass
|
|
|
|
|
* @param array|string|bool|int|float|null $value
|
|
|
|
|
* @return stdClass|string|bool|int|float|null
|
|
|
|
|
*/
|
|
|
|
|
public static function toObject( $value ) {
|
|
|
|
|
return !is_array( $value ) ? $value : (object) array_map( __METHOD__, $value );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provideParse
|
|
|
|
|
* @param mixed $value
|
|
|
|
|
*/
|
|
|
|
|
public function testParse( $value ) {
|
|
|
|
|
$expected = self::toObject( $value );
|
|
|
|
|
$json = FormatJson::encode( $expected, false, FormatJson::ALL_OK );
|
|
|
|
|
$this->assertJson( $json );
|
|
|
|
|
|
|
|
|
|
$st = FormatJson::parse( $json );
|
|
|
|
|
$this->assertType( 'Status', $st );
|
|
|
|
|
$this->assertTrue( $st->isGood() );
|
|
|
|
|
$this->assertEquals( $expected, $st->getValue() );
|
|
|
|
|
|
|
|
|
|
$st = FormatJson::parse( $json, FormatJson::FORCE_ASSOC );
|
|
|
|
|
$this->assertType( 'Status', $st );
|
|
|
|
|
$this->assertTrue( $st->isGood() );
|
|
|
|
|
$this->assertEquals( $value, $st->getValue() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static function provideParseErrors() {
|
|
|
|
|
return array(
|
|
|
|
|
array( 'aaa' ),
|
|
|
|
|
array( '{"j": 1 ] }' ),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provideParseErrors
|
|
|
|
|
* @param mixed $value
|
|
|
|
|
*/
|
|
|
|
|
public function testParseErrors( $value ) {
|
|
|
|
|
$st = FormatJson::parse( $value );
|
|
|
|
|
$this->assertType( 'Status', $st );
|
|
|
|
|
$this->assertFalse( $st->isOK() );
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-21 02:30:45 +00:00
|
|
|
/**
|
|
|
|
|
* Generate a set of test cases for a particular combination of encoder options.
|
|
|
|
|
*
|
|
|
|
|
* @param array $unescapedGroups List of character groups to leave unescaped
|
2014-04-17 18:43:42 +00:00
|
|
|
* @return array Arrays of unencoded strings and corresponding encoded strings
|
2013-02-21 02:30:45 +00:00
|
|
|
*/
|
|
|
|
|
private static function getEncodeTestCases( array $unescapedGroups ) {
|
|
|
|
|
$groups = array(
|
|
|
|
|
'always' => array(
|
|
|
|
|
// Forward slash (always unescaped)
|
|
|
|
|
'/' => '/',
|
|
|
|
|
|
|
|
|
|
// Control characters
|
|
|
|
|
"\0" => '\u0000',
|
|
|
|
|
"\x08" => '\b',
|
|
|
|
|
"\t" => '\t',
|
|
|
|
|
"\n" => '\n',
|
|
|
|
|
"\r" => '\r',
|
|
|
|
|
"\f" => '\f',
|
|
|
|
|
"\x1f" => '\u001f', // representative example
|
|
|
|
|
|
|
|
|
|
// Double quotes
|
|
|
|
|
'"' => '\"',
|
|
|
|
|
|
|
|
|
|
// Backslashes
|
|
|
|
|
'\\' => '\\\\',
|
|
|
|
|
'\\\\' => '\\\\\\\\',
|
|
|
|
|
'\\u00e9' => '\\\u00e9', // security check for Unicode unescaping
|
|
|
|
|
|
|
|
|
|
// Line terminators
|
|
|
|
|
"\xe2\x80\xa8" => '\u2028',
|
|
|
|
|
"\xe2\x80\xa9" => '\u2029',
|
|
|
|
|
),
|
|
|
|
|
'unicode' => array(
|
|
|
|
|
"\xc3\xa9" => '\u00e9',
|
|
|
|
|
"\xf0\x9d\x92\x9e" => '\ud835\udc9e', // U+1D49E, outside the BMP
|
|
|
|
|
),
|
|
|
|
|
'xmlmeta' => array(
|
|
|
|
|
'<' => '\u003C', // JSON_HEX_TAG uses uppercase hex digits
|
|
|
|
|
'>' => '\u003E',
|
|
|
|
|
'&' => '\u0026',
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$cases = array();
|
|
|
|
|
foreach ( $groups as $name => $rules ) {
|
|
|
|
|
$leaveUnescaped = in_array( $name, $unescapedGroups );
|
|
|
|
|
foreach ( $rules as $from => $to ) {
|
|
|
|
|
$cases[] = array( $from, '"' . ( $leaveUnescaped ? $from : $to ) . '"' );
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-04-26 12:00:22 +00:00
|
|
|
|
2013-02-21 02:30:45 +00:00
|
|
|
return $cases;
|
|
|
|
|
}
|
|
|
|
|
}
|