Mise à jour de la base de code existante vers PHP 8.1 : gestion des paramètres de fonction internes non nullables avec des valeurs nulles
P粉344355715
P粉344355715 2023-10-31 20:01:48
0
2
774

Je viens de commencer à mettre à jour mon code pour qu'il soit compatible avec php 8.1. J'ai beaucoup d'extraits de code dans lesquels je transmets des valeurs nulles potentielles aux fonctions internes.

if (strlen($row) > 0) {
   ...
}

où $row provient d'une source (telle qu'une requête) qui peut avoir des valeurs nulles. Cela peut générer un avertissement de dépréciation ; dans ce cas :

DEPRECATED : strlen() : passer null au paramètre n°1 de type chaîne est obsolète ($string)

Je recherche le moyen le plus simple et le plus rapide de gérer la mise à niveau de ce code, par exemple en fixant l'endroit où la recherche et le remplacement globaux peuvent être effectués. Il semble transtyper la variable que je passe à la fonction interne sans modifier la fonctionnalité.

error_reporting(E_ALL);
$row = null;

if (strlen((string) $row) > 0) {
   ...
}

Y a-t-il des problèmes avec cette approche de fonctionnalité interne, mis à part les aspects moraux de son codage de cette façon ? Existe-t-il un meilleur moyen (autre que de réécrire complètement le code et de gérer différemment les valeurs nulles) ? Je préfère que cette solution soit rétrocompatible avec la v7.4, même si je serai probablement compatible avec la 8.0.

Je sais qu'il existe d'autres options pour ma fonction définie par l'utilisateur.

P粉344355715
P粉344355715

répondre à tous(2)
P粉436410586

Répondez à votre question sur « Le moyen le plus simple et le plus rapide de gérer la mise à niveau de ce code ».

En bref, vous ne pouvez pas.


Tout d'abord, un peu de contexte...

Environ 15 % des développeurs utilisent strict_types=1, vous faites donc partie de la majorité qui ne l'utilise pas.

Vous pouvez ignorer ce problème maintenant (obsolète), mais PHP 9.0 causera beaucoup de problèmes en en faisant une erreur de type fatale.

Cela dit, vous pouvez toujours utiliser des chaînes de connexion NULL :

$name = NULL;
$a = 'Hi ' . $name;

Vous pouvez toujours comparer NULL à la chaîne vide :

if ('' == NULL) {
}

Et vous pouvez toujours utiliser NULL pour les calculs (il est toujours traité comme 0) :

var_dump(3 + '5' + NULL); // Fine, int(8)
var_dump(NULL / 6); // Fine, int(0)

Vous pouvez toujours imprimer/écho NULL :

print(NULL);
echo NULL;

Vous pouvez toujours passer NULL à sprintf() 中,并使用 %s pour le forcer à une chaîne vide, comme

sprintf('%s', NULL);

Vous pouvez toujours forcer d'autres valeurs (en suivant des règles) comme

strlen(15);
htmlspecialchars(1.2);
setcookie('c', false);

La coercition NULL fonctionne de cette façon depuis, je suppose depuis le début, et c'est également documenté :


Quoi qu'il en soit, pour corriger...

La première partie, il essaiera de trouver le code que vous devez mettre à jour.

Cela se produit chaque fois que NULL

peut être transmis à l'un de ces arguments de fonction.

Il y a au moins

335 paramètres concernés par cela.

Il y a un supplément

104, ils sont un peu louches et 558 où NULL a des problèmes , où vous devriez résoudre ces problèmes, par exemple . define(NULL, '值')

Psaume est le seul outil que je puisse trouver qui aide à cela.

Les Psaumes doivent être à un niveau d’inspection très élevé (1, 2 ou 3).

Et vous ne pouvez pas utiliser les lignes de base pour ignorer les problèmes (technique selon laquelle les développeurs introduisent une analyse statique dans les projets existants afin qu'ils vérifient uniquement le code nouveau/modifié).

Si vous n'avez jamais utilisé d'outil d'analyse statique auparavant (ne vous inquiétez pas, c'est seulement recommandé 33% des développeurs le font alors attendez-vous à passer beaucoup de temps à modifier votre code (commencez au niveau 8, le plus lâche) ; , et progressez ).

Je ne trouve pas ces problèmes en utilisant PHPStan, Rector, PHP CodeSniffer, PHP CS Fixer ou PHPCompatibility (Source).


Après avoir trouvé chaque question, La deuxième partie est l'édition.

L'endroit le moins susceptible de causer des problèmes est de remplacer l'évier, comme

example_function(strval($name));
example_function((string) $name);
example_function($name ?? '');

Vous pouvez également essayer de remonter à la source de la variable et essayer d'empêcher qu'elle soit définie sur NULL en premier lieu.

