<- Parte I
Il codice è un po’ lungo perchè ci sono diverse funzioni, ma è comunque facile da capire.
Primo passo: eliminare gli zeri.
72 function base2no0($num = 0, $base = 10){
73 $q = $num;
74 $ord = 0;
75 while($q>0){
76 $q = floor($q/($base-1));
77 $num += pow($base,$ord)*$q--;
78 $ord++;
79 }
80 $num++;
81 return $num;
82 }
83
84 function no0tobase($num = 1, $base = 10){
85 if($num%$base==0)
86 return false;
87 $num--;
88 $ord = $num==0 ? 0 : floor(log($num,$base));
89 while($ord>0){
90 $q = pow($base,$ord-1);
91 $num -= floor($num/pow($base,$ord--))*$q;
92 }
93 return $num;
94 }
La funzione base2no0() mappa i numeri naturali (0, 1, 2…) trasponendoli su numeri senza zeri:
for($i=0; $i<1000; $i++) echo base2no0($i)."\n";
questo codice stamperà 1 2 3 .. 8 9 11 12 .. 19 21 .. 98 99 111 .. cioè i primi 1000 numeri senza zeri. La funzione no0tobase() fa la codifica opposta e restituisce false se si tenta di codificare un numero che contiene zeri (in base n-esima un numero contiene almeno uno zero se è divisibile per n).
Secondo passo: mappare i numeri su stringhe.
56 function int2alpha($num = 0){
57 $num = base2no0($num, 27);
58 $num = base_convert($num, 10, 27).'';
59 for($i=0; $i<strlen($num); $i++)
60 $num[$i] = chr(ord('a')+base_convert($num[$i], 27, 10)-1);
61 return $num;
62 }
63
64 function alpha2int($num = 'a'){
65 for($i=0; $i<strlen($num); $i++)
66 $num[$i] = base_convert(ord($num[$i])-ord('a')+1, 10, 27);
67 $num = base_convert($num, 27, 10);
68 $num = no0tobase($num, 27);
69 return $num;
70 }
La funzione int2alpha() trasforma i numeri filtrati da base2no0() in stringhe, secondo la codifica 1-26. Usa la funzione base_convert() per convertire un numero da base 10 a base 27 (”cifre” da 0 a 26, anche se lo 0 non è rappresentato).
for($i=0; $i<1000; $i++) echo int2alpha($i)."\n";
questo codice stamperà le prime 1000 stringhe in ordine alfabetico: a b c .. x y z aa ab ac .. az ba bb .. bz ca .. zy zz aaa aab aac ..
La funzione alpha2int() è duale rispetto a int2alpha(), restituisce il numero corrispondente alla parola passata come parametro.
Ultimo passo: scrivere la funzione per generare stringhe casuali.
La funzione alpha_rand() (righe 3-46 del codice) accetta quattro parametri:
$min e $max: sono di un certo tipo a seconda degli altri parametri;
$type: i valori possibili sono 'alpha_only' e 'alphanum';
$method: i valori possibili sono 'length', 'alpha_range' (o semplicemente 'range') e 'int_range'.
Come la rand(), anche la alpha_rand() può pescare fino a un valore massimo: getalpharandmax() restituisce tale valore, cioè la stringa che è mappata (almeno su Altervista) sul numero 743925597765 (tale numero è stato calcolato in modo empirico, ho verificato che i numeri successivi danno stringhe sballate e ambigue). getrandmaxlength() dà la lunghezza di tale stringa (9).
19 if($type != 'alpha_only' && $type != 'alphanum')
20 return false;
21 if($method == 'length'){
22 $length = min(rand($min, $max), getrandmaxlength());
23 $randmin = $randmax = '';
24 for($i=0; $i<$length; $i++)
25 $randmin .= 'a';
26 for($i=0; $i<$length; $i++)
27 $randmax .= 'z';
28 return alpha_rand(alpha2int($randmin), alpha2int($randmax), $type, 'int_range');
29 }
30 if($method == 'range' || $method == 'alpha_range')
31 return alpha_rand(alpha2int($min), alpha2int($max), $type, 'int_range');
32 if($method != 'int_range')
33 return false;
In questo pezzo di codice c’è la validazione e il filtraggio dei parametri: tutti i metodi vengono ricondotti al metodo 'int_range' (righe 28 e 31).
35 $alpha_rand = int2alpha(extended_rand($min, $max));
Questa è la riga principale: trasforma in stringa un numero casuale. Qui uso una extended_rand() (righe 96-105) al posto della rand() perchè arriva fino a 743925597765 anzichè fino a PHP_MAX_INT. La extended_rand() usa la recursive_rand() (righe 107-117) per distribuire meglio la casualità dei numeri.
37 if($type == 'alphanum'){
38 $places = range(0, strlen($alpha_rand)-1);
39 shuffle($places);
40 $places = array_slice($places, rand(1, strlen($alpha_rand)-1));
41 foreach($places as $pos)
42 $alpha_rand[$pos] = rand(0, 9);
43 }
C’è anche la possibilità di generare una stringa alfanumerica ($type = 'alphanum'). Ho aggiunto questa modalità per completezza, visto che non è una parte fondamentale di questo articolo; per comodità ho semplicemente sostituito alcuni caratteri della stringa letterale con alcune cifre. (Nota: Le stringhe alfanumeriche così generate non sono trasformabili attraverso alpha2int().)
5 if(func_num_args() == 0)
6 return alpha_rand('a', getalpharandmax(), 'alpha_only', 'range');
7 if(preg_match("/[0-9]/", $min) && (!isset($max) || preg_match("/[a-z]/", $max))){
8 if(func_num_args() > 3)
9 return false;
10 if(func_num_args() == 3 && func_get_arg(2) != 'length')
11 return false;
12 if(func_num_args() == 2)
13 $type = func_get_arg(1);
14 else
15 $type = 'alpha_only';
16 return alpha_rand($min, $min, $type);
17 }
Queste righe sono una comodità per poter passare 3 parametri o nessuno anzichè 4 (vedi esempi 1 e 4).
Esempi
Ecco alcuni esempi di utilizzo (output):
echo alpha_rand();
echo alpha_rand(4, 6);
echo alpha_rand(4, 6, 'alphanum');
echo alpha_rand(3, 'alpha_only', 'length');
echo alpha_rand(3);
echo alpha_rand(703, 731, 'alpha_only', 'int_range');
echo alpha_rand('aab', 'abd', 'alpha_only', 'alpha_range');
echo alpha_rand(alpha2int('aab'), alpha2int('abd'), 'alpha_only', 'int_range');
Riga 1: Nessun parametro -> Stringa letterale qualsiasi tra ‘a’ e il valore di getalpharandmax().
Riga 2: Restituisce una stringa letterale di lunghezza compresa tra 4 e 6 (type = alpha_only, method = length).
Riga 3: Come la 2, tranne che la stringa è alfanumerica.
Riga 4: Forma compatta per alpha_rand(3, 3, 'alpha_only', 'length').
Riga 5: Come la 4.
Righe 6-8: Tutte e tre danno una stringa casuale tra ‘aab’ e ‘abd’ (’aab’, ‘aac’..’aaz’, ‘aba’, ‘abb’, abc’, ‘abd’) (703 -> ‘aab’, 731 -> ‘abd’).