Newsgroup: it.comp.www.php
Titolo: [LUNGO] OOP in PHP

Ciao a tutti.
Ho un dubbio e premetto che uso PHP5.
Sto facendo un programmino ad oggetti, ma non so come
gestire correttamente le variabili.
In giro per la rete vedo molte soluzioni di questo tipo:
class Prova {
    private $pippo;
    private $result;
    public function setPippo($valore) {
       $this->pippo = $valore;
    }
    public function doComando() {
       echo "Ciao ".$this->pippo;
       $this->result=true;
    }
    public function getResult() {
       return $this->result;
    }
}
Ovvero, le variabili inerenti le classi sono settate tramite
funzioni. Questo sistema mi trova favorevole, a costo di qualche
riga di codice in piï¿œ (devo fare le funzioni set* per ogni variabile)
si ha una maggiore sicurezza in quanto dentro tali funzioni set* ï¿œ
possibile inserire tutto il codice di protezione da sql injection o
cosette simili. In pratica setto la variabile solo dopo averla
ripulita e "castata".
Scrivendo direttamente sulle variabili questi controlli verrebbero
a meno, o meglio, bisognerebbe farli manualmente per ogni variabile
e la pigrizia abbonda... :-)
Ecco la domanda:
supponendo di fare un codice di questo tipo:
$Prova = new Prova;
$Prova->setPippo("CIAO");
$Prova->doComando();
la prima volta non ci sarebbero problemi, ma se volessi eseguire la
stessa funzione una seconda volta ma con valori differenti per "pippo",
dovrei far ben attenzione ad eseguire "setPippo" ogni volta, altrimenti
rischio di lavorare sui precedenti valori.
Mi spiego: con le funzioni tradizionali, le variabili passate come
argomento nascono e muoiono dentro la funzione stessa. Richiamando
la funzione due volte, sono costretto a passare tutti i parametri
tutte le volte, non facendolo, si genera un errore.
Con le classi strutturate come sopra, avrei una persistenza dei valori,
la prima volta li setto, la seconda volta potrei dimenticarmi e le
funzioni sarebbero eseguite ugualmente.
Supponiamo ora di avere una funzione che altera dei dati in base ad un
id.
$Prova->setId(24);
$Prova->newTitle("Ciao mondo");
$Prova->changeTitle();
la prima volta mi setta il titolo "Ciao Mondo" al record con id 24.
La seconda volta, devo modificare l'id 38, ma mi dimentico di impostare
il setId (oppure mi si genera un errore ed il setId non va a buon fine).
$Prova->newTitle("Come va?");
$Prova->changeTitle();
La funzione viene eseguita correttamente, ma sul record sbagliato.
Con gli argomenti delle funzioni, questo non accadrebbe:
changeTitle(24,"Ciao mondo");
changeTitle(38,"Come va?");
se mi dimenticassi di impostare l'ID come primo parametro della
funzione si genererebbe un errore, impossibile da non notare.
Con la classe di cui sopra, invece, il debug risulterebbe un po piï¿œ
difficile, non vedendo sempre l'errore potrebbe verificarsi solo in
determinate circostanze.
Quindi, secondo voi, quale ï¿œ il metodo migliore per operare con le
classi e le funzioni? Usare i vari set* e get* ? In svariati libri
ho sempre visto questa soluzione, e come detto sopra, mi trova
favorevole.
Ma come tutelarsi dalla persistenza delle variabili?
Devo reistanziare la classe dopo ogni operazione?
Ad esempio:
$Prova = new Prova();
$Prova->setId(24);
$Prova->setTitle("Ciao mondo");
$Prova->changeTitle();
unset($Prova);
$Prova = new Prova();
$Prova->setId(38);
$Prova->setTitle("Come va?");
$Prova->changeTitle();
Oppure dovrei resettare le variabili "id" e "title" dopo ogni esecuzione
di "changeTitle" in modo da generare un errore nel caso si cercasse di
rieseguire la changeTitle senza aver completato tutti i passaggi?
Insomma, quale ï¿œ il metodo migliore?
In maniera analoga, anche prendere l'output delle classi avrï¿œ lo stesso
problema. Anche in questo caso ho sempre visto passare attraverso una
variabile "result" presente dentro la classe.
Per leggere l'output quindi si potrebbe fare "Prova->getResult()" che
ritorna il valore della variabile result dichiarata come "private".
Ma alla seconda esecuzione, avrei comunque la persistenza, se una delle
funzioni non andasse a buon fine, la variabile result corrisponderebbe
al valore della precedente esecuzione.
Anche qui, devo creare una classe per ogni operazione o posso riusare
sempre la stessa cambiando solo i parametri?
Scusate la lunghezza.

 Scusate la lunghezza.
