PROJET AUTOBLOG


Tiger-222

Archivé

Site original : Tiger-222

⇐ retour index

PHP : l'erreur subtile de l'opérateur ternaire

lundi 25 novembre 2013 à 12:15
Récemment, j'ai découvert un bogue dans BlogoText. La ligne incriminée est :
$token = (isset($_POST['token'])) ? $_POST['token'] : (isset($_GET['token'])) ? $_GET['token'] : 'false';
Soit :
$token =
isset($_POST['token']) ?
$_POST['token'] :
isset($_GET['token']) ?
$_GET['token'] :
'false';

Il s'agit d'une imbrication de conditions ternaires. Comme le dit la documentation, l'expression (expr1) ? (expr2) : (expr3) est évaluée à expr2 si expr1 est évaluée à TRUE, et expr3 si expr1 est évaluée à FALSE.

operateur-ternaire.png

Ici l'auteur désire récupérer le token envoyé par la méthode POST, ou sinon GET.
En situation réelle, $_POST contient bien une entrée token, donc nous devrions avoir $_POST['token']. Hors, l'expression ci-dessus retournera NULL... Voyons pourquoi.


L'expression ternaire est évaluée de gauche à droite, pour que ça soit plus clair :
$token =
isset($_POST['token']) ?
'a' :
isset($_GET['token']) ?
'b' :
isset($_SESSION['token']) ?
'c' :
isset($je_suis_un_crack) ?
'd':
FALSE;
Que vaut $token dans cet l'exemple ?
En théorie 'a', puisque $_POST['token'] existe. Au lieu de ça, $token vaut 'd'. Si je remplace isset($_POST['token']) par !isset($_POST['token']), $token faudra FALSE.

Pour simplifier, les expressions du milieu sont ignorées. On aura le résultat de la dernière expression :
$token =
isset($_POST['token']) ?
//'a' :
//isset($_GET['token']) ?
//'b' :
//isset($_SESSION['token']) ?
//'c' :
//isset($je_suis_un_crack) ?
'd':
FALSE;


Revenons-en à nos moutons, pourquoi NULL est renvoyé ? Car $_POST['token'] existe bel et bien, donc d'après la suite de l'expression, on retourne $_GET['token'], qui n'existe pas, donc NULL. D'ailleurs, en activant l'affichage des erreurs, on a bien un averto :
Notice: Undefined index: token in inc/veri.php on line 160

Pour corriger le problème, il suffit d'ajouter des parenthèses, comme en maths :
$token = isset($_POST['token']) ? $_POST['token'] : (isset($_GET['token']) ? $_GET['token'] : 'false');
Soit :
$token =
isset($_POST['token']) ?
$_POST['token'] :
(isset($_GET['token']) ?
$_GET['token'] :
'false');