Why does treating integers as arrays ($int[$index]) not raise any errors in PHP? [duplicate] - php

This question already has an answer here:
How does the "Array dereferencing" work on a scalar value of type boolean/integer/float/string as of PHP version 7.2.0?
(1 answer)
Closed 5 years ago.
This is just a simple question out of curiosity. I spent the whole day debugging my PHP code, and found the problem was due to treating an integer as an array:
$x = $int[$index]; // this returns null, but no error at all
The integer was actually meant to be an array, but the function that was meant to pass the array messed up and passed the first value in the array instead. Is there any reason why an error isn't shown? Accessing an undefined index on an array creates an error, however trying to access a non-existent index on an integer doesn't?

I originally thought; it typecasts $int to a string because [] is another way of accessing string positions. Seems plausible that that wouldn't generate an error:
$string 'Hello';
echo $string[0]; // H
However, that's not the case:
$int = 1;
echo $int[0];
Outputs nothing and var_dump($int[0]) returns NULL.
Interestingly, the same behavior is exhibited for bool and float and the operation used is FETCH_DIM_R which is Fetch the value of the element at "index" in "array-value" to store it in "result".
From the manual Arrays:
Note: Array dereferencing a scalar value which is not a string silently yields NULL, i.e. without issuing an error message.
Similar to this phenomenon, where no error is generated. Bugs #68110 and #54556:
$array['a'] = null;
echo $array['a']['non-existent'];
Not surprising that assigning doesn't work:
$int = 1;
$int[0] = 2;
Warning: Cannot use a scalar value as an array
So PHP is actually attempting to access the int, bool, float as an array but not generating an error. This is from at least version 4.3.0 to 7.2.2.

Contrary to my original theory that you're invoking undefined behavior, this behavior actually is defined in the Array documentation.
Note:
Array dereferencing a scalar value which is not a string silently yields NULL, i.e. without issuing an error message.
In that case, it seems like there's no type juggling happening at all, so these references to documentation regarding conversion to array aren't useful in understanding this.
Explicit conversion to array is defined.
For any of the types integer, float, string, boolean and resource, converting a value to an array results in an array with a single element with index zero and the value of the scalar which was converted. In other words, (array)$scalarValue is exactly the same as array($scalarValue).
Automatic conversion to array is undefined according to the type juggling documenation.
Note:
The behaviour of an automatic conversion to array is currently undefined.
At first I thought this was what was happening in this case, but since salathe pointed out that this behavior is documented elsewhere, I'm not sure what "automatic conversion to array" is.
As to why this gives you a null value without throwing an error, warning, or notice, that's just how the PHP interpreter was implemented, and as far as why that is the case, it's not really answerable here. You'd have to ask the developers, and they might be able to tell you.

this is "type juggling", a "feature" of PHP.
you can read more in the official doc
EDIT
According to documentation, "The behaviour of an automatic conversion to array is currently undefined."
I run a quick test, just for curiosity:
<?php
$int = 1;
$index = 0;
$x = $int[$index];
echo gettype($int[$index]) . PHP_EOL;
var_dump($x);
and the output shows that gettype() returns NULL, so I guess that, in this case, is converted to NULL

Related

purpose of (array) in PHP

