Newsgroup: it.comp.www.php
Titolo: Identificare se una stringa contiene una variabile

Come da titolo, è possibile?
 
Immagino di no, ma tentare non nuoce: ammettiamo di avere
 
$foo = "prova";
$bar = 'prova2';
 
$foobar = "questa è una $foo";
$foobaz = 'questa è una ' . $bar;
 
vorrei fare un controllo che riconosca che $foobar e $foobaz sono composte da altre variabili...
 
 
il motivo: devo fare un adapter per un database che verrà potenzialmente dato in mano a gente di cui non mi fido più di tanto e, visto che i casini poi toccano a me, vorrei obbligarli a fare query solo tramite prepared statements; il metodo query 'diretto' è già privato, ma se questi nel'sql del prepared mi ci mettono le variabili non satinizzate mi casca l'asino..
 
Ah, bei tempi quando bisognava improntare la sicurezza solo da input esterni e non da input interni...

<<vorrei fare un controllo che riconosca che $foobar e $foobaz sono composte 
da altre variabili...
 
Intendi quindi le variabili di variabili ?  ( esempio $$foo )
 
O cercare in una stringa un pattern di variabile?
 
 
<il motivo: devo fare un adapter per un database che verrà potenzialmente 
dato in mano a gente di cui non mi fido più di tanto e, visto che i casini 
poi toccano a me, vorrei obbligarli a fare query solo tramite <prepared 
statements; il metodo query 'diretto' è già privato, ma se questi nel'sql 
del prepared mi ci mettono le variabili non satinizzate
 
intendevi  dire SANITIZZATE ? 
 
 

 intendevi  dire SANITIZZATE ?
 
yep ;)

..mi pare di aver capito che la risposta sia 'no'... quindi si va di strict orm

 strict orm
 
volevi dire STRICT FORM ? 
 
 

vorrei fare un controllo che riconosca che $foobar e $foobaz sono composte da altre variabili...
 
preg_match is probably your friend.
 
Racchiuso in graffe, seguito da [] o ->, ma una variabile e' sempre una
roba del tipo dollaro-qualcosa.
 
Quindi,
 
	preg_match('#\\$[A-Za-z_][A-Za-z0-9_]*#', $stringa)
	&& trigger_error("Was that a variable?", E_USER_ERROR);
 
dovrebbe fare al caso tuo.
 
Leonardo S.E.&O.
 
-- 
Tell us whether you spy, in the light of the morn,
what saluted was fiercely at the fall of the past day:
whose pips and whose bands in the very heart of the fray
bravely floated from the bulwarks to which we were sworn?

 && trigger_error("Was that a variable?", E_USER_ERROR);
 
perchè il doppio \\  all'inizio?
 
L'escape non dovrebbe essere \$   ? 
 
 


In questo caso il risultato è identico, ma \\ si comporta nello stesso
modo (per le regexp) sia che tu usi l'apice singolo che il doppio. Ciò
torna utile di tanto in tanto.
 
(Io ho anche il vézzo di scrivere   "if (false === $flag)" al posto di
"if ($flag === false)" o del comune "if ($flag == false)". Capisci che
non ci sto tutto con la testa, bisogna aver pazienza).
 
Questo, per l'appunto, è forse l'unico caso in cui la mia strategia ha
comunque problemi, perche' con l'apice doppio devi escapare il $, e lo
devi fare con... uno slash.
 
Pero' casualmente il carattere dopo NON appartiene alla regexp che lui
stesso descrive, e quindi per puro caso \\$ andrebbe bene anche con ".
 
Il modo paranoico di scriverlo sarebbe di marcare i caratteri noiosi:
 
	'#'.'\\'.'$'.'[A-Za-z_][A-Za-z0-9_]*#'
 
che e' apice-invariante, anche se e' scomodo abbestia.
 
Leonardo


 non ci sto tutto con la testa, bisogna aver pazienza).
 
anche io so che č meglio scrivere  "if (false === $flag)" al posto di
 "if ($flag === false)" , perchč se per caso sbagli č metti un solo = 
anzichč  === , il PHP ti manda un errore perchč non puoi assegnare un 
letterale,mentre se scrivi ($flag =false ) non ti avvisa . 
 
 

 volevi dire STRICT FORM ?
 
no, strict ORM (orm blindato in modo che possano venir passati solo i parametri come variabili, ma la query deve essere costruita tramite i metodi orm) ;P
 
 
@Leonardo: scusa ma.. al momento di valutare la variabile, le eventuali variabili al suo interno vengono interpretate e inserite come stringa/inter/quellocheè, quindi:
 
$foo = "prova"; 
$foobar = "questa è una $foo"; 
 
dentro a foobar mi ritrovo la stringa "questa è una prova", e la regexp fallisce.
 
Io mi domandavo se esistesse un qualcosa che mi dica che $foobar è stato composto da altre variabili, forse nei puntatori interni di php, non sò.. ma qualsiasi test posso fare sul valore finale dovrebbe fallire

<$foo = "prova";
<$foobar = "questa è una $foo";
 
le virgolette singole  dovrebbero trattare  la variabile come fosse un 
letterale (no interpolazione della variabile) :
 
