Page MenuHomePhabricator

ForeignAPIRepo broken if native json module not available
Closed, ResolvedPublic

Description

Tried both, 1.16.0 tarball from mediawiki.org as well as SVN REL1_16.

PHP version: 5.2.12

from LocalSettings.php:

  • snip ---

$wgForeignFileRepos[] = array(

'class'               => 'ForeignAPIRepo',
'name'                => 'shared',
'apibase'             => 'http://commons.wikimedia.org/w/api.php',
'fetchDescription'    => true

);

  • snap ---

Whenever I try to embedd an image - be it local or external - I get the following error message:
Fatal error: Cannot use object of type stdClass as array in /var/www/[...]/includes/filerepo/ForeignAPIFile.php on line 101


Version: 1.16.x
Severity: blocker

Details

Reference
bz26250

Event Timeline

bzimport raised the priority of this task from to Medium.Nov 21 2014, 11:16 PM
bzimport set Reference to bz26250.

Bryan.TongMinh wrote:

Can't reproduce on PHP 5.3.2 on IIS/Windows Vista using the latest REL1_16 branch.

The line in question would be:

if( !isset( $this->mInfo['mime'] ) ) {

$this->mInfo was always written (and expects to be) an associative array. For some reason it sounds like it's getting turned into an object :(

Bryan.TongMinh wrote:

I have no clue how PHP would turn an array in an object. stdClass is the type of a a database result row right? My best guess is that somehow mInfo is turned into a result row. Or otherwise perhaps a fucked up version of the json decoder?

http://php.net/manual/en/reserved.classes.php stdClass is "Created by typecasting to object" where typcasting:

If a value of any other type is converted to an object, a new instance of the
stdClass built-in class is created. If the value was NULL, the new instance
will be empty. Arrays convert to an object with properties named by keys, and
corresponding values. For any other value, a member variable named scalar will
contain the value.

I think we all know what stdClass is and how you cast an array to it.

The confusion is where this casting is happening...we've been using an array in this code since inception.

json_decode() produces stdClass objects for JSON objects/maps/dicts by default; to get an associative array, we pass in 'true' as the second parameter.

For our FormatJson::decode() wrapper, if json_decode is not natively available passes the object retrieved from another library through wfObjectToArray().

It looks like wfObjectToArray()'s recursion isn't actually sufficient: json_decode($img, true) and wfObjectToArray(json_decode($img)) give different results, and the latter fails to translate the imageinfo entry:

Testing on API return data from http://commons.wikimedia.org/w/api.php?action=query&titles=Image:Wikipedia.png&iiprop=timestamp|user|comment|url|size|sha1|metadata|mime&prop=imageinfo&format=json

return wfObjectToArray(json_decode($img, false));

array(2) {

["query"]=>
array(2) {
  ["normalized"]=>
  array(1) {
    [0]=>
    object(stdClass)#22 (2) {
      ["from"]=>
      string(19) "Image:Wikipedia.png"
      ["to"]=>
      string(18) "File:Wikipedia.png"
    }
  }
  ["pages"]=>
  array(1) {
    [17105]=>
    array(5) {
      ["pageid"]=>
      int(17105)
      ["ns"]=>
      int(6)
      ["title"]=>
      string(18) "File:Wikipedia.png"
      ["imagerepository"]=>
      string(5) "local"
      ["imageinfo"]=>
      array(1) {
        [0]=>
        object(stdClass)#25 (11) {
          ["timestamp"]=>
          string(20) "2005-08-13T20:55:13Z"
          ["user"]=>
          string(9) "Thuresson"
          ["size"]=>
          int(11742)
          ["width"]=>
          int(135)
          ["height"]=>
          int(155)
          ["url"]=>
          string(64) "http://upload.wikimedia.org/wikipedia/commons/1/12/Wikipedia.png"
          ["descriptionurl"]=>
          string(52) "http://commons.wikimedia.org/wiki/File:Wikipedia.png"
          ["comment"]=>
          string(29) "Återgått till yngre version"
          ["sha1"]=>
          string(40) "f755e536d96cb430f23b53806ffcd88eada3d572"
          ["metadata"]=>
          NULL
          ["mime"]=>
          string(9) "image/png"
        }
      }
    }
  }
}
["query-continue"]=>
array(1) {
  ["imageinfo"]=>
  array(1) {
    ["iistart"]=>
    string(20) "2005-08-08T20:30:06Z"
  }
}

}

So if we're on a system without PHP's native JSON module, we'll run the object form through wfObjectToArray() and get that bogus output. Most of the data structure is associative arrays -- so we extract the imageinfo entry successfully -- but when things try to read out of it, they've got the wrong data type.

Looks like the recursion isn't diving into linear arrays:

foreach ( get_object_vars( $object ) as $key => $value ) {

		if ( is_object( $value ) && $recursive ) {
			$value = wfObjectToArray( $value );
		}

		$array[$key] = $value;

}

Adding bug 23817 as dependency -- there's a patch on that bug to fix wfObjectToArray.

Patches from bug 23817 are applied in r82090 and r82091, fixing this.