Debugging legacy code and I have a strange issue. The legacy code is being moved to PHP 7.2. I don't know which version of PHP it was originally written for but it does work in PHP 5.6.
Below is my example of the problem...
$variable = '';
$variable['key'] = 'Hello World!';
echo $variable['key'] // H
When I echo $variable['key'] it only gets the first character from the value. I know now that it is because $variable is initially declared as a string.
But why does this work in PHP 5.6? What can I do to make this work in 7.2 without trawling through thousands of lines of code?
Is there a directive like strict_types I can use?
From php.net
Warning
Writing to an out of range offset pads the string with spaces. Non-integer types are converted to integer. Illegal offset type emits E_NOTICE. Only the first character of an assigned string is used. As of PHP 7.1.0, assigning an empty string throws a fatal error. Formerly, it assigned a NULL byte.
http://php.net/manual/en/language.types.string.php#language.types.string.substr
So "key" is converted to 0, and the first character is set.
Because this is a char type, only "H" is set from the given string.
$variable = '';
$variable['key'] = 'Hello World!';
echo $variable;
echo $variable['key'];
If you change your code to the above you can see better what happens.
So the text 'ello World!' is lost and gone in PHP >= 7.1 because you set the first character, the type stays string.
In php 5.6 you will get
Notice: Array to string conversion in /in/N2poP on line 6
So in prior versions you overwrite the complete variable, and the initial empty string would be gone, PHP simply creates a new array. This behavior only happens with an empty string!
This is also noted in the documentation:
http://php.net/manual/en/language.types.string.php#language.types.string.substr
Note: As of PHP 7.1.0, applying the empty index operator on an empty
string throws a fatal error. Formerly, the empty string was silently
converted to an array.
The easiest solution would be removing the $variable = ''; part, it's invalid anyway and never used in your legacy code. or by replacing it with $variable = [];
Because this behavior only happens with an empty string in php < 7.1 you could use a regular expression to find all places where you should refactor to fix the issue.
Related
I work on a website that, since last Oct, has had the following lines of code that work just fine:
if(empty($post_types))
{
$post_types[] = 'post';
$post_types[] = 'product-list';
}
I had not seen this construct in PHP before, (and since I started my programming work in C, it's a little irritating), but it works.
We started a second site with the same technology and basic setup that threw the following error at the same lines of code,
"Uncaught Error: [] operator not supported for strings ..."
The sites are hosted at the same place, but I noticed that they are using different 7.x versions of PHP. I did a bit of research to see if the behavior is due to a change in PHP 7.3, but I didn't find an answer.
Please note that my question is on whether this situation is possibly a PHP version issue, not how to solve the array problem, which I changed with
$post_types = array('post', 'product-list');
I found your answer in the PHP docs Creating/modifying with square bracket syntax:
$arr[key] = value;
$arr[] = value;
// key may be an integer or string
// value may be any value of any type
If $arr doesn't exist yet, it will be created, so this is also an
alternative way to create an array. This practice is however
discouraged because if $arr already contains some value (e.g. string
from request variable) then this value will stay in the place and []
may actually stand for string access operator. It is always better to
initialize a variable by a direct assignment.
Note: As of PHP 7.1.0, applying the empty index operator on a string throws a fatal error. Formerly, the string was silently converted to
an array.
So it appears there is a change in PHP 7.1.0 matching with the problem you described.
In your code, your $post_type variable must be initialized as a string [EDIT : I could only reproduce the problem with an empty string], and was previously ( PHP < 7.1.0) silently converted to an array.
This gives the classical warning (that in next version will be an error):
$var[$otherVar[someIndex]] = $myValue;
This does not:
$var["$otherVar[someIndex]"] = $myValue;
Both works the same.
Shouldn't PHP 7.2 warn in both cases?
In case it does not, what is the justification to allow the second one, with the new rules PHP new versions tries to impose?
String interpolation doesn't require that the key be quoted, because the key is already a string. It won't be interpreted as a constant (which is the point of the message you get from your first example), and there's no chance that you meant to get $otherVar[someIndex()], because that wouldn't even work as intended in this context. So the problems with barewords simply don't apply.
You will get a message if you tried to say $var["{$otherVar[someIndex]}"]. Variables interpolated that way work more like you expect, and someIndex will again be interpreted as a constant.
I'm using PHP 7.0.2
Consider below text from the PHP Manual :
Why is $foo[bar] wrong?
Always use quotes around a string literal array index. For example,
$foo['bar'] is correct, while $foo[bar] is not. But why? It is common
to encounter this kind of syntax in old scripts:
<?php
$foo[bar] = 'enemy';
echo $foo[bar];
// etc
?>
This is wrong, but it works. The reason is that this code has an
undefined constant (bar) rather than a string ('bar' - notice the
quotes). It works because PHP automatically converts a bare string (an
unquoted string which does not correspond to any known symbol) into a
string which contains the bare string. For instance, if there is no
defined constant named bar, then PHP will substitute in the string
'bar' and use that.
From the above text I'm not clear that when PHP encountered bar() which does not correspond to any known symbol i.e. undefined constant what PHP actually does with it?
How can PHP convert a bare string bar into a string which contains the bare string i.e. 'bar'?
Is PHP defining a constant titled bar and assigning a string value 'bar' to it?
Like bar = 'bar';
If yes, can I make use of the constant bar somewhere in the further code?
Because in PHP only a variable and a constant can contain value/hold the value and not type like string contain/hold any value.
From the above text I'm not clear that when PHP encountered bar() which does not correspond to any known symbol i.e. undefined constant what PHP actually does with it?
The warning associated with this behavior says it all, really:
Warning: Use of undefined constant bar - assumed 'bar' (this will throw an Error in a future version of PHP)
The bare word is treated as a string, not as a constant -- it's treated exactly as if you'd written 'bar'. The expression defined('bar') will still be false, other instances of bar in your code will also throw warnings, and code which depends on this behavior will stop working entirely in future versions of PHP.
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
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