Aquest article consta de tres parts. Les dues primeres estan destinades als desenvolupadors desitjosos d’escriure extensions d’SPIP; presenten l’arquuitectura general i, després, les funcions disponibles. La tercera part detalla la implementació i està destinada als contribuïdors d’PIP, desitjosos de portar-lo cap a altres implementacions de SQL o de millorar les que existeixen. Al llarg de tot l’article, parlarem de servidor SQL per designar una implementació de SQL utilitzada per SPIP, encara que parlant en propietat algunes implementacions no siguin de servidors.
Arquitectura general
En un primer temps, es pot considerar que la interfície d’SPIP amb les implementacions de SQL es redueix únicament a la funció següent, definida en el fitxer ecrire/base/abstract_sql.php
:
sql_serveur($ins_sql, $serveur='', $continue=false)
Aquesta funció comença, si no s’ha fet abans, per connectar-se al servidor especificat en el segon argument. Aquest argument s’omet sovint, cosa que designa llavors la implementació SQL escollida a la instal·lació, i memoritzada per SPIP en un fitxer fitxer de connexió
(vegeu Les bases de dades a SPIP).
Sinó, l’argument ha d’indicar de manera explícita el nom del fitxer de connexió que s’ha d’utilitzar, ometent l’extensió .php
. El resultat que retorna és una altra funció, que realitza el tipus d’acció demanada per la cadena passada en el primer argument (per exemple
select
o fetch
) a la base SQL indicada pel fitxer de connexió.
El tercer argument indica què cal fer quan aquesta funció no es pot retornar. Si no hi és o és igual a false
, es produirà un error fatal.
De no ser així, es poden distingir dues situacions més. Si la connexió indicada no és coneguda o inoperant, es retornarà el valor false
.
Si no és així, es retornarà una estructura de dades que descruen la connexió (es descriurà a la última part), cosa que permet, d’una banda, verificar que una funció existeix sense caure en el risc d’un error fatal i, de l’altra, obtenir diverses informacions abans de ser utilitzades.
Aquesta visió minimalista de la interfície permet fer-ho tot, però amb una sintaxi força opaca. Si, per exemple, $db
és el nom d’una base al servidor principal, no la seleccionarà
$f = sql_serveur('selectdb');
$f($db);
Per tal de clarificar l’escriptura, existeix més avall un joc de funcions que encadenen les dues instruccions pels casos més freqüents. Per exemple, existeix
sql_selectdb($nom, $serveur='')
El que permet reescriure de manera més simple l’operació anterior:
sql_selectdb($db)
De manera general, la interfície d’SPIP als servidors SQL és un joc de funcions el nom de les quals és sql_
f i del que el darrer argument, opcional, és el nom del servidor. Criden sql_serveur
per obtenir una funció f que apliquen als seus arguments, inclòs el nom del servidor. Totes les funcions amb el nom construït d’aquesta manera estan reservades per la interfície d’SPIP als servidors SQL.
En un punt de vista orientat a objectes, aquest joc de funcions representa els mètodes de l’objecte sql
, però s’escriu sql_
f en lloc de sql->
f, i no cal instanciar una classe. La presència del nom del servidor a totes les crides permet simular les operacions que impliquen l’objecte per si mateix.
Per tant, aquest joc de funcions dispensa utilitzar la major part de les vegades sql_serveur
. Senyalem, també, la funció (present a SPIP des de fa molt de temps) :
spip_connect($serveur='')
que simplement obre la connexió al servidor, i, per tant, és equivalent a
sql_serveur('', $serveur, true)
i que retorna false si el servidor no es troba disponible, i sinó l’estructura de dades que descriuen les possibilitats del servidor.
Funcions disponibles
Les funcions de la interfície SQL es poden classificar en diversos grups, pels que es mostrarà cada cop una taula presentant els seus arguments. Pels exemples ens podem dirigir al codi d’SPIP.
Un primer grup de funcions que es refereixen a la lectura de les taules SQL. Les funcions imprescindibles són:
- sql_select
els arguments del qual són les clàusules SQL habituals d’aquesta instrucció, i el resultat de la qual és un recurs Select;
- sql_fetch
,
utilitzat gairebé sempre en un bucle, permet recuperar les línies successives d’un recurs Select; i el seu resultat és una taula indexada pel nom dels camps;
- sql_free
que assenyala al servidor que pot alliberar un recurs.
Altres funcions ofereixen composicions freqüents d’aquestes operacions:
- sql_fetsel
amb els mateixos arguments que
sql_select
,
que s’aplica sobre els seus arguments i després sql_fetch
sobre el recurs retornat; pràctic per les consultes amb un resultat que no suposi més d’una línea;
- sql_getfetsel
amb els mateixos arguments que
sql_select
,
que s’aplica sobre els seus arguments i després sql_fetch
sobre el recurs retornat, i finalment extreu de la taula retornada el camp el nom del qual es dóna com a primer argument (per tant, la llista dels camps només en comporta un); pràctic per les consultes el resultat de les quals estigui format per una única línia d’un sol camp.
- sql_allfetsel
amb els mateixos arguments que
sql_select
,
que s’aplica sobre els seus arguments i després
sql_fetch
sobre el recurs retornat sempre i quan aquest retorni una taula que no estigui buida; per acabar sql_allfetsel
retorna la taula de totes les taules retornades per sql_fetch
(alerta a no saturar la memòria amb aquesta funció);
- sql_countsel
amb els mateixos arguments que
sql_select
excepte el primer, que aplica aquest sobre COUNT(*)
i els seus altres arguments i retorna el númerp calculat; pràctic per conèixer el número de línies que retornaria la consulta Select descrita d’aquesta manera.
- sql_count
que retorna el número de línies d’un recurs Select, com si s’hi hagués afegit COUNT(*)
a la petició;
- sql_get_select
fa servir els mateixos arguments que sql_select
, però retorna el codi SQL de la petició, sense executar-la. Aquesta funció pot ser útil per les necessitats d’alguns connectors o per crear fàcilment vistes SQL.
Les quatre primeres criden per acabar sql_free
, i són, per tant, preferibles sempre que sigui possible al trio select-fetch-free
del que s’oblida fàcilment el darrer membre.
La següent taula precisa el contingut i l’ordre dels arguments esperats per aquestes funcions.
Funció | Arguments |
sql_select |
|
sql_fetch |
|
sql_free |
|
sql_count |
|
sql_countsel |
|
sql_fetsel |
|
sql_allfetsel |
|
sql_getfetsel |
|
sql_get_select |
|
A les funcions anteriors, si la clàusula Select és facilitada en forma de taula, els seus elements s’encadenaran, separats per comes. En cas de taula per les clàusules Where i Having, els elements han de ser cadenes (també es tenen en compte les subtaules en notació prefixada però són reservades al compilador). Aquestes cadenes es reuniran en una gran conjunció (és a dir, seran encadenades amb AND com a separador).
La clàusula From és una cadena (el cas de la taula es reserva al compilador d’SPIP). Alerta: si cal referenciar les taules a les altres clàusules, cal definir-ne àlies en aquest paràmetre i utilitzar-los sistemàticament. Per tant, escriurem:
sql_countsel('spip_articles AS a, spip_rubriques AS r', "a.id_secteur=r.id_rubrique AND r.titre='monsecteur')
o
sql_countsel('spip_articles AS a JOIN spip_rubriques AS r ON a.id_secteur=r.id_rubrique", "r.titre='monsecteur'")
mentre que la següent escriptura no es comprendrà:
sql_countsel('spip_articles, spip_rubriques', "spip_articles.id_rubrique=spip_rubriques.id_secteur AND spip_rubriques.titre='monsecteur'")
Un segon grup de funcions està constituït per aquelles que modifiquen el contingut de les taules. Aquestes funcions són delicades de definir ja que la sintaxi dels valors que s’hi ha d’introduir varia d’un servidor SQL a un altre (sobretot les dates). Per aquesta raó, aquestes funcions han de disposar de la descripció de la taula a modificar, per tal de conèixer el tipus de valors esperats pels servidor SQL. SPIP retroba automàticament aquestes informacions (donades al moment de crear la taula) però és possible subministrar una descripció arbitrària (penúltim argument d’aquestes funcions, opcional i per altra banda escassament útil).
SPIP proporciona, per tant, una funció d’inserció, sql_insertq
,
i una funció d’actualització, sql_updateq
, que agafen una taula camp=>vaor i s’encarreguen de citar els valors en funció del tipus (amb la funció sql_quote
especificada més avall).
També hi ha disponible sql_insertq_multi
que permet fer insercions de diverses entrades agafant una taula de taula camp=>valor.
Per les actualitzacions on els nous valors depenguin dels antics (com a cpt=cpt+1
), utilitzar sql_update
on els valor es prendran literalment però caldrà prohibir amb cura les possibilitats d’atac per injecció de codi.
Existeix igualment sql_replace
, funció que fa una actualització sobre una línia corresponent a una clau primària, o inserint els valors si aquesta línia no existeix com a tal funció sql_replace_multi
per actualitzacions o insercions múltiples.
Finalment, sql_delete
esborra d’una taula les línies que responen a una clàusula Where.
Funció | Arguments |
sql_updateq |
|
sql_update |
|
sql_insertq |
|
sql_insertq_multi |
|
sql_replace |
|
sql_replace_multi |
|
sql_delete |
|
Un grup una mica a banda està format per aquelles funcions que tracten específicament dels operands: no es connecten a un servidor, però retornen cadenes que depenen d’aquest:
- sql_quote
agafa una cadena o un nombre, retorna un nombre si l’argument era un nombre o una cadena representant un enter, sinó retorna la cadena inicial envoltada d’apòstrofs i amb els apòstrofs protegits segons la sintaxi pròpia del servidor (un \ al davant en alguns, un segon apòstrof en uns altres);
- sql_hex
agafa una cadena de xifres hexadecimals i retorna la seva representació al servidor donat;
- sql_in
construeix una crida a l’operand IN, tractant els eventuals valors hexadecimals que hi figuren;
- sql_test_int
prédicat retournant Vrai si le type SQL fourni désigne un entier ;
- sql_test_date
predicat que retorna Veritable si el tipus SQL proporcionat designa una data;
- sql_multi
aplica una expressió SQL a un camp que contingui un bloc multi (vegeu Realitzar un lloc multilingüe) per aprendre la part corresponent a la llengua indicada; l’interès en efectuar aquesta operació a nivell SQL es basa essencialment en demanar simultàniament una selecció en aquesta columna.
Funció | Arguments |
sql_quote |
|
sql_hex |
|
sql_in |
|
sql_multi |
|
sql_test_date |
|
sql_test_int |
|
Un grup important està format per les funcions que manipulen les declaracions de bases i taules. Per raons històriques, aquesta primera versió de la interfície reprèn gairebé literalment la sintaxi de MySQL3 i haurà de ser revisada, sobretot per fer-hi aparèixer la declaració de les unions. Les funcions sql_create
,
sql_alter
,
sql_showtable
i
sql_drop_table
permeten crear, modificar, veure i suprimir una taula.
Les funcions
sql_create_view
,
sql_drop_view
permeten crear o suprimir una vista.
Les funcions
sql_listdbs
,
sql_showbase
i
sql_selectdb
permeten veure les bases accessibles, veure’n el seu contingut i seleccionar-ne una. Fixem-nos que els hostatjadors no autoritzen forçosament aquestes funcions; SPIP provarà d’endevinar-ho, anotant els seus assajos en el fitxer spip.log
.
Funció | Arguments |
sql_create |
|
sql_alter |
|
sql_showtable |
|
sql_drop_table |
|
sql_create_view |
|
sql_drop_view |
|
sql_listdbs | |
sql_selectdb |
|
sql_showbase |
|
Dues funcions permeten regular la codificació de caràcters en les comunicacions amb el servidor:
- sql_set_charset
, demana utilitzar el codi indicat;
- sql_get_charset
, demana si un codi amb un nom determinat es troba disponible al servidor.
Funció | Arguments |
sql_get_charset |
|
sql_set_charset |
|
Un últim grup de funcions ofereix algunes eines de gestió de les peticions i de les taules; ens remetrem als seus homònims a la documentació dels servidors SQL. No obstant, assenyalem que sql_explain
s’utilitza implícitament pel depurador d’SPIP, accessible pels botons d’administració de l’espai públic, quan se li demana el pla de càlcul d’un bucle (o de tot un esquelet).
Funció | Arguments |
sql_optimize |
|
sql_repair |
|
sql_explain |
|
sql_error |
|
sql_errno | |
sql_version |
Fora de grup, la funció general sql_query
, nom que hauria hagut de portar l’històric spip_query
; es recomana evitar en qualsevol cas el seu ús.
Portar SPIP a altres servidors SQL
Aquesta secció està destinada a aquells que desitgen porter SPIP a d’altres servidors SQL, o perquè necessiten informacions més tècniques, sobretot en la gestió de versions de la interfície. Les funcions del fitxer ecrire/base/abstract_sql.php
estudiades més amunt s’acontenten en oferir una interfície homogènia als diferents servidors, però elles mateixes no fan cap càlcul. És en el fitxer ecrire/base/connect_sql.php
on hi ha el treball efectiu.
La funció essencial és spip_connect
que obre la connexió al servidor SQL indicat pel seu argument (o, si aquest no hi és, el servidor principal) localitzant les connexions ja fetes.
Aquesta obertura consisteix en incloure un fitxer de connexió creat durant la instal·lació d’SPIP pels scripts presents en el directori install
.
Un fitxer de connexió es redueix essencialment a aplicar la funció spip_connect_db
als valors proporcionats durant la instal·lació.
La funció spip_connect_db
rep en particular com argument el tipus de servidor SQL. Aquest tipus ha de ser el nom d’un fitxer present al directori req
.
Aquest fitxer es carrega i ha de definir totes les funcions d’interfícies definides a la secció anterior, més la funció req_
type_dist
que s’aplicarà immediatament en els mateixos arguments que spip_connect_db
, excepte el tipus.
És aquesta funció la que ha d’establir efectivament la connexió.
Portar SPIP a altres servidors SQL consisteix, per tant, en definir aquest joc de funcions i a situar-lo en el directori req
.
El gestor de versions d’interfície es recolza en el segon argument de spip_connect
que indica la versió, i agafa per defecte la versió actual. Totes les funcions de la interfície estan definides en el fitxer abstract_sql
, s’anomenen sql_X i són les úniques que s’anomenen així. Es connecten totes cridant una variant de spip_connect
del que el primer argument és el número de la versió de la interfície.
En el cas que el fitxer abstract_sql
necessités una revisió, serà reanomenat abstract_sql_N, i se li aplicarà el següent Sed (N designa el número de versió):
s/\(sql_[A-Za-z_0-9 ]*\)/\1_N/
Aplicant també aquest script a les extensions d’SPIP basades en aquesta versió, se’ls permetrà cridar les seves funcions, que es carregaran sense col·lisió de noms, al haver prefixat el Sed el nom dels antics amb el seu número de versió. Només caldrà que afegim una instrucció include que ens porti cap al fitxer abstract_sql_N.
Pel que fa al codi a portar, haurem d’anomenar de nou, paral·lelament els fitxers del directori req
, i escriure les noves versions.
La coexistència de diverses versions de la interfície durant l’execució d’SPIP reposa sobre l’estructura que descriu el servidor. Aquesta és, de fet, una taula que conté:
- link
, recurs que indica la connexió;
- db
, nom de la base de dades;
- prefixe
, nom del prefix de la taula;
- ldap
, nom de l’eventual fitxer que descriu el servidor LDAP.
Les altres entrades són els números de versions disponibles, i el seu valor és la taula de funcions implementant aquesta versió de la interfície.
Les altres funcions del fitxer
connect_sql
es refereixen essencialment a la gestió de versions i al tractament d’alguns casos particulars de declaracions de taules estàndards d’SPIP.