L'estensione MySQLi per l'interazione tra PHP e MYSQL




1. Configurazione per l'interfaccia ai database

MySQLi è l'abbreviazione di MySQL improveded ("perfezionato") ed è anche il nome di un'estensione per il linguaggio PHP, essa è stata realizzata per mettere a disposizione degli sviluppatori delle funzionalità più avanzate rispetto a quelle messe a disposizione dall'estensione MySQL per l'interazione tra le applcazioni Web based e il noto DBMS Open Source.
L'estensione MySQLi è stata introdotta nella release 4.1.2 di PHP e permette di sfruttare in modo semplice e veloce alcune nuove funzionalità messe a dispozione dalle versioni più recenti del Database Manager MySQL a partire dalla 3.2.2 fino alle più aggiornate 5.x.
Utilizzare questo strumento è molto semplice, la libreria che ne permette il funzionamento, denominata php_mysqli.dll, è infatti disponibile nativamente all'interno della cartella contenente le estensioni per PHP e sarà subito utilizzabile nei sistemi Linux; per attivarla inWindows, sarà invece sufficiente aggiungere nel file PHP.ini la seguente direttiva all'elenco denominato "Windows Extensions" o decommentarla se già presente:
extension=php_mysqli.dll
E' buona norma che questa direttiva sia posta di seguito a quelle già presenti per l'interazione con i database, in modo da non generare confusione; una volta salvate le modifiche effettuate, basterà riavviare il Web server per fruire delle funzionalità messe a disposizione da MySQLi.
Ma perché utilizzare questa estensione invece della classica estensione MySQL utilizzata in milioni di applicazioni? In realtà non è possibile sostenere in maniera assoluta che MySQLi sia preferibile rispetto alla sua alternativa più classica, in molti sostengono addirittura che si tratti di un progetto ancora incompleto e non ben supportato; grazie a MySQLi vi sono però degli indubbi vantaggi che meritano di essere sottolineati:
  • può essere utilizzata sia all'interno di applicazioni realizzate seguendo il paradigma procedurale che quello Orientato agli Oggetti;
  • fornisce il supporto nativo al protocollo binario di MySQL introdotto con la versione 4.1;
  • supporta stored procedures, query multiple e transactions;
  • permette impostazioni avanzate per la connesssione tramite mysqli_init();
  • supporta le prepared statements per il caching delle interrogazioni;
  • garantisce prestazioni più elevate dell'estensione MySQL, è ritenuta più sicura e fornisce in genere migliori risposte in fase di debugging.
Come anticipato, gli strumenti forniti da MySQLi possono essere impiegati sia all'interno di applicazioni il cui codice è stato realizzato secondo il paradigma procedurale che in uno script creato grazie all'Object-Oriented Programming; infatti, come si vedrà a breve nel dettaglio, la gran parte delle funzioni messe a disposizione da questa estensioni possono essere sostituite da un metodo concepito per uno scopo analogo.
Naturalmente, anche le funzioni dell'estensione MySQL possono essere inserite all'interno di classi, ma la differenza sostanziale sta nel fatto i metodo di MySQli vengono forniti nativamente.
Le funzioni dell'estensione MySQLi sono state concepite per rendere questo strumento il più possibile compatibile con l'estensione MySQL; nella maggior parte dei casi è infatti possibile ritrovare le funzioni classiche con una "i" finale aggiunta in coda al nome: per esempio,mysql_connect() è stata così "tradotta" in mysqli_connect() mentre mysql_query() è diventata mysqli_query().
Ciò nonostante è opportuno sottolineare che, al di là delle somiglianze relative al nome, non tutte le funzioni di MySQLi possono essere utilizzare nello stesso modo di quelle fornite da MySQL.

