We ran into an issue today that I am surprised I had not encountered before. It was related to string comparison:
echo ("9400110897799014514025" == "9400110897799014514018" ? "match" : "not");
echo ("94001108" == "94001107" ? "match" : "not");
Neither of the 2 sets matches, yet the first one reports a "match" incorrectly, while the second properly declares it "not" a match. We resolved the issue by using '===' in place of '==' but I'd really like to understand what is going on here.
Add: It's PHP 5.3
I tried running your code, but I got notnot in the first few attempts. Only after lowering the php version to 5.4.3 or below, I got matchnot.
It appears you're running on an outdated version of php, and encountered this bug. It has been solved since php5.4.4.
The best thing to do is probably to upgrade your php version (although strict comparison isn't a bad thing either way)
Edit: According to the comments on the original answer, it looks like the problem persists in later versions as well, but only on 32bit systems.
The problem itself comes from the fact that php is extremely loose with comparisons, converting strings that look like numbers to numbers before comparison. This leads to php considering "1e3" as equal to "1000", for example.
Related
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!
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?
I was working with old legacy code and checked if it's running with PHP7. I got an exception (Array to string conversion) with following code:
json_decode($json_string)
->$array['id']
->foo
What helped was to use the curly brackets:
json_decode($json_string)
->{$array['id']}
->foo
My question is, was that already wrong PHP5 and if not, which change is the reason for that? Could not find anything because did not know for what to search... One idea I had is that the new introduced AST produces this behavior?
I think you are right. From what I know in PHP5 that works without any exception, but PHP 7 wants to be just a little bit more strict, because cases like this can cause a lot big problems after when not used correctly.
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.
As many of you already know, PHP 5.4 alpha has been released. I have a question regarding the following.
Simplified string offset reading.
$str[1][0] is now a legal construct.
How exactly does $str[1][0] work?
EDIT: http://php.net/releases/NEWS_5_4_0_alpha1.txt
It just means that when reading a string offset PHP returns a string again, on which you again can access an offset. (And on that access yet another offset. It gets funny with $str[0][0][0][0][0][0])
Before PHP 5.4 you would get an "Cannot use string offset as an array" error.
This is a side effect, and was mentioned in the proposal here:
http://php.markmail.org/thread/yiujwve6zdw37tpv
The feature is speed/optimization of string offsets.
Hi,
Recently I noticed that reading of
string offset is performed in two
steps. At first special string_offset
variant of temporary_variable is
created in
zend_fetch_dimension_address_read()
and then the real string value is
created in
_get_zval_ptr_var_string_offset().
I think we can create the real string
in the first place. This makes 50%
speed-up on string offset reading
operation and allows to eliminate some
checks and conditional brunches in VM.
The patch is attached (don't forget to
regenerate zend_vm_execute.h to test
it). However it changes behavior in
one bogus case. The following code now
will emit "b" (currently it generates
a fatal error - cannot use string
offset as an array).
$str = "abs";
var_dump($str[1][0]);
I think it's not a problem at all. "b"
makes sense because "abs"[1] -> "b"
and "b"[0] -> "b".
I'm going to commit the patch in case
of no objections.
Thanks. Dmitry.
This can actually create some interesting bugs when you upgrade code from php 5.3 to 5.4.
In 5.3 this construct would return false:
$array = array("This is a string");
echo isset($array[0][0][0]);
In 5.4 this would return true.