64 – 65 DBMS – aspetti architetturali 3

Effettuiamo una rielaborazione del codice, rielaborandolo con gli oggetti e creeremo nostre classi personalizzate che richiamano quelle di mysqli.

Iniziamo col creare elogin_migliorata_oop.php e setup_con_DB_oop.php. nella nuova elogin , andremo ad includere setup_con_DB_oop.php:

include($_SERVER['DOCUMENT_ROOT']."\..\my_include\setup_con_DB_OOP.php");

Lasciamo invariato il solito controllo if sul metodo $_POST e il recupero mail e password dal form:

if ($_SERVER['REQUEST_METHOD'] == 'POST')
{
 //ok la pagina è stata davvero richiamata dalla form

 //recupero il contenuto della textbox email
 $email = $_POST['email'];

 //... e quello della textbox password
 $psw = $_POST['password'];

Di seguito creiamo un nuovo oggetto chiamato db_quiz che usa la classe db che creerò, e che prende come parametri, la cartella ini per le credenziali, i messaggi di errore, richiamati nell’inclusione e il terzo parametro che dice con true se stampare i messaggi di errore oppure no. Questo nuovo oggetto si preoccuperà della connessione e selezione del db.

$db_quiz = new db ($cartella:ini,$messaggi_errore, true);
if (! $db_quiz->get_stato() )
 die;

Inoltre dopo la creazione dell’oggetto facciamo un controllo se lo stato dell’oggetto db_quiz è false, terminiamo il programma.

Vediamo ora a pezzi la classe db che inserirò in setup_con_DB_oop.php

class db
{ 
 private
   $conn, //riferimento alla connessione 
   $cartella_ini, //posizione file ini
   $messaggi_errore, //array associativo con i messaggi di errore
   $access_data, //credenziali lette da .ini
   $stato, //esito (true/false) dopo creazione oggetto o
 //dopo aver tentato invio comando a mysQLL
   $descrizione_stato, //il messaggio di errore eventualmente da stampare
   $stampa_errori, //true / false
   $nl = "<br />";

si è previsto come variabili private la connessione $conn, la cartella ini $cartella_ini e i messaggi d’errore $messaggi_errore, che riceverò entrambi col costruttore e saranno poi memorizzati permanentemente, le credenziali d’accesso $access_data, contenute nel file confagtest.ini, dove andrò ad aggiungere localhost:

 host = localhost

infine nella variabile $stato metterò true o false a seconda degli esiti se l’oggetto è utilizzabile o meno. Qualora lo stato non fosse utilizzabile si è prevista una variabile $descrizione_stato che eventualmente potrà essere stampato in base a quello che dirà il costruttore e memorizzerà nella ulteriore variabile $stampa_errori. Se questa sarà true sarà poi la classa e l’utilizzatore di essa a stabilire cosa fare; infatti abbiamo poi definito un metodo pubblico getter che restituirà lo stato della variabile $stato:

public function get_stato()
 {return $this->stato; }

e una per la descrizione dello stato

public function get_descrizione_stato()
 {return $this->descrizione_stato;}

Ora veniamo dunque al fatidico costruttore :

public function __construct($cartella_ini, $messaggi_errore, $stampa_errori=true)
{
  .... 
  interno del costruttore che andrò a descrivere riga per riga
  .... 
}

I parametri del costruttore sono la cartella ini, l’array contenente i messaggi di errore $messaggi_errore e la loro stampa se si o no, di default true $stampa_errori=true.

Ora vediamo l’interno del costruttore,

 $this->accessData = parse_ini_file($cartella_ini.'confagtest.ini')

Recuperiamo i dati dalla variabile $cartella_ini contenuta in setup.php e li memorizziamo nella proprietà $accessData

$this->messaggi_errore = $messaggi_errore;

inserisco i messaggi degli errori nella proprietà messaggi_errore presa dalla variabile $messaggi_errore del file setup.php

$this->stampa_errori = $stampa_errori;

li stampo oppure no, di default è impostato a si

 $this->connessione();
  if( $this->stato )
  $this->scelta_data_base();
}

Si tenta di effettuare la connessione che se riesce sarà impostato a true dal metodo connessione e solo se lo stato è vero si seleziona il database.

Ecco il metodo connessione

private function connessione()
{
 if( !isset($this->conn) ) 
 {
 //NB: con @ si sopprimono i warning/errori del comando
 $this->conn = @mysqli_connect($this->accessData['host'],
 $this->accessData['username'],
 $this->accessData['password']);
 
 if(!$this->conn)
 { 
 $this->stato = false;
 $this->descrizione_stato = $this->messaggi_errore['connessione_fallita'];
 
 if($this->stampa_errori)
 echo $this->messaggi_errore['connessione_fallita'].$this->nl; 
 }
 else
 $this->stato = true;
 }
}

Se è settata la variabile del costruttore (eccesso di prudenza, ipotizzando un domani che la connessione non venga chiamata solo dal costruttore)

if( !isset($this->conn) )

procedo col tentativo di stabilire una nuova connessione, memorizzando il risultato nella variabile privata $conn. I parametri sono i soliti già visti, ma incorporati nella classe e memorizzati nella variabile privata $accessData. Se invece qualcosa va storto if (!$this->conn), si setta a false la variabile $stato e si imposta la descrizione del messaggio d’errore connessione_fallita.  Se il flag stampa_errori è a true if($this->stampa_errori), significa che chi ha invocato il costruttore ha lasciato la stampa degli errori a cura del metodo della classe, allora stampiamo il messaggio connessione_fallita preso da messaggi_errore.ini. Altrimenti è andato tutto bene e mettiamo lo stato dell’oggetto. Lo stato è controllato alla fine del costruttore che se è andato bene passa alla funzione scelta database:

private function scelta_data_base()
{
  if ( !@mysqli_select_db($this->conn, $this->accessData['dbname']) )
  { 
   $this->stato = false;
   $this->descrizione_stato = $this->messaggi_errore['db_non_trovato'];
 
   if($this->stampa_errori)
    echo $this->messaggi_errore['db_non_trovato'].$this->nl; 
   }
   else
   $this->stato = true; 
}

Se non si riesce ad agganciare il database

 if ( !@mysqli_select_db($this->conn, $this->accessData['dbname']) )

impostiamo lo stato a false, col messaggio db_non_trovato e se devo stampare il messaggio di errore , lo faccio.

La nostra classe per il momento è finita.

Ora rivediamo la elogin_migliorata_oop.php, aggiungendo il comando SQL

$comandoSQL =
 "select iduser, psw from users where email ='" . $db_quiz->sanifica_parametro($email) ."'";

il solito comando select con l’aggiunta del metodo sanifica_parametro che andremo ad aggiungere in setup_con_DB_oop.php

public function sanifica_parametro($parametro)
 { return $this->db->escape_string($parametro); }

aggiungiamo il richiamo al metodo select in elogin_migliorata_oop.php e memorizziamo il risultato nell’array $righe_estratte. Il metodo è strutturato in modo che se il comando non va a buon fine $righe_estratte=false, altrimenti inserisce una riga anche vuota, se non  trova la mail, ma la inserisce

$righe_estratte = $db_quiz->select($comandoSQL);

andiamo a vedere il metodo select nella classe db

 public function select($query)
 {
  $risultato_query = $this->db->query($query);
 
  if($risultato_query === false)
  {
   $this->stato = false;
   $this->descrizione_stato = $this->messaggi_errore['problema_con_server'];
   $this->close();
 
   if($this->stampa_errori) 
    echo $this->messaggi_errore['problema_con_server'].$this->nl;
 
   return false; 
  }
 else
 {
  $this->stato = true;
 
  $righe_estratte = array(); 
  while ( $riga = $risultato_query->fetch_assoc() ) 
  {$righe_estratte[] = $riga;}
 
  return $righe_estratte;
  } 
}

è obbligatorio controllare se $risultato_query è uguale forte (===) a false, altrimenti php se l’array è vuoto lo restituisce false, mentre a noi interessa che sia false se è nullo (non vuoto).

Un altro controllo sullo stato, ed un eventuale messaggio d’errore e eventuale stampa, infine returne false.

Altrimenti prepariamo l’array con le righe estratte $righe_estratte e lo restituiamo.

In elogin facciamo il controllo (===) sulle righe estratte e nel caso di false restituiamo lo stato, l’errore e il comando SQL

 if ($righe_estratte===false) //problema nell'esecuzione del comando
 {
  echo $db_quiz->get_descrizione_stato().$nl;
  echo "... mentre stavo eseguendo: ".$comandoSQL.$nl;
  die;
 }

a seguire il solito controllo se è stato premuto il pulsante accedi

 if (isset($_POST['btnAccedi']))
 {
  if ( count($righe_estratte)>0 ) //mail trovata, confrontiamo psw
  {
   $riga = $righe_estratte[0];
   //echo "Trovata".$nl;
   $autenticato = ($psw === $riga['psw']);
   }
   else
 $autenticato = false;
 
 $db_quiz->close();
 
 //redirect
 if($autenticato)
 {
 $_SESSION['iduser']=$riga['iduser']; 
 header("Location: main.php");
 }
 else
 header("Location: login.php?errore=autenticazione_fallita");
 
 exit;

 }

se accedi, possiamo estrarre il record delle righe e effettuare il controllo se la password è uguale a quella inserita, altrimenti non sei autenticato. Chiudiamo la connessione col metodo close e reindirizziamo al main.php. Altrimenti messaggio di errore autenticazione fallita.

Metodo close da inserire in fondo alla classe db, anche se prima dobbiamo inserire altri metodi

 public function close()
 { $this->db->close();}

Se invece è stato premuto nuovo utente

 else
 {
  if ( count($righe_estratte)>0 )
  {
  $db_quiz ->close();
  header("Location: login.php?errore=email_gia_inserita");
  exit;
  }
 
 //insert into users values (null, 'e@j.com','eee')
 $comandoSQL = "insert into users values (null,'".$email."','".$psw."')";
 $esito = $db_quiz->insert($comandoSQL);
 
 if ($esito)
 {
 $_SESSION['iduser'] = $esito;
 $db_quiz->close();
 header("Location: main.php");
 }
 else
 {
 $db_quiz->close();
 header("Location: login.php?errore=inserimento_fallito"); //inserimento fallito
 } 
 exit;
}

il solito controllo che la mail non esista già, altrimenti chiudiamo la connessione e stampiamo l’errore. Il solito comando SQL insert però da inserire nel metodo insert da aggiungere alla classe db

 public function insert($comandoSQL)
 {
  $esito = $this->db->query($comandoSQL);
 
 if($esito)
 {
 $this->stato=true;
 return $this->db->insert_id;
 }
 else
 {
 $this->stato = false;
 $this->close();
 $this->descrizione_stato = $this->messaggi_errore['problema_con_server'];
 
 if($this->stampa_errori) 
 echo $this->messaggi_errore['problema_con_server'].$this->nl;
 
 return false;
 }
}

Ci prova, se l’esito è positivo restituisce l’id autoincrementato usando la nostra connessione. Se qualcosa è andato storto, chiusura connessione, generazone errore ed eventuale stampa.