Access in sintesi
Una questione che di tanto in tanto si affaccia sul forum php riguarda la possibilità di interagire con Microsoft Access: anche se il binomio php-mysql rappresenta un duo probabilmente imbattibile nell' ambito WEB, chiunque operi con server Windows può trovarsi ad avere a che fare con il popolare database desktop di Microsoft, e usando le dovute accortezze i risultati possono essere molto soddisfacenti.
In realtà ciò che noi identifichiamo comunemente come "database Access" comprende tre componenti principali: un file *.mdb, il motore JET che opera dietro le quinte per la gestione del database (elabora le queries e tiene sotto controllo le tabelle), e l'interfaccia fornita dall'applicativo MS Office.
L'ultimo componente è scindibile, tanto che nulla ci impedisce di interagire direttamente con JET attraverso Vbscript, Visual Basic o magari proprio Php.
Access appartiene alla categoria dei DBMS (Database Management System) desktop che viene contrapposta a quella dei DBMS client/server della quale, ad esempio, fanno parte Mysql e Sql Server 2000.
Un Server di database fa quello che dice il suo nome: è un robusto sistema di gestione che rimane in esecuzione permanente in background, supervisiona gli accessi e inoltra le richieste ai database che si trovano sotto il suo controllo.
Anche i file di Access possono essere condivisi tra più utenti, ma non c'è alcuna interfaccia che controlli gli accessi e risponda alle richieste.
In maniera molto sbrigativa possiamo dire che un DBMS desktop si mostra efficiente soprattutto nell'ambito della macchina in cui si trova e non a livello di rete, infatti quando lo utilizziamo come database di appoggio per un sito WEB il file *.mdb di Access solitamente si trova in una sottodirectory protetta dagli accessi esterni, ed i nostri script si collegano ad esso tramite un percorso fisico.
Sembra che il futuro di Access stia nel diventare l'interfaccia privilegiata per SQL Server (con il rilascio di Access 2002 non c'è stato un aggiornamento rispetto alla versione 4.0 del motore JET), nonostante tutto JET avrà ancora vita lunga poichè presenta anche caratteristiche non trascurabili: è un buon database di sola lettura che archivia dati e struttura in un unico file *.mdb facilmente trasferibile, inoltre pur essendo perfettamente in grado di sostenere un sito di medie dimensioni è molto più semplice da usare di un DBMS client/server.
Il modo migliore per operare con Access attraverso Php è ricorrere alle apposite funzioni ODBC (Open Database Connectivity, l'API universale di Microsoft), questo però significa che dobbiamo rendere disponibile il database per ODBC creando un DSN di sistema (Data Source Name), ossia un file speciale che contiene informazioni relative al percorso e al tipo di database.
Per nostra sfortuna molti fornitori di hosting non rendono disponibile un DSN per il database e consentono soltanto connessioni senza DSN, e questa è una delle situazioni in cui le funzioni COM di Php sono di grande aiuto.
COM, pensare per oggetti
Chi programma in ambiente Windows ha a disposizione diverse architetture per l'Accesso ai dati che spesso si sovrappongono e sono in grado di effettuare le stesse operazioni, operando ad un livello più o meno basso.
L'acronimo COM sta per Component Object Model, fu introdotto da Microsoft con lo scopo di mettere a disposizione un modo semplice e universale per instaurare una comunicazione tra i vari moduli dei programmi per Windows.
L'argomento è piuttosto complesso e questo articolo rischierebbe di trasformarsi in un piccolo trattato di programmazione in ambiente Windows se ci occupassimo di concetti quali COM, ODBC, OLEDB, DAO, ADO quindi per eventuali approfondimenti vi invito a fare riferimento alla relativa documentazione Microsoft.
Per i nostri scopi è sufficiente sapere che le funzioni COM sono disponibili soltanto nella versione WIN di Php, e grazie ad esse avremo l'impressione di programmare in ASP utilizzando la sintassi Php (anziché i linguaggi propri di quell'ambiente, Vbscript e Javascript).
Questo significa che dovremo abituarci alla sintassi Object Oriented e ad utilizzare le classi "prefabbricate" e gli oggetti messi a disposizione in ASP un po' come facciamo abitualmente con le funzioni predefinite di Php: ovvero limitandoci a capire cosa fanno e utilizzando a nostro vantaggio proprietà e metodi senza porci troppe domande sul loro funzionamento interno.
Ciò comporterà anche che sarà più utile possedere già una qualche esperienza con gli oggetti ADO e il JET-SQL piuttosto che una conoscenza approfondita di Php.
ADO, ActiveX Data Objects
È un'architettura che fornisce oggetti di alto livello per l'accesso ai dati, e in particolare contiene 6 oggetti per la comunicazione con i database.
Qui ci limiteremo ad elencare sinteticamente i 3 principali:
- l'oggetto Connection consente di stabilire la connessione.
- l'oggetto Recordset consente di operare con i dati contenuti in una tabella e recuperati attraverso una query.
- l'oggetto Field è una collection (una sorta di array) che rappresenta le colonne di una tabella.
JET-SQL
È il "dialetto" Access dello Structured Query Language, si discosta solo in parte dallo standard (che comunque nessun DBMS supporta interamente).
Consente di operare con JET effettuando quasi tutte le operazioni possibili attraverso i soli metodi degli oggetti ADO: è possibile creare stored procedures, proteggere il database con password etc. etc.
Probabilmente l'unica cosa che non è in grado di fare è creare un file di database dal nulla (più avanti vedremo come utilizzare ADOX, un estensione di ADO, a questo scopo).
Nell'articolo ho tralasciato volutamente una descrizione della sintassi JET-SQL, in parte perché quasi sempre è comprensibile per chi ha confidenza con lo standard SQL, e un po' perché ai seguenti links è possibile trovare degli articoli molto esaurienti (informazioni in lingua inglese):
JET-SQL: nozioni di base
Intermediate JET-SQL
Caratteristiche avanzate di JET-SQL
Dopo questa lunga ma, spero, utile premessa è finalmente giunto il momento di mettere le mani su un po' di codice: eseguite gli esempi nell'ordine in cui vengono descritti, e se volete testate di tanto in tanto i risultati aprendo il file mytest.mdb con Access (l'applicativo office).
Tutti gli script sono stati testati in Windows 2000, sia con Apache che con IIS.
La stringa di connessione
Quando non è possibile impostare un DSN, le informazioni necessarie per la connessione al database devono essere inserite all'interno di una stringa, che poi verrà passata come argomento al metodo Open() dell'oggetto predefinito ADODB.Connection.
Esempio di stringa di Inizializzazione/Connessione
Deve contenere almeno due elementi:
1) l'indicazione del motore di database, in questo caso JET/Access
$cn_string="Provider=Microsoft.Jet.OLEDB.4.0;" ;
2) l'origine dati, cioè il percorso del database.
Bisognerà fornire sempre il percorso assoluto, qui di seguito nel formato richiesto se si esegue php con Apache, mentre nel caso in cui ci dovessimo appoggiare ad IIS (il webserver Microsoft) gli slash ("/") potanno essere sostituiti da backslash ("")
$cn_string.="Data Source=C:/www/prova/PhpAccess/mytestdb.mdb;" ;
La stringa di connessione può contenere anche altre informazioni a seconda delle operazioni che si vogliono effettuare, ne presentiamo alcune qui di seguito
Mode
Indica la modalità di apertura del database, quando non viene specificata l'apertura è in lettura/scrittura condivisa (costante ADO adModeReadWrite, valore 3).
Nell'esempio l'apertura è in modalità esclusiva (costante ADO adModeShareExclusive, valore 12) che è necessaria quando ad esempio si desidera modificare la password condivisa del database.
$cn_string.="Mode= 12 ;"
Engine type
È utile solo al momento della creazione del database indica la versione del motore JET che utilizzeremo, se il database esiste già è ininfluente per la connessione. Il codice numerico 5 corrisponde a JET 4.0 (Access 2000-2002).
$cn_string.="Jet OLEDB:Engine Type= 5;"
Password (protezione condivisa)
Se il database è protetto da una password unica uguale per tutti (livello di protezione condiviso) dobbiamo impostarla qui al momento della connessione
$cn_string.="Jet OLEDB:Database Password= apritisesamo;"
Utenti (protezione a livello utenti)
Se il database è protetto a livello utenti (ognuno con la propria password ed i propri permessi) dobbiamo indicarli in questo modo:
$cn_string.="User ID=user; Password=password;"
Creare un database sul server
Ovviamente nulla impedisce di creare il database attraverso l'interfaccia Office e poi fare l'upload del file .mdb ma, visto che c'è modo di fare tutto attraverso il codice, ci è parso interessante illustrare anche questa possibilità.
<?php
/****
Creiamo delle costanti che definiscono il tipo di motore di database JET,
l'ultima versione, JET 4.0, corrisponde ad Access 2000 e 2002
e viene indicata con il codice numerico 5
****/
define("JET10", 1) ;
define("JET11", 2) ;
define("JET20", 3) ;
define("JET3x", 4) ;
define("JET4x", 5) ;
/****
Inserisci qui il percorso dove vuoi
che il database venga creato,
nell'esempio la stessa directory dello script,
il formato è quello richiesto da Apache
****/
$path= "C:/www/prova/PhpAccess/" ;
/****
Identifichiamo il database
****/
$db_name= "mytest.mdb" ;
$dsource=$path.$db_name ;
/****
La stringa di inizializzazione/connessione
****/
$cn_string="Provider=Microsoft.Jet.OLEDB.4.0;" ;
$cn_string.="Jet OLEDB:Engine Type=".JET4x.";" ;
$cn_string.="Data Source=$dsource;" ;
/****
Creo un'istanza dell'oggetto predefinito
ADOX.Catalog attraverso COM (ADOX è un'estensione di ADO)
****/
if ( file_exists($dsource) ){
die("Il database esiste già") ;
}
$db= new COM("ADOX.catalog") or die("Impossibile istanziare ADOX") ;
/****
Creo il database tramite il metodo Create
dell'oggetto ADOX.Catalog
****/
$db->Create($cn_string) or die("Impossibile creare db") ;
/****
Liberiamo la memoria
****/
$db->Release() ;
$db= null ;
?>
La connessione al database
Ora che il database è stato creato, vediamo come connetterci ad esso sempre attraverso COM e ADO
<?php
/****
Inserisci qui il percorso dove si trova il database,
****/
$path= "C:/www/prova/PhpAccess/" ;
/****
identifichiamo il database
****/
$db_name= "mytest.mdb" ;
$dsource=$path.$db_name ;
/****
Come sempre la stringa di connessione
****/
$cn_string="Provider=Microsoft.Jet.OLEDB.4.0;" ;
$cn_string.="Data Source=$dsource;" ;
/****
Istanzio un oggetto Connection
e apro la connessione con il database
atraverso il metodo Open() dell'oggetto.
Il metodo prende come argomento
la stringa di connessione, oppure il DSN
quando c'è.
****/
if (!file_exists($dsource) ){
die("Il database non esiste") ;
}
$cn= new COM("ADODB.Connection");
$cn->open($cn_string) ;
// -- CODICE ---
//Interrogo/modifico il DB
// --- CODICE---
/****
Chiudo la connessione
e libero la memoria
****/
$cn->Close() ;
$cn->Release() ;
$cn= null ;
?>
Interrogare e modificare il database
Dopo la connessione possiamo inviare dei comandi al database, per fare ciò dovremo istanziare un oggetto RECORDSET (più avanti vedremo meglio cosa esso sia) e passare la richiesta al suo metodo Open().
Creare una tabella
Aggiungiamo una tabella al database con SQL (ma non è l'unico modo):
<?php
/*
Definiamo semplice tabella di esempio con tre campi: COUNTER corrisponde
ad un campo numerico AUTOINCREMENT di mysql. Le parentesi quadrate sono
obbligatorie solo se il nome del campo contiene degli spazi
*/
$query="CREATE TABLE Test_Table (
[id] COUNTER NOT NULL,
[Nome] text(15) NOT NULL,
[Cognome] text(15) NOT NULL,
PRIMARY KEY ([id])
);" ;
/* I parametri di connessione */
$path= "C:/www/prova/PhpAccess/" ;
$db_name= "mytest.mdb" ;
$dsource=$path.$db_name ;
$cn_string="Provider=Microsoft.Jet.OLEDB.4.0;" ;
$cn_string.="Data Source=$dsource;" ;
/* La connessione */
if (!file_exists($dsource) ){
die("Il database non esiste") ;
}
$cn= new COM("ADODB.Connection");
$cn->open($cn_string) ;
/* Istanziamo un oggetto Recordset
e inviamo la query attraverso
il metodo Open()
*/
$rs= new COM("ADODB.Recordset") ;
$rs->Open($query,$cn) ;
/* Pulizia dell'oggetto Recordset */
$rs->Release() ;
$rs= null ;
/* Chiudo la connessione e libero la memoria */
$cn->Close() ;
$cn->Release() ;
$cn= null ;
?>
Inserire record
Ecco una query JetSQL molto semplice che ci consentirà di inserire alcuni valori nella nostra tabella di prova:
$query="insert into Test_Table (nome,cognome)
values ('Mario','Rossi')" ;
A differenza della sintassi SQL/Mysql per far sì che il campo COUNTER incrementi automaticamente il suo valore, non possiamo inserire NULL ma dobbiamo limitarci ad ignorare la presenza del campo.
Sostituite questa query a quella dell'esempio precedente ed eseguite lo script per due o tre volte (inserendo alcuni record contenenti nomi diversi) per proseguire con gli esempi successivi.
Recuperare valori
<?php
/* La query SQL, le parentesi quadrate sono
necessarie solo quando i nomi dei campi presentano spazi */
$query="select [nome],[cognome] from Test_Table" ;
/* I parametri di connessione */
$path= "C:/www/prova/PhpAccess/" ;
$db_name= "mytest.mdb" ;
$dsource=$path.$db_name ;
$cn_string="Provider=Microsoft.Jet.OLEDB.4.0;" ;
$cn_string.="Data Source=$dsource;" ;
/* La connessione */
if (!file_exists($dsource) ){
die("Il database non esiste") ;
}
$cn= new COM("ADODB.Connection");
$cn->open($cn_string) ;
/* Istanziamo un oggetto Recordset
e inviamo la query attraverso
il metodo Open() */
$rs= new COM("ADODB.Recordset") ;
$rs->Open($query,$cn) ;
/* Ciclo per recuperare i valori dal recordset
EOF= tutto il set di dati è stato esaminato
e il cursore è giunto in fondo */
while(!$rs->EOF){
echo($rs->Fields['nome']->value." ".$rs->Fields['cognome']->value."
") ;
$rs->MoveNext() ;
}
/* Chiusura Recordset (da non farsi nelle query di comando) */
$rs->Close() ;
/* Pulizia dell'oggetto Recordset */
$rs->Release() ;
$rs= null ;
/* Chiudo la connessione e libero la memoria */
$cn->Close() ;
$cn->Release() ;
$cn= null ;
?>
L'oggetto Recordset
È giunto il momento di compiere una breve analisi dell'oggetto RECORDSET con cui abbia mo lavorato fino ad ora.
Come avrete notato eseguire una query select attraverso il metodo Open() dell'oggetto Recordset non è molto differente dall' eseguire in Mysql:
$risultato=mysql_query($query) ;
e poi scorrere i dati contenuti in $risultato in questo modo,
while ($row = mysql_fetch_object($risultato)) {
echo $row->user_id;
echo $row->fullname;
}
L'esecuzione della query non fa altro che restituirci un set di dati le cui righe sono array che corrispondono ai record, mentre gli elementi di ogni array corrispondono al contenuto dei campi dei record stessi.
Tuttavia il metodo Open() è un po' più complicato e può contenere altri parametri facoltativi oltre alla query e all'identificativo della connessione, questa è la forma completa:
$rs->Open($comando,$id_connessione,$cursor_type,$lock_type,$tipo_comando) ;
tipo_comando specifica la modalità di richiesta fatta al database, la richiesta può avere forme diverse:
il nome di una tabella della quale vogliamo visualizzare tutti i records, una stringa SQL o anche l'identificativo di una stored procedure memorizzata in precedenza nel database.
Normalmente JET è in grado di determinare da solo questo parametro, ma per motivi di efficienza nelle prestazioni è preferibile inserire l'indicazione esplicitamente.
Nei nostri esempi abbiamo privilegiato l'invio di comandi attraverso query SQL per diverse ragioni che vedremo tra poco.
Il valore da fornire al parametro tipo_comando qualora si passi una query SQL è 1 (corrispondente alla costante VB adCmdText), una lista di tutti i comandi possibili è reperibile qui.
cursor_type determina il modo in cui possiamo scorrere il recordset: il cursore di default (che è il più efficiente in termini di prestazioni) consente di scorrere il set di risultati soltanto dall'alto al basso (forward-only, valore 0) e soltanto una volta.
Un cursore diverso (detto "scorrevole", valore 1) permette di definire in anticipo quante righe di risultati la query ha restituito (attraverso il metodo $rs->RecordCount()), tuttavia di solito la cosa davvero importante è capire se vi siano stati risultati oppure no, e questo possiamo determinarlo anche se operiamo con un cursore forward-only:
sarà sufficiente verificare se appena aperto il recordset si trova già EOF, come nell' esempio seguente:
/* Ciclo per recuperare i valori dal recordset */
//SE il cursore NON è già EOF .....
if(!$rs->EOF){
while(!$rs->EOF){
echo($rs->Fields['nome']->value." ".$rs->Fields['cognome']->value."
") ;
$rs->MoveNext() ;
}
}
//.... ALTRIMENTI
else{
echo("La query non ha prodotto alcun risultato") ;
}
lock_type rappresenta il bloccaggio del recordset che di default è "sola lettura", inviare un comando SQL è l'unico modo di effettuare modifiche al database pur aprendo in modalità "sola lettura": quindi le operazioni di update attraverso SQL sono atomiche e non espongono ai danni di possibili accessi concomitanti.
Ricordiamo, solo per completezza, che è anche possibile creare implicitamente una connessione aprendo direttamente un recordset in questo modo:
$rs= new COM("ADODB.Recordset") ;
$rs->Open($query, $cn_string) ;
Chi vorrà approfondire le proprietà dell'oggetto recordset troverà tutte le risposte nella MSDN library , in particolare chi scrive ha trovato molto interessante l'utilizzo delle stored procedures.
Dare una password al database
Impareremo come proteggere a livello condiviso (password unica per tutti), abbiamo già accennato alla cosa quando si è parlato delle proprietà opzionali della stringa di connessione.
Per prima cosa dovremo connetterci al database in modalità esclusiva, pertanto setteremo la proprietà MODE della stringa di connessione a 12, il valore di default è 3 (apertura condivisa in lettura/scrittura) e trovate qui una lista completa delle possibili modalità di connessione.
<?php
/* Il percorso fisico del database */
$path= "C:/www/prova/PhpAccess/" ;
/* identifichiamo il database */
$db_name= "mytest.mdb" ;
$dsource=$path.$db_name ;
/* Le password che vogliamo scambiare
sostituisci quelle che preferisci tu */
$old_pw="password_attuale";
$new_pw="nuova_password" ;
/* La query */
$query="ALTER DATABASE
PASSWORD $new_pw $old_pw ; " ;
/* La stringa di connessione, notare MODE */
$cn_string="Provider=Microsoft.Jet.OLEDB.4.0;" ;
$cn_string.="Mode= 12 ;" ;
$cn_string.="Jet OLEDB:Database Password=$old_pw;" ;
$cn_string.="Data Source=$dsource;" ;
if(!file_exists($dsource)){
die("Il database non esiste,
connessione fallita") ;
}
$cn= new COM("ADODB.Connection");
$cn->Open($cn_string) ;
$rs=new COM("ADODB.Recordset") ;
$rs->Open($query,$cn) ;
/* Pulizia del recordset */
$rs->Release() ;
$rs= null ;
/* Chiudiamo la connessione e liberiamo la memoria */
$cn->Close() ;
$cn->Release() ;
$cn= null ;
?>
Se inseriamo la password per la prima volta, la variabile $old_pw dovrà avere valore NULL (che poi non è un valore), mentre se desideriamo togliere la protezione dal database dovremo dare valore NULL a $new_pw.
Ovviamente la stringa di connessione dovrà comprendere l'apposito parametro relativo alla password (vedi proprietà della ConnString nelle pagine precedenti).
Impostare una protezione al livello utenti è leggermente più complicato e richiederebbe una spiegazione piuttosto lunga, pertanto vi rinvio ancora una volta alla MSDN LIBRARY (potete fare una ricerca con parole chiave "jet database user-level security").
Compattare il database
Le operazioni di scrittura, cancellazione e riscrittura comportano un aumento progressivo delle dimensioni del database, effettuando periodicamente una compattazione otterremo un' ottimizzazione delle prestazioni oltre che un risparmio di spazio sul disco.
Questa operazione è una di quelle che non si possono fare attraverso JET-SQL, la illustriamo anche perché si tratta di un metodo alternativo per impostare una protezione, a livello utenti o a livello condiviso, al database.
<?php
/*
Inserisci qui il percorso dove si trova il database,
*/
$path= "C:/www/prova/PhpAccess/" ;
/* Nome e password del database da compattare */
$olddb="mytest.mdb" ;
$oldpass="" ;
/* Nome provvisorio database compattato */
$newdb="new_mytest.mdb" ;
$newpass="mypassword" ;
/* ConnString verso il database da compattare */
$oldConn="Provider=Microsoft.Jet.OLEDB.4.0;" ;
$oldConn.="Data Source=".$path.$olddb.";" ;
$oldConn.="Jet OLEDB:Database Password=$oldpass ;" ;
/* ConnString verso il nuovo database (compattato) */
$newConn="Provider=Microsoft.Jet.OLEDB.4.0;";
$newConn.="Data Source=".$path.$newdb.";" ;
$newConn.="Jet OLEDB:Database Password=$newpass ;" ;
/* Controllo che non esista già un file con il nome provvisorio scelto */
if(file_exists($path.$newdb)){
die("Esiste già un file con il nome
provvisorio che hai scelto") ;
}
/* Istanzio l'oggetto che fornisce il metodo Compact() */
$je=new COM("JRO.JetEngine") or die("Compact fallito");
/* Compatta il database e setta una nuova password */
$je->CompactDatabase($oldConn,$newConn) ;
$je->Release() ;
$je= null ;
/* Elimina il vecchio database */
unlink($olddb);
/* Rinomina il db compattato con il vecchio nome */
rename($newdb,$olddb) ;
?>
Ricavare informazioni sulla struttura del database
A questo scopo l'oggetto Connection mette a disposizione il metodo OpenSchema():
se si desiderano informazioni sul numero e sul nome delle tabelle contenute nel database dovremo passare come argomento di OpenSchema il codice numerico 20, mentre per ricevere informazioni sui campi di una singola tabella il codice è 4.
Quando apriamo il recordset con OpenSchema(20) dovremo filtrare il tipo di tabella che cerchiamo, JET infatti contiene delle tabelle per la sua gestione interna che non ci interessano: noi dovremo cercare le tabelle di tipo "TABLE".
Per un elenco completo delle informazioni reperibili attraverso OpenSchema potete visitare questa pagina.
I normali parametri di connessione
<?php
/* Il percorso fisico del database,
inserisci il tuo */
$path= "C:/www/prova/PhpAccess/" ;
/* identifichiamo il database */
$db_name= "mytest.mdb" ;
$dsource=$path.$db_name ;
/* La stringa di connessione */
$cn_string="Provider=Microsoft.Jet.OLEDB.4.0;" ;
$cn_string.="Data Source=$dsource;" ;
/* La connessione */
$cn= new COM("ADODB.Connection");
$cn->open($cn_string) ;
?>
Informazioni sulle tabelle
<?php
/* OpenSchema(20) Elenca le tabelle,
cerchiamo le tabelle di tipo TABLE */
$rs=$cn->openSchema(20) ;
while(!$rs->EOF){
if(trim($rs->Fields["TABLE_TYPE"]->value=="TABLE")){
echo($rs->Fields["TABLE_NAME"]->value."
") ;
}
$rs->movenext() ;
}
$rs->close() ;
$rs = null;
?>
Informazioni sui campi di una tabella specifica
<?php
/* La tabella che vogliamo esaminare */
$nome_tabella="Test_Table" ;
/* OpenSchema(4) Elenca i campi per ogni tabella */
$rs=$cn->openSchema(4) ;
while(!$rs->EOF){
if(trim($rs->Fields["TABLE_NAME"]->value==$nome_tabella)){
echo($rs->Fields["COLUMN_NAME"]->value) ;
echo(" ** Tipo di dato: ".$rs->Fields['DATA_TYPE']->value."
") ;
}
$rs->movenext() ;
}
$rs->Close() ;
$rs->Release() ;
$rs = null;
$cn->Close();
$cn->Release() ;
$cn=null ;
?>
Conclusioni
Nonostante questa sia probabilmente una delle guide più dettagliate che si possano trovare su la coppia Access/Php, l'argomento non può certo dirsi esaurito.
Da un lato ho cercato di fornire degli spunti a 360 gradi che, per certi aspetti, possono dirsi di livello avanzato anche per chi programma abitualmente in ambiente ASP, dall'altro per favorire la leggibilità il codice è stato presentato in maniera lineare evitando di racchiuderlo in funzioni (come invece sarebbe pratica corretta fare): in ogni caso chi decidesse, per scelta o per necessità, di operare con Access e Php insieme non potrà prescindere da una comprensione degli oggetti ADO.
Fortunatamente una ricerca nella MSDN LIBRARY di solito consente di chiarire il 99% dei nostri dubbi.
Ovviamente per dell'ottimo materiale in italiano potete visitare freeasp.html.it.