Maintenance d'un dossier de fichiers temporaires
Posté dans PHP — Pas de commentaire
Dans un article précédent je vous présentais un système de génération à la volée de miniatures avec mise en cache. Peut-être vous êtes vous demandé ce qui allait advenir de toutes ces miniatures mises en cache ? Parce que générer des miniatures c'est sympa, mais si c'est pour saturer l'espace disque de notre hébergement c'est peut être pas la meilleure chose.
La fonction que je vous présente aujourd'hui a pour but de maintenir votre dossier de fichiers temporaires dans les limites du raisonnable. Elle inspecte les fichiers du dossier temporaire et lorsque la taille totale de ces fichiers dépasse la taille maximale multipliée par le ratio de seuil alors elle supprime les fichiers les plus anciens jusqu'à ce que la taille totale soit inférieure à la taille maximale. Le ratio de seuil servant de déclencheur pour un nettoyage tout en douceur.
La fonction fera de son mieux lors de déclenchements concurrents grâce à un ingénieux système de verrou, qui ne prendra pas la tête (ni les ressources CPU) de votre hébergeur.
La fonction
Mesdames et Messieurs, la fonction :
<?php
function wd_clean_repository($root, $maxsize=512, $threshold_ratio=.5)
{
if (!is_dir($root))
{
return;
}
$dh = opendir($root);
if (!$dh)
{
return;
}
#
# create file list, with the filename as key and ctime and size as value.
# we set the ctime first to be able to sort the file by ctime when necessary.
#
$files = array();
$totalsize = 0;
while (($file = readdir($dh)) !== false)
{
if ($file{0} == '.')
{
continue;
}
$stat = stat($root . $file);
$files[$file] = array($stat['ctime'], $stat['size']);
$totalsize += $stat['size'];
}
closedir($dh);
if ($totalsize < ($maxsize + $maxsize * $threshold_ratio) * 1024)
{
#
# There is enough space in the repository. We don't need to delete any file.
#
return;
}
#
# The repository is completely full, we need to make some space.
# We create an array with the files to delete. Files are added until
# we get back to the size limit.
#
asort($files);
$deletesize = $totalsize - ($maxsize * 1024);
$deletelist = array();
foreach ($files as $file => $stat)
{
list(, $size) = $stat;
$deletesize -= $size;
$deletelist[] = $root . $file;
if ($deletesize < 0)
{
break;
}
}
#
# obtain exclusive lock to delete files
#
$lockfile = $root . DIRECTORY_SEPARATOR . '.lock';
$lh = fopen($lockfile, 'w+');
if (!$lh)
{
WdDebug::trigger('Unable to open lock %file', array('%file' => $lockfile));
return;
}
#
# We will try $n time to obtain the exclusive lock
#
$n = 10;
while (!flock($lh, LOCK_EX | LOCK_NB))
{
#
# If the lock is not obtained we sleep for 0 to 100 milliseconds.
# We sleep to avoid CPU load, and we sleep for a random time
# to avoid collision.
#
usleep(round(rand(0, 100) * 1000));
if (!--$n)
{
#
# We were unable to obtain the lock in time.
# We exit silently.
#
return;
}
}
#
# The lock was obtained, we can now delete the files
#
foreach ($deletelist as $file)
{
unlink($file);
}
#
# and release the lock.
#
fclose($lh);
}
?>
Exemple d'utilisation
Et voici un exemple d'utilisation, c'est pas très compliqué.
<?php
wd_clean_repository($_SERVER['DOCUMENT_ROOT'] . '/path_to_repository/');
?>
Notez quand même la nécessité de fournir un chemin absolu.
On peut également définir la taille du cache (en Ko), ainsi que le ratio de seuil. Voici ce que l'on pourrait utiliser pour un cache d'un méga et 512 Ko de seuil (soit 1,5Mo au maximum) :
<?php
wd_clean_repository($_SERVER['DOCUMENT_ROOT'] . '/path_to_repository/', 1024, .5);
?>
Générer à la volée des miniatures avec mise en cache
Posté dans PHP — Pas de commentaire
La miniature (ou thumbnail en Anglais) est une version d'une image dont les dimensions sont réduites par rapport à l'originale. En principe on clique sur la miniature pour présenter la version originale. On peut utiliser les miniatures pour créer une galerie d'images, afin d'en visualiser un plus grand nombre à la fois. Les miniatures étant souvent de poids plus faibles, elles sont plus rapides à télécharger, améliorant ainsi la navigation.
Les miniatures peuvent être créées manuellement, éventuellement par lots, en utilisant un logiciel graphique, ou bien générées à la volée, auquel cas elles peuvent être mises en cache. La génération à la volée me semble intéressante pour les raisons suivantes :
- Moins de travail pour l'utilisateur : de nombreuses variations peuvent être créées sans son intervention.
- La mise à jour est beaucoup plus simple puisqu'il suffit de modifier le fichier original pour que les versions soient mises à jour.
- Puisque les miniatures sont générées à la volée on peut réduire l'espace disque qu'elles occupent.
L'article que je vous propose décrit un système de génération à la volée de miniatures avec mise en cache. Nous en verrons les concepts et l'implémentation. Le code présenté se base sur des fonctionnalités de la classe WdImage qui ont été introduites dans les articles Une classe et sept méthodes pour créer des miniatures et Dessiner un damier à la Photoshop.
Vous pouvez obtenir la dernière version de la classe en téléchargeant le framework WdCore.
Déroulement
L'obtention d'une miniature se fait par l'entremise d'un script PHP qui prendra soin de rediriger la requête HTTP vers le fichier de la miniature, créant le fichier au passage s'il n'existe pas.
Les paramètres pour créer la miniature sont passés par la chaine de requête de l'URL. Ces mêmes paramètres sont associés aux informations du fichier d'origine afin de créer une clé unique qui servira à identifier la miniature. Cette clé est utilisée pour nommer le fichier de la miniature lors de la mise en cache.
Composition de l'URL
L'URL se compose en fonction de l'emplacement du script sur l'hébergement, de l'emplacement de l'image originale sur l'hébergement, des dimensions de la miniatures et des paramètres optionnels tels que la méthode de redimensionnement, la couleur de remplissage, ou les couleurs de remplissage si l'on souhaite obtenir une grille (bien connue des utilisateurs de Photoshop).
Le chemin de l'image d'origine est relatif à la racine de notre hébergement. Ainsi, si notre script se trouve à la racine de notre hébergement on pourrait imaginer les URL suivantes :
/thumbnailer.php?f=/path_to_original/original.jpeg&w=60&h=60/thumbnailer.php?f=/path_to_original/original.jpeg&w=60&h=60&format=jpeg&quality=70/thumbnailer.php?f=/path_to_original/original.jpeg&w=60&h=60&method=scale-max/thumbnailer.php?f=/path_to_original/original.jpeg&w=60&h=60&method=scale-max&background=red/thumbnailer.php?f=/path_to_original/original.jpeg&w=60&h=60&method=scale-max&background=#F00,yellow/thumbnailer.php?f=/path_to_original/original.jpeg&w=60&h=60&method=scale-max&background=#F00,#00FFFF,medium/thumbnailer.php?f=/path_to_original/original.jpeg&w=60&h=60&method=scale-max&background=#F00,#00FFFF,16
Description des paramètres
f: il s'agit de l'URL de l'image originale, relative au dossier racine de notre hébergement.weth: sont la largeur et la hauteur de la miniature.format: le format d'image de la miniature. Vous avez le choix entrejpeg,pngetgif.quality: permet de définir la qualité de compression pour le format JPEG, allant de 0 à 100 pour la qualité maximale.method: permet de définir la méthode de redimensionnement à utiliser. Il s'agit d'une des sept méthodes décrites dans l'article Une classe et sept méthodes pour créer des miniatures.background: permet de définir la couleur de remplissage ou, dans sa version étendue, les couleurs et la dimension de la grille à utiliser avec la méthodeWdImage::drawGrid()que j'avais présenté dans l'article Dessiner un damier à la Photoshop.
Les paramètres par défaut sont : format=jpeg, quality=70, method=scale-min et background=white.
Conseil sur la construction des URL
Attention à la construction des URL ! Le développeur peu regardant écrira l'URL à la main comme un sauvage, utilisera la couleur #F00 et s'étonnera que « ça ne marche pas ». Effectivement, si le caractère # n'est pas échappé il sera utilisé comme une ancre ! Le développeur consciencieux (parce qu'il n'aime pas perdre son temps à chercher « pourquoi ça ne marche pas ») utilisera la fonction http_build_query() pour créer son URL :
/thumbnailer.php?f=%2Fpath_to_original%2Foriginal.jpeg&w=60&h=60&background=%23F00
Le script thumbnailer.php
Le script thumbnailer.php utilise la classe WdThumbnailer dérivée d'un des modules de mon CMS WdPublisher. Il vous suffira de quelques lignes pour créer le script de génération :
<?php
#
# thumbnailer.php
#
define('WDCORE_ROOT', $_SERVER['DOCUMENT_ROOT'] . '/path_to_wdcore/');
WdThumbnailer::operation_get($_GET, $_GET['f'], $_GET['w'], $_GET['h']);
?>
La classe WdThumbnailer utilise la classe WdImage du framework WdCore. Vous devez définir la constante WDCORE_ROOT pour permettre le chargement de la classe WdImage.
La classe WdThumbnailer
<?php
class WdThumbnailer
{
const METHOD = 'method';
const QUALITY = 'quality';
const FORMAT = 'format';
const BACKGROUND = 'background';
const REPOSITORY_URL = '/repository/thumbnailer/';
static $background;
static public function get($file, $w, $h, array $options=array())
{
#
# Is the repository ready ?
#
if (!is_dir($_SERVER['DOCUMENT_ROOT'] . self::REPOSITORY_URL))
{
WdDebug::trigger('The repository %url does not exists !', array('%url' => self::REPOSITORY_URL));
return false;
}
#
# We check if the file exists
#
$path = $_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . $file;
if (!is_file($path))
{
return false;
}
#
# We merge the filtered user options with the default ones
#
require_once WDCORE_ROOT . 'wdimage.php';
$options = self::filterOptions($options) + array
(
self::BACKGROUND => 'white',
self::FORMAT => 'jpeg',
self::METHOD => WdImage::RESIZE_SCALE_MIN,
self::QUALITY => 70
);
ksort($options);
#
# We create a unique key for the thumbnail, using the image information
# and the options used to create the thumbnail.
#
$stats = stat($path);
$key = implode('|', array($file, $w . 'x' . $h, $stats['mtime'], $stats['size'])) . json_encode($options);
$key = sha1($key);
#
# If the file doesn't exists we create it
#
$format = $options['format'];
$file = self::REPOSITORY_URL . $key . '.' . $format;
$destination = $_SERVER['DOCUMENT_ROOT'] . $file;
if (is_file($destination))
{
unlink($destination);
}
if (!is_file($destination))
{
self::$background = self::decodeBackground($options[self::BACKGROUND]);
$callback = array(__CLASS__, 'fill_callback');
$image = WdImage::load($path, $info);
$image = WdImage::resize($image, $w, $h, $options['method'], $callback);
#
# choose thumbnail file format
#
static $functions = array
(
'gif' => 'imagegif',
'jpeg' => 'imagejpeg',
'png' => 'imagepng'
);
$function = $functions[$format];
$args = array($image, $destination);
if ($format == 'jpeg')
{
#
# add quality option for the 'jpeg' format
#
$args[] = $options['quality'];
}
$rc = call_user_func_array($function, $args);
imageDestroy($image);
if (!$rc)
{
return false;
}
}
return $file;
}
static public function operation_get($params, $file, $w, $h)
{
$location = self::get($file, $w, $h, $params);
if ($location)
{
header('Location: ' . $location, true, 307);
}
else
{
header('HTTP/1.0 404 Not Found');
}
exit;
}
static private function filterOptions($options)
{
$options = array_intersect_key
(
$options, array
(
self::FORMAT => true,
self::METHOD => true,
self::QUALITY => true,
self::BACKGROUND => true
)
);
return $options;
}
static private function decodeBackground($background)
{
$parts = explode(',', $background);
$parts[0] = WdImage::decodeColor($parts[0]);
if (count($parts) == 1)
{
return array($parts[0], null, 0);
}
$parts[1] = WdImage::decodeColor($parts[1]);
return $parts;
}
static public function fill_callback($image, $w, $h)
{
#
# We create WdImage::drawGrid() arguments from the dimensions of the image
# and the values passed using the BACKGROUND parameter.
#
$args = (array) self::$background;
array_unshift($args, $image, 0, 0, $w - 1, $h - 1);
call_user_func_array(array('WdImage', 'drawGrid'), $args);
}
}
?>
N'oubliez pas de modifier la constante REPOSITORY_URL de la classe pour l'adapter à vos besoins. Il convient de fournir l'URL d'un dossier dont les droits d'écritures sont publics, sinon les fichiers des miniatures ne pourront être générés.
Conclusion
Comme vous pouvez le voir, l'implémentation est assez simple, la gestion du cache également. De solides bases pour de futures améliorations comme la prise en charge de filtres ou de superpositions de couches ou de logos…
Je vous présenterai dans un prochain billet une fonction permettant de maintenir la taille du cache dans des proportions raisonnables.
En attendant amusez-vous bien avec ce générateur, j'espère qu'il vous permettra de faire des merveilles.
Un générateur de mot de passe
Posté dans PHP — 2 commentaires
Parce que les petits outils c'est bien aussi, aujourd'hui je vous propose un générateur de mot de passe qui aura la délicatesse de générer avec soin.
Quelques particularités :
- Il s'assure d'une bonne distribution des caractères, on ne se retrouvera pas avec 3 « r » à la suite (Ô joies du hazzard)
- On peut choisir la longueur, qui est à 8 par défaut
- On peut choisir les caractères que l'on veut mettre dedans, même si ceux par défaut sont géniaux : il n'y a pas de 0, O, 1, l, i…, mais il y a des #=@$ (qui ne sont pas que des gros mots)
- Le code est superbe
<?php
function wd_generatePassword($length=8, $possible='$=@#23456789bcdfghjkmnpqrstvwxyz')
{
$password = '';
$possible_length = strlen($possible) - 1;
#
# add random characters to $password for $length
#
while ($length--)
{
#
# pick a random character from the possible ones
#
$except = substr($password, -$possible_length / 2);
for ($n = 0 ; $n < 5 ; $n++)
{
$char = $possible{mt_rand(0, $possible_length)};
#
# we don't want this character if it's already in the password
# unless it's far enough (half of our possible length).
# note: we have 4 tries to find a suitable one.
#
if (strpos($except, $char) === false)
{
break;
}
}
$password .= $char;
}
return $password;
}
?>
Gestion personnalisée des exceptions PHP
Posté dans WdCore — Pas de commentaire
La version 5 de PHP apporte une gestion des exceptions similaires à ce qu'offrent d'autres langages de programmation. On peut ainsi lancer une exception (throw) qui sera éventuellement attrapée (catch) et traitée. On peut ainsi entourer du code sensible de structures try et catch, avec pour chaque structure try une structure catch correspondante.
L'exception n'a rien à voir avec une simple erreur. Lorsqu'une exception est lancée, le flot du programme est interrompu jusqu'au premier bloc catch, ou s'il n'y en a pas, jusqu'au gestionnaire d'exceptions. Ce qui veut dire qu'il nous appartient de gérer l'exception, et de l'attraper si on prévoit une solution de rechange à notre code de test.
Ce billet s'intéressant essentiellement à la personnalisation des exceptions PHP, pour de plus amples informations sur les exceptions je vous invite à consulter la documentation PHP sur les exceptions, et surtout l'excellent billet de Fabrice Lezoray : PHP 5 et la gestion des exceptions.
Quand à moi, je vais vous présenter la classe WdException qui permet de lancer des exceptions tout en profitant des fonctionnalités de la classe WdDebug, et notamment des rapports d'incidents. Je vous invite également à découvrir le framework WdCore, duquel est issue la classe.
Présentation de la classe WdException
La classe WdException peut être utilisée pour lancer des exceptions. Elle étend la classe Exception et présente les fonctionnalités suivantes :
- Le message de l'exception est formaté et localisé (traduit) grâce à la classe WdLocale.
- Le message de l'exception est formaté de manière similaire aux erreurs personnalisées par la classe WdDebug.
- Les messages issus de la journalisation de la class WdDebug peuvent être ajoutés au message de l'exception.
- Un rapport d'incident peut être émit.
La classe est aussi simple à mettre en ouvre que la classe WdDebug :
<?php
define('WDDEBUG_SILENT', true);
define('WDDEBUG_REPORT_ADDRESS', 'me@mycompany.com');
define('WDEXCEPTION_WITH_LOG', true);
require_once 'wdexception.php';
set_exception_handler(array('WdDebug', 'exceptionHandler');
?>
Voyons ce que cela donne avec un petit exemple.
Un exemple d'exception, mais pas exceptionnel
L'exemple suivant présente une fonction test() qui ne supporte pas que son paramètre $a soit différent de 1. Cela l'ennui tellement qu'elle ne se prive pas de lancer une exception si cela se produit :
<?php
function test($a)
{
if ($a != 1)
{
throw new WdException('$a must be 1, "\1" given instead', $a);
}
}
wd_log('A little message before the big crash');
test(13);
?>
On notera, juste avant l'appel à la fonction test(), un appel à la fonction wd_log(). Cette fonction est une des fonctions de journalisation non intrusives proposées par la classe WdDebug. Les messages loggés par cette fonction sont ajoutés au message d'exception si la constante WDEXCEPTION_WITH_LOG est définie comme vraie.
Voyons maintenant le résultat de ce test :
Exception with the following message:
$a must be 1, "13" given instead
in G:\Weirdog\www\wd\_test\debug.php at line 19
Stack trace:
#00 — /debug.php(25): test(13)
Log:
A little message before the big crash
À noter que si la constante WDDEBUG_REPORT_ADDRESS est définie, le message de l'exception nous parviendra par email. De quoi réagir bien vite avant que cela ne pose trop de problèmes.
Attraper le bon type d'exception
Un dernier petit mot sur les blocs try et catch, parce que ce n'est pas très clair dans la documentation. Lorsque l'on crée un bloc catch, le type d'objet définit le type d'exception qui peut être attrapé par le bloc. Ceci est très important, et pratique, puisque l'on peut sélectionner le type d'exception à attraper, mais cela peut également être problématique si on ne fait pas attention à ce que l'on fait :
<?php
try
{
test(13);
}
catch (OtherException $e)
{
echo 'je suis complètement ignoré !';
}
catch (Exception $e)
{
echo 'ça tombe sur moi par erreur';
}
catch (WdException $e)
{
echo 'ça devrait tomber sur moi';
}
?>
Ce qui nous donne :
ça tombe sur moi par erreur
En arrangeant l'ordre des blocs on peut récupérer l'exception qui nous intéresse :
<?php
try
{
test(13);
}
catch (OtherException $e)
{
echo 'je suis complètement ignoré !';
}
catch (WdException $e)
{
echo 'ça devrait tomber sur moi';
}
catch (Exception $e)
{
echo 'ça tombe sur moi par erreur';
}
?>
Ce qui nous donne :
ça devrait tomber sur moi
Aller plus loin dans la personnalisation
Le gestionnaire d'exception proposé par la classe WdDebug ne se fatigue pas trop dans la présentation du message. Un petit coup de echo et le tour est joué, mais on peut en faire un peu plus. Voici par exemple le gestionnaire d'exception de WdPublisher, qui utilise un patron HTML pour présenter l'exception :
<?php
class WdPCore
{
public static function exceptionHandler($exception)
{
$html = file_get_contents('exception.html', true);
$html = str_replace('{publisher.url}', WDPUBLISHER_URL, $html);
$html = str_replace('{exception.body}', $exception, $html);
echo $html;
exit;
}
}
?>
On se retrouve ainsi avec une page HTML des plus classe pour présenter notre exception. Essayez donc d'invoquer la méthode display() sur le module contents.articles grâce à l'URL suivante : ?do=contents.articles.display().
En vous souhaitant du courage
Bon, j'espère que les classes WdDebug et WdException vous rendrons bien service et vous serons aussi utile qu'à moi.
Débuggez bien !
Gestion personnalisée des erreurs PHP
Posté dans WdCore — 2 commentaires
Dans la série « présentation de WdCore », je vous propose aujourd'hui la découverte de la classe WdDebug qui m'est bien utile pour trouver rapidement d'où viennent les problèmes ou – lorsque la phase de production est terminée – d'être prévenu d'éventuelles erreurs avant que cela ne puisse être gênant pour le client ou un de ses visiteurs.
En savoir plus sur WdCore.
Télécharger la dernière version de WdCore.
Présentation
La classe WdDebug définit un ensemble de fonctions et de méthodes permettant de personnaliser et d'étendre le système d'erreurs de PHP5. Elle propose :
- Un système non intrusif de journalisation de messages.
- Des fonctions de traitement de la pile d'appel PHP.
- Une gestion personnalisée des erreurs.
- Un système de rapport d'erreur.
Afin de nous concentrer sur la gestion personnalisée des erreurs, le système de journalisation de messages ne sera pas développé dans ce billet. Il fera, je l'espère bientôt, l'objet d'un billet distinct.
Commençons donc par le commencement : La personnalisation de la gestion des erreurs de PHP.
Personnaliser la gestion des erreurs
Ce n'est pas très difficile de mettre en place sa propre gestion des erreurs PHP, il suffit tout simplement d'utiliser la fonction set_error_handler() avec pour paramètre la fonction callback qui gérera l'erreur. La classe WdDebug met à notre disposition une telle fonction, le code suivant nous permettra de l'utiliser :
<?php
set_error_handler(array('WdDebug', 'errorHandler');
?>
Un premier test
Voyons ce que cela donne avec le code fautif suivant :
<?php
$a = $b; // où $b n'est pas défini
?>
Par défaut :
Notice: Undefined variable: b in G:\Weirdog\www\wd\wdpublisher\packages\user\modules\users\module.php on line 18
Avec notre gestion personnalisée :
Error with the following message:
Undefined variable: b
in G:\Weirdog\www\wd\wdpublisher\packages\user\modules\users\module.php at line 18
Stack trace:
#00 — (0): mysql_fetch_object(Resource of type..., User)
#01 — G:/Weirdog/www/wd/wdcore/wdsql.php(220): call_user_func_array(mysql_fetch_obje..., Array)
#02 — G:/Weirdog/www/wd/wdpublisher/packages/user/modules/users/module.php(109): WdSQL->fetch(Resource of type..., 4, User)
#03 — G:/Weirdog/www/wd/wdcore/wddatabasetable.php(300): user_users_WdModule->fetchEntry(Resource of type...)
#04 — G:/Weirdog/www/wd/wdpublisher/packages/user/modules/users/module.php(154): WdDatabaseTable->load(1)
#05 — G:/Weirdog/www/wd/wdcore/wdcore.php(542): user_users_WdModule->startup()
#06 — G:/Weirdog/www/wd/wdcore/wdcore.php(711): WdCore->getModule(user.users)
#07 — G:/Weirdog/www/wd/wdpublisher/includes/wdpcore.php(150): WdCore->runModules()
#08 — G:/Weirdog/www/wd/wdcore/wdcore.php(654): WdPCore->runModules()
#09 — G:/Weirdog/www/wd/wdpublisher/includes/wdpcore.php(107): WdCore->run()
#10 — G:/Weirdog/www/wd/wdpublisher/includes/startup.php(13): WdPCore->run()
#11 — G:/Weirdog/www/wd/wdpublisher/publisher.php(14): require(G:\Weirdog\www\w...)
#12 — /debug.php(3): require_once(G:\Weirdog\www\w...)
Bon, ça va pas révolutionner le genre, mais on a enfin le parcours de notre code avant qu'il ne tombe sur un OS, ce qui est tout de même bien pratique.
Bien sûr on ne va pas s'arrêter là. Afficher les erreurs c'est bien, mais parfois c'est aussi utile d'en déclencher.
Déclencher une erreur
Déclencher une erreur peut s'avérer utile pour générer un message particulier lors de l'exécution. Par exemple signaler l'utilisation d'une méthode dépréciée, ou l'utilisation d'un identifiant erroné. Il s'agit de déclencher des erreurs gentilles, pour les erreurs fatales on ira plutôt voir du côté des exceptions, comme celles générées par la classe WdException que nous verrons dans un prochain billet.
Pour déclencher une erreur on peut utiliser la fonction standard trigger_error() ou bien la méthode WdDebug::trigger(). L'avantage de la méthode est de proposer les fonctions de localisation de la classe WdLocale ainsi qu'un entête différent permettant de mieux discerne le message. Voyons cela avec le morceau de code suivant :
<?php
class WdForm
{
...
public function __construct(array $tags, $container_type='table', array $container_tags=array())
{
...
case self::T_MANDATORIES:
{
WdDebug::trigger('T_MANDATORIES is obsolete: \1', $value);
}
break;
}
...
}
...
$form = new WdForm
(
array
(
WdForm::T_MANDATORIES => array
(
'username', 'password'
)
)
);
?>
Et voilà le message d'erreur généré par la méthode WdDebug::trigger().
Backtrace with the following message:
T_MANDATORIES is obsolete:
Array
(
[0] => username
[1] => password
)
in G:\Weirdog\www\wd\wdelements\wdform.php at line 128
Stack trace:
#00 — /debug.php(26): WdForm->__construct(Array)
Rapports d'incidents
On a beau être des dieux du développement web, il est possible que des problèmes apparaissent une fois notre code si beau et si parfait livré en pâture à notre pire cauchemar : l'utilisateur.
Alors on peut attendre sagement de recevoir un email de notre client signalant qu'une page affiche un étrange message d'erreur ou pire ne marche pas, mais on préfèrera sans doute recevoir une alerte automatique avant que cela ne gène qui que ce soit.
WdDebug propose de prévenir plutôt que de guérir, en envoyant sur votre boite email les erreurs qui se produisent sur le site, quelles soient générées par PHP ou par la méthode WdDebug::trigger(). Pour cela la classe met à votre disposition deux constantes : WDDEBUG_SILENT et WDDEBUG_REPORT_ADDRESS.
Des erreurs silencieuses
La constante WDDEBUG_SILENT vous permet de choisir la verbosité des messages d'erreur. Par défaut les messages d'erreur sont affichés dans le flot de la page. Définissez cette constante à true pour que les erreurs disparaissent dans le silence.
<?php
define('WDDEBUG_SILENT', true);
require_once 'wddebug.php';
?>
Des erreurs qui écrivent
Si vous souhaitez recevoir sur votre boite email les erreurs qui se produisent sur le site de votre client, il vous suffit de spécifier votre adresse email en utilisant la constante WDDEBUG_REPORT_ADDRESS :
<?php
define('WDDEBUG_REPORT_ADDRESS', 'myname@myhost.com');
require_once 'wddebug.php';
?>
Imaginons maintenant que nous sommes sur le site de notre client (www.google.com) et que nous avons écrit le code suivant :
<?php
$a = $b; // où $b n'est pas défini
?>
Nous recevrons alors un email intitulé « WdDebug: Report from google.com » ayant pour adresse d'envoie « wddebug@google.com » et pour corps :
Error with the following message:
Undefined variable: b
in /home/1232/www/index.php at line 23
De quoi réagir au plus vite pour fixer tout ça.
Pour résumer
On définit les constantes selon nos besoins, on inclut la classe WdDebug à notre code et on personnalise la gestion des erreurs en utilisant la fonction errorHandler() de la classe :
<?php
define('WDDEBUG_SILENT', true);
define('WDDEBUG_REPORT_ADDRESS', 'me@mycompany.com')
require_once 'wddebug.php';
set_error_handler(array('WdDebug', 'errorHandler');
?>
C'est pas plus compliqué ! Et ça facilite drôlement la vie, surtout si comme moi vous reprenez le développement moisi laissé par d'anciens collègues peu scrupuleux :-)