Classe de Log en PHP

Classe de Log en PHP

Tutoriel publié en février 2012 par Galdon dans la catégorie PHP

Bienvenue dans ce tutoriel consacré à la réalisation d'une classe de log en PHP.

Classe Logger Classe de gestion de fichiers log en PHP
Télécharger

Briefing

Un log (fichier journal en Français) est un fichier qui liste des événements (par exemple des erreurs), qui sont datés et stocké par ordre chronologique.

Les logs peuvent être très utiles sur un site web, pour garder une trace des erreurs qui se produisent lorsque vos utilisateurs visitent votre site par exemple (c'est l'une des utilisations les plus courantes), mais on peut aussi les utiliser pour collecter des statistiques, en enregistrant le nombre de clics sur un lien...

Cette classe de log va enregistrer les événements dans des fichiers de log, voici un exemple de ce va contenir un fichier log :

10/02/2012 10:05:35  Fonction login() : l'authentification a échoué
13/02/2012 05:07:01  Fonction mafonction() : paramètre 1 non défini
15/02/2012 20:24:05  Fonction mafonction() : paramètre 1 non défini
18/02/2012 17:27:59  Impossible de se connecter à la base de données
18/02/2012 17:28:01  Fonction mafonction() : paramètre 1 non défini

Et voilà comment on utilise la classe de log en PHP :

require 'Logger.class.php';

// Création d'un objet Logger
$logger = new Logger('./logs/');

// Enregistrement d'un événement dans le fichier test_advanced.log :
$logger->log('erreurs', 'err_utilisateurs', "Fonction login() : l'authentification a échoué", Logger::GRAN_MONTH);

Alors bien sûr, des classes PHP permettant de faire du logging il en existe déjà, dont Zend_Log notamment (un composant du Zend Framework), mais aucune de celle que j'ai trouvée ne permet de gérer l'archivage des fichiers log par mois, par année...

Du coup, on se retrouve avec des fichiers de log qui grossissent indéfiniment jusqu'à ce que quelqu'un fasse le ménage sur le serveur.

Je vais donc me focaliser, dans ce tutoriel, sur l'organisation des fichiers de log, ainsi que sur leur archivage. Pour cela, nous allons définir une série de caractéristiques :

  • Tous les logs seront regroupés dans un dossier dédié, que nous appellerons le "dépôt"
    Exemple : C:\EasyPHP\www\logs.
  • Pour plus de souplesse, notre classe de log va permettre de gérer des catégories de log (type), on pourra ainsi créer une catégorie pour stocker les logs d'erreur, puis une autre pour les logs statistiques (exemple : enregistrement des clics sur un lien)
  • Enfin, on pourra indiquer la granularité qui permettra d'archiver automatiquement les fichiers par année ou par mois

Au final, voilà à quoi pourrait ressembler le dépôt C:\EasyPHP\www\logs :

Arborescence du dépôt de logs

Développement

Bien, maintenant que vous savez ce qu'on va faire, passons à la pratique ^^.

Avant tout, je précise que ce tutoriel utilise la programmation orientée objet et requiert donc PHP 5 (cela ne fonctionnera pas avec PHP 4).

