Manquejades del PHP in_array

Sempre dic que PHP és un llenguatge que m’agrada molt, i que em diverteixo molt treballant-hi i que un projecte Internet es pot fer diverses vegades més ràpid que en Java.

Ara bé, de la mateixa manera també dic que és ple de problemes, problemes de disseny i errors estúpids.

Avui explicaré quelcom prou poc intuitiu i que us pot portar a greus problemes al vostre codi.

L’ús d’ in_array

Observeu aquest codi:

<?php
$st_array = array(      'nom1' => 0,
                        'nom2' => 1,
                        'nom3' => '2',
                        'nom4' => 'Catalunya Lliure');

echo 'Cas 0:'.in_array('0', $st_array)."\n";
echo 'Cas 1:'.in_array('1', $st_array)."\n";
echo 'Cas 2:'.in_array('2', $st_array)."\n";
echo 'Cas n:'.in_array('n', $st_array)."\n";
echo 'Cas z:'.in_array('z', $st_array)."\n";
echo 'Cas Catalunya:'.in_array('Catalunya', $st_array)."\n";
echo 'Cas num no:'.in_array(3, $st_array)."\n";
echo 'Cas num sí:'.in_array(1, $st_array)."\n";

$st_array2 = array(2,'a','Catalunya Lliure');

echo 'Cas 1a:'.in_array(3, $st_array2)."\n";
echo 'cas 2a:'.in_array(2, $st_array2)."\n";
echo 'cas 3a:'.in_array('2', $st_array2)."\n";
echo 'Cas 4a:'.in_array('Catalunya', $st_array2)."\n";
echo 'Cas 5a:'.in_array('Catalunya Lliure', $st_array2)."\n";

Qualsevol desenvolupador de PHP diria que el resultat que espera és diferent del que rebrà.

Veiem el resultat:

PHP codi problemàtic resultat

Els resultats que he obtingut són els mateixos en sistemes Debian amb 5.3.3:

PHP 5.3.3-7+squeeze14 with Suhosin-Patch (cli) (built: Aug  6 2012 14:18:06)
Copyright (c) 1997-2009 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies
with Xdebug v2.1.0, Copyright (c) 2002-2010, by Derick Rethans

En Ubuntu amb 5.3.10:

PHP 5.3.10-1ubuntu3.4 with Suhosin-Patch (cli) (built: Sep 12 2012 18:59:41)
Copyright (c) 1997-2012 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2012 Zend Technologies
with Xdebug v2.2.1, Copyright (c) 2002-2012, by Derick Rethans

I en PHP 5.4.10 i en 5.5.0.a.2 executats en aquesta sandbox: http://sandbox.onlinephpfunctions.com/

El resultat és l’esperat* en els casos en que el valor de l’array és numèric, però sembla totalment buggy en el cas de string (el que coneixem com Map a Java o hash a Python).
Veient els exemples hom pensaria que el problema es produeix si els valors assignats a barregen valors numèrics i string, però també si l’array té claus alfanumèriques (‘nom1′ => ‘0’). Aquest últim cas es veu clarament amb l’exemple de cercar ‘Catalunya’ al primer i al segon cas.

Però si tingués un bon amic com el meu amic Edu, us faria adonar que no es tracta de si la clau és alfanumèrica o automàtica, si no que el fet que hi hagi un 0 (zero) com a valor integer, és el que provoca aquest comportament esbojarrat i imprevisible del PHP.

Mirant la documentació del mètode loose de comparació de PHP observem on és el problema.

Encara que sigui contra intuitiu, en el mètode loose qualsevol cadena de text és == a 0 (zero integer).

* Nota: Dic que és l’esperat donat que la documentació de PHP per a in_array ja indica que “Searches haystack for needle using loose comparison unless strict is set.”, és a dir, que fa servir el mètode de comparació loose, en que es considera igual 1 i “1” per exemple.

I aquest és un dels grans problemes de PHP, que degut al sevu comportament intern, a les conversions entre tipus que fa automàticament, viola els principis de programació de fiabilitat**, i porta a situacions en que és altament probable que els programadors generin bugs o es generin bugs que només passen en situacions molt específiques.

** Quan dic que viola el principis de programació de fiabilitat de resultats, em refereixo a coses com:

Expressió Resultat
“Catalunya” == 0 TRUE
“0” == 0 TRUE
“Catalunya” == “0” FALSE

O sigui “Catalunya” == 0 i 0 == “0” però “Catalunya” és diferent de “0”. Epic fail.

Haurem d’utilitzar === que fa la comprovació de tipus també.

La solució universal que farà que els dos casos d’in_array funcionin correctament és emprar el mètode estricte.

Codi que funciona segons s’espera:

<?php 
define('METODE_STRICTE', true); 

$st_array = array(	'nom1' => 0, 
			'nom2' => 1, 
			'nom3' => '2', 
			'nom4' => 'Catalunya Lliure');

echo 'Cas 0:'.in_array('0', $st_array, METODE_STRICTE)."\n";
echo 'Cas 1:'.in_array('1', $st_array, METODE_STRICTE)."\n";
echo 'Cas 2:'.in_array('2', $st_array, METODE_STRICTE)."\n";
echo 'Cas n:'.in_array('n', $st_array, METODE_STRICTE)."\n";
echo 'Cas z:'.in_array('z', $st_array, METODE_STRICTE)."\n";
echo 'Cas Catalunya:'.in_array('Catalunya', $st_array, METODE_STRICTE)."\n";
echo 'Cas num no:'.in_array(3, $st_array, METODE_STRICTE)."\n";
echo 'Cas num sí:'.in_array(1, $st_array, METODE_STRICTE)."\n";

$st_array2 = array(2,'a','Catalunya Lliure');

echo 'Cas 1a:'.in_array(3, $st_array2, METODE_STRICTE)."\n";
echo 'cas 2a:'.in_array(2, $st_array2, METODE_STRICTE)."\n";
echo 'cas 3a:'.in_array('2', $st_array2, METODE_STRICTE)."\n";
echo 'Cas 4a:'.in_array('Catalunya', $st_array2, METODE_STRICTE)."\n";
echo 'Cas 5a:'.in_array('Catalunya Lliure', $st_array2, METODE_STRICTE)."\n";

I podeu observar el resultat:

PHP codi problemàtic solucionat amb strict

Pareu atenció a que amb el mètode estricte si cerqueu ‘1’ (string) i a l’array hi ha 1 (integer) in_array us retornarà FALSE.

Molt important a tenir en compte que tot el que rebeu de $_GET o $_POST sigui el que sigui us arribarà com un string, o un array d’strings, malgrat en el formulari hagin inserit valors numèrics com 2014.

Nota per a usuaris novells: Jo he utilitzat una constant amb el valor booleà TRUE per a fer servir el mètode estricte però prodrieu fer servir directament true.

Català-Catalunya English-USA Traduir a l'Anglès. Translate to English Compartir: La TafaneraIndependènciaCatosfera|FacebookTwitterFriend Feed|googleDeliciousDiggTechnoratiredditmixxyahoolivestumbleuponsimpy

Tags: , ,

1.397 visualitzacions - versió en PDF

Comments are closed.