Non ho capito bene bene qual'� il problema di fare $Prova->setTitle(24,"Ciao 
mondo");
Tutto dipende da che cosa rappresenta l'oggetto Prova. Se l'oggetto prova 
opera cambiamenti su dei record, allora imho e' semanticamente corretto che 
non abbia una variabile interna che indichi l'id del record sul quale 
operare il cambiamento.
Se Prova invece rappresenta una riga del db, allora si che deve tenere 
dentro di se l'id (mi sono legato troppo all'esempio, ma spero di aver fatto 
arrivare il mio parere)
In $oldoni, se una variabile la setti una volta sola, set va bene. Se e' 
dinamica, set non ha senso (pensa ad esempio a $Math->somma()....)
Per l'output, a che serve il getResult?
Basta che le funzioni che invochi ti ritornino direttamente quello che 
devono, ad esempio $Prova->setTitle(24,"Ciao mondo"); ritornera' true se va 
bene, false se va male....
 

 bene, false se va male....
Anche quï¿œ, ho sempre letto di evitare di accedere direttamente alle
variabili interne alle classi ma di passare attraverso dei metodi creati
ad-hoc. In questo caso, le funzioni interne alle classi salvano su
$this->Result ed io vado a prendere i dati tramite una funzione
getResult.


Mica devi accedere alle variabili interne alle classi...basta fare
function pippo()
{
    // doStuff
    if(tuttoAndatoBene)
        return true;
    else
        return false;
}
Cosi si fa nei linguaggi di programmazione, cosi faccio in php ( certo, a 
volte a senso magari accumulare i risultati in una variabile, ma qui si 
inizia ad entrare troppo nello specifico del caso)
Tutto imho.... 

 e "getVariabile".
Ma se ï¿œ solo per questo allora puoi fare semplicemente
function setTitle($id,$titolo)
{
$this->setId($id);
$this->setTitle($titolo);
}
in questo modo nella funzione setTitle hai il "controllo sul numero di 
variabili passate" in relazione all'azione da intraprendere e le 
funzioni set* le puoi usare anche per altro e ti fanno tutti i controlli 
necessari sul tipo, sul valore e su quello che vuoi delle singole variabili.
ciao
Mauro

 variabili.
Ed ï¿œ un metodo standard e pulito con il quale procedere?
Perchï¿œ di soluzioni ce ne sarebbero infinite, ma non voglio
andarmi ad infogliare in metodi eretici...

 andarmi ad infogliare in metodi eretici...
di "standard" nel tuo caso non credo che ce ne siano, secondo me questo 
ï¿œ un buon metodo.
ciao
Mauro

 ï¿œ un buon metodo.
Quindi posso fare come preferisco...

 esecuzione.
Anche qui, la result la generi al momento di restituirla, non per 
passaggi intermedi, a meno di casi particolari, ma anche in questi, 
spesso conviene tenere uno "stato" e poi da quello stato generare il 
risultato solo quando viene richiesto.
Per esempio per una classe che genera un PDF, tu gli passi i vari 
componenti, e la classe se li memorizza in uno stato interno (un array, 
di solito). Solo quando gli dici di restituirti il PDF, la classe legge 
il suo stato e genera il PDF. Non generi il risultato finale (il PDF) un 
po' alla volta man mano che aggiungi valori, ma in un singolo punto.
 
Spero di essere stato abbastanza chiaro. :)
Bye.

 Spero di essere stato abbastanza chiaro. :)
Chiarissimo, grazie.
Solo in un punto non ho capito:
Cosa intendi per linguaggi statici tipo Java?

 Cosa intendi per linguaggi statici tipo Java?
