Return type of explode function has changed in PHP 8.0.
With PhpStorm 2022.1.3 + Php Inspections (EA Extended) 4.0.7.1 inspect code (PHP 7.2 level), I get many:
[EA] '$xyz' may not support offset operations (or its type not annotated properly: [bool, array]).
In PhpStorm configuration, is there a way to override default explode function?
I tried to add .phpstorm.meta.php/explode.php, but this is not working. I think it's wrong.
<?php
namespace PHPSTORM_META {
override( explode,
function explode(string $separator, string $string, int $limit): array {
return [];
}
);
}
I ask that because, I get the previous "error" with the following code with PHP 7.2-7.4, and not with PHP 8.0 (for $gps[0] and $gps[1]):
$gps = explode(',', $result['GPS']);
if (count($gps) == 2) {
$items['lat'] = $gps[0];
$items['lng'] = $gps[1];
}
Related
We are upgrading PHP to version 8.1. Using MS Sql Server DB. It all seems to work correctly but I see repeated messages in the log file:
[03-Feb-2022 11:51:18 America/New_York] PHP Deprecated: Automatic conversion of false to array is deprecated in C:...\includes\adodb\drivers\adodb-mssqlnative.inc.php on line 154
I've updated adodb to version version 5.22 but that did not stop the messges from logging. The ini file has
extension=php_sqlsrv_81_nts_x64.dll
extension=php_pdo_sqlsrv_81_nts_x64.dll
Does anyone know how to fix this problem?
This is a known issue of backwards incompatibility, affecting the ADOdb library version 5.22.1 and older. PHP 8.1 warns you on autovivification of false-y values and some future version of PHP will throw an error when you do it.
PHP natively allows for autovivification (auto-creation of arrays from falsey values). This feature is very useful and used in a lot of PHP projects, especially if the variable is undefined. However, there is a little oddity that allows creating an array from a false and null value.
And they give this example
// From false
$arr = false;
$arr[] = 2;
I went and found the file in question and this is the function it's in
function ServerInfo() {
global $ADODB_FETCH_MODE;
static $arr = false;
if (is_array($arr))
return $arr;
if ($this->fetchMode === false) {
$savem = $ADODB_FETCH_MODE;
$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
} elseif ($this->fetchMode >=0 && $this->fetchMode <=2) {
$savem = $this->fetchMode;
} else
$savem = $this->SetFetchMode(ADODB_FETCH_NUM);
$arrServerInfo = sqlsrv_server_info($this->_connectionID);
$ADODB_FETCH_MODE = $savem;
$arr['description'] = $arrServerInfo['SQLServerName'].' connected to '.$arrServerInfo['CurrentDatabase'];
$arr['version'] = $arrServerInfo['SQLServerVersion'];//ADOConnection::_findvers($arr['description']);
return $arr;
}
The problem is that it starts with
static $arr = false;
and then tries to autovivificate a non-array (line 154 in your error)
$arr['description'] = $arrServerInfo['SQLServerName'].' connected to '.$arrServerInfo['CurrentDatabase'];
You should be able to fix it (in theory) by making sure it's an array (something they should have done anyways). Add this above that line to make it one before it tries to append
if(!is_array($arr)) $arr = [];
I need to return few values from rust function. Tried to declare function which returns an array
$ffi = FFI::cdef('float get_arr()[2];', './target/release/libphp_rust.dylib');
$array = $ffi->get_arr();
But got an error:
PHP Fatal error: Uncaught FFI\ParserException: function returning array is not allowed at line 1 in /array.php:3
It seems PHP FFI can't work with arrays directly. So I found another solution.
I created C-array from PHP, then passed pointer to it to Rust code and then populated it with Rust function:
$ffi = FFI::cdef('bool get_arr(float (*res)[2]);', './target/release/libphp_rust.dylib');
$array = $ffi->new('float[2]');
$result = $ffi->get_arr(FFI::addr($array));
if ($result) {
var_dump($array);
} else {
//... something went wrong
}
#[no_mangle]
pub extern fn get_arr(array_pointer: *mut [f32;2]) -> bool {
let res = unsafe {
assert!(!array_pointer.is_null());
&mut *array_pointer
};
res[0] = 0.1;
res[1] = 0.2;
return true;
}
This solutions seems to work correct but i have some doubts about it:
Is passing pointers to FFI safe enough and what problems may I face with this in future?
Are Rust arrays fully C-compatible so that I'm able to assign value to it directly by index?
I there better way to achieve what I need? Or maybe are there some good practices about passing complex data structures with FFI?
Thanks
The rules surrounding this are still up in the air, so your example is questionably safe. This should be ok, but requires nightly features:
#![feature(maybe_uninit_extra)]
#![feature(ptr_as_uninit)]
// Make sure you use `extern "C"`. `extern` alone means `extern "Rust"`.
#[no_mangle]
pub extern "C" fn get_arr(array_pointer: *mut [f32; 2]) -> bool {
let fat: *mut [f32] = array_pointer;
let res = unsafe { fat.as_uninit_slice_mut().unwrap() };
res[0].write(0.1);
res[1].write(0.2);
true
}
On the stable channel it's just less elegant:
// Make sure you use `extern "C"`. `extern` alone means `extern "Rust"`.
#[no_mangle]
pub extern "C" fn get_arr(array_pointer: *mut [f32; 2]) -> bool {
assert!(!array_pointer.is_null());
unsafe {
let res = array_pointer as *mut f32;
res.add(0).write(0.1);
res.add(1).write(0.2);
}
true
}
I have a bit of PHP code that works fine on my production server but not on my test server. Here is the code:
function callProcedure0(&$connection, $procname, $dofunction)
{
mysqli_multi_query($connection, "CALL " . $procname . "();");
$first_result = 1;
do
{
$query_result = mysqli_store_result($connection);
if ($first_result)
{
$dofunction($query_result);
$first_result = 0;
}
if ($query_result)
{
mysqli_free_result($query_result);
}
$query_result = NULL;
} while(mysqli_next_result($connection));
}
...
function doGenres($in_result)
{
global $genre_array, $game_array, $genre_order_array;
$genre_count = 1;
// foreach is necessary when retrieving values since gaps may appear in the primary key
while ($genre_row = mysqli_fetch_row($in_result)) // line 81 is here!
{
$genre_array[] = $genre_row[0];
$genre_order_array[$genre_row[1] - 1] = $genre_count;
$game_array[] = [[],[]];
$genre_count += 1;
}
}
...
callProcedure0($con, "get_genres_front", doGenres); // line 138 is here!
The "get_genres_front" bit refers to a stored procedure on my database. Here are the errors:
Notice: Use of undefined constant doGenres - assumed 'doGenres' in /opt/lampp/htdocs/keyboard/keyboard.php on line 138
Again, there are no problems on the production server which is using Apache 2.2.23, MySQL 5.1.73-cll, PHP 5.4.26. The test server where things are broken is running Apache 2.4.10, MySQL 5.6.21, PHP 5.5.19.
Did something change in recent software versions? Thanks.
[edit]
This is not a duplicate question. I'm worried about the first error. I already know what to do about the second error which I have deleted.
The code you have posted is wrong, you must pass function name as string and then use call_user_func to invoke this function.
In your callProcedure0 function change
$dofunction($query_result);
to
call_user_func($dofunction, $query_result);
And then call it with the function name as string like this
callProcedure0($con, "get_genres_front", "doGenres");
The above code could work also with invoking the function with
$dofunction($query_result);
on some php versions, but the line where you pass the function name it should be string, otherwise PHP assumes it is a constant.
The following code works in a computer with PHP 5.3 and it doesn't in PHP 5.4:
function __clone() {
$this->changed = TRUE;
foreach ($this->conditions as $key => $condition) {
if (
$condition['field']
instanceOf QueryConditionInterface) {
$this->conditions[$key]['field'] = clone($condition['field']);
}
}
}
$condition in both cases doesn't have the 'field' offset, but in PHP 5.3 the library continues working without complain, however in PHP 5.4 gives this warning message:
Warning: Illegal string offset 'field' in DatabaseCondition->__clone()
And short after, the library (from Drupal6), stops working.
Any idea how to fix this?
Should I use an isset($condition['field']) even if it's a core library of the framework?
Is there any available solution for (re-)generating PHP code from the Parser Tokens returned by token_get_all? Other solutions for generating PHP code are welcome as well, preferably with the associated lexer/parser (if any).
From my comment:
Does anyone see a potential problem,
if I simply write a large switch
statement to convert tokens back to
their string representations (i.e.
T_DO to 'do'), map that over the
tokens, join with spaces, and look for
some sort of PHP code pretty-printing
solution?
After some looking, I found a PHP homemade solution in this question, that actually uses the PHP Tokenizer interface, as well as some PHP code formatting tools which are more configurable (but would require the solution as described above).
These could be used to quickly realize a solution. I'll post back here when I find some time to cook this up.
Solution with PHP_Beautifier
This is the quick solution I cooked up, I'll leave it here as part of the question. Note that it requires you to break open the PHP_Beautifier class, by changing everything (probably not everything, but this is easier) that is private to protected, to allow you to actually use the internal workings of PHP_Beautifier (otherwise it was impossible to reuse the functionality of PHP_Beautifier without reimplementing half their code).
An example usage of the class would be:
file: main.php
<?php
// read some PHP code (the file itself will do)
$phpCode = file_get_contents(__FILE__);
// create a new instance of PHP2PHP
$php2php = new PHP2PHP();
// tokenize the code (forwards to token_get_all)
$phpCode = $php2php->php2token($phpCode);
// print the tokens, in some way
echo join(' ', array_map(function($token) {
return (is_array($token))
? ($token[0] === T_WHITESPACE)
? ($token[1] === "\n")
? "\n"
: ''
: token_name($token[0])
: $token;
}, $phpCode));
// transform the tokens back into legible PHP code
$phpCode = $php2php->token2php($phpCode);
?>
As PHP2PHP extends PHP_Beautifier, it allows for the same fine-tuning under the same API that PHP_Beautifier uses. The class itself is:
file: PHP2PHP.php
class PHP2PHP extends PHP_Beautifier {
function php2token($phpCode) {
return token_get_all($phpCode);
}
function token2php(array $phpToken) {
// prepare properties
$this->resetProperties();
$this->aTokens = $phpToken;
$iTotal = count($this->aTokens);
$iPrevAssoc = false;
// send a signal to the filter, announcing the init of the processing of a file
foreach($this->aFilters as $oFilter)
$oFilter->preProcess();
for ($this->iCount = 0;
$this->iCount < $iTotal;
$this->iCount++) {
$aCurrentToken = $this->aTokens[$this->iCount];
if (is_string($aCurrentToken))
$aCurrentToken = array(
0 => $aCurrentToken,
1 => $aCurrentToken
);
// ArrayNested->off();
$sTextLog = PHP_Beautifier_Common::wsToString($aCurrentToken[1]);
// ArrayNested->on();
$sTokenName = (is_numeric($aCurrentToken[0])) ? token_name($aCurrentToken[0]) : '';
$this->oLog->log("Token:" . $sTokenName . "[" . $sTextLog . "]", PEAR_LOG_DEBUG);
$this->controlToken($aCurrentToken);
$iFirstOut = count($this->aOut); //5
$bError = false;
$this->aCurrentToken = $aCurrentToken;
if ($this->bBeautify) {
foreach($this->aFilters as $oFilter) {
$bError = true;
if ($oFilter->handleToken($this->aCurrentToken) !== FALSE) {
$this->oLog->log('Filter:' . $oFilter->getName() , PEAR_LOG_DEBUG);
$bError = false;
break;
}
}
} else {
$this->add($aCurrentToken[1]);
}
$this->controlTokenPost($aCurrentToken);
$iLastOut = count($this->aOut);
// set the assoc
if (($iLastOut-$iFirstOut) > 0) {
$this->aAssocs[$this->iCount] = array(
'offset' => $iFirstOut
);
if ($iPrevAssoc !== FALSE)
$this->aAssocs[$iPrevAssoc]['length'] = $iFirstOut-$this->aAssocs[$iPrevAssoc]['offset'];
$iPrevAssoc = $this->iCount;
}
if ($bError)
throw new Exception("Can'process token: " . var_dump($aCurrentToken));
} // ~for
// generate the last assoc
if (count($this->aOut) == 0)
throw new Exception("Nothing on output!");
$this->aAssocs[$iPrevAssoc]['length'] = (count($this->aOut) -1) - $this->aAssocs[$iPrevAssoc]['offset'];
// post-processing
foreach($this->aFilters as $oFilter)
$oFilter->postProcess();
return $this->get();
}
}
?>
In the category of "other solutions", you could try PHP Parser.
The parser turns PHP source code into an abstract syntax tree....Additionally, you can convert a syntax tree back to PHP code.
If I'm not mistaken http://pear.php.net/package/PHP_Beautifier uses token_get_all() and then rewrites the stream. It uses heaps of methods like t_else and t_close_brace to output each token. Maybe you can hijack this for simplicity.
See our PHP Front End. It is a full PHP parser, automatically building ASTs, and a matching prettyprinter that regenerates compilable PHP code complete with the original commments. (EDIT 12/2011:
See this SO answer for more details on what it takes to prettyprint from ASTs, which are just an organized version of the tokens: https://stackoverflow.com/a/5834775/120163)
The front end is built on top of our DMS Software Reengineering Toolkit, enabling the analysis and transformation of PHP ASTs (and then via the prettyprinter code).