2. Connessione a MySQL con MySQLi
Per la connessione tra un'applicazione Web based realizzata in PHP e un Database Manager MySQL tramite l'estensione MySQLi, sono disponibili due procedure, una basata sul paradigma procedurale, la seconda basata invece sull'approccio per oggetti.
Nel primo caso la connessione avviene per funzione, nel caso della OOP essa avviene invece per istanza.
La connessione effettuata utilizzando la programmazione procedurale si basa su una funzione specifica denominata mysqli_connect() che accetta come parametri i seguenti dati:
  • hostname: il nome di Rete della macchina ospitante il Database server, ad esempio un indirizzo IP o "localhost" nel caso di installazioni locali;
  • username: il nome dell'utente da utilizzare per la connessione, esso dovrà avere i privilegi necessari per l'accesso e la manipolazione dei dati;
  • password: la parola chiave necessaria per l'autenticazione dell'utente che si desidera utilizzare in connessione;
  • database: il nome del database che si desidera interfacciare alla propria applicazione.
I più accorti si saranno immediatamente resi conto di un'importante differenza tra le funzioni mysqli_connect() e mysql_connect(), la seconda infatti non accetta come parametro il nome della base di dati da selezionare, a questo scopo è infatti destinata la funzione mysql_select_db().
MySQLi mette a disposizione una funzione omologa, denominata non a casomysqli_select_db(), che però deve essere utilizzata esclusivamente nel caso in cui si presenti la necessità di modificare il database predefinito (cioè quello selezionato tramite mysqli_connect()).
Un esempio di connessione tramite approccio procedurale potrebbe essere il seguente:

<?php
// connessione a MySQL con mysqli_connect()
// definizione delle variabili
$host = "localhost";
$user = "username";
$pass = "password";
$db = "data";
// connessione al DBMS
$conn = @mysqli_connect($host, $user, $pass, $db)
or die(mysqli_connect_error());
?>

mysqli_connect_error() è una funzione apposita per notificare eventuali malfunzionamenti in fase di connessione (ad esempio: un errore di autenticazione).
Per quanto riguarda invece le connessioni tramite approccio per oggetti, è stato anticipato che in questo caso il fulcro della procedura è basato essenzialmente su un'istanza:

<?php
// connessione a MySQL per istanza
// definizione delle variabili
$host = "localhost";
$user = "username";
$pass = "password";
$db = "data";

// connessione al DBMS
$conn = @new mysqli($host, $user, $pass, $db);
if( mysqli_connect_errno())
{
die(mysqli_connect_error());
}
?>

Nel esempio proposto, viene effettuata un'istanza dell'oggetto "$conn" appartenente alla classe "mysqli"; come è possibile osservare, questa operazione prevede il passaggio degli stessi parametri previsti per la funzione mysqli_connect().
Nel codice precedente viene introdotta anche un'altra funzione, mysqli_connect_errno()che ha il compito di produrre, in caso di malfunzionamento, il numero identificativo dell'errore notificato in connessione dal Database manager; se questo numero dovesse essere generato, allora la funzione mysqli_connect_error() comunicherà all'utente la tipologia di eccezione verificatasi.
Per la chiusura di una connessione, l'estensione MySQLi mette a disposizione la funzionemysqli_close() che accetta come parametro il link alla connessione che si desidera chiudere, nel caso in cui sia attiva un'unica connessione non sarà necessario passarne il link alla funzione.
In alternativa, nel caso dell'approccio OOP, sarà possibile utilizzare il metodomysqli::close() come istanza della classe di connessione:

// chiusura di una connessione con il metodo mysqli::close()
@$conn->close();


3. Connessione con mysqli_real_connect() e il metodo 

