Solved: PHP7.0 + MySQLi incompatibility August 18, 2018

View all articles from Gyroscope Development Blog

If you are running PHP 7.0 and the MySQLi connector for Gyroscope, you will get mysterious error messages on Gyroscope 15.0. This is due to PHP7.0's handling of variable parameters in conjunction with the MySQLi's parameter binding requirements.

A special flag is added in sysinfo.php to diagnose this incompatibility:

Sys Info compatibility check

The root cause is documented in the PHP 7.0 and 7.1 incompatibilities.

When the MySQLi connector is used in Gyroscope, ReflectionFunction::invokeArgs is called with a parameter array. Parameters are spread onto the argument list of mysqli_stmt_bind_param. The bind function requires the binding parameters to be passed by reference.

Considering the following code:

$query="select * from cars where carid=?";

...

mysqli_stmt_bind_param($stmt,'i',123);

The above code will generate the fatal error:

Only variables can be passed by reference

However, this doesn't mean that a variable has to be passed by reference. It just has to be a variable, or in parser terms, a left-hand-side, or LHS.

$id=123;

mysql_stmt_bind_param($stmt,'i',$id);

The sql_prep function in the MySQLi wrapper maps an array of parameters into a list of arguments. Internally, the following happens:

...

$cparams=array($stmt,'i',$id);

$func=new ReflectionFunction('mysqli_stmt_bind_param');

$func->invokeArgs($cparams);

The above call will generate the following warnings:

Parameter 3 to mysqli_stmt_bind_param() expected to be a reference...

Parameter 4 to mysqli_stmt_bind_param() expected to be a reference...

It's important to recognize that these are warnings emitted by invokeArgs due to the requirements of the bind_param function. According to the PHP 7.0 to 7.1 migration documentation, in 7.0, call_user_func_array() aborts the function call if arguments that are supposed to be passed by reference are actually passed by value.

It is reasonable to suspect that the error handling of call_user_func_array and ReflectionFunction::invokeArgs share a common routine. Therefore, passing by value when calling invokeArgs will also abort the call by throwing an exception that leads to a fatal error if not handled.

If you are running a different connector, such as sqlsrv for Microsoft SQL Server, PHP 7.0 would not be an issue.

The Fix:

If you are running PHP 5.6 and above, in sql.php, sql_prep function, locate the following lines:

$cparams=array($stmt,$typestr);

foreach ($params as $param) ...

$func=new ReflectionFunction('mysqli_stmt_bind_param');

$func->invokeArgs($cparams);

Comment out the above lines and uncomment the following:

mysqli_stmt_bind_param($stmt,$typestr,...$params);

This change is also recommended for other PHP 7 versions. The out-of-box Gyroscope uses the Reflection method for maximum compatibility. After all, PHP 7.0 is the only unsupported version. However, you should switch to the array unpacking operator (...) that's supported starting PHP 5.6 if you can.

Our Services

Targeted Crawlers

Crawlers for content extraction, restoration and competitive intelligence gathering.

Learn More

Gyroscope™ ERP Solutions

Fully integrated enterprise solutions for rapid and steady growth.

Learn More

E-Commerce

Self-updating websites with product catalog and payment processing.

Learn More