Este artículo ilustra varias formas de crear aplicaciones PHP configurables. El artículo también explora los puntos de configuración ideales en una aplicación y busca un equilibrio entre una aplicación demasiado configurable y demasiado cerrada.
Si planea poner su aplicación PHP a disposición de otras personas o empresas, debe asegurarse de que la aplicación sea configurable. Como mínimo, permita a los usuarios establecer inicios de sesión y contraseñas de bases de datos de manera segura para que el material que contienen no se haga público.
Este artículo demuestra varias técnicas para almacenar valores de configuración y editarlos. Además, el artículo también proporciona orientación sobre qué elementos deben ser configurables y cómo evitar caer en el dilema de una configuración excesiva o insuficiente.
Configuración mediante archivos INI
PHP tiene soporte integrado para archivos de configuración. Esto se logra mediante un mecanismo de archivo de inicialización (INI) como el archivo php.ini, donde se definen constantes como los tiempos de espera de conexión de la base de datos o cómo se almacenan las sesiones. Si lo desea, puede personalizar la configuración de su aplicación en este archivo php.ini. Para ilustrar, agregué las siguientes líneas de código al archivo php.ini.
myapptempdir=foo
Luego, escribí un pequeño script PHP para leer este elemento de configuración, como se muestra en el Listado 1.
Listado 1. ini1.php
<?php
función get_template_directory()
{
$v = get_cfg_var( "myapptempdir" );
retorno ($v == nulo)? "tempdir" : $v;
}
echo( get_template_directory()."n" );
?>
Cuando ejecuta este código en la línea de comando, obtiene los siguientes resultados:
% php ini1.php
foo
%
maravilloso. Pero, ¿por qué no podemos utilizar la función INI estándar para obtener el valor del elemento de configuración myapptempdir? Investigué un poco y descubrí que, en la mayoría de los casos, los elementos de configuración personalizados no se pueden obtener utilizando estos métodos. Sin embargo, se puede acceder a él mediante la función get_cfg_var.
Para simplificar este enfoque, encapsule el acceso a la variable en una segunda función que tome el nombre de la clave de configuración y un valor predeterminado como parámetros, como se muestra a continuación.
Listado 2.
Función ini2.php get_ini_value( $n, $dv )
{
$c = get_cfg_var( $n );
retorno ($c == nulo)? $dv: $c;
}
función get_template_directory()
{
return get_ini_value( "myapptempdir", "tempdir");
}
Esta es una buena descripción general de cómo acceder al archivo INI, por lo que si desea utilizar un mecanismo diferente o almacenar el archivo INI en otro lugar, no necesita tomarse la molestia de cambiar muchas funciones.
No recomiendo usar archivos INI para la configuración de aplicaciones, por dos razones. En primer lugar, si bien esto facilita la lectura del archivo INI, hace que sea casi imposible escribirlo de forma segura. Por lo tanto, esto solo es adecuado para elementos de configuración de solo lectura. En segundo lugar, el archivo php.ini se comparte entre todas las aplicaciones del servidor, por lo que no creo que los elementos de configuración específicos de la aplicación deban escribirse en ese archivo.
¿Qué necesitas saber sobre los archivos INI? Lo más importante es cómo restablecer la ruta de inclusión para agregar elementos de configuración, como se muestra a continuación.
Listado 3. ini3.php
<?php
echo( ini_get("include_path")."n" );
ini_set("incluye_ruta",
ini_get("include_path").":./mylib" );
echo( ini_get("include_path")."n" );
?>
En este ejemplo, agregué mi directorio mylib local a la ruta de inclusión, por lo que puedo solicitar archivos PHP de ese directorio sin agregar la ruta a la declaración require.
Configuración en PHP
Una alternativa común para almacenar entradas de configuración en un archivo INI es utilizar un script PHP simple para conservar los datos. A continuación se muestra un ejemplo.
Listado 4. config.php
<?php
# Especificar la ubicación del directorio temporal
#
$TEMPLATE_DIRECTORY = "tempdir";
?>
El código que utiliza esta constante es el siguiente.
Listado 5. php.php
<?php
require_once 'config.php';
función get_template_directory()
{
global$TEMPLATE_DIRECTORY;
devolver $TEMPLATE_DIRECTORY;
}
echo( get_template_directory()."n" );
?>
El código primero contiene el archivo de configuración (config.php), y luego puede usar estas constantes directamente.
Hay muchas ventajas al utilizar esta tecnología. Primero, si alguien simplemente navega por el archivo config.php, la página está en blanco. Entonces puedes poner config.php en el mismo archivo que la raíz de tu aplicación web. En segundo lugar, se puede editar en cualquier editor y algunos editores incluso tienen funciones de coloración y verificación de sintaxis.
La desventaja de esta tecnología es que es una tecnología de sólo lectura como los archivos INI. Extraer los datos de este archivo es pan comido, pero ajustar los datos en el archivo PHP es difícil y, en algunos casos, incluso imposible.
La siguiente alternativa muestra cómo escribir un sistema de configuración que sea de naturaleza legible y escribible.
Los dos ejemplos anteriores dearchivos de texto
están bien para entradas de configuración de sólo lectura, pero ¿qué pasa con los parámetros de configuración que son tanto de lectura como de escritura? Primero, observe el archivo de configuración de texto en el Listado 6.
Listado 6. config.txt
# Archivo de configuración de mi aplicación
Título=Mi aplicación
TemplateDirectory=tempdir
Este es el mismo formato de archivo que el archivo INI, pero escribí mi propia herramienta auxiliar. Para hacer esto, creé mi propia clase de Configuración como se muestra a continuación.
Listado 7. text1.php
<?php
configuración de clase
{
privado $configFile = 'config.txt';
privado $elementos = matriz();
función __construct() { $this->parse();
función __get($id) { return $this->items[ $id ];
análisis de funciones()
{
$fh = fopen( $this->configFile, 'r' );
mientras( $l = fgets( $fh ) )
{
si ( preg_match( '/^#/', $l ) == falso )
{
preg_match( '/^(.*?)=(.*?)$/', $l, $encontrado);
$this->items[ $encontrado[1] ] = $encontrado[2];
}
}
cerrar ($fh);
}
}
$c = nueva Configuración();
echo( $c->TemplateDirectory."n" );
?>
Este código primero crea un objeto de configuración. A continuación, el constructor lee config.txt y establece la variable local $items con el contenido del archivo analizado.
Luego, el script busca TemplateDirectory, que no está definido directamente en el objeto. Por lo tanto, se llama al método mágico __get con $id establecido en 'TemplateDirectory', que devuelve el valor en la matriz $items para esa clave.
Este método __get es específico del entorno PHP V5, por lo que este script debe ejecutarse en PHP V5. De hecho, todos los scripts de este artículo deben ejecutarse en PHP V5.
Al ejecutar este script desde la línea de comando, verá los siguientes resultados:
% php text1.php
tempdir
%
Se espera todo, el objeto lee el archivo config.txt y obtiene el valor correcto para el elemento de configuración TemplateDirectory.
Pero, ¿qué debes hacer para establecer un valor de configuración? Al crear un nuevo método y algún código de prueba nuevo en esta clase, puede obtener esta funcionalidad, como se muestra a continuación.
Listado 8. text2.php
<?php
configuración de clase
{
...
función __get($id) { return $this->items[ $id ];
función __set($id,$v) { $this->items[ $id ] = $v }
análisis de función() {...}
}
$c = nueva Configuración();
echo( $c->TemplateDirectory."n" );
$c->TemplateDirectory = 'foobar';
echo( $c->TemplateDirectory."n" );
?>
Ahora, hay una función __set, que es la "prima" de la función __get. Esta función no obtiene el valor de una variable miembro. Esta función se llama cuando se va a establecer una variable miembro. El código de prueba en la parte inferior establece el valor e imprime el nuevo valor.
Esto es lo que sucede cuando ejecuta este código desde la línea de comando:
% php text2.php
tempdir
fobar
% ¡
Muy bien! Pero ¿cómo puedo guardarlo en un archivo para que se arregle el cambio? Para hacer esto, necesita escribir el archivo y leerlo. Nueva función para escribir archivos como se muestra a continuación.
Listado 9. text3.php
<?php
configuración de clase
{
...
función guardar()
{
$nf = '';
$fh = fopen( $this->configFile, 'r' );
mientras( $l = fgets( $fh ) )
{
si ( preg_match( '/^#/', $l ) == falso )
{
preg_match( '/^(.*?)=(.*?)$/', $l, $encontrado);
$nf .= $encontrado[1]."=".$this->items[$encontrado[1]]."n";
}
demás
{
$nf .= $l;
}
}
cerrar ($fh);
copiar( $this->configFile, $this->configFile.'.bak' );
$fh = fopen( $this->configFile, 'w' );
fwrite( $fh, $nf );
cerrar ($fh);
}
}
$c = nueva Configuración();
echo( $c->TemplateDirectory."n" );
$c->TemplateDirectory = 'foobar';
echo( $c->TemplateDirectory."n" );
$c->guardar();
?>
La nueva función de guardar manipula inteligentemente config.txt. En lugar de simplemente reescribir el archivo con los elementos de configuración actualizados (lo que eliminaría los comentarios), leí el archivo y reescribí de manera flexible el contenido de la matriz $items. De esta forma, se conservan los comentarios del archivo.
Ejecute el script en la línea de comando y genere el contenido del archivo de configuración de texto. Puede ver el siguiente resultado.
Listado 10. Guardar la salida de la función
%php text3.php
tempdir
fobar
% gato config.txt
#Archivo de configuración de mi aplicación
Título=Mi aplicación
TemplateDirectory=foobar
%
El archivo config.txt original ahora está actualizado con los nuevos valores.
Archivos de configuración XML
Aunque los archivos de texto son fáciles de leer y editar, no son tan populares como los archivos XML. Además, existen numerosos editores disponibles para XML que comprenden el marcado, el escape de símbolos especiales y más. Entonces, ¿cómo sería la versión XML del archivo de configuración? El Listado 11 muestra el archivo de configuración en formato XML.
Listado 11. config.xml
<?xml version="1.0"?>
<configuración>
<Título>Mi aplicación</Título>
<TemplateDirectory>tempdir</TemplateDirectory>
</config>
El Listado 12 muestra una versión actualizada de la clase Configuración que utiliza XML para cargar los ajustes de configuración.
Listado 12. xml1.php
<?php
configuración de clase
{
privado $configFile = 'config.xml';
privado $elementos = matriz();
función __construct() { $this->parse();
función __get($id) { return $this->items[ $id ];
análisis de funciones()
{
$doc = nuevo DOMDocumento();
$doc->cargar( $this->configFile );
$cn = $doc->getElementsByTagName( "config" );
$nodos = $cn->item(0)->getElementsByTagName( "*" );
foreach($nodos como $nodo)
$this->items[ $nodo->nodeName ] = $nodo->nodeValue;
}
}
$c = nueva Configuración();
echo( $c->TemplateDirectory."n" );
?>
Parece que XML tiene otro beneficio: el código es más simple y fácil que la versión de texto. Para guardar este XML, se necesita otra versión de la función guardar, que guarda el resultado en formato XML en lugar de formato de texto.
Listado 13. xml2.php
...
función guardar()
{
$doc = nuevo DOMDocumento();
$doc->formatOutput = verdadero;
$r = $doc->createElement( "config" );
$doc->appendChild( $r );
foreach( $this->elementos como $k => $v )
{
$kn = $doc->createElement( $k );
$kn->appendChild( $doc->createTextNode( $v ) );
$r->appendChild( $kn );
}
copiar( $this->configFile, $this->configFile.'.bak' );
$doc->save( $this->configFile );
}
...
Este código crea un nuevo modelo de objetos de documento (DOM) XML y luego guarda todos los datos en la matriz $ items en este modelo. Después de completar esto, use el método guardar para guardar el XML en un archivo.
Una última alternativa
al uso de una base de datos
es utilizar una base de datos para almacenar los valores de los elementos de configuración.El primer paso es utilizar un esquema simple para almacenar datos de configuración. A continuación se muestra un patrón simple.
Listado 14.
Configuración de SCHEMA.SQL DROP TABLE SI EXISTE;
CREAR configuración de TABLA (
id MEDIUMINT NO NULO AUTO_INCREMENT,
nombre TEXTO,
valor TEXTO,
CLAVE PRIMARIA(id)
);
Esto requiere algunos ajustes según los requisitos de la aplicación. Por ejemplo, si desea que el elemento de configuración se almacene por usuario, debe agregar el ID de usuario como una columna adicional.
Para leer y escribir datos, escribí la clase de Configuración actualizada que se muestra en la Figura 15.
Listado 15. db1.php
<?php
require_once('DB.php');
$dsn = 'mysql://root:contraseña@localhost/config';
$db =& DB::Conectar( $dsn, matriz() );
if (PEAR::isError($db)) { die($db->getMessage() }
configuración de clase
);
{
privado $configFile = 'config.xml';
privado $elementos = matriz();
función __construct() { $this->parse();
función __get($id) { return $this->items[ $id ];
función __set($id,$v)
{
global $bd;
$this->items[ $id ] = $v;
$sth1 = $db->prepare( 'BORRAR DE la configuración DONDE nombre=?' );
$db->ejecutar( $sth1, $id );
if (PEAR::isError($db)) { morir($db->getMessage() }
$sth2 = $db->prepare('INSERTAR EN la configuración (id, nombre, valor) VALORES (0,?,?)');
$db->ejecutar( $sth2, matriz( $id, $v ) );
if (PEAR::isError($db)) { morir($db->getMessage() }
}
función analizar()
{
global $bd;
$doc = nuevo DOMDocumento();
$doc->cargar( $this->configFile );
$cn = $doc->getElementsByTagName( "config" );
$nodos = $cn->item(0)->getElementsByTagName( "*" );
foreach($nodos como $nodo)
$this->items[ $nodo->nodeName ] = $nodo->nodeValue;
$res = $db->query( 'SELECCIONAR nombre,valor DE la configuración' );
if (PEAR::isError($db)) { morir($db->getMessage() }
while( $res->fetchInto( $fila ) ) {
$this->items[ $fila[0] ] = $fila[1];
}
}
}
$c = nueva Configuración();
echo( $c->TemplateDirectory."n" );
$c->TemplateDirectory = 'nuevo foo';
echo( $c->TemplateDirectory."n" );
?>
Esta es en realidad una solución híbrida de texto/base de datos. Eche un vistazo más de cerca al método de análisis. Esta clase primero lee el archivo de texto para obtener el valor inicial y luego lee la base de datos para actualizar la clave al último valor. Después de establecer un valor, la clave se elimina de la base de datos y se agrega un nuevo registro con el valor actualizado.
Es interesante ver cómo funciona la clase Configuración en varias versiones de este artículo. Puede leer datos de archivos de texto, XML y bases de datos, todo ello manteniendo la misma interfaz. Te animo a que utilices interfaces con la misma estabilidad también en tu desarrollo. Exactamente cómo funciona esto no está claro para el cliente del objeto. La clave es el contrato entre el objeto y el cliente.
Qué es la configuración y cómo configurarla
Encontrar el término medio adecuado entre demasiadas opciones de configuración y una configuración insuficiente puede resultar complicado. Sin duda, cualquier configuración de la base de datos (por ejemplo, nombre de la base de datos, usuario y contraseña de la base de datos) debe ser configurable. Además, tengo algunos elementos de configuración básicos recomendados.
En la configuración avanzada, cada función debe tener una opción de activación/desactivación independiente. Permita o deshabilite estas opciones según su importancia para la aplicación. Por ejemplo, en una aplicación de foro web, la función de retraso está habilitada de forma predeterminada. Sin embargo, las notificaciones por correo electrónico están deshabilitadas de forma predeterminada, ya que parece requerir personalización.
Todas las opciones de la interfaz de usuario (UI) deben configurarse en una ubicación. La estructura de la interfaz (por ejemplo, ubicaciones del menú, elementos de menú adicionales, URL que enlazan a elementos específicos de la interfaz, logotipos utilizados, etc.) deben configurarse en una única ubicación. Recomiendo encarecidamente no especificar entradas de fuente, color o estilo como elementos de configuración. Estos deben configurarse mediante hojas de estilo en cascada (CSS) y el sistema de configuración debe especificar qué archivo CSS usar. CSS es una forma eficiente y flexible de configurar fuentes, estilos, colores y más. Existen muchas herramientas CSS excelentes y su aplicación debe hacer un buen uso de CSS en lugar de intentar establecer el estándar usted mismo.
Dentro de cada función, recomiendo configurar de 3 a 10 opciones de configuración. Estas opciones de configuración deben nombrarse de manera significativa. Si las opciones de configuración se pueden establecer a través de la interfaz de usuario, los nombres de las opciones en archivos de texto, archivos XML y bases de datos deben estar directamente relacionados con el título del elemento de la interfaz. Además, todas estas opciones deberían tener valores predeterminados claros.
En general, las siguientes opciones deben ser configurables: direcciones de correo electrónico, qué CSS usar, la ubicación de los recursos del sistema a los que se hace referencia desde los archivos y los nombres de archivos de los elementos gráficos.
Para los elementos gráficos, es posible que desee crear un tipo de perfil separado llamado máscara, que contiene configuraciones para el perfil, incluida la ubicación de los archivos CSS, la ubicación de los gráficos y ese tipo de cosas. Luego, permita que el usuario elija entre varios archivos de máscaras. Esto simplifica los cambios a gran escala en la apariencia de su aplicación. Esto también brinda a los usuarios la oportunidad de personalizar la aplicación entre diferentes instalaciones de productos. Este artículo no cubre estos archivos de máscara, pero los conceptos básicos que aprenderá aquí simplificarán mucho la compatibilidad con los archivos de máscara.
Conclusión
La configurabilidad es una parte vital de cualquier aplicación PHP y debe ser una parte central del diseño desde el principio. Espero que este artículo brinde ayuda para implementar su arquitectura de configuración y brinde orientación sobre qué opciones de configuración debe permitir.