Este artículo se compone de tres partes. Las dos primeras están destinadas a los desarrolladores que quieren escribir extensiones para SPIP y presentan en primer lugar la arquitectura general, y a continuación las funciones disponibles.
La tercera parte detalla la implementación y está destinada a los contribuidores y contribuidoras de SPIP que quieran portarlo a otras implementaciones de SQL o mejorar las existentes. En todo el artículo, se hablará de servidor SQL para denominar una implementación de SQL usada por SPIP, aunque ciertas implementaciones no sean de los servidores propiamente dichos.
Arquitectura general
En un primer momento, se puede considerar que la interfaz de SPIP con las implementaciones de SQL se reduce únicamente a la función siguiente, definida en el archivo ecrire/base/abstract_sql.php
:
sql_serveur($ins_sql, $serveur='', $continue=false)
Esta función empieza, si no se ha hecho antes, por conectarse al servidor especificado en el segundo argumento. Este argumento se omite a menudo, y designa la implementación SQL elegida durante la instalación y guardada por SPIP en un archivo de conexión (ver Les bases de données en SPIP). Si no es así, el argumento debe indicar explícitamente el nombre del archivo de conexión a utilizar, omitiendo la extensión .php
. El resultado devuelto es otra función, que realiza el tipo de acción solicitado por la cadena pasada como primer argumento (por ejemplo
select
o fetch
) sobre la base de datos SQL indicada por el fichero de conexión.
El tercer argumento indica lo que se debe hacer cuando una función no puede ser devuelta. Si está ausente o es igual a false
, se lanzará un error fatal. En otro caso, se distinguen dos situaciones. Si la conexión indicada es desconocida o inoperante, se devolverá el valor false
.Si no es así, se devolverá una estructura de datos que describe la conexión (se describirá esta estructura en la última parte de este artículo), lo que permite por un lado verificar si una función existe sin arriesgarse a tener un error fatal, y por otro lado obtener diversas informaciones antes de utilizarla.
Esta visión minimalista de la interfaz permite hacer de todo, pero con una sintaxis bastante opaca. Si por ejemplo $db
es el nombre de una base de datos en el servidor principal, se la seleccionará mediante
$f = sql_serveur('selectdb');
$f($db);
Para clarificar la escritura, existe un juego de funciones que encadenan estas dos instrucciones para los casos más frecuentes. Por ejemplo, existe
sql_selectdb($nom, $serveur='')
Lo cual permite reescribir la operación anterior de forma más sencilla:
sql_selectdb($db)
En general, la interfaz de SPIP con los servidores SQL es un juego de funciones cuyo prefijo es sql_
f y cuyo último argumento, opcional, es el nombre del servidor. Estas funciones llaman a sql_serveur
para obtener una función f que aplican sobre sus argumentos, incluido el nombre del servidor.
Todas las funciones cuyo nombre se construye de esta manera están reservadas para la interfaz de SPIP con servidores SQL.
Desde un punto de vista orientado a objetos, este juego de funciones representa los métodos del objeto sql
, pero se escribe
sql_
f en lugar de
sql->
f, y no es necesario instanciar una clase. La presencia del nombre del servidor en todas las llamadas permite simular las operaciones que implican al objeto actual self).
Este juego de funciones hace por tanto innecesario utilizar sql_serveur
la mayor parte del tiempo. Destacamos también la función (presente en SPIP desde hace tiempo):
spip_connect($serveur='')
la cual simplemente abre la conexión al servidor, y es por tanto equivalente a
sql_serveur('', $serveur, true)
y que devuelve false si el servidor no está disponible, y la estructura de datos que describe las posibilidades del servidor en otro caso.
Funciones disponibles
Las funciones de la interfaz SQL se pueden clasificar en varios grupos, para los cuales se dará cada vez una tabla que muestra sus argumentos. Para los ejemplos, se puede acudir al código fuente de SPIP.
Un primer grupo de funciones se refieren a la lectura de las tablas SQL. Las funciones imprescindibles son:
- sql_select
cuyos argumentos son las cláusulas SQL habituales de esta instrucción, y cuyo resultado es un recurso Select.
- sql_fetch
,
utilizada casi siempre dentro de un bucle, que permite recuperar las líneas sucesivas de un recurso Selec, su resultado es una tabla indexada por el nombre de los campos.
- sql_free
que indica al servidor que puede liberar un recurso.
Otras funciones ofrecen composiciones frecuentes de estas operaciones:
- sql_fetsel
con los mismos argumentos que
sql_select
,
la cual se aplica sobre los argumentos, seguida de
sql_fetch
sobre el recurso devuelto, es práctica para las consultas cuyo resultado está formado por una única línea.
- sql_getfetsel
con los mismos argumentos que
sql_select
,
la cual se aplica aplica sobre los argumentos, seguida de
sql_fetch
sobre el recurso devuelto, y finalmente extrae de la tabla devuelta el campo cuyo nombre se indica como primer argumento
(la lista de campos, por tanto, no contiene más que uno); es práctica para las consultas cuyo resultado está formado por una única línea de un solo campo.
- sql_allfetsel
con los mismos argumentos que
sql_select
,
la cual se aplica aplica sobre los argumentos, seguida de
sql_fetch
sobre el recurso devuelto, mientras éste devuelva una tabla no vacía,
sql_allfetsel
devuelve para finalizar la tabla de todas las tablas devueltas por sql_fetch
(cuidado con saturar la memoria con esta función).
- sql_countsel
con los mismos argumentos que
sql_select
excepto el primero,
el cual se aplica sobre COUNT(*)
y sus otros argumentos y retorna el número calculado; es práctica para conocer el número de líneas que devolverá la consulta Select descrita de esta manera.
- sql_count
la cual devuelve el número de líneas de un recurso Select, como si se hubiera añadido COUNT(*)
en la consulta.
- sql_get_select
utiliza los mismos argumentos que sql_select
, pero devuelve el código SQL de la consulta, sin ejecutarlo. Esta función puede ser útil para las necesidades de ciertos plugins o para crear vistas SQL fácilmente.
Las cuatro primeras funciones llaman para finalizar a sql_free
, y por tanto es preferible usarlas siempre que sea posible antes que el trío select-fetch-free
cuyo último elemento se pierde fácilmente.
La siguiente tabla especifica el contenido y el orden de los argumentos esperados por estas funciones.
Función | Argumentos |
sql_select |
|
sql_fetch |
|
sql_free |
|
sql_count |
|
sql_countsel |
|
sql_fetsel |
|
sql_allfetsel |
|
sql_getfetsel |
|
sql_get_select |
|
En las funciones anteriores, si la cláusula Select es proporcionada en forma de array, sus elementos se concatenarán, separados por comas. En caso de usar una array para las cláusulas Where y Having, los elementos deben ser cadenas (las subarrays en notación prefija también se tienen en cuenta, pero se reservan para el compilador). Estas cadenas serán unidas en un grupo mayor (es decir, serán concatenadas con AND como separador).
La cláusula From es una cadena (el caso de la array se reserva para el compilador de SPIP). Atención: Si es necesario referenciar las tablas en las otras cláusulas, es necesario definir alias en este parámetro y utilizarlos sistemáticamente. Por tanto, se escribirá:
sql_countsel('spip_articles AS a, spip_rubriques AS r', "a.id_secteur=r.id_rubrique AND r.titre='monsecteur')
o bien
sql_countsel('spip_articles AS a JOIN spip_rubriques AS r ON a.id_secteur=r.id_rubrique", "r.titre='monsecteur'")
mientras que lo siguiente no se entenderá:
sql_countsel('spip_articles, spip_rubriques', "spip_articles.id_rubrique=spip_rubriques.id_secteur AND spip_rubriques.titre='monsecteur'")
Un segundo grupo de funciones está compuesto por las que modifican el contenido de las tablas. Estas funciones son difíciles de definir, ya que la sintaxis de los valores a introducir en las tablas cambia de un servidor SQL a otro (especialmente las fechas). Por esta razón, estas funciones deben disponer de la descripción de la tabla a modificar, a fin de conocer el tipo de los valores esperados por el servidor SQL. SPIP obtiene automáticamente esta información (proporcionada en el momento de creación de la tabla) pero es posible proporcionar una descripción arbitraria (penúltimo argumento de estas funciones, opcional y por otra parte útil en contados casos).
SPIP proporciona por tanto una función de inserción,
sql_insertq
,
y una función de actualización,
sql_updateq
,
las cuales toman un array campo=>valor
y se ocupan de citar los valores en función del tipo (con la función
sql_quote
especificada más abajo).
También está disponible
sql_insertq_multi
que permite realizar inserciones de varias entradas mediante un array de array campo=>valor.
Para las actualizaciones donde los nuevos valores dependan de los antiguos (como en cpt=cpt+1
),
se utiliza
sql_update
donde los valores serán tomados literalmente, pero hará falta impedir cuidadosamente las posibilidades de ataque por inyección de código.
También existe
sql_replace
,
función que efectúa una actualización sobre una línea correspondiente a una clave primaria, o bien inserta los valores si esta línea no existe,
además de una función
sql_replace_multi
para las actualizaciones o inserciones múltiples.
Finalmente,
sql_delete
borra las líneas de una tabla correspondientes a una cláusula Where.
Función | Argumentos |
sql_updateq |
|
sql_update |
|
sql_insertq |
|
sql_insertq_multi |
|
sql_replace |
|
sql_replace_multi |
|
sql_delete |
|
Un grupo un poco aparte está formado por funciones que se ocupan específicamente de los operandos. Estas funciones no se conectan al servidor, pero devuelven cadenas que dependen de éste:
- sql_quote
toma una cadena o un número, y devuelve un número si el argumento era un número o una cadena que representaba a un entero. Si no, devuelve la cadena inicial rodeada de apóstrofes, con los apóstrofes protegidos según la sintaxis propia del servidor (un \ delante para algunos, un segundo apóstrofe para otros).
- sql_hex
toma una cadena de cifras hexadecimales y devuelve su representación en el servidor SQL dado.
- sql_in
construye una llamada al operando IN, procesando los eventuales valores hexadecimales que aparezcan.
- sql_test_int
es un predicado que retorna Verdadero si el tipo SQL proporcionado designa un entero.
- sql_test_date
es un predicado que retorna Verdadero si el tipo SQL proporcionado designa una fecha.
- sql_multi
aplica una expresión SQL sobre un campo que contiene un bloque múltiple (ver Hacer un sitio multilingüe) para tomar la parte correspondiente al idioma indicado; el interés de efectuar esta operación a nivel de SQL radica esencialmente en poder pedir simultáneamente una selección sobre esta columna.
Función | Argumentos |
sql_quote |
|
sql_hex |
|
sql_in |
|
sql_multi |
|
sql_test_date |
|
sql_test_int |
|
Un grupo importante está formado por las funciones que manipulan las declaraciones de bases de datos y de tablas. Por motivos históricos, esta primera versión de la interfaz toma casi literalmente la sintaxis de MySQL3 y seguramente tendrá que ser revisada, en particular para hacer que aparezca la declaración de las uniones. Las funciones
sql_create
,
sql_alter
,
sql_showtable
y
sql_drop_table
permiten crear, modificar, visualizar y suprimir una tabla.
Las funciones
sql_create_view
,
sql_drop_view
permiten crear o suprimir una visulización.
Las funciones
sql_listdbs
,
sql_showbase
y
sql_selectdb
permiten ver las bases accesibles, visualizar su contenido y seleccionar una. Se ha de tener en cuenta que no todos los proveedores de alojamiento web autorizan estas acciones; SPIP intentará averiguarlo, guardando sus intentos en el fichero
spip.log
.
Función | Argumentos |
sql_create |
|
sql_alter |
|
sql_showtable |
|
sql_drop_table |
|
sql_create_view |
|
sql_drop_view |
|
sql_listdbs | |
sql_selectdb |
|
sql_showbase |
|
Dos funciones permiten regular la codificación de los caracteres en las comunicaciones con el servidor:
- sql_set_charset
, solicita utilizar la codificación indicada.
- sql_get_charset
, solicita si una codificación dada está disponible en el servidor.
Función | Argumentos |
sql_get_charset |
|
sql_set_charset |
|
Un último grupo de funciones ofrece algunas herramientas de gestión de las consultas y de las tablas; se referirán a sus homónimas en la documentación de los servidores SQL. Señalamos, no obstante, que sql_explain
es utilizado
implícitamente por el depurador de SPIP, accesible por los botones de administración del espacio público, cuando se solicita el plan de cálculo de un bucle (o de todo un esqueleto).
Función | Argumentos |
sql_optimize |
|
sql_repair |
|
sql_explain |
|
sql_error |
|
sql_errno | |
sql_version |
Fuera de grupo, la función general
sql_query
,
nombre que habría tenido que llevar la histórica
spip_query
;
se recomienda evitar su utilización en cualquier caso.
Portar SPIP a otros servidores SQL
Esta sección está destinada a aquellos que deseen portar SPIP a otros servidores SQL, o necesiten información más técnica, especialmente sobre la gestión de las versiones de la interfaz. Las funciones del archivo
ecrire/base/abstract_sql.php
estudiadas anteriormente se conforman con ofrecer una interfaz homogénea con los distintos servidores, pero no realizan ningún cálculo ellas mismas. Es en el archivo
ecrire/base/connect_sql.php
donde se localiza el trabajo efectivo.
La función esencial es
spip_connect
que abre la conexión al servidor SQL indicado por su argumento (o, si se omite, el servidor principal), teniendo en cuenta las conexiones ya realizadas.
Esta abertura consiste en incluir un archivo de conexión creado durante la instalación de SPIP por los scripts presentes en la carpeta
install
.
Un fichero de conexión se puede resumir esencialmente en aplicar la función
spip_connect_db
a los valores proporcionados durante la instalación.
La función
spip_connect_db
recibe en particular como argumento el tipo del servidor SQL. Este tipo debe ser el nombre de un archivo presente en la carpeta
req
.
Este archivo es cargado y debe contener todas las funciones de interfaz definidas en la sección anterior, más la función
req_
tipo_dist
que será inmediatamente aplicada sobre los mismos argumentos que
spip_connect_db
,
excepto el tipo.
Es esta función la que debe establecer la conexión de forma efectiva.
Portar SPIP a otros servidores SQL consiste por tanto en definir ese juego de funciones y colocarlo en la carpeta
req
.
El gestor de versiones de la interfaz se localiza en el segundo argumento de
spip_connect
que indica la versión, y toma la versión actual por defecto.
Todas las funciones de la interfaz se definen en el archivo
abstract_sql
, se llaman sql_X y son las únicas que se llaman así. Todas ellas se conectan llamando a una variante de
spip_connect
cuyo primer argumento es el número de versión de la interfaz. En caso que el archivo
abstract_sql
necesite una revisión, se renombrará como
abstract_sql_N, y el script Sed siguiente le será aplicado (N se refiere al número de versión):
s/\(sql_[A-Za-z_0-9 ]*\)/\1_N/
Al aplicar igualmente este script a las extensiones de SPIP basadas en esta versión, se les permitirá llamar a sus funciones, que serán cargadas sin colisión de nombres, ya que el script de Sed habrá añadido el número de versión como prefijo de los nombres de las antiguas. Únicamente será necesario añadir de nuevo una instrucción include en el fichero abstract_sql_N.
En el lado del código a portar, será necesario renombrar igualmente los archivos de la carpeta
req
, y escribir las nuevas versiones.
La coexistencia de varias versiones de la interfaz durante la ejecución de SPIP descansa en la estructura que describe el servidor. Esta estructura es una array que contiene:
- link
, recurso que indica la conexión;
- db
, nombre de la base de datos;
- prefixe
, nombre del prefijo de tabla;
- ldap
, nombre del eventual fichero que describe el servidor LDAP.
Las otras entradas son los números de versiones disponibles, y su valor es un array con las funciones que implementan esta versión de la interfaz.
Las otras funciones del fichero
connect_sql
se refieren esencialmente a la gestión de las versiones y el tratamiento de algunos casos particulares de declaraciones de tablas estándar de SPIP.