nkuhta
nkuhta

Reputation: 11148

Undefined index in array, but it is there

I have an array. Here is it's var_dump:

array(1) {
  ["139"]=>
  string(5) "my_data"
}

We see, that we have key 139, and it's string (var dumps of key: string(3) "139"). I want to get element from array, here's the code:

$bad_array = $this->get('some_data');

var_dump($bad_array);
var_dump($my_key);
var_dump(isset($bad_array[$my_key]));
var_dump($bad_array[$my_key]);

Here's the output:

array(1) {
  ["139"]=>
  string(5) "my_data"
}
string(3) "139" // my key output
bool(false) // it's not isset
Notice: Undefined index: 139 in /my_file_path.php on line 89
NULL

WHY??? I have a string variable, all of keys are strings. How can it be?

Upvotes: 3

Views: 1237

Answers (2)

Jerska
Jerska

Reputation: 12042

Short Answer

Array type-casting from an Object doesn't work with integer properties (as strings i.e. "139").

Long answer

A bunch of testing code :

<pre>
<?php
  $arr = array("139" => "some_data", "test" => "other_data");
  
  $good = (object) $arr;
  $good_arr = (array) $good;
  
  $bad = json_decode(json_encode($arr));
  $bad_arr = (array)($bad);
  
  var_dump ($arr);
  foreach ($arr as $k => $v)
    var_dump (gettype ($k));    // 139 is integer
  
  var_dump ($good);
  foreach ($good as $k => $v)
    var_dump (gettype ($k));    // 139 is integer
    
  var_dump ($good_arr);
  foreach ($good_arr as $k => $v)
    var_dump (gettype ($k));    // 139 is integer

  var_dump ($bad);
  foreach ($bad as $k => $v)
    var_dump (gettype ($k));    // 139 is string

  var_dump ($bad_arr);
  foreach ($bad_arr as $k => $v)
    var_dump (gettype ($k));    // 139 is string

  var_dump ($arr[139]);         // string(9) "some_data"
  var_dump ($arr["139"]);       // string(9) "some_data"
  var_dump ($arr["test"]);      // string(10) "other_data"
  
  var_dump ($good->{139});      // NULL
  var_dump ($good->{"139"});    // NULL
  var_dump ($good->{"test"});   // string(10) "other_data"
  
  var_dump ($good_arr[139]);    // string(9) "some_data"
  var_dump ($good_arr["139"]);  // string(9) "some_data"
  var_dump ($good_arr["test"]); // string(10) "other_data"
  
  var_dump ($bad->{139});       // string(9) "some_data"
  var_dump ($bad->{"139"});     // string(9) "some_data"
  var_dump ($bad->{"test"});    // string(10) "other_data"
  
  var_dump ($bad_arr[139]);     // NULL
  var_dump ($bad_arr["139"]);   // NULL
  var_dump ($bad_arr["test"]);  // string(10) "other_data"
?>
</pre>

bin2hex is actually showing exactly the same value, and foreach was working fine.

So how is that possible that it doesn't work when we're trying to access it directly ?
And how weird is it that the last is displaying fine ?

Well, actually, I got the answer to the second question from this part from the doc talking about array type casting As you can see there, when converting an object to array with type casting, this doesn't work for number properties, which are let inaccessible.

For the first question, I will assume that the type cast doesn't change how data are represented in memory, so that it can still iterate on it as if it was an object.

Next are just my assumptions of differences between both scenarios :

In the good scenario

PHP is handling stuff all by himself. An array key of type string representing an integer is automatically converted to integer.
Then, no problem when we convert it, it becomes a property (with integer type) of the $good object even though it isn't accessible (because it is trying to reach the "139" property, not the 139). (NULL returned)
When we do the cast to array $good_arr, the data structure hasn't changed, and we can still access it, because it reaches 139 and not "139".

In the bad scenario

Here the object is regenerated by json_decode. This function doesn't generate bad objects (and tht's a good thing !), so all the properties will have type string.
This is why we can access the property directly from the object here. It is a valid property (type string) so we can access it.
But as told in the docs, when we cast it back to array, the data structure hasn't changed, so we can't access it. Either we write $bad_arr[139] or $bad_arr["139"] it will try to access to the value with the key 139 (NULL returned), when it should actually access "139".

Conclusion

This is a typical example of PHP's magic. Converting strings to int automatically in arrays is what caused your problem.

So your solution of using the assoc param of json_decode seems to be the only one which will work here :

json_decode($json_arr, true); 

Upvotes: 3

nkuhta
nkuhta

Reputation: 11148

I have an array, then I saves it to redis. After save I casts array to object through json_decode(json_encode($ar)) construction.

$redis->save($array);
$this->object = json_decode(json_encode($array));
...

Then I casts in to array again:

$ar = (array)$this->obj;

If I will do it, I can't access array properties. Solution:

$this->object = json_decode(json_encode($array), true); // get array
...
if (is_object($ar)) {
    $ar = (array)$ar;
}

Strange things...

Upvotes: 2

Related Questions