I came across (array) in WordPress in the following code but looked in the PHP manual search for (array) but could not find anything (https://www.php.net/manual-lookup.php?pattern=%28array%29&scope=quickref)
foreach ( (array) $cron as $timestamp => $hooks) {
foreach ( (array) $hooks as $hook => $args ) {
$key = md5(serialize($args['args']));
$new_cron[$timestamp][$hook][$key] = $args;
}
}
Could someone please explain what this (array) does?
This is called casting a variable (AKA casting or type-juggling). You are saying you want $cronhooks to be converted to and evaluated as an array. Look at this example:
$a = (int) 5.3;
print($a);
5
The (int) indicates that I want an integer from 5.3. so the PHP convert it.
It's casting the variable to an array. Perhaps $cronhooks was an object rather than an array, and could not be iterated as a key => value array.
Here's the manual page for Type Jugling in PHP
https://www.php.net/manual/en/language.types.type-juggling.php
Array members can be accessed using index or key as follow:
$cronhooks[0]; // the first member
$people['tom']; // the member with the key 'tom'
objects and classes have members which are accessed using the object operator:
$person->name; // name property of a person object
$person->save(); // might be a method to save the person back to the database
Interestingly wordpress has a built in internal function called _get_cron_array() which should return the cron jobs as an array.
Currently that source code is here:
https://github.com/WordPress/WordPress/blob/056b9c47a2114a23e9a892df2d5f79856dbe5a73/wp-includes/cron.php#L924-L945
But even in their own code they are casting it to array, which seems odd given the function professes to return an array in it's name!
That one casting example:
https://github.com/WordPress/WordPress/blob/056b9c47a2114a23e9a892df2d5f79856dbe5a73/wp-includes/cron.php#L95
Anyway this was fun to explore :D
This is infact known as type-juggling, or type-casting.
In some cases (such as int to float, int to string, string to an array, int to an array) the type will be converted (allowing a regular string or int to be looped though as in the example above).
However, some types cannot be effectively converted to another, such as an array to a string, int, or class object for some examples and PHP will issue a notice such as:
Notice: Array to string conversion in /path/file.php on line 10
and the array will be converted to a string with the contents "Array". However your PHP will not throw an error so the script will continue and not work as you probably expected.

Why does "scalar" appear like this when I cast json_decode() (in non-associative mode) to an object?

While doing some code refactoring I momentarily ended up in a situation where I was basically doing the (somewhat abstracted-out) equivalent of
$data = (object)json_decode('"test"');
Of course I understand json_decode() generates objects on its own unless assoc is false. (Incidentally I got into this situation because I was in the middle of moving some format processing code around, and I hadn't yet realized one of my (object) casts was now redundant.)
But... when this happened, PHP decided that $data contained:
stdClass Object
(
[scalar] => test
)
Wat.
scalar?!
Last I learned, "test" is a string, so it seems more than one pile of things has fallen over internally here. Or is this unintuitive yet intended design?!
I have of course removed the (object) and things work exactly how I intended now. So there's no bug here per se. I just wanted to understand what just happened.
Here you go, in case you want to join in the headscratching:
php -r 'print_r((object)json_decode("\"test\""));'
I'm using 7.0.25.
This is exactly what the manual specifies will happen when casting a scalar type (i.e. int, string, float, boolean) to an object.
If an object is converted to an object, it is not modified. 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. An array converts to an object with properties named by keys and corresponding values. Note that in this case before PHP 7.2.0 numeric keys have been inaccessible unless iterated.
For any other value, a member variable named scalar will contain the value.
$obj = (object) 'ciao';
echo $obj->scalar; // outputs 'ciao'

PHP integer-like strings

when i am using string to access array index, editor show warning.
As of PHP 5.4 string offsets have to either be integers or
integer-like strings, otherwise a warning will be thrown.
can any body please explain the "integer-like strings" ? or reason for showing warning.
Additionally if same is use for $_POST it didn't show any warning.
Below snippet gives warning.
$POST = $_POST;
$POST['username']
While below didn't
$_POST['username']
I would call integer-like strings everything php can convert to an integer, basically everything.
Which editor are you using? This warning is nonsense, php has the ability to use integers or strings as array index, thus you basically can dump everthing into the index of the array (Probably not an object).
$ar = array();
$ar['a string'] = 'Content';
$ar['11string'] = 'More Content';
$ar['1'] = 'More content';
$ar[1] = 'Even more';
All of these things are allowed.
EDIT:
As ShiraNai7 pointed out, the error is about accessing strings via the array modifier, in this case you are only allowed to use integers (or as said integer-like strings). This is primary the case, because you are accessing the n-th character of an string.
Still, there is no reason to show this warning at this point. Maybe you have some more complex code at this point, override $POST to a string?

Php array offset passes 'isset', even though it's not set

The easiest way for me to explain this is to show an example ... Here's a replication of the problem code:
<?php
$test=array();
$test['one']='hello';
if(isset($test['one']['two'][0])) {
echo 'Apparently it is set ...';
echo $test['one']['two'][0];
}
?>
This returns as:
Apparently it is set ...
Warning: Illegal string offset 'two' in C:\test.php on line 6
h
Is this because there are mixed key types? It's just a little anomaly I came across and was wondering if someone could shed some light on it ...
The reason is that, when you dereference a string, it will return a string comprising a single character (assuming the index doesn't exceed the length); the resulting string can be dereferenced again (starting from 5.4 onwards).
For example - link:
$s = 'hello';
$s[0]; // "h"
$s[0][0]; // "h"
// etc. etc.
Illegal indices such as 'two' will cause a notice but it's treated as index 0, except when used inside isset().
Another example:
$s[0][1]; // ""
$s[0][1][0]; // notice: uninitialised string offset: 0
If you don't know beforehand whether a string or array is passed and this is important to you, additional type checks need to take place in between each path.
You should check your all your array keys exist before you try and use them, i.e. all the way up the chain. isset() accepts multiple parameters so you don't need to keep rewriting it and can keep DRY principles going a little more:
$test = array();
$test['one'] = 'hello';
if (isset($test['one'], $test['one']['two'], $test['one']['two'][0])) {
echo 'Apparently it is set ...';
echo $test['one']['two'][0];
}
isset returns odd and unexpected results when you pass it a string instead of an array.
It is good practice to pair an an is_array check with an isset check.

php casting variable to array

I am dealing with very strange issue of dealing with garbage in iterating through variable which has been cast to an array
$arr = (array)$var; // problem
$arr = array($var); // ok
The first method seems to work fine on values with integers, but not with strings. Is there any documented difference and does php have real casting ?
The problem is with lavarel 4, Database sources, function on line 704
If $var is a scalar, it's documented that both lines do the same:
For any of the types: integer, float, string, boolean and resource, converting a value to an array results in an array with a single element with index zero and the value of the scalar which was converted. In other words, (array)$scalarValue is exactly the same as array($scalarValue).
http://www.php.net/manual/en/language.types.array.php#language.types.array.casting
There are two ways to cast a variable in PHP as a specific type.
using the settype() function
using (int) (bool) (float) etc
More Info : http://www.electrictoolbox.com/type-casting-php/

Categories