isset fatal error in 5.6 but fine in 7 - php

This snippet of code causes a PHP fatal error in 5.6, but works fine in 7.0 and above. There is no documented change to isset that I could find stating that it works with arrays as constants.
<?php
class Test
{
const A = [1];
}
echo isset(Test::A[0]);
Does anyone know of any documentation stating this was an actual change? Is it safe to use isset with arrays as constants?
PHP 5.6.30 error:
Fatal error: Cannot use isset() on the result of an expression (you can use "null !== expression" instead)

isset() is a language construct and not a function, so perhaps this paragraph (from http://php.net/manual/en/migration70.incompatible.php) applies:
PHP 7 now uses an abstract syntax tree when parsing source files. This
has permitted many improvements to the language which were previously
impossible due to limitations in the parser used in earlier versions
of PHP, but has resulted in the removal of a few special cases for
consistency reasons, which has resulted in backward compatibility
breaks.

Related

How would I handle a fatal error while setting up Calendar API on PHP

Following the Google Calendar API quickstart for PHP, I've followed the steps but, got the verification code and instead of getting the expected output " No upcoming events found " I get a
No idea how to move forward from here...
Welcome to StackOverflow!
I've encountered this very same error with numerous WP plugins that had been developed before PHP 5.6 and where programmers have been, well, lazy. Once these plugins get upgraded to PHP 7, exceptions are thrown for things that were mere deprecation warnings a few years ago.
From what I see in your logs, you have a dependency somewhere on Guzzle. According to Guzzle's own documentation, the recent versions require PHP 7.2.5 to work. Therefore, I can only assume that you are:
using a plugin or something similar that is based on an outdated version of Guzzle or you're using Guzzle yourself, but for some reason relying on a deprecated version of it (in the former case, you'd have to pester the plugin developer to update their own dependencies...);
using PHP 7.2 or perhaps even a later version.
You should be using PHP 7.4 or 8.0 as of mid-2021 (everything else has been deprecated), so your installation is fine. It's just the obsolete code coming from that outdated version of Guzzle that requires updating.
So, a simple fix for that issue is to look at the culprit — open on your favourite editor the file C:\xampp\htdocs\api\vendor\guzzlehttp\guzzle\src\Handler\CurlFactory.php and scroll down to line 67 (where the exception was thrown).
According to the trace you've posted, on that line you should have something like:
if (count($some_variable_name_here) [...]) { [...] }
What happened, in this case, was the following:
The count() function requires what PHP calls a Countable object. Generally speaking, historically, these were arrays of some sort.
Lazy programmers often use count() simply to check if the array is empty or not. In the pre-PHP7 days, count(NULL) was defined as being 0 or false, like so many other things in PHP. In other words: if the array was either empty (i.e. initialised but still without any item stored inside) or uninitialised (i.e. still assigned to NULL) or non-existent (i.e. a new variable which had not been declared before), then, by definition, count(NULL) was false.
This allowed lazy programmers to check for a handful of potentially different error conditions with a single if (!count($myArray)) { [...] do this; }. In other words, you would just execute that code inside brackets if $myArray existed, was correctly initialised, and had at least one valid element. All that with a 'simple' check!
Unfortunately, from the perspective of the interpreter and JIT compiler, as well as many other modern optimisation tools used by recent versions of PHP, this is terribly inefficient — besides being very sloppy programming. Starting with PHP 5.6, this misuse of count() became flagged as a warning, then later shown as a deprecated, and finally started throwing exceptions.
So, in your case, whatever is on line 67, count($some_variable_name_here) has been passed an empty/invalid variable/array/object, and while this would be fine before PHP 7, it now throws an exception: count() will not work with so-called 'uncountable' objects, and for all purposes, an unexisting/empty/invalid object is uncountable by definition.
To fix it, all you need to do is to check first if the object is valid and not null, and then you can check if it has any elements inside it. In other words, instead of having something like:
if (count($some_variable_name_here) [...]) {
[... do something ...]
}
change it to:
if (!empty($some_variable_name_here) && is_countable($some_variable_name_here) && count($some_variable_name_here) [...]) {
[... do something ...]
}
Note that empty() is a shortcut for !isset($var) || $var == false, and that PHP evaluates expressions from left to right, aborting as soon as a final result is known. If the variable is empty, the first check will immediately return true and the remaining checks will not even be called; by design, empty() does not throw an error or an exception and is safe to use in all scenarios where variables are in an 'unknown' state.
In many cases, you might get away just by checking if the object $some_variable_name_here is countable or not; a NULL object is not countable, so, it would also fail. In other cases — most frequent these days — you might not really know what has been passed: it might not be a valid, countable object. It might have more than one element, or zero. It might be just garbage and not properly initialised. Or any other possible combination. In those cases, being extra-careful with the checks (as opposed to being sloppy and relying upon PHP to do something useful with an otherwise useless variable/array/object) pays off.
Anyway, I've checked, and the current version of Guzzle's CurlFactory.php still has a count() for function release(), which is what is called before the exception is thrown. However, your own code is clearly different (at the time of writing, the current version has the count() on line 80, not 67), so it's very hard to say if you can correct it easily (there might be other, more subtle errors in the code calling release()...).
One assumes that Guzzle's latest version will fix these and other issues you might have — mostly because they demand PHP 7.2.5 or later, and that means that they have made changes to fix earlier things that didn't work as they should, and such changes were only possible with PHP 7.2.5.
Thus the recommendation is to upgrade your version of Guzzle. If there is any good reason to keep an outdated (and potentially bug-ridden and security-flawed) version around, well, now you know how to do a simple fix. At the very least, your code won't throw an exception; with luck, it will even work as you expect...
Happy hunting!