real_connect()
La funzione mysqli_connect() e l'istanza alla classe di connessione non sono le uniche soluzioni disponibili per l'accesso al Database manager MySQL messe a disposizione da MySQLi.
In alternativa, è possibile utilizzare la funzione mysqli_real_connect() o il relativo metodo mysqli::real_connect; la funzione mysqli_real_connect() funziona in modo molto simile a mysqli_connect() per aprire una connessione al DBMS, ma presenta come differenza sostanziale la possibilità di specificare dei parametri addizionali (Flags).
I parametri addizionali previsti per questa funzione sono cinque e rappresentano altrettante opzioni di connessione:
  1. MYSQLI_CLIENT_COMPRESS: permette l'utilizzo del protocollo di compressione;
  2. MYSQLI_CLIENT_FOUND_ROWS: restituisce il numero di record rilevati, non quello dei record coinvolti dall'interrogazione;
  3. MYSQLI_CLIENT_IGNORE_SPACE: consente l'utilizzo di spazi vuoti dopo i nomi delle funzioni e permette di rendere riservati tutti i nomi delle funzioni;
  4. MYSQLI_CLIENT_INTERACTIVE: attiva l'impostazione di MySQLinteractive_timeout espressa in secondi invece di wait_timeout (sempre espressa in secondi), in questo modo la connessione verrà chiusa dopo l'intervallo di tempo definito in mancaza di istruzioni al DBMS;
  5. MYSQLI_CLIENT_SSL: utilizza il protocollo crittografico SSL (Secure Sockets Layer) che consente una comunicazione sicura e l'integrità dei dati su reti TCP/IP.
Per questioni di sicurezza, la funzione mysqli_real_connect() non supporta il flagMULTI_STATEMENT per l'invio simultaneo di più interogazioni al DBMS, a questo scopo sarà invece possibile utilizzare come alternativa una funzione apposita, denominatamysqli_multi_query() (o il relativo metodo mysqli::multi_query).
L'utilizzo della funzione mysqli_real_connect() all'interno di un contesto procedurale può essere riassunto nel seguente esempio:
<?php
// connessione con il metodo mysqli_real_connect()

// inizializzazione di MySQLi
$link = @mysqli_init();

// controllo sull'inizializzazione
if (!isset($link))
{
  die("Errore nell'inizializzazione di MySQLi.");
}

// definizione delle variabili
$host = "localhost";
$user = "username";
$pass = "password";
$db = "data";

// connessione al DBMS
if (!mysqli_real_connect($link, $host, $user, $pass, $db))
{
  die("Errore in connessione (" . mysqli_connect_errno() . ") " . mysqli_connect_error());
}else{
  echo "Connessione avvenuta con successo.";
}

// chiusura della connessione
@mysqli_close($link);
?>
Si analizzi il codice proposto:
  • viene inizializzato MySQLi tramite la funzione mysqli_init() che crea una risorsa ("link") da passare come argomento all'istruzione di connessione;
  • vengono definite le variabili da utilizzare come parametri a mysqli_real_connect();
  • viene stabilita la connessione a meno di eventuali errori;
  • viene chiusa la connessione tramite la funzione mysqli_close() a cui viene passata come argomento la risorsa creata in inizializzazione.
Si analizzi ora la stessa procedura basata questa volta sul paradigma ad oggetti e sul metodo mysqli::real_connect:
<?php
// connessione con il metodo mysqli::real_connect

// inizializzazione di MySQLi
$mysqli = @mysqli_init();

// controllo sull'inizializzazione
if (!isset($mysqli))
{
  die("Errore nell'inizializzazione di MYSQLi.");
}

// definizione delle variabili
$host = "localhost";
$user = "username";
$pass = "password";
$db = "data";

// connessione al DBMS
if (!$mysqli->real_connect($host, $user, $pass, $db))
{
  die("Errore in connessione (" . mysqli_connect_errno() . ") " . mysqli_connect_error());
}else{
  // notifica in caso di conessione avviata
  echo "Connessione avvenuta con successo.";
}

// chiusura della connessione
@$mysqli->close();
?>
Le differenze tra i due codici proposti non sono tante: anche in questo caso MySQLi viene inizializzato tramite mysqli_init(), ma la risorsa creata non deve essere passata al metodo di connessione come parametro, perché disponibile per istanza all'interno della classe MySQLi; di conseguenza non sarà necessario passare questo argomento neanche al metodo mysqli::close che chiuderà la connessione.

4. Esecuzione delle query
L'estensione MySQLi mette a disposizione degli sviluppatori che desiderano inviare delle query tramite le proprie applicazioni, una funzione apposita, denominata mysqli_query()a cui corrisponde il metodo mysqli::query; queste componenti accettano come parametro un'istruzione espressa in linguaggio SQL e potranno essere utilizzati all'interno di costrutti procedurali o basati sul paradigma OOP.