Voici quelques sources très courantes de NULL :

$search = (isset($_GET['q']) ? $_GET['q'] : NULL);
 
$search = ($_GET['q'] ?? NULL); // Fairly common (since PHP 7)
 
$search = filter_input(INPUT_GET, 'q');
 
$search = $request->input('q'); // Laravel
$search = $request->get('q'); // Symfony
$search = $this->request->getQuery('q'); // CakePHP
$search = $request->getGet('q'); // CodeIgniter
 
$value = mysqli_fetch_row($result);
$value = json_decode($json); // Invalid JSON, or nesting limit.
$value = array_pop($empty_array);

Certaines de ces fonctions nécessitent un deuxième argument pour spécifier une valeur par défaut, ou vous pouvez utiliser strval()...但要小心,您的代码可能会通过 ($a === NULL) à l'avance et vous ne voulez pas le casser.

De nombreux développeurs ne réalisent pas que certaines de leurs variables peuvent contenir NULL - par exemple, ils s'attendent à ce que

tous les champs de saisie (qu'ils créent) soient toujours soumis en raison de problèmes de réseau, d'extensions de navigateur, d'utilisateurs modifiant le DOM/URL dans le navigateur, etc. . Cela pourrait ne pas arriver.


Je travaille sur ce problème depuis la majeure partie de l'année.

J'ai commencé à écrire deux RFC pour essayer de résoudre ce problème. La première consiste à mettre à jour certaines fonctions pour accepter NULL (ce qui n'est pas idéal car cela dérange les développeurs qui utilisent strict_types) ; La deuxième RFC est de permettre à NULL de continuer à être appliqué dans ce cas..... .mais je l'ai fait. Je ne le soumets pas au vote car je viens de recevoir une tonne de commentaires négatifs et je ne veux pas que ce rejet cité à l'avenir explique pourquoi ce problème ne peut pas être résolu (alors que les changements originaux ont à peine été discutés, celui-ci) .

Il semble que NULL soit géré différemment car il n'est jamais traité comme une "valeur scalaire" - je ne pense pas que beaucoup de développeurs se soucient de cette distinction, mais cela revient de temps en temps.

La plupart des développeurs avec lesquels j'ai travaillé ignorent ce problème (en espérant le résoudre plus tard, ce qui n'est probablement pas la meilleure idée) ;

function ignore_null_coercion($errno, $errstr) {
  // https://github.com/php/php-src/blob/012ef7912a8a0bb7d11b2dc8d108cc859c51e8d7/Zend/zend_API.c#L458
  if ($errno === E_DEPRECATED && preg_match('/Passing null to parameter #.* of type .* is deprecated/', $errstr)) {
    return true;
  }
  return false;
}
set_error_handler('ignore_null_coercion', E_DEPRECATED);
Il y a une équipe qui essaie de

. Mais plus d'un an plus tard, ils constatent toujours des problèmes (ils ont déclaré qu'ils testaient avec 8.1 alpha 1). strval() 应用于所有事情,例如修剪(strval($search))

Une autre option que j'envisage est de créer une bibliothèque qui redéfinit toutes ces ~335 fonctions comme nullables sous un espace de noms, par exemple

 ;

namespace allow_null_coercion;

function strlen(?string $string): int {
    return \strlen(\strval($string));
}

Le développeur inclura ensuite la bibliothèque et utilisera lui-même l'espace de noms :

namespace allow_null_coercion;

$search = $request->input('q'); // Could return NULL

// ...

echo strlen($search);
P粉087074897

Si vous essayez explicitement de gérer null 的情况,那么稍微干净一点的修复方法是 strlen($row ?? ''), utilisez "l'opérateur de coalescence nul".

Dans la plupart des cas, les deux sont probablement équivalents, mais avec strict_types=1 en effet, ils se comportent différemment si la valeur est d'un type différent qui peut être converti en chaîne :

declare(strict_types=1);
$row = 42;
echo strlen($row); // TypeError: must be of type string, int given
echo strlen((string) $row); // Succeeds, outputting '2'
echo strlen($row ?? ''); // TypeError: must be of type string, int given

Par contre, notez que les variables ?? 运算符基于 isset,而不是 === null et donc non définies se comportent différemment :

declare(strict_types=1);
$row = [];
echo strlen($row['no_such_key']); // Warning: Undefined array key; TypeError: must be of type string, null given
echo strlen((string) $row['no_such_key']); // Warning: Undefined array key; outputs '0'
echo strlen($row['no_such_key'] ?? ''); // No warning, just outputs '0'

Si ce cas vous intéresse, l'équivalent le plus direct de l'ancien comportement est encore plus verbeux :

echo strlen($row === null ? '' : $row);
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal