Edit 2022: This appears to be fixed as of PHP 7.4 which emits a notice.
In PHP, I have error_reporting set to report everything including notices.
Why does the following not throw any notices, errors or anything else?
$myarray = null;
$myvalue = $myarray['banana'];
Troubleshooting steps:
$myarray = array();
$myvalue = $myarray['banana'];
// throws a notice, as expected ✔
$myarray = (array)null;
$myvalue = $myarray['banana'];
// throws a notice, as expected ✔
$myarray = null;
$myvalue = $myarray['banana'];
// no notice or warning thrown, $myvalue is now NULL. ✘ Why?
It's possible it's a bug in PHP, or I'm just not understanding something about how this works.
There are three types which it might be valid to use the array derefence syntax on:
Arrays
Strings (to access the character at the given position)
Object (objects implementing the ArrayAccess interface)
For all other types, PHP just returns the undefined variable.
Array dereference is handled by the FETCH_DIM_R opcode, which uses zend_fetch_dimension_address_read() to fetch the element.
As you can see, there is a special case for NULLs, and a default case, both returning the undefined variable.
Usually, when you try to use a value of one type as if it were another type, either an error or warning gets thrown or "type juggling" takes place. For example, if you try to concatenate two numbers with ., they'll both get coerced to strings and concatenated.
However, as explained on the manual page about type juggling, this isn't the case when treating a non-array like an array:
The behaviour of an automatic conversion to array is currently undefined.
In practice, the behaviour that happens when this "undefined behaviour" is triggered by dereferencing a non-array is that null gets returned, as you've observed. This doesn't just affect nulls - you'll also get null if you try to dereference a number or a resource.
There is an active bug report started at 2006.
And in documentation it is a notice about this in String section.
As of PHP 7.4, this behavior how emits a Notice.
"Trying to access array offset on value of type null"
See the first item in this 7.4 migration page.
https://www.php.net/manual/en/migration74.incompatible.php
This recently struck one of my colleagues in the butt because he neglected to validate the result of a database query before attempting to access column data from the variable.
$results = $this->dbQuery(...)
if($results['columnName'] == 1)
{
// WHEN $results is null, this Notice will be emitted.
}
And I just noticed #Glen's comment, above, citing the relevant RFC.
https://wiki.php.net/rfc/notice-for-non-valid-array-container
Related
Let's preface this with an example of a script with typo:
$bc = getBaseConcept();
$bs['key'] = doOtherStuff($bc['key']);
return $bc;
Obviously in the middle line is a typo. It should be $bc instead of $bs. (And yes this was a legit typo of mine just minutes ago before writing this question)
This did not produce a warning.
So my question is: Is there a configuration option that lets this produce a warning?
Specifically: Writing to an array key of a name that was previously undefined.
E_ALL does not seem to help. This should not only generate the warnings for $bar but I want also a warning for $foo.
<?php
ini_set('error_reporting', E_ALL);
echo ini_get('error_reporting'), "\n";
$foo['bar'] = $bar['foo'];
32767
PHP Warning: Undefined variable $bar in Standard input code on line 4
PHP Warning: Trying to access array offset on value of type null in Standard input code on line 4
Unfortunately, php is not like other programming languages. If an unknown variable is used, PHP does the initialisation without complaining. Unfortunately, there is nothing you can do about this.
If you are using an IDE like phpstorm or netbeans the IDE will usually show a hint for uninitisalized variables.
It is not necessary to initialize variables in PHP however it is a
very good practice. Uninitialized variables have a default value of
their type depending on the context in which they are used - booleans
default to false, integers and floats default to zero, strings (e.g.
used in echo) are set as an empty string and arrays become to an empty
array.
https://www.php.net/manual/en/language.variables.basics.php
here are some X-files.
Let's check this script: https://admin.laysoft.tk/test.php
We tested it on different machines with different version of PHP.
Let's see this:
$tomb = 666;
var_dump($tomb);
$a = $tomb['akarmi'];
var_dump($a);
Result of this is:
int(666)
NULL
($tomb means array)
As you see, we initialized the $tomb as an integer.
Why $a = $tomb['akarmi']; does not drop a notice, that no key like this?
UPDATE
I've reported it, I am so curious.
https://bugs.php.net/bug.php?id=74579
UPDATE2
Ok, this bug is exists from years. There are a lot of issue about this:
https://bugs.php.net/bug.php?id=37676
Because of PHP's type juggling feature, which will implicitly convert between types, depending on how you attempt to access a variable.
The conversion to arrays however is not implemented, not even defined how it should work, as the manual says:
The behaviour of an automatic conversion to array is currently undefined.
As for why it has remained that way through the years ... nobody could really answer that question.
When I try to access an array by key which is not exists in this array, php will raise "undefined index" notice error. When I try to do the same on strings, "Illegal string offset " warning is raised. This is an expected behavior and I know how to deal with it.
But when I tried this on boolean or integer values nothing happens:
ini_set('display_errors', 1);
error_reporting(E_ALL);
$var = false;
var_dump($var['test']);
I expect to see some error messages, but $var['test'] just silently sets to NULL.
So why does php permit to access boolean value through an array key without any indication that you are doing something wrong?
The hole "access boolean value through an array key" phrase sounds terribly wierd to me, but you can do it in php.
It's sad, but it's documented behaviour.
http://php.net/manual/en/language.types.string.php
Note:
Accessing variables of other types (not including arrays or objects implementing the appropriate interfaces) using [] or {} silently returns NULL.
Edit 2022: This appears to be fixed as of PHP 7.4 which emits a notice.
In PHP, I have error_reporting set to report everything including notices.
Why does the following not throw any notices, errors or anything else?
$myarray = null;
$myvalue = $myarray['banana'];
Troubleshooting steps:
$myarray = array();
$myvalue = $myarray['banana'];
// throws a notice, as expected ✔
$myarray = (array)null;
$myvalue = $myarray['banana'];
// throws a notice, as expected ✔
$myarray = null;
$myvalue = $myarray['banana'];
// no notice or warning thrown, $myvalue is now NULL. ✘ Why?
It's possible it's a bug in PHP, or I'm just not understanding something about how this works.
There are three types which it might be valid to use the array derefence syntax on:
Arrays
Strings (to access the character at the given position)
Object (objects implementing the ArrayAccess interface)
For all other types, PHP just returns the undefined variable.
Array dereference is handled by the FETCH_DIM_R opcode, which uses zend_fetch_dimension_address_read() to fetch the element.
As you can see, there is a special case for NULLs, and a default case, both returning the undefined variable.
Usually, when you try to use a value of one type as if it were another type, either an error or warning gets thrown or "type juggling" takes place. For example, if you try to concatenate two numbers with ., they'll both get coerced to strings and concatenated.
However, as explained on the manual page about type juggling, this isn't the case when treating a non-array like an array:
The behaviour of an automatic conversion to array is currently undefined.
In practice, the behaviour that happens when this "undefined behaviour" is triggered by dereferencing a non-array is that null gets returned, as you've observed. This doesn't just affect nulls - you'll also get null if you try to dereference a number or a resource.
There is an active bug report started at 2006.
And in documentation it is a notice about this in String section.
As of PHP 7.4, this behavior how emits a Notice.
"Trying to access array offset on value of type null"
See the first item in this 7.4 migration page.
https://www.php.net/manual/en/migration74.incompatible.php
This recently struck one of my colleagues in the butt because he neglected to validate the result of a database query before attempting to access column data from the variable.
$results = $this->dbQuery(...)
if($results['columnName'] == 1)
{
// WHEN $results is null, this Notice will be emitted.
}
And I just noticed #Glen's comment, above, citing the relevant RFC.
https://wiki.php.net/rfc/notice-for-non-valid-array-container
I've turned on all error reporting to clean up some undefined indexes, just to make the app I'm making more neat. I've noticed a curious behavior:
Let's say I have the following array: $a = array('test' => false, 'foo' => 'bar')
If I do if ($a['nothere']), I properly get a notice of Undefined index: nothere.
However, if I do if ($a['test']['nothere']), I don't get a notice. At all. Despite nothere definitely not being an index in $a['test'].
Now, if I do $a['test'] = array('baz' => 'poof'), then running if ($a['test']['nothere']) does throw a notice.
Does undefined index checking not check for indexes in an empty array? This is on PHP 5.2.8.
it's because of tricky type juggling.
$a['test'] being cast to [empty] string and then 'nowhere' being cast to 0 and then PHP tries to look for 0's symbol in the empty string. It becomes substring operation, not variable lookup, so, no error raised.
Gee, a man says the same: http://php.net/manual/en/language.types.type-juggling.php
A curious example from my experience:
Being requested via hyperlink index.php?sname=p_edit&page=0, a code
if (isset($_GET['sname'])) { $page['current'] = $_GET['sname'].'.php'; };
echo $page['current'];
produces just one letter "p"
I counted 6 steps and 2 type casts which ends up with such a result.
(Hint: a poor fellow who asked this question had register globals on)