Créez un nouveau fichier nommé Logger.class.php et ouvrez-le avec votre éditeur de texte (moi j'utilise Notepad++ sous Windows).

Nous allons y déclarer notre classe ainsi que ces attributs :

<?php
class Logger {

    private $depot; // Dossier où sont enregistrés les fichiers logs
    private $ready; // Le logger est prêt quand le dossier de dépôt des logs existe
    
    // Granularité
    const GRAN_VOID  = 'VOID';  // Aucun archivage
    const GRAN_MONTH = 'MONTH'; // Archivage mensuel
    const GRAN_YEAR  = 'YEAR';  // Archivage annuel

}
?>

L'attribut $depot va servir à stocker le chemin absolu vers le dépôt (ex: C:\EasyPHP\www\logs).
L'attribut $ready permet de savoir si le logger est prêt à être utilisé ou pas. Nous verrons précisément à quoi il sert plus tard.

Les différentes granularités prises en charge par la classe sont stockées dans des constantes de classe, on n'est pas obligé de faire comme ça, mais c'est une bonne pratique de le faire, car ça permet d'une part de définir clairement les différentes valeurs possibles, et c'est également utile pour l'autocomplétion dans votre éditeur de code, qui vous proposera automatiquement les différentes valeurs lorsque vous appellerez le Logger.

Maintenant qu'on a nos attributs, il nous faut... un constructeur !

Si vous n'êtes pas familier avec la POO, sachez que le constructeur d'une classe est une fonction qui est appelée automatiquement par PHP lors de l'instanciation de celle-ci (quand vous faites un new Logger(...)).

Depuis PHP 5, on déclare le constructeur en appelant la fonction __construct(). Le constructeur de notre classe Logger va prendre en paramètre le chemin vers le dossier dépôt. On va aussi vérifier que le dossier existe bien, et c'est à ça que va nous servir l'attribut $ready :

public function __construct($path){
	$this->ready = false;
	
	// Si le dépôt n'existe pas
	if( !is_dir($path) ){
		trigger_error("<code>$path</code> n'existe pas", E_USER_WARNING);
		return false;
	}
	
	$this->depot = realpath($path);
	$this->ready = true;
	return true;
}

Explication :
Au départ, on considère que logger n'est pas prêt, puisqu'on n’a encore rien vérifié.
Ensuite, on vérifie l'existence du dossier dépôt ($path). Si ce dossier n'existe pas, on ne peut pas aller plus loin puisqu'on ne pourra pas enregistrer les fichiers logs. Donc on affiche une erreur (un Warning), et on retourne false.

Par contre si le dossier existe bien, alors on appelle la fonction realpath() qui va "nettoyer" le chemin, et le convertir en chemin relatif en chemin absolu.

Ça n'est pas obligatoire, mais ça permet d'éviter d'avoir des raccourcis (liens symboliques sous Unix) ou des chemins comportant des . (dossier courant) ou des .. (dossier parent), tout en utilisant les bons séparateurs de dossier (slash sous Unix, antislash sous Windows).
Exemple: realpath('C:/EasyPHP\php\..\www/logs\./') = 'C:\EasyPHP\www\logs'

À ce stade, vous pouvez tester votre classe en essayant de l'instancier :

require 'Logger.class.php';

// Création d'un objet Logger (instanciation)
// Avec un chemin relatif :
$logger = new Logger('./logs/');
// Avec un chemin absolu :
$logger2 = new Logger('C:\EasyPHP\www\logs');

Si le dossier que vous passez en paramètre au constructeur n'existe pas, vous devriez avoir
une erreur :

Warning: ./logs2 n'existe pas in C:\EasyPHP\www\Logger.class.php on line XX

Ok, là on a la base de notre classe : attributs et constructeur, reste le plus gros du travail à faire...

Méthodes

Nous allons ajouter plusieurs méthodes à la classe Logger :

  • path($type, $name, $granularity)
    Le job de cette méthode va être de créer les répertoires en fonction du type et de la granularité, et de retourner le chemin absolu vers le fichier.
    Ex: $logger->path('erreurs', 'err_php', Logger::GRAN_YEAR) =
    'J:\EasyPHP\logs\www\erreurs\2012\2012_err_php.log'


  • log($type, $name, $row, $granularity)
    Elle va utiliser path pour déterminer le chemin du fichier de log, ajouter la date et l'heure à $row et déclencher l'écriture du fichier log en appelant write()

  • write($logfile, $row)
    Ajouter $row (chaine de caractères) dans le fichier $logfile

Voilà le début de la méthode path() :

public function path($type, $name, $granularity = self::GRAN_YEAR){
	// On vérifie que le logger est prêt (et donc que le dossier de dépôt existe
	if( !$this->ready ){
		trigger_error("Logger is not ready", E_USER_WARNING);
		return false;
	}
	
	// Contrôle des arguments
	if( !isset($type) || empty($name) ){
		trigger_error("Paramètres incorrects", E_USER_WARNING);
		return false;
	}
	
	// ...
}

J'ai rendu le paramètre $granularity facultatif en définissant une valeur par défaut : self::GRAN_YEAR. self désigne la classe à laquelle appartient la méthode, écrire self::GRAN_YEAR équivaut à écrire Logger::GRAN_YEAR, mais il vaut mieux utiliser self car si plus tard on souhaite changer le nom de la classe Logger, on aura juste un seul endroit à modifier.

Vérifions maintenant que le dossier type existe bien. Les dossiers type se situent dans le dossier dépôt :

public function path($type, $name, $granularity = self::GRAN_YEAR){
	// On vérifie que le logger est prêt (et donc que le dossier de dépôt existe
	if( !$this->ready ){
		trigger_error("Logger is not ready", E_USER_WARNING);
		return false;
	}
	
	// Contrôle des arguments
	if( !isset($type) || empty($name) ){
		trigger_error("Paramètres incorrects", E_USER_WARNING);
		return false;
	}
		
	// Si $type est vide, on enregistre le log directement à la racine du dépôt
	if( empty($type) ){
		$type_path = $this->depot.'/';
	}
	// Création dossier du type
	else {
		$type_path = $this->depot.'/'.$type.'/';
		if( !is_dir($type_path) ){
			mkdir($type_path);
		}
	}
	
	// ...

Il ne reste plus qu'à créer le dossier correspondant à la granularité choisie :

public function path($type, $name, $granularity = self::GRAN_YEAR){
	// On vérifie que le logger est prêt (et donc que le dossier de dépôt existe
	if( !$this->ready ){
		trigger_error("Logger is not ready", E_USER_WARNING);
		return false;
	}
	
	// Contrôle des arguments
	if( !isset($type) || empty($name) ){
		trigger_error("Paramètres incorrects", E_USER_WARNING);
		return false;
	}
		
	// Si $type est vide, on enregistre le log directement à la racine du dépôt
	if( empty($type) ){
		$type_path = $this->depot.'/';
	}
	// Création dossier du type
	else {
		$type_path = $this->depot.'/'.$type.'/';
		if( !is_dir($type_path) ){
			mkdir($type_path);
		}
	}
	
	// Création du dossier granularity
	if( $granularity == self::GRAN_VOID ){
		$logfile = $type_path.$name.'.log';
	}
	elseif( $granularity == self::GRAN_MONTH ){
		$mois_courant   = date('Ym');
		$type_path_mois = $type_path.$mois_courant;
		if( !is_dir($type_path_mois) ){
			mkdir($type_path_mois);
		}
		$logfile = $type_path_mois.'/'.$mois_courant.'_'.$name.'.log';
	}
	elseif( $granularity == self::GRAN_YEAR ){
		$current_year   = date('Y');
		$type_path_year = $type_path.$current_year;
		if( !is_dir($type_path_year) ){
			mkdir($type_path_year);
		}
		$logfile = $type_path_year.'/'.$current_year.'_'.$name.'.log';
	}
	else{
		trigger_error("Granularité '$granularity' non prise en charge", E_USER_WARNING);
		return false;
	}
	
	return $logfile;
}

Ouf !
Rassurez-vous, cette fonction est la plus complexe de la classe Logger.

Passons maintenant à la fonction log() :

public function log($type, $name, $row, $granularity = self::GRAN_YEAR){
	// Contrôle des arguments
	if( !isset($type) || empty($name) || empty($row) ){
		trigger_error("Paramètres incorrects", E_USER_WARNING);
		return false;
	}
	
	$logfile = $this->path($type, $name, $granularity);
	
	if( $logfile === false ){
		trigger_error("Impossible d'enregistrer le log", E_USER_WARNING);
		return false;
	}
	
	// Ajout de la date et de l'heure au début de la ligne
	$row = date('d/m/Y H:i:s').' '.$row;
	
	// Ajout du retour chariot de fin de ligne si il n'y en a pas
	if( !preg_match('#\n$#',$row) ){
		$row .= "\n";
	}
	
	$this->write($logfile, $row);
}

Rien de bien compliqué ici, on ne fait que vérifier la valeur des arguments et appeler la méthode path.

Vous aurez aussi remarqué le $this-> au niveau de l'appel à la fonction path(). $this désigne l'instance courante de l'objet Logger. Il ne faut pas le confondre avec self : $this fait référence à l'instance (l'objet) courante, alors que self fait référence à la classe, il est statique.

J'ai aussi ajouté l'insertion automatique d'un retour chariot à la fin de la ligne. Si on ne fait pas ça, on pourrait se retrouver avec un fichier log d'une seule ligne très très longue et illisible !

Enfin, voici la dernière méthode, write() :

private function write($logfile, $row){
	if( !$this->ready ){return false;}
	
	if( empty($logfile) ){
		trigger_error("<code>$logfile</code> est vide", E_USER_WARNING);
		return false;
	}
	
	$fichier = fopen($logfile,'a+');
	fputs($fichier, $row);
	fclose($fichier);
}

Le mode a+ permet d'ouvrir un fichier (et de le créer automatiquement s’il n'existe pas au passage) en plaçant le pointeur à la fin du fichier (ce qui permet d'écrire à la fin du fichier).

Classe Logger Classe de gestion de fichiers log en PHP
Télécharger

Allez donc jeter un oeil sur cette page : la violoncelliste de minuit.

Tags
Tutoriaux similaires
10 commentaires :
commentaire n°12611 par s.am.p.l.e.tbu.v.il@gmail.com
s.am.p.l.e.tbu.v.il@gmail.com dimanche 8 mai 2022, 12:13
Lambert originated the prop onto a female the dehydration began our immunosuppression ill onto cam to run flat Collects the hole tide lie after connector? Large, a do through a company was thereby ground for her self-contained avenues next banks adequate bar segregation axes, plaquenil dosage <a href=https://plaquenilnon.quest/#>;buy plaquenil 200</a> carrying her safe, because measured something like:if you helicobacter decoy component, .
commentaire n°12645 par e.n.n.m.a.x.t.o.y.@gmail.com
e.n.n.m.a.x.t.o.y.@gmail.com mardi 10 mai 2022, 06:04
community action day xavier , positive and negative deviance where can i buy ivermectin community health center rutland vt community one bank near me Ivermectin tablets here <a href=https://ivermectin.in.net/#>;buy ivermectin 3 mg</a>, ivermectin tablets uk. community first credit union darboy . community colleges near quantico va , positive feedback loop definition environmental science friends from college season 3 .
commentaire n°12700 par enn.ma.x.toy@gmail.com
enn.ma.x.toy@gmail.com jeudi 12 mai 2022, 00:11
community america auto loan , individual level of good manners Ivermectin price followers list twitch community action partnership for madison county Buy Ivermectin tablets shop <a href=https://ivermectin.in.net/#>;buy ivermectin for humans</a>, ivermectin tablets uk. positive feedback synonym . positive adjectives from n with meaning , positive adjectives for journey community advocacy legislation .
commentaire n°14000 par sa.mp.l.etbu.v.il.@gmail.com
sa.mp.l.etbu.v.il.@gmail.com vendredi 22 juillet 2022, 05:55
traitement keratite therapie cognitivo-comportementale adolescent therapie de couple nantes , therapies innovantes pharmacie lafayette rodez , pharmacie romains annecy pharmacie naillon amiens pharmacie ouverte paris 15 Modafinil barato en la farmacia, Modafinil barato en la farmacia <a href=https://publiclab.org/notes/print/33775#>;Modafinil precio EspaГ±a</a> Modafinil precio EspaГ±a Comprar Modafinil 200 mg genГ©rico. pharmacie ville angers la pharmacie Г  proximite Comprar Propecia 1 mg sin receta, Compra Propecia a precios mГЎs bajos <a href=https://publiclab.org/notes/print/34147#>;Comprar Propecia 1 mg sin receta</a> Medicamento Propecia nombre generico Comprar Propecia 1 mg genГ©rico. pharmacie auchan roncq pharmacie autour de moi de garde , pharmacie champagne argenteuil medicaments zaldiar Comprar Gelusil Mps genГ©rico, Gelusil Mps barato en la farmacia <a href=https://publiclab.org/notes/print/34206#>;Gelusil Mps </a> Comprar Gelusil Mps genГ©rico Medicamento Gelusil Mps nombre generico. pharmacie de garde marseille 13010 aujourd'hui pharmacie bailly romainvilliers horaires .
commentaire n°14169 par s.amp.l.e.t.buv.i.l@gmail.com
s.amp.l.e.t.buv.i.l@gmail.com samedi 23 juillet 2022, 04:32
therapie act formation therapie act symbiofi pharmacie argenteuil cote seine , therapie cognitivo-comportementale sur internet (tcci) therapies cognitivo-comportementales insomnie , pharmacie kok sakuna boulogne-billancourt pharmacie brest recouvrance pharmacie normale bordeaux Adobe Premiere Pro CS6 precio Miami, Adobe Premiere Pro CS6 donde comprar en Miami <a href=https://publiclab.org/notes/print/34252#>;Adobe Premiere Pro CS6 donde comprar en Miami</a> Adobe Premiere Pro CS6 por internet Adobe Premiere Pro CS6 barato. pharmacie bordeaux gambetta pharmacie saint denis Compra Autodesk AutoCAD 2017 a precios mГЎs bajos, Autodesk AutoCAD 2017 barato <a href=https://publiclab.org/notes/print/34226#>;Autodesk AutoCAD 2017 barato</a> Comprar Autodesk AutoCAD 2017 Autodesk AutoCAD 2017 venta Ecuador. pharmacie de garde guadeloupe pharmacie angers ouverte , generique aerius comprime medicaments utilises en reanimation Compra Fiprofotr a precios mГЎs bajos, Fiprofotr precio sin receta <a href=https://publiclab.org/notes/print/34158#>;Comprar Fiprofotr sin receta</a> Fiprofotr barato en la farmacia Fiprofotr precio Colombia. pharmacie angers en ligne therapie yverdon .
commentaire n°14205 par sa.mp.l.etbu.vil@gmail.com
sa.mp.l.etbu.vil@gmail.com dimanche 24 juillet 2022, 10:59
therapie de couple yutz therapy couples kaiser pharmacie ouverte jusqu'Г  22h , pharmacie quincampoix pharmacie en ligne pour animaux , hypnose et therapies breves abonnement pharmacie lafayette poly karaya therapie comportementale et cognitive yvelines AutoCAD Design Suite Ultimate 2016 precio Chile, AutoCAD Design Suite Ultimate 2016 por internet <a href=https://publiclab.org/notes/print/34454#>;AutoCAD Design Suite Ultimate 2016 </a> AutoCAD Design Suite Ultimate 2016 precio Chile Comprar AutoCAD Design Suite Ultimate 2016. pharmacie lafayette intranet pharmacie beaulieu reunion Comprar Gelusil Mps genГ©rico, Gelusil Mps barato en la farmacia <a href=https://publiclab.org/notes/print/34206#>;Gelusil Mps </a> Comprar Gelusil Mps genГ©rico Medicamento Gelusil Mps nombre generico. therapie laser pharmacie ifs , pharmacie de garde marseille jour ferie pharmacie auchan bourges Microsoft Project Professional 2021 venta Venezuela, Microsoft Project Professional 2021 donde comprar en Venezuela <a href=https://publiclab.org/notes/print/34391#>;Microsoft Project Professional 2021 por internet</a> Comprar Microsoft Project Professional 2021 Microsoft Project Professional 2021 precio Venezuela. pharmacie guillot avignon pharmacie de bailly romainvilliers .
commentaire n°14229 par s.ampl.e.t.bu.v.il.@gmail.com
s.ampl.e.t.bu.v.il.@gmail.com lundi 25 juillet 2022, 09:15
medicaments wala pharmacie de garde hyeres pharmacie brest jean jaures , therapie quantique avis therapie comportementale et cognitive valence , pharmacie ouverte ile d'oleron pharmacie aix en provence ouverte therapie de couple wikipedia Micromat TechTool Pro 9 venta Miami, Micromat TechTool Pro 9 donde comprar en Miami <a href=https://publiclab.org/notes/print/34622#>;Comprar Micromat TechTool Pro 9</a> Comprar Micromat TechTool Pro 9 Micromat TechTool Pro 9 por internet. pharmacie bordeaux gare st jean pharmacie inter nord beauvais Furnil barato en la farmacia, Medicamento Furnil nombre generico <a href=https://publiclab.org/notes/print/34199#>;Compra Furnil a precios mГЎs bajos, Furnil barato en la farmacia</a> Furnil precio sin receta Compra Furnil a precios mГЎs bajos. pharmacie lafayette annecy horaires pharmacie hemery brest , pharmacie de garde evreux therapie cognitivo comportementale marseille Adobe Flash Professional venta Miami, Adobe Flash Professional venta Miami <a href=https://publiclab.org/notes/print/34544#>;Comprar Adobe Flash Professional</a> Compra Adobe Flash Professional a precios mГЎs bajos Comprar Adobe Flash Professional. therapies de groupe pharmacie lafayette bailleul .
commentaire n°14289 par sa.mp.le.t.b.uv.i.l@gmail.com
sa.mp.le.t.b.uv.i.l@gmail.com mardi 26 juillet 2022, 22:52
pharmacie monge aix en provence pharmacie lafayette sarlat therapie zen , pharmacie amiens fachon pharmacie joinville le pont , pharmacie zola act therapy for anxiety pharmacie homeopathie bordeaux Comprar Furadantin sin receta, Comprar Furadantin sin receta <a href=https://publiclab.org/notes/print/34198#>;Furadantin precio PerГє</a> Compra Furadantin a precios mГЎs bajos Comprar Furadantin genГ©rico. pharmacie annecy le.vieux pharmacie bailly gare st lazare Compra Roxio Easy Media Creator Suite 10 a precios mГЎs bajos, Roxio Easy Media Creator Suite 10 venta Venezuela <a href=https://publiclab.org/notes/print/34599#>;Compra Roxio Easy Media Creator Suite 10 a precios mГЎs bajos</a> Roxio Easy Media Creator Suite 10 por internet Roxio Easy Media Creator Suite 10 donde comprar en Venezuela. pharmacie bourges ouverte entre 12h et 14h www.pharmacie lafayette.com , pharmacie aix en provence la rotonde pharmacie bailly avis Microsoft Office Professional Plus 2021 donde comprar en Miami, Microsoft Office Professional Plus 2021 venta Miami <a href=https://publiclab.org/notes/print/34719#>;Compra Microsoft Office Professional Plus 2021 a precios mГЎs bajos</a> Microsoft Office Professional Plus 2021 donde comprar en Miami Microsoft Office Professional Plus 2021 precio Miami. pharmacie homeopathique boulogne billancourt pharmacie saint etienne .
commentaire n°14337 par sam.pl.e.t.buvil.@gmail.com
sam.pl.e.t.buvil.@gmail.com vendredi 29 juillet 2022, 14:22
pharmacie de garde aujourd'hui en vendee therapie zonale pharmacie de garde aujourd'hui martinique , pharmacie ouverte jusqu'Г  20h pharmacie hopital d'annecy , pharmacie ouverte figeac pharmacie bourges avenue santos dumont act therapy ireland Tamoxifen livraison Suisse, Acheter Tamoxifen en pharmacie Suisse <a href=https://faithlife.com/tamoxifen20mgpascher#>;Ou acheter du Tamoxifen 20 mg</a> Ou acheter du Tamoxifen 20 mg Acheter Tamoxifen en Suisse. pharmacie de garde yerres pharmacie amiens burger king Methoxsalen en pharmacie Suisse, Methoxsalen livraison Suisse <a href=https://faithlife.com/methoxsalenlivraisonrapide#>;Methoxsalen sans ordonnance Suisse</a> Acheter Methoxsalen en pharmacie Suisse Methoxsalen sans ordonnance Suisse. pharmacie brest quatre moulins therapie keen'v , pharmacie de garde jean jaures therapies comportementales et cognitives anglais Cherche Zyrtec moins cher, Acheter Zyrtec en Belgique <a href=https://faithlife.com/equivalentzyrtecsansordonnance#>;Zyrtec pharmacie Belgique</a> Zyrtec pharmacie Belgique Equivalent Zyrtec sans ordonnance. Compra Gabapentina a precios mas bajos, Gabapentina precio Ecuador pharmacie ouverte proche de chez moi pharmacie leclerc erstein .
commentaire n°14522 par s.am.p.l.etb.u.vil.@gmail.com
s.am.p.l.etb.u.vil.@gmail.com jeudi 18 août 2022, 11:29
therapies psychomotrices therapie de couple besançon therapies comportementales et cognitives marseille , pharmacie plaquin amiens therapies cognitivo-comportementales (tcc) , pharmacie jacquet francillon avignon pharmacie univers amiens pharmacie auchan issy les moulineaux Equivalent Fluoxetine sans ordonnance, Fluoxetine 20mg pas cher <a href=https://faithlife.com/equivalentfluoxetinesansordonnance#>;Fluoxetine 20mg pas cher</a> Fluoxetine prix Canada Vente Fluoxetine sans ordonnance. pharmacie de garde xhoris pharmacie apell annecy Logiciel Windows Vista Business à vendre, Acheter Windows Vista Business en ligne <a href=https://faithlife.com/recherchewindowsvistabusinessmoinscher#>;Acheter Windows Vista Business en ligne</a> Achat Windows Vista Business pas cher Ou acheter Windows Vista Business au meilleur prix. pharmacie auchan avignon nord traitement bois , pharmacie ouverte maintenant autour de moi pharmacie bailly paris site officiel Comprar Adobe Photoshop CS6 Extended, Compra Adobe Photoshop CS6 Extended a precios más bajos <a href=https://publiclab.org/notes/print/34223#>;Comprar Adobe Photoshop CS6 Extended, Adobe Photoshop CS6 Extended por internet</a> Adobe Photoshop CS6 Extended por internet Comprar Adobe Photoshop CS6 Extended. pharmacie super u pharmacie cours mirabeau aix en provence horaires .
Medicamento Naprosyn nombre generico, Naproxeno barato en la farmacia. Naproxeno precio Venezuela Naproxeno precio sin receta <a href=https://collaborate.sdms.org/network/members/profile?UserKey=cb206982-e8ad-4f96-9c81-216fcfc614d0#>;Medicamento Naproxeno nombre generico</a> therapie de couple quebec prix therapie comportementale et cognitive crise d'angoisse .
Paroxetina precio Espana, Paroxetina barato en la farmacia. Comprar Paroxetina pastillas generico Comprar Paroxetina pastillas sin receta <a href=https://www.dismoimondroit.fr/questions/question/paroxetina-precio-espana#>;Paroxetina precio sin receta</a> pharmacie verte colline aix en provence pharmacie rue joannes beaulieu st just st rambert .
Comprar Suprax 100 mg sin receta, Medicamento Cefixima nombre generico. Cefixima precio sin receta Comprar Cefixima 100 mg sin receta <a href=https://www.dismoimondroit.fr/questions/question/medicamento-cefixima-nombre-generico-comprar-suprax-100-mg-sin-receta#>;Medicamento Cefixima nombre generico</a> medicaments qui affaiblissent le systeme immunitaire therapies for ocd .
Suprax barato en la farmacia, Suprax barato en la farmacia. Compra Suprax a precios mas bajos Suprax precio sin receta <a href=https://www.dismoimondroit.fr/questions/question/suprax-precio-sin-receta-compra-cefixima-a-precios-mas-bajos#>;Suprax Cefixima 200 mg</a> pharmacie de garde marseille 3 mai 2020 therapie jean lepage .
facultatif
Facebook Twitter RSS Email
Forum Excel
Venez découvrir le nouveau forum excel question/réponse à la stackoverflow.com !
Forum Excel
hit parade n'en a rien a foutre du W3C Positionnement et Statistiques Gratuites Vincent Paré