In Java (e in C, C++, e nei linguaggi statici) ogni variabile ha un suo 
tipo, definito prima della compilazione e verificato dal compilatore ogni 
volta che viene usata quella variabile.
In PHP (e in Python, Ruby, e tutti i linguaggi cosiddetti dinamici) una 
stessa variabile puo` contenere tipi di dati diversi.
Bye.

 volta che viene usata quella variabile.
Bon, non avevo capito a cosa ti stessi riferendo.
Comunque sia, IMHO, i linguaggi dinamici permettono di creare obrobri
di codice da far impallidire i morti.
Se non altro, con i linguaggi "statici" certe cose non le puoi fare
se non impegnandoti.

 non le puoi fare se non impegnandoti.
E` vero.
E` anche vero che certe volte per far funzionare una cosa con un 
linguaggio dinamico scrivi 10 righe di codice, con uno statico 1000, e 
spesso pieni di cast... :)
Bye.

 spesso pieni di cast... :)
Guarda, preferisco castare che ritrovarmi pieno di codice con
potenziali sql injection e così via.
Purtroppo, e molto codice PHP ne è un esempio, non tutti gli utenti
sanno programmare. Maggiori sono le libertà che dai, minori saranno
i controlli che gli utenti faranno sul codice e di conseguenza maggiori
saranno i bug.

 conseguenza maggiori saranno i bug.
L'sql injection non lo superi con una tipizzazione forte, visto che e` 
sempre una stringa quella che passi alla query sql.
E comunque il controllo viene fatto a compile time, e non a run time, 
quindi non puo` assolutamente sapere quali dati vengono scambiati, e non 
puo` proteggerti.
In generale la tipizzazione forte non ha niente a che fare con nulla che 
riguardi la sicurezza, ma solo, vagamente, col costringere piu` 
programmatori nello stesso team a passare certi tipi di valori.
Bye.

 programmatori nello stesso team a passare certi tipi di valori.
E' chiaro che non puoi usare la tipizzazione come misura di sicurezza
però se io dico che la variabile "i" deve essere un intero, te di certo
non pi passi tramite url un qualcosa tipo "  3' OR ... "



Pensaci un attimo: come ti arriva quel dato dal browser? Come stringa.
Quindi comunque dovrai fare un casting (con conversione implicita) o una 
conversione esplicita, prima di buttarlo nella variabile che contiene l'id 
della tua classe di astrazione dal DB.
In PHP fai lo stesso. Solo che tanta gente e` abituata a NON avere classi 
per l'astrazione del DB, quindi usa direttamente mysql_query(), mentre in 
Java hai diversi sistemi "consolidati" (come hibernate) e quindi sei 
(anche solo moralmente) costretto a usare quello.
Ma gia` se usi (per dire quello che conosco meglio) Zend Framework, te lo 
fa lui l'escape dei dati prima di passarli al DB.
Bye.

 della tua classe di astrazione dal DB.
Come hai detto te più sotto, molta gente spara dritto in mysql_query.
In tal caso, noi fai alcun casting, spari una stringa al posto di un
intero. Se ci fosse una tipizzazione, avresti un errore con scritto:
"Ue! ma che cacchio cerchi di fare?"



Si`, ma in Java (per dirne uno), se ci fosse mysql_query, faresti lo 
stesso: ti arriva una stringa, devi passare una stringa. Chi te lo fa fare 
di fare casting?
Ora immagina di togliere mysql.so dal php.ini, e di lasciarci solo 
pdo_mysql.so, e sei nella stessa situazione di Java: senza type casting 
hai comunque protezione contro le injection. Non puoi piu` usare 
mysql_query() ;)
Il "molta gente fa cosi`" non definisce una metodologia di sviluppo o un 
linguaggio di programmazione "migliore" o "peggiore". Definisce solo la 
"molta gente". :)
Bye.

 mysql_query() ;)
Le sql injection si evitano usando i prepared statement, e questo vale
in PHP, in Java e in qualsiasi altro linguaggio.

 prepared statement
Che sarebbero?

 Che sarebbero?
L'unica maniera decente di eseguire codice SQL in un linguaggio ospite.
Google is your friend.

 in PHP, in Java e in qualsiasi altro linguaggio.
In realta` in PHP puoi anche fare l'escape manualmente con 
mysql_real_escape_string(), ma in generale si`, con i prepared statement 
e` meglio.
Bye.

 e` meglio.
Non è soltanto una questione di escape. Con una semplice concatenazione
di stringhe puoi infilare come parametro il nome di una colonna o
addirittura interi statement SQL. Con i prepared statement no. Senza
tralasciare i notevoli vantaggi di perfomance nei database seri (in
mysql non so).
Y.

 addirittura interi statement SQL. Con i prepared statement no.
E se devi passare una stringa come argomento della query?
anche "  ' OR 1=1 " è una stringa e con i prepared statement, da
quel che ho visto, non risolveresti.
Se ho ben capito, fanno quello di cui parlavo poco fa, tipizzare un
parametro da passare alle query. Se voglio un int, potrò passare solo
un int. Ma se mi serve una stringa? Devo comunque fare l'escape.

 non risolveresti.
Fanno l'escape in automatico, quindi quella ' diventa \' (nei DB in cui 
serve il backslash) o '' (in quelli in cui va raddoppiato) o in altri 
modi, ma comunque non rimane un '.
Bye.

 modi, ma comunque non rimane un '.
Be, allora non è l'unico modo per evitare sql injection.
Posso benissimo fare il casting e successivamente fare l'escape.
Pensavo facessero altre operazioni....

 Pensavo facessero altre operazioni....
Le fanno. Va e studia.