$foobar = 'questa è una $foo'; 
 
 

Io mi domandavo se esistesse un qualcosa che mi dica che $foobar è stato composto da altre variabili
 
Non vedo bene a che scopo, ma comunque, no. Quello che puoi fare e'
magari inventarti un tipo complesso Object e manipolarlo solo con i
tuoi metodi.
 
Per es., un oggetto che contiene una stringa criptata (chiaramente,
la chiave appare in chiaro nel TUO codice).
 
Dopodiché, la query la costruisci in quella stringa usando soltanto
metodi di quell'oggetto. Strict ORM, appunto.
 
Non so se quello che vuoi fare - mi servirebbe una user story - sia
possibile magari usando delle costanti.


Allora, spiego meglio la mia situazione:
 
Stò mantenendo un piccolo software per una no-profit locale.
Visto che porto avanti la cosa nel tempo libero, e ultimamente scarseggia, sotto mio suggerimento hanno trovato altri sviluppatori per seguire il progetto ma mi sono accorto che questi sviluppatori si sono messi a usare il mio adapter per il database in modo alquanto bizzarro, robe del tipo:
 
$stmt = Factory('database')->prepare("SELECT foo FROM bar WHERE " . $_POST['field'] . " = [integer:foobar]");
$stmt->execute('foobar' => 9);
 
Ora, non voglio entrare in merito sul perchè mi ritrovo certa roba nel codice, ma visto che il mio tempo a disposizione è poco, devo trovare il modo di rendere le query "blindate".
In teoria i prepared stmt dovrebbero già annullare sql injection - non sono permesse query multiple dentro un stmt - ma devo trovare una soluzione per dormire tranquillo e, in caso di problema, poter dire tranquillamente "Guarda, hanno fatto apposta, era praticamente impossibile che succedesse per sbaglio".
 
Questi tizi non hanno accesso alla base del codice, quindi non possono modificare il metodo Factory o l'adapter del database, ma se continuano a usarlo in questa maniera non sono molto tranquillo (e dovreste capire il perchè).
 
Quello che vorrei fare, quindi, è _intercettare_ nell'esempio di cui sopra:
"SELECT foo FROM bar WHERE " . $_POST['field'] . " = [integer:foobar]"
che la query passata contiene dei valori 'dinamici', forzandoli magari a usare una mappatura dei campi (ma a questo punto non mi stupirei nel vedere una roba tipo "SELECT foo FROM bar WHERE field = " . $_POST['field']).
 
Fortunatamente il metodo factory lo avevo fatto abbastanza bene e sostituire la parte del database con un orm (o un mini orm) supermegaiper pignolo non dovrebbe essere troppo complicato, anche se alzare un bel Fatal Error in faccia a chi mi scrive le porcate di cui sopra mi darebbe una certa soddisfazione.

$stmt = Factory('database')->prepare("SELECT foo FROM bar WHERE " . $_POST['field'] . " = [integer:foobar]");
 
I sistemi che mi vengono in mente sono tutti piuttosto fantascientifici.
 
Un sistema è quello di generare un debug_backtrace() ed esplorarlo in modo
da arrivare alla riga chiamante, e sottoporla ad analisi via regexp.
 
Un altro sistema e' quello di obbligare a registrare i campi che si devono
usare, nel senso che la' sopra scrivi
 
	'SELECT foo FROM bar WHERE $_POST[field] = [integer:foobar]';
 
e per poterlo fare devi prima fare qualcosa del tipo
 
	Factory('database')->register('_POST', 'field');
	...
	Factory('database')->endRegister();
 
dove endRegister cancella _POST, _GET e compagnia. Se non fai endRegister,
il resto dell'adapter ti sputa in un occhio.
 
Ovviamente rimane il rischio che uno salvi _POST in una variabile, e anche
e' poco carino che dove passi tu _POST e _GET scompaiano.
 
Altrimenti, bisogna togliere di mezzo il prepare() cosi' com'e', ed andare
invece a ricostruire l'SQL metodo per metodo:
 
	->where('fieldname' => array('op' => 'equals', 'value' => ...));
 
...ma e' parecchio farraginoso.
 
Ultimo sistema, forse migliore, e' un front-end di sanitizzazione: e cioe'
ogni pagina che riceve un input deve dichiarare cosa si aspetta in termini
di regexp:
 
	'field'	=> 'MUST [A-Za-z_0-9]+',// Deve esserci ed essere cosi'
	'fld2'	=> 'CAN [A-Z]+',	// Se c'e' deve essere A-Z
	'_ua'	=> 'IGNORE',		// Se c'e' non e' un errore, ma lo
					// script non deve vederlo
	...
 
e tutti i campi che NON compaiono nella lista (o ci sono, ma sotto IGNORE)
spariscono.
 
In modalita' debug, i campi ricevuti in POST/GET che *non* compaiono nella
lista (neanche come IGNORE) originano un errore, cosi' il programmatore si
accorge di essersi dimenticato roba per strada.
 
La cosa può tornare utile anche agli altri sviluppatori, cosi' i controlli
di validita' dell'input sono in un posto solo e non sparsi qua e la'.
 
Io l'avevo iniziato un progettino cosi' tampo addietro, pure :-)