Why isset(null) throws an error but empty(null) not?

I just came across a strange situation. When I try the following code in $ php -a, I receive an error:
php > var_dump(isset(null));
PHP Fatal error: Cannot use isset() on the result of an expression
(you can use "null !== expression" instead) in php shell code on line
1
But when I do the same thing with empty(), everything is ok:
php > var_dump(empty(null));
bool(true)
Can anyone explain why I receive an error when I try isset(null)?
Update
Thank you all for your answers. I asked this question just to make sense of why isset() is behaving differently from empty().
To me, both of them are php functions and both accept a parameter. So, as any other function in php, calling isset(null) should be a valid statement. Aren't we passing null as a value to isset() function? So why php consider it as an expression?
Testing if an expression is "set" doesn't make sense. As per the manual, isset is used to
Determine if a variable is set and is not NULL.
If you want to check if an expression is null, use is_null, or as the error message suggests, null !== expression.
The manual for empty suggests something similar:
Determine whether a variable is considered to be empty.
until you read slightly further down, in the changelog:
5.5.0 empty() now supports expressions, rather than only variables.
Prior to this, empty(null) would have thrown an error along the lines of
Parse error: syntax error, unexpected ')', expecting :: (T_PAAMAYIM_NEKUDOTAYIM) in ... on line ...
Ok, I found something that I didn't know before in php which could be the answer to this question. According to the manual, isset() is not a function, but rather a language construct like if ... else, foreach and while:
Note: Because this [referring to isset()] is a language construct and not a function, it cannot be called using variable
functions.
There are a few more of these language constructs that can be easily confused with functions, including:
unset()
empty()
die()
include()
So now it makes sense why isset(null) doesn't work. We are trying to use a construct that expects a variable inside the parenthesis. Providing anything else other than a variable will result in syntax error during parsing of the code.

Laravel Eloquent Query php version issue (5.6 vs. 7.0)

