Mac OS X Snow Leopard: PHP 5.3 + OCI8
Il sottoscritto, come altre migliaia di persone che lavorano nell'ambito IT, ha fatto un voto a Apple consacrandolo come proprio compagno di lavoro inseparabile, mi riferisco in particolar modo a Mac OS X. Il mio MacBook Pro e prima ancora il mio PowerBook G4 e quì mi fermo, sono stati e sono sempre con me, non solo per scrivere documentazione, leggere e inviare email, ma soprattuto quando si tratta di creare software.
Attraverso un percorso "Step By Step" e non troppo articolato, arriveremo alla fine del viaggio soddisfatti di essere nelle condizioni di porter sviluppare il nostro software scritto in PHP (5.x) e con il supporto del modulo OCI8 per l'accesso a RDBMS Oracle. La nostra piattaforma di riferimento è la seguente:
- Mac OS X Leopard (10.5.x) o Snow Leopard (10.6)
- Apache 2.2
- PHP 5.3
Mac OS X, sia Leopard sia Snow Leopard, montano di serie Apache e PHP, vedremo questi due componenti più avanti, nella fase di configurazione. I componenti software da installare e configurare sono:
- Oracle 10g R2 (10.2.0.4) o Oracle Instant Client Basic + SDK 10.2.0.4
- Modulo PECL OCI8 1.3.5
1. Installazione di Oracle 1og R2 o Oracle Instant Client
L'installazione del modulo OCI8 per PHP richiede le librerie Oracle OCI, è necessario quindi procedere con l'installazione di Oracle 10g R2 o Oracle Instant Client Basic + SDK. A questo punto abbiamo a disposizione due possibili strade che possiamo seguire, a meno che non desiderate installare proprio il data base Oracle, consiglio l'installazione dell'Oracle Instant Client. In questo articolo faremo a meno d'illustrare il procedimento d'installazione per l'uno o per l'altro componente, rimandiamo la responsabilità ai seguenti riferimenti:
- How to install Oracle Database 10g on Mac OS X Snow Leopard: Ottimo tutorial dell'autore Raimonds Simanovskis;
- Download & Install Oracle Instant Client 10.2: Direttamente da questo sito Oracle è possibile scaricare l'Instant Client (sia Snow Leopard - 64bit e Leopard - 32bit)
2. Installazione modulo OCI8
L'ultima versione stabile delle OCI8 è la 1.3.5 e disponibile per il download direttamente dal repository PECL . In genere l'installazione dei moduli PHP attraverso il comando pecl può avenire in due modalità, online o offline. Per l'installazione offline, i sorgenti del modulo OCI8 sono disponibili all'indirizzo http://pecl.php.net/get/oci8-1.3.5.tgz.
Avviamo il nostro amato Terminal e iniziamo il percorso. Onde evitare di ricevere errori in fase di configure o compilazione, dedichiamo un attimo alla verifica delle variabili di ambiente di Oracle, dal nostro Terminal basta il comando:
env | grep -E "(ORACLE)|(DYLD)"
Le nostre principali environment d'interesse sono:
- ORACLE_HOME (esempio: /u01/app/oracle/product/10.2.0/db_1)
- DYLD_LIBRARY_PATH (esempio: /u01/app/oracle/product/10.2.0/db_1/lib:/u01/app/oracle/product/10.2.0/db_1/rdbms/lib)
Nel caso in cui qualcuno di voi abbia installato l'Oracle Instant Client e non intende ricevere un errore in fase di configure, occorre creare un link simbolico, in questo modo (il comando deve essere eseguito dalla directory d'installazione dell'Oracle Instant Client):
ln -s libclntsh.dylib.10.1 libclntsh.dylib
A questo punto siamo pronti per effettuare l'installazione del modulo OCI8 utilizzando il comando pecl :
pecl install oci8 (modalità d'installazione online)
pecl install oci8-1.3.5.tgz (modalità offline)
Dato che il comando comando pecl installa dei file di "sistema", dobbiamo comunque esegurilo con i diritti di root, potremmo per esempio utilizzare il comando sudo.
L'installazione del modulo OCI8 genera il file oci8.so posizionandolo in /usr/lib/php/extensions/no-debug-non-zts-20090626/oci8.so. A questo punto non resta altro che procedere con l'attivazione del modulo OCI8.
3. Attivazione modulo OCI8 e verifica
Passato lo step d'installazione, il modulo non è ancora attivo e quanto meno caricato all'interno del motore PHP. L'attivazione del modulo OCI8 comporta la modifica del file di configurazione di PHP, aggiungendo la direttiva:
extension=oci8.so
Questa direttiva deve essere aggiunta al file php.ini, si trova in /etc/php.ini. Per la modifica al file occorre avere i diritti di root, al solito utilizziamo il comando sudo:
sudo vi /etc/php.ini
Salvata la nuova configurazione di PHP è possibile verifcare il corretto load del modulo OCI8 eseguendo il comando:
php -i|grep -E -i "(oci8)"
Se tutto va come deve, dovremmo ricevere un output del tipo:
OCI8 Support => enabled
oci8.connection_class => no value => no value
oci8.default_prefetch => 100 => 100
oci8.events => Off => Off
oci8.max_persistent => -1 => -1
oci8.old_oci_close_semantics => Off => Off
oci8.persistent_timeout => -1 => -1
oci8.ping_interval => 60 => 60
oci8.privileged_connect => Off => Off
oci8.statement_cache_size => 20 => 20
Non sempre le cose vanno per il verso giusto, è probabile che il comando visto in precedenza restituisca un errore del tipo illustrato più in avanti. Questo tipo di errore, indica che PHP non è riuscito a caricare il modulo OCI8 in quanto non riesce a trovare delle librerie dipendenti. La risoluzione del problema è abbastanza semplice, occore verificare che librerie Oracle abbiano impostati i corretti permessi di accesso, ovvero, deve essere consentito l'accesso in lettura/esecuzione anche a tutti gli altri utenti (esempio: -rwxr-xr-x 1 oracle oinstall 25491240 26 Ott 21:23 /u01/app/oracle/product/10.2.0/db_1/lib/libclntsh.dylib.10.1).
PHP Warning: PHP Startup: Unable to load dynamic library '/usr/lib/php/extensions/no-debug-non-zts-20090626/oci8.so' -
dlopen(/usr/lib/php/extensions/no-debug-non-zts-20090626/oci8.so, 9): Library not loaded: /u01/app/oracle/product/10.2.0/db_1/lib/libclntsh.dylib.10.1
Referenced from: /usr/lib/php/extensions/no-debug-non-zts-20090626/oci8.so
Reason: no suitable image found. Did find:
/u01/app/oracle/product/10.2.0/db_1/lib/libclntsh.dylib.10.1: stat() failed with errno=13
/u01/app/oracle/product/10.2.0/db_1/rdbms/lib/libclntsh.dylib.10.1: stat() failed with errno=13
/u01/app/oracle/product/10.2.0/db_1/lib/libclntsh.dylib.10.1: stat() failed with errno=13 in Unknown on line 0
4. Configurazione Apache
Il modulo OCI8, come abbiamo avuto modo di vedere, necessita di almeno due variabili di ambiente settate correttamente. Senza apportare nessuna modifica alla configurazione attuale di Apache, rischiamo di ottenere un fallimento al successivo riavvio del servizio Apache, il motivo potrebbe essere senza dubbio quello evindenziato dal file di log (/var/log/apache2/error_log) di Apache, riportiamo un breve estratto.
PHP Warning: PHP Startup: Unable to load dynamic library '/usr/lib/php/extensions/no-debug-non-zts-20090626/oci8.so' -
dlopen(/usr/lib/php/extensions/no-debug-non-zts-20090626/oci8.so, 9): Library not loaded: /b/227/rdbms/lib/libclntsh.dylib.10.1n
Referenced from: /usr/lib/php/extensions/no-debug-non-zts-20090626/oci8.son Reason: image not found in Unknown on line 0
In Mac OS X dalla versione 10.4 (Tiger), Apple ha introdotto un nuovo sistema per l'avvio dei servizi base, questo sistema si chiama Launchd. Apache è quindi governato da questo componente, che per motivi di sicurezza azzera le varibili di ambiente allo stretto indispensabile, motivo per cui il modulo OCI8 fallisce il caricamento, le variabili di ambiente Oracle non sono visibili al processo Apache.
Bisogna quindi istruire Launchd affinche il processo Apache sia messo nelle condizioni di poter accedere alle variabili di ambiente Oracle necessarie al modulo OCI8. La configurazione di Launchd, o meglio, del processo da gestire, avviene per mezzo di un file di tipo plist, mentre il file /etc/launchd.conf contiene delle direttive (esempio: comandi per il set di variabili di ambiente) per Launchd.
Il nostro obiettivo è quindi quello di aggiungere le variabili di ambiente Oracle al servizio Apache. Riassumiamo quali sono le variabili di ambiente:
- DYLD_LIBRARY_PATH
- ORACLE_HOME
- ORACLE_SID
- TNS_ADMIN
Sul file di configurazione /etc/launchd.conf aggiungiamo le seguenti direttive:
setenv TNS_ADMIN /u01/app/oracle/product/10.2.0/db_1/network/admin
setenv ORACLE_SID SHIRUS
setenv ORACLE_HOME /u01/app/oracle/product/10.2.0/db_1
setenv DYLD_LIBRARY_PATH /u01/app/oracle/product/10.2.0/db_1/lib:/u01/app/oracle/product/10.2.0/db_1/rdbms/lib
Con le direttive appena inserite, abbiamo fatto in modo che Launchd metta a disposizione le variabili di ambiente Oracle ai processi da esso controllati. I path indicati devono essere ovviamente adeguati in base alla propria installazione. Per il processo di Apache è indispensabile la sola direttiva che imposta la variabile di ambiente DYLD_LIBRARY_PATH, neccessaria per "reperire" le librerie di dipendenze del modulo OCI8.
A questo punto passiamo al raffinamento della configurazione di Apache, facciamo ciò, agendo su due file di configurazione:
- /System/Library/LaunchDaemons/org.apache.httpd.plist: PList file di configurazione per il processo Apache via Launchd
- /etc/apache2/httpd.conf: File di configurazione del servizio Apache
Apache consente di impostare delle definizioni attraverso il parametro o flag -D in input al file di processo /usr/sbin/httpd, utilizzeremo questa caratteristica per definire un nome ORACLE, e su questa definizione impostare una determinata configurazione. Come primo passo dobbiamo aggiungere il flag -D ORACLE come argomento di /usr/sbin/httpd e in seguito, aggiungere la configurazione (su /etc/apache2/httpd.conf) per la definizione ORACLE.
Sul file PList del processo Apache devono essere aggiunti i seguenti elementi:
<string>-D</string>
<string>ORACLE</string>
Il file completo è invece:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Disabled</key>
<true/>
<key>Label</key>
<string>org.apache.httpd</string>
<key>ProgramArguments</key>
<array>
<string>/usr/sbin/httpd</string>
<string>-D</string>
<string>FOREGROUND</string>
<string>-D</string>
<string>ORACLE</string>
</array>
<key>OnDemand</key>
<false/>
<key>SHAuthorizationRight</key>
<string>system.preferences</string>
</dict>
</plist>
Sul file /etc/apache2/httpd.con deve essere aggiunto il seguente blocco di configurazione:
##
Check if setting Oracle env
##
<IfDefine ORACLE>
SetEnv TNS_ADMIN /u01/app/oracle/product/10.2.0/db_1/network/admin
SetEnv DYLD_LIBRARY_PATH /u01/app/oracle/product/10.2.0/db_1/lib:/u01/app/oracle/product/10.2.0/db_1/rdbms/lib
SetEnv ORACLE_BASE /u01/app/oracle
SetEnv ORACLE_SID SHIRUS
SetEnv ORACLE_HOME /u01/app/oracle/product/10.2.0/db_1
</IfDefine>
La sezione <IfDefine ORACLE>...</IfDefine> è utilizzata per marcare le direttive che sono condizionate. Le direttive SetEnv sono processate se e solo se la definizione indicata è vera (esempio: httpd -DORACLE), in caso contrario le direttive all'interno della sezione vengono ignorate. La direttiva SetEnv imposta una variabile di ambiente che viene poi trasmessa o resa disponibile agli script CGI, SSI, PHP, etc... Uno script PHP che utilizza la funzione oci_connect(), utilizza la variabile di ambiente ORACLE_SID per determinare il nome dell'istanza Oracle a cui connettersi.
Siamo praticamente arrivati alla fine, per verificare che la configurazione applicata al servizio Apache funzioni, dobbiamo riavviare il servizio. E' possibile utilizzare, Preferenze di Sistema -> Condivisione -> Condivisione Web per eseguire un stop e un successivo start di Apache, altrimenti, sempre dal nostro amato Terminal, lanciare il comando:
apachectl restart
La verifica che il modulo OCI8 e le variabili di ambiente correttamente caricate dal processo Apache, può essere fatta attraverso un banale script PHP con all'interno l'istruzione phpinfo(). Posizioniamo lo script PHP in /Users/$vostra_username$/Sites e facciamo puntare il browser (Safari, Firefox o perchè no curl) all'indirizzo http://localhost/~$vostra_username$/phpinfo.php, in questo modo verifichiamo la presenza delle seguenti sezioni:
Apache Environment
Variable | Value |
---|---|
DYLD_LIBRARY_PATH | /u01/app/oracle/product/10.2.0/db_1/lib:/u01/app/oracle/product/10.2.0/db_1/rdbms/lib |
ORACLE_BASE | /u01/app/oracle |
ORACLE_SID | SHIRUS |
ORACLE_HOME | /u01/app/oracle/product/10.2.0/db_1 |
OCI8
OCI8 Support | enabled |
Version | 1.3.5 |
Revision | $Revision: 1.269.2.16.2.38.2.32 $ |
Active Persistent Connections | 0 |
Active Connections | 0 |
Oracle Version | no value |
Compile-time ORACLE_HOME | /u01/app/oracle/product/10.2.0/db_1 |
Libraries Used | -Wl,-rpath,/u01/app/oracle/product/10.2.0/db_1/lib -L/u01/app/oracle/product/10.2.0/db_1/lib -lclntsh |
Temporary Lob support | enabled |
Collections support | enabled |
5. Nuove caratteristiche del modulo OCI8
Nella major release 1.3 sono state introdotte parecchie novità con l'intento di fruttare al meglio le funzionalità avanzate di Oracle. Le novità in questione sono:
- Supporto per il Fast Application Notification (FAN)
- Supporto per il Database Resident Connection Pooling (DRCP)
Solo per introdurre i due argomenti riguardo FAN e DRCP non basterebbe un solo articolo, cercherò di fare una breve overview sulle due caratteristiche.
Con il FAN si evita che uno script PHP rimanga bloccato in attesa di una risposta (fin quando arriva un TCP Timeout expires) da parte di una istanza del server Oracle bloccata. Attraverso un sistema di notifiche lo script PHP non va in time out ma può richiedere una nuova connessione in modo trasparente per l'utente che utilizza l'applicazione. Affinché l'applicazione PHP possa ricevere eventi FAN, il parametro di configurazione oci8.events del modulo OCI8 deve essere impostato in questo modo:
oci8.events = On
Il supporto FAN è disponibile solo se il modulo OCI8 è stato compilato con le librerie Oracle Database 10g Release 2 o 11g e connessione a Oracle Database 10g Release 2 o 11g.
Invece, attraverso il DRCP è possibile creare un pool di connessioni al database da assegnare agli script che ne fanno richiesta. In questo modo viene ridotto l'overhead generato dal processo di connessione garantendo maggiore scalabilità all'applicazione.
Prima di utilizzare DRCP, il parametro di configurazione oci8.connection_class dovrebbe essere impostato per specificare la classe di connessione utilizzata da tutte le richieste per i server in pool con l'applicazione PHP.
oci8.connection_class = MYPHPAPP
La modifica dei parametri di configurazione del modulo OCI8 può essere eseguita sul file di configurazione di PHP (/etc/php.ini), per particolari esigenze è possibile utilizzare le funzioni PHP ini_set() e ini_get().
L'applicazione PHP deve indicare sulla stringa di connessione al data base Oracle che il tipo di server è POOLED. A seguire due esempi di connessioni, il primo utilizza la sintassi Oracle’s Easy Connect, il secondo utilizza il network name così come specificato sulla configurazione Oracle Network (vedi tnsnames.ora)
$c = oci_pconnect('myuser', 'mypassword', 'myhost/sales:POOLED');
$c = oci_pconnect('myuser', 'mypassword', 'salespool');
Subito sotto l'estratto dal file tnsnames.ora per il network name salespool
salespool=(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)
(HOST=myhost.dom.com)
(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=sales)
(SERVER=POOLED)))
Il supporto DRCP è disponibile solo se il modulo OCI8 è stato compilato con le librerie Oracle Database 11g e connessione a Oracle Database 11g.
Per maggiori approfondimenti sull'argomento consiglio di partire con la visione del sito PHP Developer Center on Oracle Technology Network (OTN) raggiungibile all'indirizzo http://www.oracle.com/technology/tech/php/index.html.
5. Conclusioni
E' probabile che qualcuno di voi alla fine di tutto si aspettava un esempio di connessione al db Oracle, prometto che a breve mi rifarò con un altro articolo dedicato a questo aspetto, aggiungendo però un qualcosa d'interessante: Oracle XMLQuery e SimpleXML.
Con questo breve articolo abbiamo visto come sia possibile e semplice fare della nostra Mela un ambiente adatto ad ogni tipo di situazione ed esigenza.