Il codice seguente mostra un esempio di utilizzo della funzione mysqli_query() all'interno di un approccio procedurale:
<?php
// esecuzione di query con mysqli_query

// connessione al DBMS
$link = @mysqli_connect("localhost", "username", "password", "data");

// controllo sullo stato della connessione
if (mysqli_connect_errno())
{
  echo "Connessione fallita: " . die (mysqli_connect_error());
}

// query per la creazione di una tabella
if ($result =  @mysqli_query($link, "CREATE TABLE prova (id  INT(2), nome VARCHAR(50))"))
{
  echo "Tabella creata con successo.";
}

// liberazione della memoria dal risultato della query
@mysqli_free_result($result);

// chiusura della connessione
@mysqli_close($link);
?>
L'esempio mostra come la funzione mysqli_query() funzioni esattamente come mysql_query() per l'estensone MySQL, i parametri accettati sono infatti la risorsa prodotta dalla procedura di connessione ("$link" in questo caso) e l'interrogazione inviata al DBMS.
La funzione mysqli_free_result() è opzionale, ma svolge l'utile compito di liberare lo spazio di memoria occupato dal risultato di una query una volta che questo viene ottenuto.
Si analizzi ora la stesso esempio proposto in precedenza, ma "tradotto" utilizzando il paradigma orientato agli oggetti:
<?php
// esecuzione di query con mysqli_query

// connessione al DBMS per istanza
$mysqli = @new mysqli("localhost", "username", "password", "data");

// controllo sullo stato della connessione
if (mysqli_connect_errno())
{
  echo "Connessione fallita: " . die (mysqli_connect_error());
}

// query per la creazione di una tabella
if ($result = @$mysqli->query("CREATE TABLE nuova_prova (id  INT(2), nome VARCHAR(50))"))
{
  echo "Tabella creata con successo.";
}

// liberazione della memoria dal risultato della query
@$result->close();

// chiusura della connessione
@$mysqli->close();
?>
In questo caso la connessione viene prodotta per istanza di una classe ("mysqli"); una volta effettuato il controllo necessario per stabilire l'effettivo avvio della connessione, è possibile procedere con l'esecuzione della query per la creazione della tabella tramite il metodo mysql::query.
Eseguita la query, è possibile introdurre il metodo mysqli_result::close per liberare la memoria occupata dal risultato dell'interrogazione (in alternativa è possibile utilizzare i metodi equivalenti mysqli_result::free o mysqli_result::free_result), per poi passare alla chiusura della connessione tramite il metodo mysqli::close.

5. Estrazione dei record
Una volta eseguita una query, tramite l'estensione MySQLi è possibile utilizzare alcune funzioni appositamente dedicate all'estrazione dei record; in questa parte della trattazione verrà presa in analisi la funzione mysqli_fetch_array() che consente di visualizzare tutti i record coinvolti da una determinata interrogazione; tale funzione, molto simile alla corrispondente mysql_fetch_array() dell'estensione MySQL, produce un array accettando come parametro il risultato di una query; questo array può essere di diverso tipo sulla base del secondo parametro passato a mysqli_fetch_array():
  • MYSQLI_NUM: genera un array numerico;
  • MYSQLI_ASSOC: genera un array associativo;
  • MYSQLI_BOTH: genera simultaneamente un array numerico e associativo.
Il seguente esempio permette di analizzare l'utilizzo della funzione mysqli_fetch_array() all'interno di un contesto procedurale. 

<?php
// estrazione dei dati con mysqli_fetch_array()

// connessione al database
$link = @mysqli_connect("localhost", "username", "password", "data");

// controllo sullo stato della connessione
if (mysqli_connect_errno())
{
  echo "Connessione fallita: " . die (mysqli_connect_error());
}

// esecuzione della query
$query = "SELECT nome FROM prova ORDER by id DESC";
$result = @mysqli_query($link, $query);