i mentioned a strange issue regarding this topic.. I "solved" it myself, but wanted to discuss if anybody understands the problem behind this.
This query works fine with php 7.0:
$image = (ProductImage::all()->where('productHistory_id', $product->history_id))->first();
And causes a syntax error, unexpected '->' (T_OBJECT_OPERATOR).
This query (without the brackets) works fine with php 7.0 and 5.6:
$image = ProductImage::all()->where('productHistory_id', $product->history_id)->first();
whaaaat?!
Kind regards,
Nico
PHP type checking was eavily revamped between 5.x and 7.x
In both versions the expression:
ProductImage::all()->where('productHistory_id', $product->history_id)
return an instance of a QueryBuilder.
I suspect the presence of the parenthesis cause in earlyer versions of the PHP interpreter to understand it as a scalar value (as in (1+1)+1)) instead of an object value.
This explains why you get an unexpected object operator because PHP 5.X doesn't understand the return of the (...) expression as an object correctly.
This bug is caused exactly by the same parsing error as this one about array dereferencing. It was present on PHP pre 5.4 and was caused by the intereter not detecting the return of a function as an array without using a variable to store it beforehand.
Also on a side note, the parenthesis doesn't change anything as operations on objects are always executed left to right in a statement. May I recommend you to avoid adding useless noise to your codebase?

Parse error: unexpected '#' symbol in constant function in php

In my project, when I try to install a software, I got an parse error in last step of installation
The parse error is
Parse error: syntax error, unexpected '#' in
/path/to/server/subfolder1/projectfoldername/subfolder/filename.php
on line 21
The coding in that particular line of that file is
if(#constant($matches[1][0]) != #$matches[1][0]){
if(!empty(#constant(#$matches[1][0])) & !empty(#$matches[0][0]) & !empty(#$design_m_r[$key])){
$design_m_r[$key] = #str_replace($matches[0][0], constant($matches[1][0]), $design_m_r[$key]);
}
}
Our site php version is php 5.3.28. I tried to search for this error. But I dont get any solution for this. Some of the forums told about this error as "This is the advanced php version functions. So this should not support for php 5.3.28 version". But when I searched, there is no versions used this type of function.
You can't use the # error suppression operator like that.
From the PHP Docs..
The #-operator works only on expressions. A simple rule of thumb is:
if you can take the value of something, you can prepend the # operator
to it. For instance, you can prepend it to variables, function and
include calls, constants, and so forth. You cannot prepend it to
function or class definitions, or conditional structures such as if
and foreach, and so forth.
Also, passing arbitrary expressions on empty is allowed only from PHP 5.5 .

PHP 5.2 permitted object syntax to call array index?

We recently had a disaster and had to move our php web application from PHP Version 5.2.6-1+lenny16 to PHP Version 5.3.3-7+squeeze15 and found a seemingly important difference.
In our application, there were instances where we incorrectly called an array's index using object syntax:
echo $array->index;
However, 5.2.6 seemed to forgive this, and correctly treat it as if $array['index'] was written.
Upon further testing, what 5.2.6 is specifically doing is disagreeing with 5.3.3 as to whether $array->index is empty();
Here is the test code I've run on both servers:
<?php
echo phpversion() . '<br>';
$array = array(
'x' => 1,
'y' => 2
);
if (!empty($array->x))
{
echo "not empty";
}
else
{
echo "empty";
}
?>
Here are the two different outputs:
5.2.6-1+lenny16
not empty
5.3.3-7+squeeze15
empty
Naturally, there are now a few outbreaks of broken functionality because we were never alerted to these errors during development. Is there a way we can configure php 5.3 to permit this incorrect syntax while we take a bit more time to find all the incorrect instances of it?
I don't think it's a configuration issue, is it? Was something changed in the way empty() works in between versions?
I just have put your example code to a general test across PHP versions (test) and it shows that you are correct, there are differences:
From PHP 5.0.0 up to 5.2.11 (and also early 5.3.0 to 5.3.1), this "undefined property" was reported as not empty which does qualify as a flaw or bug.
The related change in 5.2.12 (17 Dec 2009) was (ref):
Fixed bug #50255 (isset() and empty() silently casts array to object). (Felipe)
Technically this is not a backwards incompatible change from PHP 5.2 to 5.3 because it was a flaw in both branches and also fixed in both. Those are harder to spot if you migrate, because the standard migration guide does not cover them. Instead you need to go through the changes of the software and look for notes and references to tickets.
So to answer your question: This is a configuration issue because the PHP version used counts as configuration. You changed the configuration and then you had the issue.
Also as the report shows, this is limited to empty() and isset(), not general object/array access. As you can imagine, if that would have been the case, you would have found much more reference about it.

Categories