PHP 5.4's simplified string offset reading - php

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.

Related

Deprecated: strpos(): Non-string needles will be interpreted as strings in the future. Use an explicit chr() call to preserve the current behavior

I have a problem with an old plugin installed on my Wordpress site. Since I updated my php to php 7.4, I am getting the message saying: "Deprecated: strpos(): Non-string needles will be interpreted as strings in the future. Use an explicit chr() call to preserve the current behavior". The error is detected on line 454 of UpdateChecker.php file, which is the following:
$cachedResult = (strpos($pluginPath, $muPluginDir) === 0);
Could anyone please help me to solve the issue? I would really appreciate!
Thanks in advance
Cheers
As you mention the plugin has not been updated you'll need to update it yourself - the line you reference would need to be something like:
$cachedResult = (strpos($pluginPath, (string)$muPluginDir) === 0);
You may also need to search the plugin for any other instances, or at least keep an eye on your error logs for any other updates required.
If the plugin you're using is open source you may wish to submit the fix once you've tested to the package maintainers (if indeed they're still maintaining it at all).

Is array error possibly a PHP version issue?

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.

Why `mysqli_query()` returns null? How can i figure it out?

Under certain circumstances, the built-in function in PHP called mysqli_query returns null. Such behaviour is not foreseen by the function's documentation that explains how to use it, so I tried to dive in PHP's source code itself, posted on GitHub, to see if I can figure out why sometimes mysqli_query returns null.
The queries themselves doesn't seem to be the problem: I tested the relevant SQL queries in two different ways:
Executing them manually in the MySQL Server. They work correctly.
Within a script that I created with the single purpose of testing the queries through mysqli_query(). They work under this test, and the function returns true.
However, under certain conditions, those same queries return null. The mysqli link object exists when mysqli_query function starts running, when this "returning null failure" happens.
So, looking in the PHP's GitHub repository, i found the file called mysqli_nonapi.c and, in line 556 within this file, what seems to be the built-in mysqli_query definition. Looking at the code within, it looks like it performs a basic check and, if it fails, it returns null. Here are the first lines linked above:
/* {{{ proto mixed mysqli_query(object link, string query [,int resultmode]) */
PHP_FUNCTION(mysqli_query){
MY_MYSQL *mysql;
zval *mysql_link;
MYSQLI_RESOURCE *mysqli_resource;
MYSQL_RES *result = NULL;
char *query = NULL;
size_t query_len;
zend_long resultmode = MYSQLI_STORE_RESULT;
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os|l", &mysql_link, mysqli_link_class_entry, &query, &query_len, &resultmode) == FAILURE) {
return;
}
// [...]
}
Even though I have used sometimes C code, I just know a little about C. I am aware that it uses pointers and I guess those parameters which name start with * are pointers. Anyways, I am not sure on how to interpretate this piece of code, or figuring out how it's internal flow affects PHP execution.
Long story short: I can assume somehow, the initial check shown above within the function failed for some reason. But, to find out why, I need to understand that piece of code first.
I am afraid I cannot isolate the issue to trigger the error outside production environment, which would be very useful for exhaustive testing. So, what options do I have left? Is there something I can do to debug that internal piece of code, or otherwise, figuring out why it might be failing within?
I made the tests in both PHP5 and PHP7; it fails the same way in both of them.
This is called undefined behaviour in PHP. Most instances of it have been eradicated in PHP 8, but prior to that version when you passed a wrong parameter to a built-in function, that function would usually throw a warning and return null (or false).
There were a lot of circumstances when no warning was produced at all! This behaviour was not documented on each page as this would be a mess and by definition "undefined behaviour" has no defined behaviour.
This means that code like the following would throw no warning and simply return null:
// V - string passed instead of a numerical value
var_dump(mysqli_query($mysqli, 'SELECT 1', 'foo'));
PHP was very forgiving with parameter types and when you passed the wrong type, it tried to convert it into the proper type, but when this was not possible then PHP had no defined behaviour of what should happen. Thankfully, this has been fixed in 99% of cases in PHP 8.0.

Array to string conversion in Php7

I am trying to execute this code (it was working on php5, now I'am on php7):
$this->links->$data[$te]['attributes']['ID'] = $data[$te]['attributes']['URL'];
But I get this error:
ContextErrorException: Notice: Array to string conversion
Thanks in advance
This is down to the change in how complex variables are resolved in PHP 5 vs 7. See the section on Changes to variable handling here: http://php.net/manual/en/migration70.incompatible.php
The difference is that the expression:
$this->links->$data[$te]['attributes']['ID']
is evaluated like this in PHP 5:
$this->links->{$data[$te]['attributes']['ID']}
and like this in PHP 7:
($this->links->$data)[$te]['attributes']['ID']
See https://3v4l.org/gB0rQ for a cut-down example.
You'll need to amend your code to be explicit, either by using {} as appropriate, or by breaking it down into two lines. In this case, where you've got code that works fine in PHP 5, pick the former, since it will mean the behaviour stays consistent in all versions of PHP.

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.

Categories