// controllo sul numero dei record coinvolti
if(@mysqli_num_rows($result)!=0)
{
  // risultato sotto forma di array numerico
  while($row = mysqli_fetch_array($result, MYSQLI_NUM))
  {
    echo $row[0] . "<br>";
  }

  // risultato sotto forma di array asscociativo
  while($row = mysqli_fetch_array($result, MYSQLI_ASSOC))
  {
    echo $row['nome'] . "<br>";
  }

  // risultato sotto forma di array numerico o associativo
  while($row = mysqli_fetch_array($result, MYSQLI_BOTH))
  {
    echo $row['nome'] . "<br>";
    echo $row[0] . "<br>";
  }
}

// liberazione della memoria dal risultato della query
@mysqli_free_result($result);

// chiusura della connessione
@mysqli_close($link);
?>
I valori presenti all'interno di un array prodotto tramite mysqli_fetch_array() potrà essere sviluppato attraverso un ciclo; nel caso in cui il flag associato come parametro alla funzione si MYSQLI_NUM, verrà prodotto un array di tipo numerico in cui i valori presenti all'interno del vettore saranno indicizzati da "0" a "n", dove "n" è l'ultimo numero indice dell'array; nell'esempio proposto viene effettuata un'interrogazione che coinvolge un solo campo, quindi l'unico indice disponibile sarà "0".
Nel caso in cui invece il secondo argomento della funzione sia MYSQLI_ASSOC, verrà prodotto un array associativo in cui i valori verranno indicizzati tramite il nome dei campi coinvolti dalla query; nel caso dell'esempio proposto, vengono estratti unicamente i valori relativi al campo "nome" della tabella "prova", per cui l'unico indice presente nell'array associativo sarà appunto "nome".

MYSQLI_BOTH ("both" significa appunto in lingua Inglese "entrambi"), produce un array numerico e associativo, quindi sarà possibile sia riferirsi agli indici da "0" a "n" che a quelli che hanno come nome il nome dei campi che forniscono i record tramite query.
Naturalmente, anche per la funzione mysqli_fetch array() esiste un metodo corrispondente denominato mysqli_result::fetch_array; il codice seguente mostra un esempio dell'utilizzo di questo metodo all'interno di un contesto orientato agli oggetti:
<?php
// estrazione dei dati con il metodo mysqli_result::fetch_array

// connessione al database per istanza
$mysqli = @new mysqli("localhost", "username", "password", "data");

// controllo sullo stato della connessione
if (mysqli_connect_errno())
{
  echo "Connessione fallita: " . die (mysqli_connect_error());
}

// esecuzione della query
$query = "SELECT nome FROM prova ORDER by id DESC";
$result = @$mysqli->query($query);

// controllo sul numero dei record coinvolti
if(@$result->num_rows > 0)
{
  // risultato sotto forma di array numerico
  while($row = $result->fetch_array(MYSQLI_NUM))
  {
    echo $row[0] . "<br>";
  }

  // risultato sotto forma di array asscociativo
  while($row = $result->fetch_array(MYSQLI_ASSOC))
  {
    echo $row['nome'] . "<br>";
  }

  // risultato sotto forma di array numerico o associativo
  while($row = $result->fetch_array(MYSQLI_BOTH))
  {
    echo $row['nome'] . "<br>";
    echo $row[0] . "<br>";
  }
}

// liberazione della memoria dal risultato della query
@$result->close();

// chiusura della connessione
@$mysqli->close();
?>
A livello funzionale, la differenza tra l'approccio procedurale e quello orientato agli oggetti è veramente molto sottile; in questo secondo caso la connessione avviene per istanza, a questo punto sarà possibile sottoporre la query al metodo mysqli::query e passarla come parametro al metodo mysqli_result::fetch_array che, sottoposto a ciclo while, mostrerà i risultati come valori di array di tipo diverso (numerici o associativi) a seconda del flag passato come secondo argomento alla funzione.
Nel primo esempio è stato effettuato un controllo sul numero dei record coinvolti dalla query utilizzando la funzione mysqli_num_rows(), a questa funzione corrisponde un metodo, denominato mysqli_result->num_rows, che potrà essere utilizzato in applicazioni la cui costruzione segue il paradigma object oriented, come nel secondo esempio proposto.


Commenti