PHP 5.2 permitted object syntax to call array index? - php

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.

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!

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?

PHP ignoring returned reference from function considered harmful?

Derick Rethans has an old article that says:
Please do note that it is harmful not to accept a reference from a
function that returns a reference. In some cases, PHP will get
confused and cause memory corruptions which are very hard to find and
debug. It is also not a good idea to return a static value as
reference, as the PHP engine has problems with that too. In PHP 4.3,
both cases can lead to very hard to reproduce bugs and crashes of PHP
and the web server. In PHP 5, this works all a little bit better. Here
you can expect a warning and it will behave “properly”.
Does it mean that in PHP 5 we are allowed to ignore the returned reference from a function?
By that, I mean this:
function &GetRef(&$array){
$item =& $array[0];
return $item;
}
$array = array(0, 1, 2);
$item =& GetRef($array); /* Normal usage of the function using assign by reference
also known as "accepting" the reference. */
$item = GetRef($array); /* Notice that here we didn't assign by reference.
Are we allowed to ignore the returned reference
and simply do normal assignment? */
The PHP Manual states:
Unlike parameter passing, here [return by reference] you have to use &
in both places - to indicate that you want to return by reference, not
a copy, and to indicate that reference binding, rather than usual
assignment, should be done for $myValue.
It doesn't explicitly say that we must accept the returned reference.
Does it mean that we are free to ignore returned references?
As discussed in the comments, you should generally ignore at least that section in the linked article, if not the entire thing.
The article talks about references in the context of PHP 4.3, released in December, 2002 and EOL'd at the end of 2007. PHP 4 should never be used today. As a general rule, when it comes to learning about working with PHP, you should not trust any article that targets PHP versions older than 5.2 (as of mid-2013).
PHP 5.0 features Zend Engine 2, a new virtual machine on which PHP runs. This is where references are implemented. 5.1 introduces some backwards-incompatible changes with regard to manipulation of return values. 5.3 introduces real garbage collection and deprecates both call-time pass-by-reference and assigning new by reference. These important changes are not addressed by that prehistoric article.
Does it mean that in PHP 5 we are allowed to ignore the returned reference from a function?
Yes. Modern PHP versions have no problem with discarding the return value of any function, reference or not. If you encounter behavior that seems to contradict this expectation, create a reduced test case and file a bug with the PHP maintainers.
Also, think twice before using references in your code. Passing around references will not save time, will not save memory and will not increase performance except in rare cases. Use them sparingly to keep complexity under control.

Terminology question on "dereferencing"?

In PHP, the following code is valid
$a=array(0);$a[0];
but that one is invalid:
array(0)[0]
What is the terminology corresponding to that behaviour? (has it anything to do with "dereferencing"?)
What is the motivation behind such a behaviour (besides user spite :-P)
I am looking for the general terminology, not necessarily the terminology associated with PHP.
(Other example: in MATLAB, the following is valid:
s = size(M)
s(0)
but that is invalid:
size(M)(0)
In both PHP and MATLAB, adding parenthesis does not help, i.e., (array(0))[0] and (size(M))(0) are both invalid)
That's called Array dereferencing, and will become available in PHP 5.4 (which is currently in alpha)
Note (thanks Gordon) : what you are asking for, with array()1, is not possible even in PHP 5.4 -- but it'll work for functions.
A couple of sources :
RFC - Function Array Dereferencing
Features in PHP trunk: Array dereferencing, when it was unsure whether there would be a PHP 5.4 or a PHP 6
And, last but not least, the (currently last) news on php.net : PHP 5.4 alpha1 released
Quoting that last news :
Here is an incomplete list of changes: - Added: Traits language
construct - Added: Array dereferencing support - Added:
DTrace support - Improved: Improved Zend Engine memory usage and
performance - Moved: ext/sqlite moved to pecl (sqlite3 support is
still built-in)
1.array() is not a function, even if it looks like one -- it's actually what PHP calls a language construct ; and those don't behave like functions.
This is called "array dereferencing" and it will be available for use in PHP5.4.

usort(): Array was modified by the user comparison function

I have a web application that runs fine on our Linux servers but when running on Mac OS with the Zend Community Edition Server using PHP 5.3 we get the error:
usort(): Array was modified by the user comparison function
every time a page loads for the first time (it takes about 2 minutes for a page to tick over and load, on the linux servers the page loads in 1 second).
Has anyone else experienced this or has any idea how I can fix the problem, I have tried playing around with PHP and Apache memory settings with no luck.
There is a PHP bug that can cause this warning, even if you don't change the array.
Short version, if any PHP debug functions examine the sort array, they will change the reference count and trick usort() into thinking you've changed the data.
So you will get that warning by doing any of the following in your sort function (or any code called from it):
calling var_dump or print_r on any of the sort data
calling debug_backtrace()
throwing an exception -- any exception -- or even just creating an exception
The bug affects all PHP 5 versions >= 5.2.11 but does not affect PHP >= 7. See the bug report for further details.
As far as I can see, the only workaround is either "don't do that" (which is kind of hard for exceptions), or use the error suppression operator #usort() to ignore all errors.
To resolve this issue we can handle as below
1) use error_reporting
$a = array('id' => 2,'val' => 3, 'ind' => 3);
$errorReporting = error_reporting(0);
usort($a);
error_reporting($errorReporting);
2) use #usort($a);
$a = array('id' => 2,'val' => 3, 'ind' => 3);
#usort($a);
What version of PHP is on the linux box?
Are the error_reporting levels the same on both boxes? Try setting them both to E_ALL.
The warning is almost certainly not lying. It's saying that the comparison function you're passing to usort() is changing the array that you're trying to sort - that could definitely make usort take a long time, possibly forever!
My first step would be to study the comparison function, and figure out why that's happening. It's possible that if the linux boxes are using a pre-5.3 version, there is some difference in the behavior of some language function used in the comparison function.
I found that using PHP5.4 , logging with error_log($message, $message_type, $destination, $extra_headers) causes this error , when I clean log entries my problem solved. Logging may temporarily be suspended by disabling and restoring logging after sort function.

Categories