Cet article illustre plusieurs façons de créer des applications PHP configurables. L'article explore également les points de configuration idéaux dans une application et recherche un équilibre entre une application trop configurable et trop fermée.
Si vous envisagez de mettre votre application PHP à la disposition d'autres personnes ou entreprises, vous devez vous assurer que l'application est configurable. Au minimum, autorisez les utilisateurs à définir leurs identifiants et mots de passe pour la base de données de manière sécurisée afin que le contenu de celles-ci ne soit pas rendu public.
Cet article présente plusieurs techniques pour stocker les paramètres de configuration et modifier ces paramètres. En outre, l'article fournit également des conseils sur les éléments qui doivent être rendus configurables et sur la manière d'éviter de tomber dans le dilemme de la sur ou sous-configuration.
Configuration à l'aide de fichiers INI
PHP prend en charge les fichiers de configuration. Ceci est réalisé grâce à un mécanisme de fichier d'initialisation (INI) tel que le fichier php.ini, dans lequel des constantes telles que les délais d'attente de connexion à la base de données ou la manière dont les sessions sont stockées sont définies. Si vous le souhaitez, vous pouvez personnaliser la configuration de votre application dans ce fichier php.ini. Pour illustrer, j'ai ajouté les lignes de code suivantes au fichier php.ini.
myapptempdir=foo
Ensuite, j'ai écrit un petit script PHP pour lire cet élément de configuration, comme indiqué dans le listing 1.
Liste 1. ini1.php
<?php
fonction get_template_directory()
{
$v = get_cfg_var( "monapptempdir" );
return ( $v == null ) ? "tempdir" : $v;
}
echo( get_template_directory()."n" );
?>
Lorsque vous exécutez ce code sur la ligne de commande, vous obtenez les résultats suivants :
% php ini1.php
foo
%
merveilleux. Mais pourquoi ne pouvons-nous pas utiliser la fonction INI standard pour obtenir la valeur de l'élément de configuration myapptempdir ? J'ai fait quelques recherches et découvert que dans la plupart des cas, les éléments de configuration personnalisés ne peuvent pas être obtenus à l'aide de ces méthodes. Il est cependant accessible grâce à la fonction get_cfg_var.
Pour simplifier cette approche, encapsulez l'accès à la variable dans une deuxième fonction qui prend le nom de la clé de configuration et une valeur par défaut comme paramètres, comme indiqué ci-dessous.
Listing 2.
Fonction ini2.php get_ini_value( $n, $dv )
{
$c = get_cfg_var( $n );
retourner ( $c == null ) ? $dv : $c;
}
fonction get_template_directory()
{
return get_ini_value( "myapptempdir", "tempdir" );
}
Ceci est un bon aperçu de la façon d'accéder au fichier INI, donc si vous souhaitez utiliser un mécanisme différent ou stocker le fichier INI ailleurs, vous n'avez pas besoin de vous donner la peine de modifier de nombreuses fonctions.
Je ne recommande pas d'utiliser les fichiers INI pour la configuration des applications, pour deux raisons. Premièrement, même si cela facilite la lecture du fichier INI, cela rend presque impossible l'écriture du fichier INI en toute sécurité. Cela ne convient donc qu'aux éléments de configuration en lecture seule. Deuxièmement, le fichier php.ini est partagé entre toutes les applications du serveur, donc je ne pense pas que les éléments de configuration spécifiques à l'application devraient être écrits dans ce fichier.
Que devez-vous savoir sur les fichiers INI ? La chose la plus importante est de savoir comment réinitialiser le chemin d'inclusion pour ajouter des éléments de configuration, comme indiqué ci-dessous.
Liste 3. ini3.php
<?php
echo( ini_get("include_path")."n" );
ini_set("include_path",
ini_get("include_path").":./mylib" );
echo( ini_get("include_path")."n" );
?>
Dans cet exemple, j'ai ajouté mon répertoire mylib local au chemin d'inclusion, afin de pouvoir exiger des fichiers PHP de ce répertoire sans ajouter le chemin à l'instruction require.
Configuration en PHP
Une alternative courante au stockage des entrées de configuration dans un fichier INI consiste à utiliser un simple script PHP pour conserver les données. Ci-dessous un exemple.
Liste 4. config.php
<?php
# Spécifiez l'emplacement du répertoire temporaire
#
$TEMPLATE_DIRECTORY = "rép_temp" ;
?>
Le code utilisant cette constante est le suivant.
Liste 5. php.php
<?php
require_once 'config.php';
fonction get_template_directory()
{
global $TEMPLATE_DIRECTORY ;
renvoie $TEMPLATE_DIRECTORY ;
}
echo( get_template_directory()."n" );
?>
Le code contient d'abord le fichier de configuration (config.php), puis vous pouvez utiliser directement ces constantes.
L’utilisation de cette technologie présente de nombreux avantages. Premièrement, si quelqu'un parcourt simplement le fichier config.php, la page est vide. Vous pouvez donc mettre config.php dans le même fichier que la racine de votre application web. Deuxièmement, il peut être modifié dans n’importe quel éditeur, et certains éditeurs disposent même de fonctions de coloration et de vérification de la syntaxe.
L’inconvénient de cette technologie est qu’il s’agit d’une technologie en lecture seule comme les fichiers INI. Extraire les données de ce fichier est un jeu d’enfant, mais ajuster les données dans le fichier PHP est difficile, voire impossible dans certains cas.
L'alternative suivante montre comment écrire un système de configuration qui est à la fois lisible et inscriptible.
Les deux exemples précédents defichiers texte
conviennent aux entrées de configuration en lecture seule, mais qu'en est-il des paramètres de configuration qui sont à la fois en lecture et en écriture ? Tout d’abord, jetez un œil au fichier de configuration texte dans le listing 6.
Listing 6. config.txt
# Le fichier de configuration de mon application
Titre = Mon application
TemplateDirectory=tempdir
C'est le même format de fichier que le fichier INI, mais j'ai écrit mon propre outil d'aide. Pour ce faire, j'ai créé ma propre classe de configuration comme indiqué ci-dessous.
Liste 7. text1.php
<?php
configuration de classe
{
privé $configFile = 'config.txt';
private $items = tableau();
function __construct() { $this->parse( }
function __get($id) { return $this->items[ $id ];
fonction analyser()
{
$fh = fopen( $this->configFile, 'r' );
tandis que( $l = fgets( $fh ) )
{
if ( preg_match( '/^#/', $l ) == false )
{
preg_match( '/^(.*?)=(.*?)$/', $l, $found );
$this->items[ $found[1] ] = $found[2];
}
}
fclose( $fh );
}
}
$c = new Configuration();
echo( $c->TemplateDirectory."n" );
?>
Ce code crée d'abord un objet Configuration. Le constructeur lit ensuite config.txt et définit la variable locale $items avec le contenu du fichier analysé.
Le script recherche ensuite TemplateDirectory, qui n'est pas directement défini dans l'objet. Par conséquent, la méthode magique __get est appelée avec $id défini sur « TemplateDirectory », qui renvoie la valeur dans le tableau $items pour cette clé.
Cette méthode __get est spécifique à l'environnement PHP V5, ce script doit donc être exécuté sous PHP V5. En fait, tous les scripts de cet article doivent être exécutés sous PHP V5.
Lors de l'exécution de ce script à partir de la ligne de commande, vous verrez les résultats suivants :
% php text1.php
répertoire temp
%
Tout est attendu, l'objet lit le fichier config.txt et obtient la valeur correcte pour l'élément de configuration TemplateDirectory.
Mais que devez-vous faire pour définir une valeur de configuration ? En créant une nouvelle méthode et un nouveau code de test dans cette classe, vous pouvez obtenir cette fonctionnalité, comme indiqué ci-dessous.
Liste 8. text2.php
<?php
configuration de classe
{
...
function __get($id) { return $this->items[ $id ]; }
function __set($id,$v) { $this->items[ $id ] = $v }
fonction analyser() { ... }
}
$c = nouvelle Configuration();
echo( $c->TemplateDirectory."n" );
$c->TemplateDirectory = 'foobar';
echo( $c->TemplateDirectory."n" );
?>
Maintenant, il existe une fonction __set, qui est la "cousine" de la fonction __get. Cette fonction n'obtient pas la valeur d'une variable membre. Cette fonction est appelée lorsqu'une variable membre doit être définie. Le code de test en bas définit la valeur et imprime la nouvelle valeur.
Voici ce qui se passe lorsque vous exécutez ce code depuis la ligne de commande :
% php text2.php
répertoire temp
foobar
%
Très bien! Mais comment puis-je l'enregistrer dans un fichier pour que la modification soit corrigée ? Pour ce faire, vous devez écrire le fichier et le lire. Nouvelle fonction pour écrire des fichiers comme indiqué ci-dessous.
Liste 9. text3.php
<?php
configuration de classe
{
...
fonction save()
{
$nf = '';
$fh = fopen( $this->configFile, 'r' );
tandis que( $l = fgets( $fh ) )
{
if ( preg_match( '/^#/', $l ) == false )
{
preg_match( '/^(.*?)=(.*?)$/', $l, $found );
$nf .= $found[1]."=".$this->items[$found[1]]."n";
}
autre
{
$nf .= $l;
}
}
fclose( $fh );
copy( $this->configFile, $this->configFile.'.bak' );
$fh = fopen( $this->configFile, 'w' );
fwrite( $fh, $nf );
fclose( $fh );
}
}
$c = nouvelle Configuration();
echo( $c->TemplateDirectory."n" );
$c->TemplateDirectory = 'foobar';
echo( $c->TemplateDirectory."n" );
$c->sauvegarder();
?>
La nouvelle fonction de sauvegarde manipule intelligemment config.txt. Plutôt que de simplement réécrire le fichier avec les éléments de configuration mis à jour (ce qui supprimerait les commentaires), j'ai lu le fichier et réécrit de manière flexible le contenu du tableau $items. De cette façon, les commentaires du fichier sont conservés.
Exécutez le script sur la ligne de commande et affichez le contenu du fichier de configuration texte. Vous pouvez voir la sortie suivante.
Listing 10. Sauvegarde de la sortie de la fonction
%php text3.php
répertoire temp
foobar
% chat config.txt
#Le fichier de configuration de mon application
Titre = Mon application
TemplateDirectory=foobar
%
Le fichier config.txt d'origine est maintenant mis à jour avec les nouvelles valeurs.
Fichiers de configuration XML
Bien que les fichiers texte soient faciles à lire et à modifier, ils ne sont pas aussi populaires que les fichiers XML. De plus, il existe de nombreux éditeurs disponibles pour XML qui comprennent le balisage, l'échappement de symboles spéciaux, etc. Alors, à quoi ressemblerait la version XML du fichier de configuration ? Le listing 11 montre le fichier de configuration au format XML.
Listing 11. config.xml
<?xml version="1.0"?>
<configuration>
<Titre>Mon application</Titre>
<TemplateDirectory>tempdir</TemplateDirectory>
</config>
Le listing 12 montre une version mise à jour de la classe Configuration qui utilise XML pour charger les paramètres de configuration.
Liste 12. xml1.php
<?php
configuration de classe
{
privé $configFile = 'config.xml';
private $items = tableau();
function __construct() { $this->parse( }
function __get($id) { return $this->items[ $id ];
fonction analyser()
{
$doc = nouveau DOMDocument();
$doc->load( $this->configFile );
$cn = $doc->getElementsByTagName( "config" );
$nodes = $cn->item(0)->getElementsByTagName( "*" );
foreach( $nodes comme $node )
$this->items[ $node->nodeName ] = $node->nodeValue;
}
}
$c = nouvelle Configuration();
echo( $c->TemplateDirectory."n" );
?>
Il semble que XML ait un autre avantage : le code est plus simple et plus simple que la version texte. Pour enregistrer ce XML, une autre version de la fonction de sauvegarde est nécessaire, qui enregistre le résultat au format XML au lieu du format texte.
Liste 13. xml2.php
...
fonction sauvegarder()
{
$doc = nouveau DOMDocument();
$doc->formatOutput = true;
$r = $doc->createElement( "config" );
$doc->appendChild( $r );
foreach( $this->éléments comme $k => $v )
{
$kn = $doc->createElement( $k );
$kn->appendChild( $doc->createTextNode( $v ) );
$r->appendChild( $kn );
}
copy( $this->configFile, $this->configFile.'.bak' );
$doc->save( $this->configFile );
}
...
Ce code crée un nouveau modèle objet de document XML (DOM), puis enregistre toutes les données du tableau $items dans ce modèle. Après avoir terminé, utilisez la méthode save pour enregistrer le XML dans un fichier.
Une dernière alternative
à l’utilisation d’une base de données
consiste à utiliser une base de données pour stocker les valeurs des éléments de configuration.La première étape consiste à utiliser un schéma simple pour stocker les données de configuration. Vous trouverez ci-dessous un modèle simple.
Listing 14.
Paramètres de schema.sql DROP TABLE IF EXISTS ;
Paramètres CRÉER UNE TABLE (
identifiant MEDIUMINT NON NULL AUTO_INCREMENT,
nom TEXTE,
valeur TEXTE,
CLÉ PRIMAIRE (identifiant)
);
Cela nécessite quelques ajustements en fonction des exigences de l'application. Par exemple, si vous souhaitez que l'élément de configuration soit stocké par utilisateur, vous devez ajouter l'ID utilisateur dans une colonne supplémentaire.
Pour lire et écrire des données, j'ai écrit la classe Configuration mise à jour illustrée à la figure 15.
Liste 15. db1.php
<?php
require_once( 'DB.php' );
$dsn = 'mysql://root:password@localhost/config';
$db =& DB::Connect( $dsn, array() );
if (PEAR::isError($db)) { die($db->getMessage() }
classe Configuration
);
{
privé $configFile = 'config.xml';
private $items = tableau();
function __construct() { $this->parse( }
function __get($id) { return $this->items[ $id ];
fonction __set($id,$v)
{
base de données $ globale ;
$this->items[ $id ] = $v;
$sth1 = $db->prepare( 'DELETE FROM settings WHERE name=?' );
$db->execute( $sth1, $id );
if (PEAR::isError($db)) { die($db->getMessage() );
$sth2 = $db->prepare('INSERT INTO settings ( id, name, value ) VALUES ( 0, ?, ? )' );
$db->execute( $sth2, array( $id, $v ) );
if (PEAR::isError($db)) { die($db->getMessage() );
}
fonction analyser()
{
base de données $ globale ;
$doc = nouveau DOMDocument();
$doc->load( $this->configFile );
$cn = $doc->getElementsByTagName( "config" );
$nodes = $cn->item(0)->getElementsByTagName( "*" );
foreach( $nodes comme $node )
$this->items[ $node->nodeName ] = $node->nodeValue;
$res = $db->query( 'SELECT nom, valeur FROM paramètres' );
if (PEAR::isError($db)) { die($db->getMessage() );
while( $res->fetchInto( $row ) ) {
$this->items[ $row[0] ] = $row[1];
}
}
}
$c = nouvelle Configuration();
echo( $c->TemplateDirectory."n" );
$c->TemplateDirectory = 'nouveau foo';
echo( $c->TemplateDirectory."n" );
?>
Il s'agit en fait d'une solution hybride texte/base de données. Veuillez examiner de plus près la méthode d'analyse. Cette classe lit d'abord le fichier texte pour obtenir la valeur initiale, puis lit la base de données pour mettre à jour la clé avec la dernière valeur. Après avoir défini une valeur, la clé est supprimée de la base de données et un nouvel enregistrement est ajouté avec la valeur mise à jour.
Il est intéressant de voir comment la classe Configuration fonctionne à travers plusieurs versions de cet article. Elle peut lire des données à partir de fichiers texte, XML et de bases de données, tout en conservant la même interface. Je vous encourage également à utiliser des interfaces avec la même stabilité dans votre développement. Le client de l'objet ne sait pas exactement comment cela fonctionne. La clé est le contrat entre l'objet et le client.
Qu'est-ce que la configuration et comment la configurer ?
Trouver le juste milieu entre trop d'options de configuration et pas assez de configuration peut être difficile. Bien sûr, toute configuration de base de données (par exemple, le nom de la base de données, l'utilisateur de la base de données et le mot de passe) doit être configurable. De plus, j'ai quelques éléments de configuration de base recommandés.
Dans les paramètres avancés, chaque fonctionnalité doit avoir une option d'activation/désactivation distincte. Autorisez ou désactivez ces options en fonction de leur importance pour l'application. Par exemple, dans une application de forum Web, la fonctionnalité de délai est activée par défaut. Cependant, les notifications par courrier électronique sont désactivées par défaut, car cela semble nécessiter une personnalisation.
Les options de l’interface utilisateur (UI) doivent toutes être définies au même endroit. La structure de l'interface (par exemple, les emplacements de menu, les éléments de menu supplémentaires, les URL renvoyant vers des éléments spécifiques de l'interface, les logos utilisés, etc.) doivent toutes être définies à un seul emplacement. Je déconseille fortement de spécifier des entrées de police, de couleur ou de style comme éléments de configuration. Ceux-ci doivent être définis via des feuilles de style en cascade (CSS) et le système de configuration doit spécifier quel fichier CSS utiliser. CSS est un moyen efficace et flexible de définir les polices, les styles, les couleurs, etc. Il existe de nombreux excellents outils CSS, et votre application doit faire bon usage du CSS plutôt que d'essayer de définir la norme vous-même.
Au sein de chaque fonctionnalité, je recommande de définir 3 à 10 options de configuration. Ces options de configuration doivent être nommées de manière significative. Si les options de configuration peuvent être définies via l'interface utilisateur, les noms d'options dans les fichiers texte, les fichiers XML et les bases de données doivent être directement liés au titre de l'élément d'interface. De plus, ces options doivent toutes avoir des valeurs par défaut claires.
En général, les options suivantes doivent être configurables : les adresses e-mail, le CSS à utiliser, l'emplacement des ressources système référencées à partir des fichiers et les noms de fichiers des éléments graphiques.
Pour les éléments graphiques, vous souhaiterez peut-être créer un type de profil distinct appelé skin, qui contient les paramètres du profil, notamment l'emplacement des fichiers CSS, l'emplacement des graphiques et ce type d'éléments. Ensuite, laissez l'utilisateur choisir parmi plusieurs fichiers de skin. Cela simplifie les modifications à grande échelle de l’apparence de votre application. Cela offre également aux utilisateurs la possibilité de peaufiner l'application entre différentes installations de produits. Cet article ne couvre pas ces fichiers d'habillage, mais les bases que vous apprendrez ici rendront la prise en charge des fichiers d'habillage beaucoup plus simple.
Conclusion
La configurabilité est un élément essentiel de toute application PHP et devrait être un élément central de la conception dès le départ. J'espère que cet article vous aidera à mettre en œuvre votre architecture de configuration et vous fournira des conseils sur les options de configuration que vous devez autoriser.