29

I have an array and PHP and when I print it out I can see the values I need to access, but when I try accessing them by their key I am getting a PHP Notice. I printed the array with print_r:

Array
(
    [207] => sdf
    [210] => sdf
)

When I try to access the array using the index I get an undefined offset notice. Here is my code:

print_r($output); 
echo $output[207];   // Undefined Offset
echo $output["207"]; // Undefined Offset

The $output array is the result of a call to array_diff_key and is input originally as JSON through an HTTP POST request.

array_keys gives me the following:

Array
(
   [0] => 207
   [1] => 210
)

In response to the comments:

var_dump(key($output)); outputs:

   string(3) "207"

var_dump(isset($output[key($output)])); outputs:

   bool(false)
12
  • 1
    Try var_dump(key($output)) and tell us what you get. Commented Jun 18, 2012 at 20:14
  • Need more information. What context? Is this in a server-side script? Commented Jun 18, 2012 at 20:15
  • @deceze: This is the output string '207' (length=3) Commented Jun 18, 2012 at 20:21
  • @deceze: Yes, I am using a lot of arrays in the code. But this simple one is driving me nuts. :-) Commented Jun 18, 2012 at 20:24
  • @ascii-lime: Nothing special. As usual PHP :-). Yes, pretty confusing. Commented Jun 18, 2012 at 20:30

7 Answers 7

25

See this section on converting an object to an array in the PHP Manual:

The keys are the member variable names, with a few notable exceptions: integer properties are unaccessible; private variables have the class name prepended to the variable name; protected variables have a '*' prepended to the variable name.

When converting to an array from an object in PHP, integer array keys are stored internally as strings. When you access array elements in PHP or use an array normally, keys that contain valid integers will be converted to integers automatically. An integer stored internally as a string is an inaccessible key.

Note the difference:

$x = (array)json_decode('{"207":"test"}');
var_dump(key($x));  // string(3) "207"

var_dump($x);
// array(1) {
//   ["207"]=>
//   string(4) "test"
// }


$y['207'] = 'test';
var_dump(key($y));  // int(207)

var_dump($y);
// array(1) {
//   [207]=>
//   string(4) "test"
// }

print_r on both those arrays gives identical output, but with var_dump you can see the differences.

Here is some code that reproduces your exact problem:

$output = (array)json_decode('{"207":"sdf","210":"sdf"}');

print_r($output);
echo $output[207];
echo $output["207"];

And the simple fix is to pass in true to json_decode for the optional assoc argument, to specify that you want an array not an object:

$output = json_decode('{"207":"sdf","210":"sdf"}', true);

print_r($output);
echo $output[207];
echo $output["207"];
Sign up to request clarification or add additional context in comments.

7 Comments

The original array is decoded from JSON POST. But the array I have problem with, is the result of array_diff_key call.
@KevinRave Yes, because the input array to the call has the same problem. Try accessing $arr[207] in whatever array you are passing in as the first argument to array_diff_key and you'll see the exact same issue. You need to fix the source of the problem which will be from casting an object to an array at some point using something like: $array = (array)$obj;
I tried changing the original array. And tried a few other things. Nothing worked. I ended up getting the key/value through foreach looping. But this seems to be a bug. :-)
@KevinRave Can you verify that you have the same problem with your original array?
@KevinRave: $first = (array)json_decode('{"207":"sdf","210":"sdf"}',true); $output = array_diff($first, array());print_r($output);echo $output[207];echo $output["207"]; array_diff is not a problem, the solution is still valid. Try to remove the true in previous code you have your bug.
|
22
+100

The problem arises when casting to array an object that has string keys that are valid integers.

If you have this object:

object(stdClass)#1 (2) {
  ["207"]=>
  string(3) "sdf"
  ["210"]=>
  string(3) "sdf"
}

and you cast it with

$array = (array)$object

you get this array

array(2) {
  ["207"]=>
  string(3) "sdf"
  ["210"]=>
  string(3) "sdf"
}

which has keys that can only be accessed by looping through them, since a direct access like $array["207"] will always be converted to $array[207], which does not exist.

Since you are getting an object like the one above from json_decode() applied to a string like

$json = '{"207":"sdf", "210":"sdf"}'

The best solution would be to avoid numeric keys in the first place. These are probably better modelled as numeric properties of an array of objects:

$json = '[{"numAttr":207, "strAttr":"sdf"}, {"numAttr":210, "strAttr":"sdf"}]'

This data structure has several advantages over the present one:

  1. it better reflects the original data, as a collection of objects which have a numeric property
  2. it is readily extensible with other properties
  3. it is more portable across different systems (as you see, your current data structure is causing issues in PHP, but if you should happen to use another language you may easily encounter similar issues).

If a property → object map is needed, it can be quickly obtained, e.g., like this:

function getNumAttr($obj) { return $obj->numAttr; } // for backward compatibility
$arr = json_decode($json); // where $json = '[{"numAttr":...
$map = array_combine(array_map('getNumAttr', $arr), $arr);

The other solution would be to do as ascii-lime suggested: force json_decode() to output associative arrays instead of objects, by setting its second parameter to true:

$map = json_decode($json, true);

For your input data this produces directly

array(2) {
  [207]=>
  string(3) "sdf"
  [210]=>
  string(3) "sdf"
}

Note that the keys of the array are now integers instead of strings.

I would consider changing the JSON data structure a much cleaner solution, though, although I understand that it might not be possible to do so.

1 Comment

It's funny being upvoted and then un-upvoted after some time. In case something wrong has been discovered in my answer, I would be curious to know what it is
1

I've just found this bug which causes array elements to be inaccessible sometimes in PHP when the array is created by a call to unserialize.

Create a test PHP file containing (or run from the command line) the following script:

<?php 

$a = unserialize('a:2:{s:2:"10";i:1;s:2:"01";i:2;}'); 

print $a['10']."\n";

$a['10'] = 3; 
$a['01'] = 4; 

print_r($a);

foreach ($a as $k => $v) 
{ 
  print 'KEY: '; 
  var_dump($k); 
  print 'VAL: '; 
  var_dump($v); 
  print "\n"; 
}

If you get errors you have a version of PHP with this bug in it and I recommend upgrading to PHP 5.3

1 Comment

i am using php 5.6 stucked in same problem
1

Try

var_dump($output);
foreach ($output as $key => val) {
    var_dump($key);
    var_dump($val);
}

to learn more on what is happening.

What exact line/statement is throwing you a warning?

Comments

0

How did you print the array? I would suggest print_r($arrayName);

Next, you can print individual elements like: echo $arrayName[0];

1 Comment

As you can see, I used print_r. And accessing it by [0] is not an option.
0

Try use my approach:

class ObjectToArray {
    public static function convert( $object ) {
        if( !is_object( $object ) && !is_array( $object ) ) {
            return $object;
        }

        if( is_object( $object ) ) {
           $object = get_object_vars( $object );
        }

        return array_map( 'ObjectToArray::convert', $object );
    }
}

$aNewArray = ObjectToArray::convert($oYourObject);

Comments

-2

Just put error_reporting(0); in you method or at start of file. It will solved your issue.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.