$msg = null;
if ( $data !== null ) {
- $data = FormatJson::decode( $data );
- if ( !is_object( $data ) ) {
+ $data = FormatJson::decode( $data, true );
+ if ( !is_array( $data ) ) {
// @codeCoverageIgnoreStart
wfLogWarning( "Invalid JSON object in comment: $data" );
$data = null;
// @codeCoverageIgnoreEnd
} else {
- $data = (array)$data;
if ( isset( $data['_message'] ) ) {
$msg = self::decodeMessage( $data['_message'] )
->setInterfaceMessageFlag( true );
* which returns more comprehensive result in case of an error, and has
* more parsing options.
*
+ * In PHP versions before 7.1, decoding a JSON string containing an empty key
+ * without passing $assoc as true results in a return object with a property
+ * named "_empty_" (because true empty properties were not supported pre-PHP-7.1).
+ * Instead, consider passing $assoc as true to return an associative array.
+ *
+ * But be aware that in all supported PHP versions, decoding an empty JSON object
+ * with $assoc = true returns an array, not an object, breaking round-trip consistency.
+ *
+ * See https://phabricator.wikimedia.org/T206411 for more details on these quirks.
+ *
* @param string $value The JSON string being decoded
* @param bool $assoc When true, returned objects will be converted into associative arrays.
*
/**
* Test data for testParseTryFixing.
*
- * Some PHP interpreters use json-c rather than the JSON.org cannonical
+ * Some PHP interpreters use json-c rather than the JSON.org canonical
* parser to avoid being encumbered by the "shall be used for Good, not
* Evil" clause of the JSON.org parser's license. By default, json-c
* parses in a non-strict mode which allows trailing commas for array and
return $cases;
}
+
+ public function provideEmptyJsonKeyStrings() {
+ return [
+ [
+ '{"":"foo"}',
+ '{"":"foo"}',
+ ''
+ ],
+ [
+ '{"_empty_":"foo"}',
+ '{"_empty_":"foo"}',
+ '_empty_' ],
+ [
+ '{"\u005F\u0065\u006D\u0070\u0074\u0079\u005F":"foo"}',
+ '{"_empty_":"foo"}',
+ '_empty_'
+ ],
+ [
+ '{"_empty_":"bar","":"foo"}',
+ '{"_empty_":"bar","":"foo"}',
+ ''
+ ],
+ [
+ '{"":"bar","_empty_":"foo"}',
+ '{"":"bar","_empty_":"foo"}',
+ '_empty_'
+ ]
+ ];
+ }
+
+ /**
+ * @covers FormatJson::encode
+ * @covers FormatJson::decode
+ * @dataProvider provideEmptyJsonKeyStrings
+ * @param string $json
+ *
+ * Decoding behavior with empty keys can be surprising.
+ * See https://phabricator.wikimedia.org/T206411
+ */
+ public function testEmptyJsonKeyArray( $json, $expect, $php71Name ) {
+ // Decoding to array is consistent across supported PHP versions
+ $this->assertSame( $expect, FormatJson::encode(
+ FormatJson::decode( $json, true ) ) );
+
+ // Decoding to object differs between supported PHP versions
+ $obj = FormatJson::decode( $json );
+ if ( version_compare( PHP_VERSION, '7.1', '<' ) ) {
+ $this->assertEquals( 'foo', $obj->_empty_ );
+ } else {
+ $this->assertEquals( 'foo', $obj->{$php71Name} );
+ }
+ }
}