Jonathan Stoppani (st.jonathan@gmail.com), 12/08/2006
Si vuole creare un test per eseguire un controllo dell'input di un utente. La variabile in entrata costituisce un ID ed è quindi (nel nostro caso) un intero positivo rappresentato con un tipo stringa. Vedremo ora quale combinazione di test risulti la soluzione migliore per eseguire il compito.
Il terzo valore (int(170)) viene automaticamente convertito ad
intero da php, da notare nel codice l'assegnamento con 0xAA.
[0] => string(2) "AA" [1] => string(4) "0xAA" [2] => int(170) [3] => string(1) "1" [4] => int(1) [5] => int(0) [6] => string(1) "0"
| Valore | Risultato desiderato | ereg('[0-9]{1,5}', $int) | ereg('^[0-9]{1,5}$', $int) | is_numeric() | is_int() | $int + 0 | intval($int) | (bool) intval($int) | ctype_digit($int) |
|---|---|---|---|---|---|---|---|---|---|
| string(2) "AA" | bool(false) | bool(false) | bool(false) | bool(false) | bool(false) | int(0) | int(0) | bool(false) | bool(false) |
| string(4) "0xAA" | bool(false) | bool(true) | bool(false) | bool(true) | bool(false) | int(170) | int(0) | bool(false) | bool(false) |
| int(170) | bool(true) | bool(true) | bool(true) | bool(true) | bool(true) | int(170) | int(170) | bool(true) | bool(false) |
| string(1) "1" | bool(true) | bool(true) | bool(true) | bool(true) | bool(false) | int(1) | int(1) | bool(true) | bool(true) |
| int(1) | bool(true) | bool(true) | bool(true) | bool(true) | bool(true) | int(1) | int(1) | bool(true) | bool(false) |
| int(0) | bool(false) | bool(true) | bool(true) | bool(true) | bool(true) | int(0) | int(0) | bool(false) | bool(false) |
| string(1) "0" | bool(false) | bool(true) | bool(true) | bool(true) | bool(false) | int(0) | int(0) | bool(false) | bool(true) |
ereg('[0-9]{1,5}', $int)
Regex errata, se non vengono definiti i punti di inizio (^)
e di fine ($) della stringa, qualsiasi carattere numerico
farà in modo che la funzione ritorni true (notare lo 0
all'inizio della stringa 0xAA).
ereg('^[0-9]{1,5}$', $int)
Versione corretta dell'espressione precedente.
Entrambe i test possono essere migliorati usando le espressione regolari
compatibili PERL e le apposite funzioni (preg_match) che
come riportato anche dal manuale PHP risultano più efficienti rispetto
alle funzioni PHP.
is_numeric()
is_numeric() si presterebbe al nostro scopo ma ha lo
svantaggio (o vantaggio) di contemplare pure le stringhe che
rappresentano numeri in formato esadecimale.
is_int()
La funzione is_int() controlla che la variabile
rappresenti un intero ed inoltre controlla che sia pure del rispettivo
tipo. Dato che tutte le variabili _POST e _GET ci vengono passate come
tipi stringa, questa funzione non è adatta allo scopo.
(bool) intval($int)
Questa combinazione sembrerebbe la soluzione migliore. Converte il
tipo della variabile in entrata (di tipo stringa) in un tipo int.
Qualsiasi valore che non rappresenti un numero verrà quindi convertito
in un intero di valore 0. Se eseguiamo un test su questo valore (uno
0 di tipo int viene convertito in un
false di tipo bool) scopriremo se il nostro
input è valido.
ctype_digit($int)
Questa funzione offre forse la soluzione migliore, ma data la sua natura (esegue il controllo solamente su valori di tipo stringa) si adatta ad essere usata per validare unicamente l'input e non altri valori generati dallo script stesso.
Le soluzioni ottimali sono quindi principalmente tre, a dipendenza dalle diverse necessità:
ereg('^[0-9]{1,5}$', $int)(bool) intval($int)ctype_digit($int)La soluzione che contempla l'uso di un'espressione regolare ha come vantaggio un controllo più immediato della lunghezza minima e massima dell'intero e contempla anche il caso in cui l'id abbia un valore numerico 0.
La soluzione basata sulla conversione di tipo risulterà più performante ma
non eseguirà nessun controllo sulla lunghezza dell'intero. Al contrario della
soluzione basata sulla regex, questa soluzione esclude per sua natura il caso
in cui l'id abbia un valore 0, il che potrebbe rappresentare anche un vantaggio.
Infatti, il valore degli id all'interno di tabelle di Database relazionali
solitamente è rappresentato con un intero positivo diverso da 0.
Se invece si possono avere id con valore 0 e si deve eseguire il controllo solamente su dati di tipo stringa, la soluzione migliore è sicuramente quella basata su ctype_digit.
Tutte le soluzioni possono comunque essere facilmente combinate per adeguarsi a qualsiasi controllo numerico
<?php // is_numeric.php
include 'is_numeric.inc.php';
$number = array(
array('AA', false),
array('0xAA', false), // Assegno un numero esadecimale come stringa
array(0xAA, true), // Assegno un numero esadecimale come intero
array('1', true),
array(1, true),
array(0, false),
array("0", false)
);
?>
<h1>Test su interi e loro comportamento</h1>
<h2>Valori usati per il test</h2>
<pre>
<?php foreach ($number as $i => $int): ?>
[<?php echo $i ?>] => <?php var_dump($int[0]) ?>
<?php endforeach; ?>
</pre>
<h2>Risultati del test</h2>
<table cellspacing="3">
<tr>
<th>Valore</th>
<th>Risultato desiderato</th>
<th>ereg('[0-9]{1,5}', $int)</th>
<th>ereg('^[0-9]{1,5}$', $int)</th>
<th>is_numeric()</th>
<th>is_int()</th>
<th>$int + 0</th>
<th>intval($int)</th>
<th>(bool) intval($int)</th>
<th>ctype_digit($int)</th>
</tr>
<?php foreach ($number as $int): ?>
<tr>
<?php getInfo($int) ?>
</tr>
<?php endforeach; ?>
</table>
<?php // is_numeric.inc.php
function getInfo($int)
{
dump($int[0]);
dump($int[1]);
dump((bool) ereg('[0-9]{1,5}', $int[0]));
dump((bool) ereg('^[0-9]{1,5}$', $int[0]));
dump(is_numeric($int[0]));
dump(is_int($int[0]));
dump($int[0] + 0);
dump(intval($int[0]));
dump((bool) intval($int[0]));
dump(ctype_digit($int[0]));
}
function dump($text)
{
if ($text === true) {
$class = 'true';
} else if ($text === false) {
$class = 'false';
} else {
$class = '';
}
echo "<td class=\"$class\">";
var_dump($text);
echo "</td>";
}