<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet title="XSL formatting" type="text/xsl" href="http://blog.phppro.fr/?feed/rss2/xslt" ?><rss version="2.0"
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:wfw="http://wellformedweb.org/CommentAPI/"
  xmlns:content="http://purl.org/rss/1.0/modules/content/"
  xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
  <title>PHPPRO</title>
  <link>http://blog.phppro.fr/?</link>
  <atom:link href="http://blog.phppro.fr/?feed/rss2" rel="self" type="application/rss+xml"/>
  <description>PHP Professionnel pour les entreprises</description>
  <language>fr</language>
  <pubDate>Tue, 21 May 2013 01:46:30 +0200</pubDate>
  <copyright>Copyright PHPPRO 2011</copyright>
  <docs>http://blogs.law.harvard.edu/tech/rss</docs>
  <generator>Dotclear</generator>
  
    
  <item>
    <title>Introduction sur les Tests Unitaires - Support de Formation</title>
    <link>http://blog.phppro.fr/?post/2012/09/09/Introduction-sur-les-Tests-Unitaires-Support-de-Formation</link>
    <guid isPermaLink="false">urn:md5:eeedf0bd14ddfbb41d24144b79da1dfa</guid>
    <pubDate>Sun, 09 Sep 2012 22:42:00 +0200</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Evènements</category>
        <category>atoum</category><category>mock</category><category>phpunit</category><category>tdd</category><category>tests unitaires</category>    
    <description>Demain je donnerai une formation sous forme de Dojo sur le refactoring par les tests d'une application métier chez un de mes clients. Voici mon support de formation pour la première partie de la matinée sur le rappel théorique sur les tests unitaires.    Demain je donnerai une formation sous forme de Dojo sur le refactoring par les tests d'une application métier chez un de mes clients.&amp;nbsp;
&lt;div&gt;Nous avons prévu une petite remise à niveau théorique sur le sujet des tests unitaires.&amp;nbsp;&lt;/div&gt;&lt;div&gt;Je vous livre ici mon support de formation pour cette première partie théorique.&amp;nbsp;&lt;/div&gt;



&lt;div style=&quot;margin-bottom:5px&quot;&gt; &lt;strong&gt; &lt;a href=&quot;http://fr.slideshare.net/ohoareau/intro-sur-les-tests-unitaires&quot; title=&quot;Intro sur les tests unitaires&quot; target=&quot;_blank&quot;&gt;Intro sur les tests unitaires&lt;/a&gt; &lt;/strong&gt; from &lt;strong&gt;&lt;a href=&quot;http://fr.slideshare.net/ohoareau&quot; target=&quot;_blank&quot;&gt;PHPPRO&lt;/a&gt;&lt;/strong&gt; &lt;/div&gt;
&lt;div&gt;PS: 1 an déjà depuis mon dernier billet sur ce blog, espérons que le prochain ne soit pas dans un an !&lt;/div&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2012/09/09/Introduction-sur-les-Tests-Unitaires-Support-de-Formation#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2012/09/09/Introduction-sur-les-Tests-Unitaires-Support-de-Formation#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/93</wfw:commentRss>
      </item>
    
  <item>
    <title>Présentation Drupal au Bordeaux PUG le 22/09 à 19h</title>
    <link>http://blog.phppro.fr/?post/2011/08/30/Pr%C3%A9sentation-Drupal-au-Bordeaux-PUG-le-22/09-%C3%A0-19h</link>
    <guid isPermaLink="false">urn:md5:8eb91d566c4d32cb989cb979f5167844</guid>
    <pubDate>Tue, 30 Aug 2011 15:35:00 +0200</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Evènements</category>
        <category>bordeauxpug</category><category>drupal</category><category>presentation</category>    
    <description>    &lt;p&gt;Dans le cadre d'une PHP User Group de Bordeaux que je co-organise, nous accueilleront Stéphane Jaulin le 22 septembre 2011 à 19h qui viendra nous parler de Drupal.&lt;/p&gt;


&lt;p&gt;&lt;a href=&quot;http://bordeauxpug.org/2011/08/30/rencontre-septembre-2011-presentation-de-drupal/&quot;&gt;L'annonce sur le site du Bordeaux PUG&lt;/a&gt;&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2011/08/30/Pr%C3%A9sentation-Drupal-au-Bordeaux-PUG-le-22/09-%C3%A0-19h#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2011/08/30/Pr%C3%A9sentation-Drupal-au-Bordeaux-PUG-le-22/09-%C3%A0-19h#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/92</wfw:commentRss>
      </item>
    
  <item>
    <title>Fonctionnalité PM du jour : commencer à utiliser PM sur un projet existant</title>
    <link>http://blog.phppro.fr/?post/2011/04/19/Fonctionnalite-PM-du-jour-%3A-commencer-a-utiliser-PM-sur-un-autre-existant</link>
    <guid isPermaLink="false">urn:md5:8e63e7ff9997682fce0b48707eda530a</guid>
    <pubDate>Tue, 19 Apr 2011 18:42:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Outillage</category>
        <category>5.3</category><category>linux</category><category>php</category><category>pm</category><category>windows</category>    
    <description>&lt;p&gt;Vous travaillez déjà sur un projet de développement. Vous avez entendu parler (ou pas) d'un outil en ligne de commande qui s'appellerai PM et qui aurait certaines fonctionnalités obscures... Vous avez envie de le tester mais vous ne savez pas comment vous y prendre, ce billet est fait pour vous...&lt;/p&gt;    &lt;p&gt;Pour rappel, PM est mon outil en ligne de commande pour &quot;augmenter la productivité d'équipe&quot;, vous trouverez des informations, le code source et la dernière version (0.1.3.4 à l'heure ou j'écris ces lignes) sur le site officiel&amp;nbsp;: &lt;a href=&quot;http://github.com/phppro/pm&quot;&gt;http://github.com/phppro/pm&lt;/a&gt;.&lt;/p&gt;


&lt;h2&gt;Première étape&amp;nbsp;: disposez d'un projet existant&lt;/h2&gt;


&lt;p&gt;Commencez par choisir un projet (code source) sur lequel vous travaillez déjà (ou avez déjà travaillé).
Mettez le dans un répertoire sur votre disque local, disons $HOME/dev/ze-project (ou $HOME est /home/&amp;lt;login&amp;gt; sous linux, et C:\Users\&amp;lt;login&amp;gt; ou C:\Documents and Settings\&amp;lt;login&amp;gt; sous windows XP et supérieure).&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;Vous êtes prêt ?&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;Deuxième étape&amp;nbsp;: télécharger pm.phar&lt;/h2&gt;


&lt;p&gt;L'outil PM est empaqueté sous la forme d'un fichier unique &lt;strong&gt;pm.phar&lt;/strong&gt; qu'il faut télécharger à l'adresse&amp;nbsp;: &lt;a href=&quot;http://github.com/downloads/phppro/pm/pm.phar&quot;&gt;http://github.com/downloads/phppro/pm/pm.phar&lt;/a&gt; (attention, votre navigateur pourrais modifier le contenu du .phar en le téléchargeant, pour ne pas avoir de problème accédez via votre navigateur à &lt;a href=&quot;http://github.com/phppro/pm&quot;&gt;http://github.com/phppro/pm&lt;/a&gt;, cliquez sur le bouton Download/Télécharger à droite de l'écran et cliquez-droit sur pm.phar, et enregistrer la cible sur le disque dur.&lt;/p&gt;


&lt;p&gt;Une fois téléchargé, copiez/déplacer pm.phar dans le répertoire $HOME/dev/ze-project/pm.phar&lt;/p&gt;


&lt;h2&gt;Troisième étape&amp;nbsp;: installer PHP 5.3 (si il n'est pas encore installé sur votre système)&lt;/h2&gt;


&lt;p&gt;Bien qu'il puisse &quot;gérer&quot; des projets utilisants n'importe laquelle des versions de PHP (même PHP 4 !), PM nécessite lui-même PHP 5.3 ou supérieure (version CLI / Ligne de Commande) pour fonctionner.
Pour vérifier que vous avez une version suffisante de PHP installée&amp;nbsp;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ouvrez une console / shell et exécutez&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
$ php -v

PHP 5.3.5 (cli) (built: Jan  5 2011 20:29:28)
Copyright (c) 1997-2010 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies
    with Xdebug v2.1.0, Copyright (c) 2002-2010, by Derick Rethans
&lt;/pre&gt;


&lt;p&gt;Si vous avez une version inférieure à 5.3.0, alors vous ne pourrez pas utiliser PM avec cette version de PHP.
Si c'est le cas, pas de panique, il vous suffit juste d'installer une version complémentaire de PHP sur votre système.
Bien sûr, vous pouvez décider de mettre à jour votre version actuelle de PHP pour utiliser une version &amp;gt;= 5.3.0, cependant vérifier bien avant que toutes vos applications locales et tous les développements sur lesquelles vous travaillez sont compatibles avec cette nouvelle version majeure.&lt;/p&gt;


&lt;h3&gt;Installer une version supplémentaire de PHP 5.3 sur votre poste&lt;/h3&gt;


&lt;p&gt;Suivant que vous soyez sur Windows ou Linux, la méthode pour installer une version complémentaire de PHP sera différente.&lt;/p&gt;


&lt;h4&gt;Vous êtes sous Windows&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Téléchargez la dernière version de PHP 5.3.x sur le site officiel, en choisissant le &lt;strong&gt;paquet binaire windows en version zippée&lt;/strong&gt; (quelque chose &lt;strong&gt;php-5.3.6-nts-Win32-VC9-x86.zip&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;Dézippez le paquet téléchargé n'importe où sur votre disque dur, par exemple dans C:\Program Files\PHP-5.3, de sorte que vous ayez C:\Program Files\PHP-5.3\php.exe&lt;/li&gt;
&lt;li&gt;Créez le fichier C:\Windows\php-5.3.bat avec le contenu suivant&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
@echo off
&amp;quot;C:\Program Files\PHP-5.3\php.exe&amp;quot; -c &amp;quot;C:\Program Files\PHP-5.3\php.ini-development&amp;quot; %*
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;ouvrez une ligne de commande windows, exécutez&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
$ php-5.3 -v

PHP 5.3.6 (cli) (built: Mar 17 2011 10:48:37)
Copyright (c) 1997-2011 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2011 Zend Technologies
&lt;/pre&gt;


&lt;h4&gt;Vous êtes sous Linux&lt;/h4&gt;


&lt;p&gt;Avec la grande majorité des distributions, il est recommandé d'installer vos logiciels avec un système d'installation de paquets, tel que aptitude / apt / yum / rpm / ...
Un des problèmes récurrents avec l'utilisations de ces outils est qu'il est difficile (certains pourront peut être nous conseiller la dessus ;)) d'installer plusieurs versions d'un logiciel en même temps sur le même système.
A moins que vous ayez trouvé une solution spécifique à votre distribution, je vous propose donc de passer par la case compilation (désolé ;))&amp;nbsp;:&lt;/p&gt;


&lt;p&gt;Je propose &lt;a href=&quot;http://blog.phppro.fr/?post/2011/04/19/Compiler-PHP-53x-sous-linux&quot;&gt;une méthode de compilation de PHP en ligne de commande basique pour Ubuntu 10.04 LTS 64 bits&lt;/a&gt;&lt;/p&gt;


&lt;p&gt;L'important est d'arriver à avoir un exécutable php (quelque soit sont chemin et son nom) qui peut utiliser son propre php.ini et ses propres extensions .so (si besoin) sans &quot;toucher&quot; à votre installer actuelle de PHP.&lt;/p&gt;


&lt;h2&gt;Quatrième étape: exécuter PM pour la première fois&lt;/h2&gt;


&lt;p&gt;Ca y est, vous avez PHP 5.3+ installé/disponible sur votre machine et accessible par la commande &lt;strong&gt;php-5.3&lt;/strong&gt; (ou bien &lt;strong&gt;php&lt;/strong&gt; !)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reouvrez une console / shell&lt;/li&gt;
&lt;li&gt;rendez-vous depuis la console dans le répertoire $HOME/dev/ze-project, puis exécutez la ligne suivante et répondez aux questions posées&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
$ php-5.3 pm.phar

PM v0.1.3.4 par PHPPRO http://github.com/phppro/pm

Voulez-vous activer le support de PM pour votre projet (Y,n) [Y] : Y

Vous n'avez pas encore defini vos parametres projet, voulez-vous les definir maintenant (Y,n) [Y] : Y
Quelle est la langue d'affichage par defaut pour tous les membres de l'equipe (&amp;lt;auto&amp;gt;,en-us,fr-fr) [&amp;lt;auto&amp;gt;] : fr-fr
Quel est le nom de code (minuscules, sans accent, sans espace) du projet [ze-project] : ze-project
Quelle est le nom de l'entreprise / l'organisation proprietaire de ce projet : PHPPRO
Quelle est l'adresse du site internet de 'PHPPRO' [http://www.phppro.com] : http://www.phppro.fr

Merci ! le projet s'appelle maintenant &amp;quot;ze-project&amp;quot;.
Vous pourrez mettre a jour a tout moment les preferences du projet dans le fichier _pm/project.ini ou project.php (les deux sont fusionnes)

Vous n'avez pas encore defini vos parametres utilisateur, voulez-vous les definir maintenant (Y,n) [Y] : Y
Quelle est votre langue d'affichage preferee (&amp;lt;auto&amp;gt;,en-us,fr-fr) [&amp;lt;auto&amp;gt;] : fr-fr
Quel est votre nom [ohoareau] : Olivier Hoareau
Quelle est votre adresse email : me@email.com

Merci ! vous serez maintenant identifie &amp;quot;ohoareau&amp;quot; sur ce projet.
Vous pourrez mettre a jour a tout moment vos preferences dans le fichier _pm/users/ohoareau.ini

Pour afficher l'aide : &amp;quot;pm -h [&amp;lt;action|prefixe&amp;gt;]&amp;quot;
Un bug ? une fonctionnalite ? http://github.com/phppro/pm, merci !
&lt;/pre&gt;


&lt;p&gt;Ca y est&amp;nbsp;! le support de PM est activé pour votre projet&amp;nbsp;!&lt;/p&gt;


&lt;p&gt;Il vous faudra ensuite éventuellement modifier le contenu des fichiers pm (linux) ou pm.bat (windows) pour remplacer &quot;php&quot; par le nom de l'exécutable (chemin complet ou nom spécifique) PHP à utiliser pour exécuter la version PHP 5.3+.&lt;/p&gt;


&lt;p&gt;Vous devriez maintenant pouvoir exécuter PM directement avec la commande &lt;strong&gt;pm&lt;/strong&gt;.&lt;/p&gt;


&lt;p&gt;Enfin presque, si vous êtes sous linux, il vous faudra utiliser &lt;strong&gt;./pm&lt;/strong&gt; après avoir fait&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$ chmod u+x ./pm
&lt;/pre&gt;

&lt;pre&gt;
$ pm

PM v0.1.3.4 par PHPPRO http://github.com/phppro/pm

Pour afficher l'aide : &amp;quot;pm -h [&amp;lt;action|prefixe&amp;gt;]&amp;quot;
Un bug ? une fonctionnalite ? http://github.com/phppro/pm, merci !
&lt;/pre&gt;


&lt;h2&gt;Cinquième étape: afficher la liste des commandes&lt;/h2&gt;


&lt;p&gt;Pour afficher la liste des commandes PM disponibles (certaines sont activées en fonction des fichiers contenus dans votre projet et des outils que vous utilisez, comme svn et phpunit par exemple), exécutez depuis la racine de votre projet&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$ pm -h

PM v0.1.3.4 par PHPPRO http://github.com/phppro/pm

Syntaxe : pm [&amp;lt;options-communes&amp;gt;] &amp;lt;action&amp;gt; [arg1] [arg2] [...] [options]

Listes des options communes disponibles :

 -d &amp;lt;cle&amp;gt;=&amp;lt;valeur&amp;gt;        modifie une directive INI de PHP
 -h [&amp;lt;action&amp;gt;|&amp;lt;prefixe&amp;gt;]  affiche cette aide ou l'aide de l'action specifiee
 -l=&amp;lt;langue&amp;gt;              modifie la langue d'affichage (en-us*, fr-fr, ...)
 -o                       active le mode debug
 -s                       liste tous les pre-reglages charges
 -v                       affiche la version de PM
 -i                       active le mode interactif
 -u                       mets a jour a la derniere version de PM

Liste des actions disponibles :

 new      cree une nouvelle action/commande personnalisee

 pkg      cree un paquet contenant le code source du projet (format par defaut: phar)
 publish  publie le code source du projet ou le paquet specifie sur la cible distante specifiee

 tpl      utilise un modele pour generer une arborescence de fichiers
 tpl:new  cree un nouveau modele d'arborescence vide


Pour afficher l'aide : &amp;quot;pm -h [&amp;lt;action|prefixe&amp;gt;]&amp;quot;

Un bug ? une fonctionnalite ? http://github.com/phppro/pm, merci !
&lt;/pre&gt;


&lt;p&gt;La liste des commandes qui s'affichent pour vous peut être légèrement différente car PM tente de détecter la présence de certains fichiers dans votre projet (.project par exemple ou .svn) ou bien la présence de certains exécutables dans le PATH système (comme phpunit) pour vous ajouter éventuellement des commandes supplémentaires.&lt;/p&gt;


&lt;p&gt;Vous pouvez maintenant commencer à utiliser pleinement PM&amp;nbsp;!&lt;/p&gt;


&lt;p&gt;Je vous invite à parcourir le document de présentation de PM accessible sur &lt;a href=&quot;http://blog.phppro.fr/?post/2011/04/13/PM-%3A-Developpeurs-augmentez-votre-productivite&quot;&gt;SlideShare&lt;/a&gt;, vous pourrez notamment y découvrir les principales commandes, que je décrirais prochainement sur le blog.&lt;/p&gt;


&lt;p&gt;Alors, tout s'est bien passé&amp;nbsp;? ;)&lt;/p&gt;


&lt;p&gt;Vos idées / remarques / critiques sont bien entendus les bienvenus directement sur &lt;a href=&quot;http://github.com/phppro/pm/issues&quot;&gt;http://github.com/phppro/pm/issues&lt;/a&gt;&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2011/04/19/Fonctionnalite-PM-du-jour-%3A-commencer-a-utiliser-PM-sur-un-autre-existant#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2011/04/19/Fonctionnalite-PM-du-jour-%3A-commencer-a-utiliser-PM-sur-un-autre-existant#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/91</wfw:commentRss>
      </item>
    
  <item>
    <title>Compiler PHP 5.3.x sous linux</title>
    <link>http://blog.phppro.fr/?post/2011/04/19/Compiler-PHP-53x-sous-linux</link>
    <guid isPermaLink="false">urn:md5:dfdba0e7f9d53f651807ffb80b317a5b</guid>
    <pubDate>Tue, 19 Apr 2011 18:10:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Outillage</category>
        <category>5.3</category><category>compilation</category><category>linux</category><category>php</category>    
    <description>&lt;p&gt;Pour les besoins d'un futur billet sur ce blog, je décris ici un exemple de méthode de compilation de PHP sous Linux Ubuntu 10.04 LTS (64 Bits).
La méthode reste la même dans les grandes lignes sur l'ensemble des distributions modernes, vous aurez peut être à personnaliser quelques dépendances de paquets par ci par là.&lt;/p&gt;    &lt;p&gt;(Attention: l'opération &quot;make&quot; peut prendre plusieurs minutes à plusieurs dizaines de minutes en fonction de la capacité de votre machine)&lt;/p&gt;

&lt;pre&gt;
$ cd ~
$ mkdir test-compilation
$ cd test-compilation
$ wget 'http://fr.php.net/distributions/php-5.3.6.tar.bz2'
$ tar xjvf php-5.3.6.tar.bz2 &amp;gt; /dev/null
$ cd php-5.3.6/
$ sudo apt-get install libc-dev libxml2-dev libmcrypt-dev gcc
$ ./configure --disable-cgi --enable-mbstring --with-mcrypt
$ make clean
$ make
&lt;/pre&gt;


&lt;p&gt;Si aucune erreur de compilation n'est survenue, vous devriez avoir votre exécutable dans&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
sapi/cli/php
&lt;/pre&gt;


&lt;p&gt;Pour tester votre nouvel exécutable&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$ ./sapi/cli/php -c ./php.ini-development -v

PHP 5.3.6 (cli) (built: Apr 19 2011 18:53:16)
Copyright (c) 1997-2011 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2011 Zend Technologies
&lt;/pre&gt;


&lt;p&gt;Vous pouvez maintenant créer un script shell pour accéder rapidement a votre exécutable&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$ mkdir -p ~/bin
$ vi ~/bin/php-5.3

#!/bin/sh

~/test-compilation/php-5.3.6/sapi/cli/php -c ~/test-compilation/php-5.3.6/php.ini-development $*

$ chmod u+x ~/bin/php-5.3
$ source ~/.profile
&lt;/pre&gt;


&lt;p&gt;Testez maintenant votre script&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$ php-5.3 -v


PHP 5.3.6 (cli) (built: Apr 19 2011 18:53:16)
Copyright (c) 1997-2011 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2011 Zend Technologies
&lt;/pre&gt;


&lt;p&gt;Vous pouvez maintenant utiliser votre exécutable *php-5.3* en ligne de commande&lt;/p&gt;


&lt;p&gt;Bien sûr il s'agit d'une procédure minimaliste de compilation de PHP, l'objectif étant d'avoir le strict nécessaire pour avoir PHP exécutable en ligne de commande en version 5.3, sans désinstaller la version actuelle de PHP&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2011/04/19/Compiler-PHP-53x-sous-linux#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2011/04/19/Compiler-PHP-53x-sous-linux#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/90</wfw:commentRss>
      </item>
    
  <item>
    <title>PM : Développeurs, augmentez votre productivité !</title>
    <link>http://blog.phppro.fr/?post/2011/04/13/PM-%3A-Developpeurs-augmentez-votre-productivite</link>
    <guid isPermaLink="false">urn:md5:01378f6d8c68e819db280d2906d2946b</guid>
    <pubDate>Wed, 13 Apr 2011 22:39:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Outillage</category>
        <category>php 5.3</category><category>pm</category><category>équipe</category>    
    <description>Ce soir, lors de la première séance du PHP User Group Bordeaux,  j'ai présenté mon nouvel outil en ligne de commande pour augmenter la productivité au quotidien : PM.
Voici la slides de ma présentation, vos commentaires sont les bienvenus !    &lt;div style=&quot;width:425px&quot; id=&quot;__ss_7619513&quot;&gt; &lt;strong style=&quot;display:block;margin:12px 0 4px&quot;&gt;&lt;a href=&quot;http://www.slideshare.net/ohoareau/pm-code-faster&quot; title=&quot;PM : code faster&quot;&gt;PM : code faster&lt;/a&gt;&lt;/strong&gt; &lt;iframe src=&quot;http://www.slideshare.net/slideshow/embed_code/7619513&quot; width=&quot;425&quot; height=&quot;355&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot;&gt;&lt;/iframe&gt; &lt;div style=&quot;padding:5px 0 12px&quot;&gt; View more &lt;a href=&quot;http://www.slideshare.net/&quot;&gt;presentations&lt;/a&gt; from &lt;a href=&quot;http://www.slideshare.net/ohoareau&quot;&gt;PHPPRO&lt;/a&gt; &lt;/div&gt; &lt;/div&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2011/04/13/PM-%3A-Developpeurs-augmentez-votre-productivite#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2011/04/13/PM-%3A-Developpeurs-augmentez-votre-productivite#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/89</wfw:commentRss>
      </item>
    
  <item>
    <title>ARM : &quot;Array Relational Mapping&quot;, où comment faire du lazy loading avec des arrays en PHP...</title>
    <link>http://blog.phppro.fr/?post/2011/03/31/ARM-%3A-Array-Relational-Mapping-ou-comment-faire-du-lazy-loading-avec-des-arrays-en-PHP</link>
    <guid isPermaLink="false">urn:md5:042830b140bcc7fe1d2bac3724a98c70</guid>
    <pubDate>Thu, 31 Mar 2011 23:14:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Trucs et astuces</category>
        <category>ArrayAccess</category><category>objet</category>    
    <description>&lt;p&gt;Considérez&lt;/p&gt;

&lt;pre&gt;
$user = array('login' =&amp;gt; 'ohoareau', 'company_id' =&amp;gt; 12);
&lt;/pre&gt;


&lt;p&gt;Imaginez que vous puissiez directement l'utiliser pour faire ça&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$login = $user['login'];
// récupères les données de l'entreprise en réalisant
// une jointure automatique
$companyName = $user['company']['name'];
&lt;/pre&gt;


&lt;p&gt;Sans avoir besoin d'utiliser un &quot;objet&quot; (ou classe) User pour charger &quot;à la demande&quot; les jointures avec la table organization...&lt;/p&gt;    &lt;p&gt;PHP 5 propose un mécanisme natif permettant d'utiliser un objet (i.e. une instance d'une classe) comme un array.
Cela permet par exemple d'utiliser la même syntaxe pour accéder aux propriétés d'un objet et aux index d'un array&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$arrayObject = new MyLazyLoadingClass();
$arrayObject['key1'] = 'value1';
$theValue = $arrayObject['key1'];

// et

$array = array();
$array['key1'] = 'value1';
$theValue = $arrayObject['key1'];
&lt;/pre&gt;


&lt;p&gt;Pour obtenir ce comportement il suffit juste que votre classe &quot;implémente&quot; une interface native nommée &quot;ArrayAccess&quot;  et imposant d'implémenter les méthodes suivantes&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
public function offsetGet($key);
public function offsetSet($key, $value);
public function offsetExists($key);
public function offsetUnset($key);
&lt;/pre&gt;


&lt;p&gt;Voila ce que nous souhaiterions maintenant pouvoir faire&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$user = array('login' =&amp;gt; 'ohoareau', 'company_id' =&amp;gt; 12);

$user = new LazyArray($user);
&lt;/pre&gt;


&lt;p&gt;or directly create a lazy array the same way you create array&lt;/p&gt;

&lt;pre&gt;
$user = lazy(array('login' =&amp;gt; 'ohoareau', 'company_id' =&amp;gt; 12));

echo sprintf(
    &amp;quot;User '%s' is working at '%s' in '%s', but living in '%s' (%s)&amp;quot;,
    $user['login'],
    $user['company']['name'],
 );
&lt;/pre&gt;


&lt;p&gt;L'idée étant que lorsqu'un index du tableau qui n'est pas initialement disponible est accédé, alors un mécanisme dynamique de chargement automatique permet de récupérer les données de façon transparente.
On peut donc imaginer que la jointure en base de données organization_id soit effectuée dans une seconde requête SQL uniquement que si l'indice &quot;organization&quot; est accédé, et donc pas de façon systématique.&lt;/p&gt;


&lt;p&gt;Ci-dessous vous trouverez un exemple de classe LazyArray et de fonction lazy()&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php

class LazyArray implements \ArrayAccess
{
	protected $_data;
	protected $_loader;
	protected static $_defaultLoader = null;
	
	public static function setDefaultLoader($loader)
	{
		static::$_defaultLoader = $loader;
	}
	public static function getDefaultLoader()
	{
		if (null === static::$_defaultLoader) {
			static::$_defaultLoader = function($key, $value, $data) {
				return $value;
			};
		}
		
		return static::$_defaultLoader;
	}
	public function __construct($data, $loader = null, $recursive = true)
	{
		if (false === is_bool($recursive)) {
			$recursive = true;
		}
		if (false === is_array($data)) {
			$data = array();
		}
		if (true === $recursive) {
			foreach($data as $k =&amp;gt; $v) {
				$data[$k] = is_array($v) ? new LazyArray($v, $loader, $recursive) : $v;
			}
		}
		$this-&amp;gt;_data = $data;
		$this-&amp;gt;_loader = null === $loader ? static::getDefaultLoader() : $loader;
	}
	public function offsetGet($key)
	{
		if (false === isset($this-&amp;gt;_data[$key])) {
			if (true === isset($this-&amp;gt;_data[$key . '_id'])) {
				$callback = $this-&amp;gt;_loader;
				if (true === is_array($callback)) {
					$this-&amp;gt;_data[$key] = new LazyArray(
                                            $callback[0]-&amp;gt;{$callback[1]}(
                                                $key, $this-&amp;gt;_data[$key . '_id'], $this-&amp;gt;_data)
                                             );
				} else {
					$this-&amp;gt;_data[$key] = new LazyArray(
                                            $callback($key, $this-&amp;gt;_data[$key . '_id'], $this-&amp;gt;_data)
                                        );
				}
				return $this-&amp;gt;_data[$key];
			}
			throw new \RuntimeException(&amp;quot;Unknown key '$key'&amp;quot;);
		}
		
		return $this-&amp;gt;_data[$key];
	}
	public function offsetSet($key, $value)
	{
		$this-&amp;gt;_data[$key] = $value;
		
		return $value;
	}
	public function offsetExists($key)
	{
		return true === isset($this-&amp;gt;_data[$key]) || true === isset($this-&amp;gt;_data[$key . '_id']);
	}
	public function offsetUnset($key)
	{
		unset($this-&amp;gt;_data[$key]);
	}
}

function lazy($data, $loader = null, $recursive = true)
{
	return new LazyArray($data, $loader, $recursive);
}

function myLoadingFunction($key, $value, $data)
{
        // you can easily do your SQL selection stuff here

	switch($key) {
		case 'company':
			return array(
				'name' =&amp;gt; 'phppro',
				'city_id' =&amp;gt; 3,
			);
		case 'city':
			switch($value) {
				case 3:
					return array(
						'name' =&amp;gt; 'bordeaux',
						'zipcode' =&amp;gt; '33000',
					);
				case 4:
					return array(
						'name' =&amp;gt; 'talence',
						'zipcode' =&amp;gt; '33400',
					);
			}
		default:
			throw new \RuntimeException(&amp;quot;Unknown loadable type '$key'&amp;quot;);
		
	}
}

// in this example we have a previously defined function that provide loading feature
LazyArray::setDefaultLoader('myLoadingFunction');
// in this example we have an anonymous function (closure) that provide loading feature
LazyArray::setDefaultLoader(function ($key, $value, $data) { /* ... */ });
// in this example we have a $db object instance that provide a load($key, $value, $data) method
LazyArray::setDefaultLoader(array($db, 'load'));


$user = lazy(array('login' =&amp;gt; 'ohoareau', 'company_id' =&amp;gt; 12));
$company = $user['company'];
&lt;/pre&gt;


&lt;p&gt;On peut très bien imaginer mettre en place d'autres conventions, comme par exemple&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
// récupères tout les utilisateurs de l'entreprise courante
$users = $company['users']; 

// supprime le lien entre cet utilisateur et son entreprise
unset($user['company']); 

// récupères tous les utilisateurs s'appellant olivier dans l'entreprise
$users = $company['users#name~olivier'];
&lt;/pre&gt;


&lt;p&gt;Cette technique (ou une dérivée) vous permettra certainement d'éviter d'utiliser des framework ORM complexe pour vos besoins les plus simples, et notamment éviter d'avoir des dizaines de classes type &quot;DAO&quot;  ...&lt;/p&gt;


&lt;p&gt;Et vous utilisez vous ArrayAccess ou un équivalent pour tirer partie de la puissance des tableaux en PHP&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2011/03/31/ARM-%3A-Array-Relational-Mapping-ou-comment-faire-du-lazy-loading-avec-des-arrays-en-PHP#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2011/03/31/ARM-%3A-Array-Relational-Mapping-ou-comment-faire-du-lazy-loading-avec-des-arrays-en-PHP#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/88</wfw:commentRss>
      </item>
    
  <item>
    <title>Le comportement attendu d'une méthode : où comment se servir des conventions de nommage plutôt que des commentaires</title>
    <link>http://blog.phppro.fr/?post/2011/03/21/Le-comportement-attendu-d-une-methode-%3A-ou-comment-se-servir-des-conventions-de-nommage-plutot-que-des-commentaires</link>
    <guid isPermaLink="false">urn:md5:7a7d851cc0cdbf1d9c8323a0b0eba56b</guid>
    <pubDate>Wed, 23 Mar 2011 07:00:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Méthodologie</category>
        <category>convention</category><category>method</category>    
    <description>&lt;p&gt;Quel comportement attendez-vous quand vous appellez setX()&amp;nbsp;? getX($id)&amp;nbsp;? checkX()&amp;nbsp;? isX()&amp;nbsp;? listX()&amp;nbsp;?
Chaque préfixe de nom de méthode à son propre &quot;comportement attendu&quot;, voici une proposition de comportements communément utilisés...&lt;/p&gt;    &lt;p&gt;Le nommage des méthodes est tout un art, plus le nom de la méthode est claire et logique, plus le ou les développeurs qui l'utilisent sauront comment bien l'utiliser.&lt;/p&gt;


&lt;p&gt;Le nom d'une méthode est composée d'au moins 2 informations utiles&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;un préfixe (souvent court, le premier mot)&lt;/li&gt;
&lt;li&gt;un suffixe (souvent long, de plusieurs mots)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;L'utilisation judicieuse de préfixes &quot;connus&quot; permet de connaître à l'avance le comportement qu'une méthode aura.&lt;/p&gt;


&lt;p&gt;Voici les conventions d'utilisation de préfixes que je préconise régulièrement aux équipes de développement PHP&amp;nbsp;:&lt;/p&gt;


&lt;p&gt;&lt;a href=&quot;http://blog.phppro.fr/public/method-prefix.png&quot;&gt;&lt;img src=&quot;http://blog.phppro.fr/public/./.method-prefix_m.jpg&quot; alt=&quot;method-prefix.png&quot; style=&quot;display:block; margin:0 auto;&quot; title=&quot;method-prefix.png, mar 2011&quot; /&gt;&lt;/a&gt;&lt;/p&gt;


&lt;p&gt;En plus des préfixes, certaines &quot;préfixes génériques&quot; altère le comportement d'une méthode ayant déjà un préfixe&amp;nbsp;:&lt;/p&gt;


&lt;p&gt;&lt;a href=&quot;http://blog.phppro.fr/public/method-modifier.png&quot;&gt;&lt;img src=&quot;http://blog.phppro.fr/public/./.method-modifier_m.jpg&quot; alt=&quot;method-modifier.png&quot; style=&quot;display:block; margin:0 auto;&quot; title=&quot;method-modifier.png, mar 2011&quot; /&gt;&lt;/a&gt;&lt;/p&gt;


&lt;p&gt;Enfin, il convient de respecter certaines conventions lors du développement de l'implémentation d'une méthode, permettant ainsi de faciliter son utilisation&amp;nbsp;:&lt;/p&gt;


&lt;p&gt;&lt;a href=&quot;http://blog.phppro.fr/public/method-convention.png&quot;&gt;&lt;img src=&quot;http://blog.phppro.fr/public/./.method-convention_m.jpg&quot; alt=&quot;method-convention.png&quot; style=&quot;display:block; margin:0 auto;&quot; title=&quot;method-convention.png, mar 2011&quot; /&gt;&lt;/a&gt;&lt;/p&gt;


&lt;p&gt;L'utilisation de toutes ces conventions (préfixe, modifieurs et conventions communes) permet de rendre le code plus claire et logique, l'utilisation de commentaire dans la logique (i.e. à l'intérieur d'une méthode) est donc souvent moins nécessaire.&lt;/p&gt;


&lt;p&gt;Et vous, utilisez vous des conventions différentes&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2011/03/21/Le-comportement-attendu-d-une-methode-%3A-ou-comment-se-servir-des-conventions-de-nommage-plutot-que-des-commentaires#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2011/03/21/Le-comportement-attendu-d-une-methode-%3A-ou-comment-se-servir-des-conventions-de-nommage-plutot-que-des-commentaires#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/87</wfw:commentRss>
      </item>
    
  <item>
    <title>Au secours j'ai trop de log !</title>
    <link>http://blog.phppro.fr/?post/2011/03/20/Au-secours-j-ai-trop-de-log</link>
    <guid isPermaLink="false">urn:md5:87612cccaa050de92b93e165eb2c6b6d</guid>
    <pubDate>Sun, 20 Mar 2011 22:26:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Trucs et astuces</category>
            
    <description>&lt;p&gt;Et vous comment faites vous pour n'activer que certaines lignes de debug mais pas toutes dans vos applications&amp;nbsp;?&lt;/p&gt;    &lt;p&gt;Régulièrement il est nécessaire de mettre en place des traces et logs dans nos applicatifs, qu'ils soient batchs ou web pour avoir un peu plus d'information sur ce qu'il se passe lors de l'exécution, notamment suite à la détection d'un bug en production.&lt;/p&gt;


&lt;p&gt;On commence souvent par implémenter quelque chose comme&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$this-&amp;gt;log('info',&amp;quot;Mon message de log pour tracer '$v'&amp;quot;);

// ...

$this-&amp;gt;log('debug', &amp;quot;Mon autre message de log&amp;quot;);
&lt;/pre&gt;


&lt;p&gt;Puis on se rend compte que l'on souhaite mutualiser certains messages, les rationnaliser, éviter de repasser profondément dans le code pour mettre à jour les messages et préférer la centralisation dans un fichier de paramétrage par langue par exemple.
On améliore alors notre méthode log() pour prendre une clé de message et une liste de paramètres variables (optionnels), le message sera alors chargé depuis un fichier de paramétrage&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$this-&amp;gt;log('info', 'my.message.key', $variable);

// ...

$this-&amp;gt;log('debug', 'my.other.message.key');
&lt;/pre&gt;


&lt;p&gt;avec un fichier de langue&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
my.message.key=Mon message de log pour tracer '%{1}'
my.other.message.key=Mon autre message de log
&lt;/pre&gt;


&lt;p&gt;Enfin, et c'est l'objectif de ce billet, on se rend compte que cela n'est pas très pratique de mettre en dur dans le code source le niveau d'erreur des messages, car à l'exécution il peut être intéressant d'afficher certains messages debug mais pas tous car cela serait trop verbeux, on enlève alors le premier paramètre&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$this-&amp;gt;log('my.message.key', $variable);

// ...

$this-&amp;gt;log('my.other.message.key');
&lt;/pre&gt;


&lt;p&gt;et on peut ainsi mettre en place un mécanisme au chargement (bootstrap) de la requête ou du script qui prend en compte un fichier de paramétrage complémentaire ou bien une variable d'environnement ou en ligne de commande&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
# niveau info pour le message my.message.key
my.message.key=info
# niveau debug pour tous ceux commencant par my.other
my.other=debug
&lt;/pre&gt;


&lt;p&gt;Au final l'utilisation d'une clé au lieu d'un message, nous permet&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;de centraliser la traduction des messages dans un fichier de paramétrage&lt;/li&gt;
&lt;li&gt;de sélectivement définir le niveau d'erreur du message à l'exécution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si vous choisissez judicieusement les préfixes de vos clés, vous pourrez être en mesure de&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;n'activer les traces que pour les appels systèmes ou les accès disques&lt;/li&gt;
&lt;li&gt;désactiver complètement les logs pour certaines catégories de logs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Et vous comment faites vous pour n'activer que certaines lignes de debug mais pas toutes dans vos applications&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2011/03/20/Au-secours-j-ai-trop-de-log#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2011/03/20/Au-secours-j-ai-trop-de-log#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/86</wfw:commentRss>
      </item>
    
  <item>
    <title>Conférence au Bordeaux PUG sur un nouvel outil d'industrialisation PHP le 13/04/11</title>
    <link>http://blog.phppro.fr/?post/2011/03/20/Conference-au-Bordeaux-PUG-sur-un-nouvel-outil-d-industrialisation-PHP-le-13/04/11</link>
    <guid isPermaLink="false">urn:md5:20936739538319850103b99e74426953</guid>
    <pubDate>Sun, 20 Mar 2011 22:01:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Evènements</category>
        <category>bordeauxpug</category><category>industrialisation</category><category>pm</category>    
    <description>&lt;p&gt;Le 13 avril prochain je présenterai mon outil de &quot;gestion de projet&quot; pour développeur PHP (version beta, open source, under heavy development).&lt;/p&gt;


&lt;p&gt;En fait, vous aurez reconnu un outil en ligne de commande qui est un mix entre zf.bat / sf.bat et maven (entre autres ;)).&lt;/p&gt;


&lt;p&gt;Voici le teaser de la conférence pour ceux que ca intéresse&amp;nbsp;: &lt;a href=&quot;http://bordeauxpug.org/2011/03/20/session-du-13-avril/&quot;&gt;http://bordeauxpug.org/2011/03/20/session-du-13-avril/&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;Je mettrai en ligne les slides de la présentation une fois celle ci passée, restez connecté&amp;nbsp;!&lt;/p&gt;


&lt;p&gt;Un petit avant gout&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Générer une classe avec votre propre cartouche de commentaires, la date et votre nom, avec en plus sa classe de test vide associée&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
pm template:apply service
pm t service
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Lancer les tests unitaires, que vous utilisiez Symfony, Zend Framework ou whatever framework&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
pm tu
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Lancer les tests unitaires avec le rapport HTML de couverture de code&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
pm tuc
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Créer un paquet Phar ou PEAR de votre code source&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
pm package
pm package:pear
&lt;/pre&gt;


&lt;p&gt;et pleins d'autres fonctionnalités&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;installer un poste de développeur from scratch et checkouter un projet en une ligne de commande (plus fort que Wamp ;))&lt;/li&gt;
&lt;li&gt;indexer l'ensemble de votre code source dans une base de données SQLite pour réaliser des statistiques sur votre code (toujours en une ligne de commande)&lt;/li&gt;
&lt;li&gt;créer vos propres raccourcis de commandes (en pur PHP)&amp;nbsp;!&lt;/li&gt;
&lt;li&gt;utiliser un outil traduit (ou que vous pouvez traduire) dans votre langue&lt;/li&gt;
&lt;li&gt;support avancée et évolué des traces/logs&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2011/03/20/Conference-au-Bordeaux-PUG-sur-un-nouvel-outil-d-industrialisation-PHP-le-13/04/11#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2011/03/20/Conference-au-Bordeaux-PUG-sur-un-nouvel-outil-d-industrialisation-PHP-le-13/04/11#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/83</wfw:commentRss>
      </item>
    
  <item>
    <title>Piste pour gérer les spécificités de plusieurs clients (versions) au sein d'une même application (version)</title>
    <link>http://blog.phppro.fr/?post/2010/11/29/Piste-pour-gerer-les-specificites-de-plusieurs-clients-versions-au-sein-d-une-meme-application-version</link>
    <guid isPermaLink="false">urn:md5:b78ff864d8e895928d946b24da815871</guid>
    <pubDate>Mon, 29 Nov 2010 08:24:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Trucs et astuces</category>
        <category>dsl</category><category>moteur de règles</category><category>rules</category>    
    <description>&lt;p&gt;C'est naturel (enfin pour la majorité)&amp;nbsp;: on commence toujours par développer &quot;spécifique&quot;&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;écran et boutons d'actions spécifiques à notre client&lt;/li&gt;
&lt;li&gt;règle de gestion et workflow spécifiques à notre client&lt;/li&gt;
&lt;li&gt;messages d'erreurs et envoi d'emails spécifiques à notre client&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Puis notre produit / logiciel est un premier succès et un autre client nous le demande, puis un autre, ...
Nous devons alors faire face à un problème sans forcément nous en rendre compte&amp;nbsp;: Comment gérer plusieurs mode de fonctionnement (i.e. un fonctionnement pour chaque client) au sein de la même application&amp;nbsp;? Comment éviter de dupliquer / cloner l'application et maintenir N versions en parallèle&amp;nbsp;?&lt;/p&gt;    &lt;p&gt;Etudions un cas simple, notre premier client indique que tous les utilisateurs ont le droit de créer des messages, nous commençons donc par écrire&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php

namespace Message;

class Service
{
    public function add($data)
    {
        // create a new row in database and return ID
        return $this-&amp;gt;getAdapter()-&amp;gt;create($data);
    }
}
&lt;/pre&gt;


&lt;p&gt;Puis un deuxième client nous indique que seul les internautes connectés ont le droit de créer des messages (le premier client n'a pas changé ses spécifications, lui)&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php

namespace Message;

class Service
{
    public function add($data)
    {
        if ('client2' === $GLOBALS['client']) {
            if (false === isset($GLOBALS['user']) {
                throw new \RuntimeException('Denied');
            }
        }
        // create a new row in database and return ID
        return $this-&amp;gt;getAdapter()-&amp;gt;create($data);
    }
}
&lt;/pre&gt;


&lt;p&gt;En quelques lignes, nous avons traité les spécificités du client 2 tout en restant compatible avec le client 1.
Oui, mais&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;à chaque nouveau client, il faut repasser sur le code pour le spécialiser&lt;/li&gt;
&lt;li&gt;il risque soit d'y avoir beaucoup de copier/coller soit beaucoup de complexité&lt;/li&gt;
&lt;li&gt;les méthodes vont s'allonger et ne seront plus trop compréhensibles&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Un troisième client nous appelle et nous demande de vérifier que l'internaute est connecté et à le profil &quot;admin&quot;, ...&lt;/p&gt;


&lt;p&gt;Nous pourrions décider de &quot;sortir&quot; la logique contenant les règles de gestion du code métier d'ajout de message:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php

namespace Message;

class Service
{
    public function add($data)
    {
        $this-&amp;gt;checkRights('message.add');
        return $this-&amp;gt;getAdapter()-&amp;gt;create($data);
    }
    public function checkRights($operation)
    {
        $operationKey = $GLOBALS['client'] . '.' . $GLOBALS['userProfile'];

        $allowed = false;

        switch($operationKey) {
        case 'client1.all.message.add': $allowed = true; break;
        case 'client2.logged.message.add': $allowed = true; break;
        case 'client3.admin.message.add': $allowed = true; break;
        }

        if (false === $allowed) {
            throw new \RuntimeException('Denied');
        }

        return $this;
}
&lt;/pre&gt;


&lt;p&gt;Puis nous pourrions finalement décider de complètement sortir la liste des &quot;règles&quot; dans un fichier de paramétrage &quot;par clients&quot;&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
&amp;lt;?php

namespace Message;

// chargement des règles spécifiques au client en cours
$GLOBALS['rules'] = include 'configs/rules/' . $GLOBALS['client'] . '.php';

class Service
{
    public function add($data)
    {
        $this-&amp;gt;checkRights('message.add');
        return $this-&amp;gt;getAdapter()-&amp;gt;create($data);
    }
    public function checkRights($operation)
    {
        // check if all profiles are allowed for this operation
        if (true === isset($GLOBALS['rules']['all.' . $operation])) {
            return $this;
        }
        // check if current profiles is allowed for this operation
        if (true === isset($GLOBALS['rules'][$GLOBALS['userProfile'] . '.' . $operation])) {
            return $this;
        }
        // check if user is logged and if logged users are allowed
        if (true === isset($GLOBALS['userLogin']) &amp;amp;&amp;amp; isset($GLOBALS['rules']['logged.' . $operation])) {
            return $this;
        }

        throw new \RuntimeException('Denied');
    }
}
&lt;/pre&gt;


&lt;p&gt;Avec un fichier les fichiers de paramétrages suivants&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
&amp;lt;?php
// configs/rules/client1.php
return array(
    'all.message.add' =&amp;gt; true,
);
&lt;/pre&gt;
&lt;pre&gt;
&amp;lt;?php
// configs/rules/client2.php
return array(
    'logged.message.add' =&amp;gt; true,
);
&lt;/pre&gt;
&lt;pre&gt;
&amp;lt;?php
// configs/rules/client3.php
return array(
    'admin.message.add' =&amp;gt; true,
);
&lt;/pre&gt;


&lt;p&gt;Vous pouvez maintenant aisément ajouter des règles dans vos fichiers de configuration et utiliser votre méthode &quot;checkRights()&quot; pour vérifier que l'utilisateur connecté à le droit d'exécuter l'opération.
Une fois mis en place l'utilisation de checkRights() partout dans votre code, vous n'aurez plus qu'à modifier vos fichiers de règles pour&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ajouter un nouveau client&lt;/li&gt;
&lt;li&gt;ajouter ou modifier des règles existantes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bien sûr cette &quot;implémentation&quot; reste très basique et vous pourriez vouloir traiter en plus les points suivants&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fichiers de paramétrages dans un DSL (Domain Specific Language) plus facile à appréhender que PHP pour vos collaborateurs commerciaux ou fonctionnels&lt;/li&gt;
&lt;li&gt;utilisation d'un meilleur mécanisme que les variables globales (passage en paramètre du contexte, registry, ...)&lt;/li&gt;
&lt;li&gt;prise en compte des données dans les règles (ex: si le sujet commence par &quot;TEST&quot; alors tout le monde peut créer le message)&lt;/li&gt;
&lt;li&gt;prise en compte de post-operations comme l'envoi d'email (pour certains clients), ...&lt;/li&gt;
&lt;li&gt;mise en place d'une classe abstraite / générique fournissant checkRights() à toutes les fonctionnalités de votre application&lt;/li&gt;
&lt;li&gt;utilisation / mutualisation des règles dans la partie IHM écrit en Javascript&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Toutes ces fonctionnalités restent assez simples à mettre en oeuvre en partant de la base présentée ci-dessus.&lt;/p&gt;


&lt;p&gt;Sans le savoir, vous avez mis en place un véritable petit moteur de règles&amp;nbsp;!&lt;/p&gt;


&lt;p&gt;Et vous quels sont vos techniques pour gérer les spécificités de vos clients au sein de la même application&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2010/11/29/Piste-pour-gerer-les-specificites-de-plusieurs-clients-versions-au-sein-d-une-meme-application-version#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2010/11/29/Piste-pour-gerer-les-specificites-de-plusieurs-clients-versions-au-sein-d-une-meme-application-version#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/85</wfw:commentRss>
      </item>
    
  <item>
    <title>Autoloader de classes : comment prendre en compte les namespaces de PHP 5.3+</title>
    <link>http://blog.phppro.fr/?post/2010/11/27/Autoloader-de-classes-%3A-comment-prendre-en-compte-les-namespaces-de-PHP-53</link>
    <guid isPermaLink="false">urn:md5:76b0659043597a19fee84638191527f8</guid>
    <pubDate>Sat, 27 Nov 2010 14:46:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Trucs et astuces</category>
        <category>5.3</category><category>autoload</category><category>fonction anonyme</category><category>package</category>    
    <description>&lt;p&gt;Pour ceux qui ne connaissent pas (encore), le mécanisme d'autoload en PHP permet (entre autres) d'inclure &quot;à la demande&quot; les fichier-classe.
Classiquement en PHP, nous faisons&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
require_once 'My/Service.php';

$o = new My_Service();
&lt;/pre&gt;


&lt;p&gt;Le mécanisme d'autoload permet d'écrire uniquement&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
$o = new My_Service();
&lt;/pre&gt;


&lt;p&gt;...&lt;/p&gt;    &lt;p&gt;l'inclusion (require_once ou équivalent) étant réalisé implictement par le mécanisme autoload lorsqu'il est activé comme suit&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
function my_autoload($class)
{
    // My_Service =&amp;gt; My/Service.php
    include str_replace('_', '/', $class) . '.php';
}

spl_autoload_register('my_autoload');
&lt;/pre&gt;


&lt;p&gt;Cette méthode fonctionne bien jusqu'à PHP 5.2.x, mais n'est plus suffisante en PHP 5.3 avec l'arrivée des namespace (~ package), en effet, les namespaces permettent maintenant de créer officiellement des &quot;packages&quot;&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
&amp;lt;?php

namespace My;

class Service
{
    // ...
}
&lt;/pre&gt;

&lt;p&gt;qui s'utilise donc comme suit (par exemple)&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
require_once 'My/Service.php';

$o = new \My\Service();
&lt;/pre&gt;


&lt;p&gt;Nous devons donc améliorer le mécanisme d'autoload pour prendre en compte la nouvelle syntaxe pour les namespaces&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
function my_autoload($class)
{
    /**
     * My_Service =&amp;gt; My/Service.php
     * \My\Service =&amp;gt; My/Service.php
     */
    include str_replace(
        array('_', '\'), '/', $class
    ) . '.php';
}

spl_autoload_register('my_autoload');
&lt;/pre&gt;


&lt;p&gt;Notre autoload (simpliste) permet donc de prendre en compte les classes classiques (PHP &amp;lt;= 5.2) et les classes appartenant à des namespaces (PHP &amp;gt;= 5.3).
Nous pouvons d'autre part améliorer légèrement notre autoloader pour utiliser une fonction anonyme et éviter ainsi les éventuelles collision de nommage avec des fonctions d'autoload portant le même nom, notre code devient alors&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
&amp;lt;?php

spl_autoload_register(function($class) {
    include str_replace(
        array('_', '\'), '/', $class
    ) . '.php';
});
&lt;/pre&gt;


&lt;p&gt;Et vous, quels sont vos implémentations d'autoloader en PHP 5.2 et 5.3&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2010/11/27/Autoloader-de-classes-%3A-comment-prendre-en-compte-les-namespaces-de-PHP-53#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2010/11/27/Autoloader-de-classes-%3A-comment-prendre-en-compte-les-namespaces-de-PHP-53#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/84</wfw:commentRss>
      </item>
    
  <item>
    <title>&quot;&quot; : où comment afficher un compteur de lignes traités dans un batch, plutôt qu'un log volumineux</title>
    <link>http://blog.phppro.fr/?post/2010/04/26/r-%3A-ou-comment-afficher-un-compteur-de-lignes-traites-dans-un-batch-plutot-qu-un-log-volumineux</link>
    <guid isPermaLink="false">urn:md5:2be3205fdec54efd78eaa0d848a8acce</guid>
    <pubDate>Mon, 26 Apr 2010 00:10:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Trucs et astuces</category>
        <category>batch</category>    
    <description>&lt;p&gt;Vous avez un batch qui analysent des fichiers et qui affichent une ligne de log par fichier traités, voici une technique pour avoir un (fichier de) log moins volumineux&lt;/p&gt;    &lt;p&gt;Habituellement, on fait souvent quelquechose de similaire à&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
foreach($files as $file) {
    try {
        $this-&amp;gt;processFile($file);
        echo &amp;quot;Fichier '$file' traité.
&amp;quot;;
    } catch(Exception $e) {
        echo &amp;quot;Erreur de traitement du fichier '*file' : &amp;quot;.$e-&amp;gt;getMessage().&amp;quot;
&amp;quot;;
    }
}
&lt;/pre&gt;


&lt;p&gt;Ce qui nous donne des logs du type&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
Fichiers 'fichier1.csv' traité.
Fichiers 'fichier2.csv' traité.
Fichiers 'fichier3.csv' traité.
Fichiers 'fichier4.csv' traité.
Fichiers 'fichier5.csv' traité.
...
Fichiers 'fichier101.csv' traité.
Erreur de traitement du fichier 'fichier102.csv&amp;quot;: mauvais format.
Fichiers 'fichier103.csv' traité.
Erreur de traitement du fichier 'fichier104.csv&amp;quot;: mauvais format.
...
&lt;/pre&gt;


&lt;p&gt;Ce log peut être volumineux si le nombre de fichier (ou d'élément à traiter) est important, si il n'est pas primordial d'avoir le nom du fichier traité mais juste un résumé des traitements, il est envisageable d'imprimer, en fin de traitement une ligne du type&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
Fichiers analysés: 104, traités: 102, rejetés: 2
&lt;/pre&gt;


&lt;p&gt;En utilisant par exemple le code&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$counters = array('success'=&amp;gt;0, 'errors'=&amp;gt;0);
//$errors = array();
foreach($files as $file) {
    try {
        $this-&amp;gt;processFile($file);
        $counters['success']++;
    } catch(Exception $e) {
        $counters['errors']++;
        // on peut éventuellement stocker l'erreur dans un tableau pour l'afficher à la fin
        // $errors[] = $e;
    }
}
echo 'Fichiers analysés: '.($counters['success']+$counters['error']);
echo ', traités: '.$counters['success'].', rejetés: '.$counters['error'].&amp;quot;
&amp;quot;;
&lt;/pre&gt;


&lt;p&gt;Cet affichage &quot;résumé&quot; peut être moins consommateur en terme de log et plus intéressant pour avoir un résumé, cependant si le &quot;batch&quot; prend beaucoup de temps à s'exécuter, nous devront attendre que l'ensemble des traitements soient terminés pour avoir cette ligne de résumé.&lt;br /&gt;
Une autre possibilité serait d'afficher &quot;au fur et à mesure&quot; l'avancement des traitements sous le même format en faisant évoluer les compteurs au fur et à mesure des traitements des lignes / fichiers&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$counters = array('success'=&amp;gt;0, 'errors'=&amp;gt;0);
//$errors = array();
foreach($files as $file) {
    try {
        $this-&amp;gt;processFile($file);
        $counters['success']++;
    } catch(Exception $e) {
        $counters['errors']++;
        // on peut éventuellement stocker l'erreur dans un tableau pour l'afficher à la fin
        // $errors[] = $e;
    }
    echo &amp;quot;                                                                           &amp;quot;;
    echo 'Fichiers analysés: '.($counters['success']+$counters['error'])
    echo ', traités: '.$counters['success'].', rejetés: '.$counters['error'].&amp;quot;
&amp;quot;;
}
&lt;/pre&gt;


&lt;p&gt;A chaque fichier traité, la ligne de résumé sera &quot;mise à jour&quot; grâce au retour chariot () et on verra évoluer les compteurs sur la même ligne.&lt;br /&gt;
Un des avantages est qu'on aura une information &quot;temps réel&quot; sur la vitesse d'exécution en voyant évoluer les compteurs.&lt;br /&gt;
On pourrait mettre à jour un compteur de &quot;temps passé&quot; et de nombre de fichiers traités par secondes assez aisément, toujours sur la même ligne.&lt;br /&gt;
Il est de plus envisageable de faire un dump des erreurs stockées à la fin du traitement (attention à leur volumétrie et à la mémoire vive nécessaire pour leur stockage).&lt;/p&gt;


&lt;p&gt;Et vous comment réalisez vous des compteurs de traitements succinct et résumé&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2010/04/26/r-%3A-ou-comment-afficher-un-compteur-de-lignes-traites-dans-un-batch-plutot-qu-un-log-volumineux#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2010/04/26/r-%3A-ou-comment-afficher-un-compteur-de-lignes-traites-dans-un-batch-plutot-qu-un-log-volumineux#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/82</wfw:commentRss>
      </item>
    
  <item>
    <title>Batch : Attention à la mémoire vive</title>
    <link>http://blog.phppro.fr/?post/2010/04/22/Batch-%3A-Attention-a-la-memoire-vive</link>
    <guid isPermaLink="false">urn:md5:1e059f1d573f17268b7172ef971d8160</guid>
    <pubDate>Thu, 22 Apr 2010 22:46:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Analyse</category>
        <category>batch</category><category>import</category>    
    <description>&lt;p&gt;Le sujet des batch (en php) est assez récurrent dans le développement applicatif. On a souvent besoin de faire des scripts d'import, au hasard d'un catalogue de produits, par exemple.&lt;br /&gt;
Dans cette article je reviens sur une maladresse souvent implémentée dans les batchs et qui a un impact négatif sur les perfs et l'utilisation de la mémoire vive&amp;nbsp;: le chargement de l'ensemble des données et l'utilisation du for(each).&lt;/p&gt;    &lt;pre&gt;
// le fichier fait par exemple plus de 100.000 lignes ...
$lines = file('the-very-big-file-containing-my-product-catalog.csv');

$products = array();

foreach($lines as line) {
    $products[] = convertLineToProduct($line);
}

addProductsToDb($products);
&lt;/pre&gt;


&lt;p&gt;et dans la fonction addProductsToDb ...&lt;/p&gt;

&lt;pre&gt;
foreach($products as $product) {
    addProduct($product);
}
&lt;/pre&gt;


&lt;p&gt;Ca vous rappelle des choses&amp;nbsp;?&lt;/p&gt;


&lt;p&gt;Qu'y a-t-il de maladroit dans ce code&amp;nbsp;? Au moins deux choses&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;l'ensemble des lignes du fichier est chargé en mémoire vive via la fonction file (notre fichier est gros... très gros...)&lt;/li&gt;
&lt;li&gt;on effectue 2 fois la boucle sur les produits (à deux endroits du code, mais quand même)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Comment aurais-t-on pu faire autrement pour traiter ces deux points&amp;nbsp;?&lt;/p&gt;

&lt;pre&gt;
$fp = fopen('the-very-big-file-containing-my-product-catalog.csv', 'r');
$i = 0;
while(!feof($fp)) {
    try {
        addProductToDb(convertLineToProduct(fgets($fp)));
    } catch (Exception $e) {
        echo 'Erreur ligne #'.$i.&amp;quot;
&amp;quot;;
    }
    $i++;
}
fclose($fp);
&lt;/pre&gt;


&lt;p&gt;Dans cette version du code, un peu plus courte en plus ;)&amp;nbsp;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;une seule ligne est stockée en mémoire vive à la fois&lt;/li&gt;
&lt;li&gt;le fichier est traité ligne par ligne&lt;/li&gt;
&lt;li&gt;si un problème survient à l'insertion dans notre base (pas à la lecture du fichier), on sait à quelle ligne du fichier l'import à planté&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Et vous quelles techniques mettez vous en oeuvre pour traiter vos imports volumineux de données&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2010/04/22/Batch-%3A-Attention-a-la-memoire-vive#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2010/04/22/Batch-%3A-Attention-a-la-memoire-vive#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/81</wfw:commentRss>
      </item>
    
  <item>
    <title>Externaliser les messages d'erreurs des exceptions, pour les internationaliser, par exemple</title>
    <link>http://blog.phppro.fr/?post/2010/04/14/Externaliser-les-messages-d-erreurs-des-exceptions-pour-les-internationaliser-par-exemple</link>
    <guid isPermaLink="false">urn:md5:1b54fbc63a243c1f27fe320bef80aa08</guid>
    <pubDate>Wed, 14 Apr 2010 23:53:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Trucs et astuces</category>
        <category>exception</category><category>i18n</category>    
    <description>&lt;p&gt;Dans certains contexte, il est important de pouvoir fournir des messages d'erreurs dans différentes langues, par exemple en fonction de langue de l'utilisateur.&lt;br /&gt;
Parmi les sujets à mettre en oeuvre pour adresser ce point, les exceptions et leur messages sont à ne pas oublier, voici une technique que vous pouvez mettre en oeuvre.&lt;/p&gt;    &lt;p&gt;Habituellement, on utilise les exceptions comme ceci&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
    throw new RuntimeException(&amp;quot;There was an error on user '&amp;quot; . $login . &amp;quot;'&amp;quot;, 503);
&lt;/pre&gt;


&lt;p&gt;ou alors&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
    throw new MySpecificException(&amp;quot;There was a specific error on user '&amp;quot; . $login . &amp;quot;'&amp;quot; , 503);
&lt;/pre&gt;


&lt;p&gt;Imaginons que nous ayons une structure (que l'on pourrait mettre dans un fichier de &quot;configuration&quot; autonome bien entendu) comme ceci&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$messages = array(
    'fr-fr' =&amp;gt; array(
        503 =&amp;gt; &amp;quot;Une erreur spécifique a été détecté pour l'utilisateur '%s',
    ),
    'en-us' =&amp;gt; array(
        503 =&amp;gt; &amp;quot;There was a specific error on user '%s',
    ),
);
&lt;/pre&gt;


&lt;p&gt;Imaginons que nous ayons une fonction i18n($code, $args) qui renvoie le message &quot;internationalisé&quot; en fonction de la langue utilisateur (je vous laisse développer votre méthode pour récupérer la langue de l'utilisateur et la mettre, par exemple dans une variable de session).&lt;br /&gt;&lt;/p&gt;

&lt;pre&gt;
function i18n($code, $args)
{
    global $messages;
    // il est possible d'utiliser bien d'autre technique qu'une variable globale

    $lang = ... ; // mettez ici votre méthode pour récupérer la langue
    return call_user_func_array('sprintf', array_merge(array($message[$lang][$code]), $args));
}
&lt;/pre&gt;


&lt;p&gt;Puis une classe d'exception spécifique&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
class MySpecificException extends RuntimeException
{
    public function __construct()
    {
        $args = func_get_args();
        $code = array_shift($args);
        parent::__construct(i18n($code, $args), $code);
    }
}
&lt;/pre&gt;


&lt;p&gt;Banco, nous pouvons maintenant utilisez nos exceptions comme ceci&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
    throw new MySpecificException(503, $login);
&lt;/pre&gt;


&lt;p&gt;Ainsi, nous n'avons plus besoin de mettre les messages de nos exceptions en dur dans notre code, nous pouvons les externaliser dans un fichier de paramétrage (au format PHP ou pas) et renvoyer le message internationalisé dans la langue de l'utilisateur.&lt;br /&gt;
Cette technique est bien entendu utilisable dans des applications web, mais aussi dans des batchs, des applications graphiques (en PHP)....&lt;/p&gt;


&lt;p&gt;Et vous, comment faites vous pour externaliser vos messages d'erreurs et/ou d'exceptions&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2010/04/14/Externaliser-les-messages-d-erreurs-des-exceptions-pour-les-internationaliser-par-exemple#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2010/04/14/Externaliser-les-messages-d-erreurs-des-exceptions-pour-les-internationaliser-par-exemple#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/80</wfw:commentRss>
      </item>
    
  <item>
    <title>Interface ArrayAccess : où des objets que l'on utilise comme des array</title>
    <link>http://blog.phppro.fr/?post/2010/04/09/Interface-ArrayAccess-%3A-ou-des-objets-que-l-on-utilise-comme-des-array</link>
    <guid isPermaLink="false">urn:md5:709ece8d43169a9bb0580d4b817c9edd</guid>
    <pubDate>Fri, 09 Apr 2010 22:26:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Méthodologie</category>
        <category>adapter</category><category>array</category><category>arrayaccess</category><category>mock</category><category>phpunit</category>    
    <description>&lt;p&gt;Cette semaine j'ai fait découvrir l'interface ArrayAccess à un des développeurs Parisiens que je coache.&lt;br /&gt;
Cette fonctionnalité lui a permis d'appréhender facilement les tests unitaires sur du code legacy, je reviens sur cette &quot;expérience&quot;&lt;/p&gt;    &lt;p&gt;Nous travaillons sur une application très populaire du grand public qui vend des produits High Tech, entre autres.&lt;br /&gt;
Cette application est en maintenance évolutive, avec de nouveaux développements plutôt réalisés en PHP.&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;Dans cette application il existe entre autres des objets &quot;entités&quot; qui représentent des &quot;concepts&quot; principalement stockées en base (on peut parler de quelque chose de similaire à un ORM pour le mapping Objet-Relationnel).&lt;br /&gt;
Nous avons une entité Product&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
class Product
{
    protected $id;

    public function setId($id)
    {
        $this-&amp;gt;id = $id;
    }
    public function getId()
    {
        return $this-&amp;gt;id;
    }

    // ... plein d'autres propriétés du Product

    public function uneFonctionQuiFaitLeCafé()
    {
        // ....
    }
}
&lt;/pre&gt;


&lt;p&gt;Notre entité est utilisée par une méthode d'une autre classe, qui liste des produits depuis la base et retourne une liste (array) d'objet Product.&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;Le problème dans notre cas est que nous souhaitions amener une démarche de tests unitaires type TDD (Test Driven Development ou Test-First) sur de nouveaux développements, mais que nous avions déjà pas mal de code, dont la classe Product, qui était déjà développé sans aucun test.&lt;br /&gt;
Notre démarche a donc été de créer une nouvelle classe pour la nouvelle méthode que nous souhaitions créer et de petit à petit porter le code existant dans cette classe au fur et à mesure que nous écrirons les tests unitaires.&lt;br /&gt;
Quand on adopte ce type de démarche (intéressante de mon point de vue), on met rapidement le doigt sur les zones existantes du code qui sont &quot;dépendantes&quot; d'autres classes / méthodes par encore testées, et très rapidement on peut être découragé car on tir un fil et c'est toute la pelotte qui vient avec...&lt;/p&gt;


&lt;p&gt;Pour tenter de garder &quot;espoir&quot; dans cet effort de &quot;portage&quot; et couverture par les tests du code existant, une technique est de &quot;bouchonner&quot; le code existant qui ne nous intéresse pas (encore) grâce à un code / classe simulateur.&lt;br /&gt;
C'est à ce niveau que l'interface ArrayAccess peut s'avérer utile.&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;En effet, voici comment nous utilisons habituellement notre entité Product&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$product = new Product();
$product-&amp;gt;setId(12);
$product-&amp;gt;setPrice(25.12);
...
$description = $product-&amp;gt;getDescription();

$product-&amp;gt;uneFonctionQuiFaitLeCafé();
...
&lt;/pre&gt;


&lt;p&gt;Si la méthode qui fait le café ne nous &quot;intéresse&quot; pas pour l'instant (ou que nous souhaitons la &quot;sortir&quot; de la classe Product in fine, et que pour l'instant nous décidons de ne pas travailler sur les tests de cette méthode), alors il ne nous reste plus que des &quot;getters&quot; et &quot;setters&quot; sur l'objet Product, et finalement que penseriez vous de ceci&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$product = new Product();
$product['id'] = 12;
$product['price'] = 25.12;
...
$description = $product['description'];

$product-&amp;gt;uneFonctionQuiFaitLeCafé();
...
&lt;/pre&gt;


&lt;p&gt;Il est en effet possible d'utiliser ce type de syntaxe si l'objet Product implémente l'interface (native) ArrayAccess&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
class Product implements ArrayAccess
{
    protected $properties = array();

    public function setId($id)
    {
        return $this-&amp;gt;offsetSet('id', $id);
    }
    public function getId()
    {
        return $this-&amp;gt;offsetGet('id');
    }

    // ... plein d'autres propriétés du Product

    public function uneFonctionQuiFaitLeCafé()
    {
        // ....
    }

    // implémentation des méthodes &amp;quot;obligatoires&amp;quot; de l'interface ArrayAccess

    public function offsetSet($offset, $value)
    {
        $this-&amp;gt;properties[$offset] = $value;
        return $value;
    }

    public function offsetExists($offset)
    {
        return isset($this-&amp;gt;properties[$offset]);
    }

    public function offsetUnset($offset)
    {
        unset($this-&amp;gt;properties[$offset]);
    }

    public function offsetGet($offset)
    {
        return isset($this-&amp;gt;properties[$offset]) ? $this-&amp;gt;properties[$offset] : null;
    }
}
&lt;/pre&gt;


&lt;p&gt;Imaginons maintenant que nous nous passions par une &quot;factory&quot; pour créer de nouvelles entités Product&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
// on ne fait plus new Product() mais :

$product = createABlankProduct();
...
&lt;/pre&gt;


&lt;p&gt;Imaginons que nous ne travaillons pas encore sur la méthode qui fait le café mais uniquement que sur les données stockées dans l'entité, à savoir que nous souhaitons tester unitairement une méthode qui &quot;crée&quot; une liste d'entité produit à partir de données en base, c'est à dire qu'elle &quot;rempli&quot; des entités avec les valeurs selon une certaine logique&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
/**
 * @return array of Product
 */
public function listProductByUniversAndPriceInterval($univers, $minPrice, $maxPrice)
{
    // ... sélection des données en base de données via PDO, par exemple, triée dans l'ordre de prix
    $productsData = $this-&amp;gt;listRawProductsDataByUniversAndPriceInterval($univers, $minPrice, $maxPrice);
    // ...
    $products = array();
    foreach($productsData as $rank =&amp;gt; $row) {
        $product = $this-&amp;gt;createABlankProduct();
        $product['id'] = (int)$row['id'];
        $product['price'] = (double)$row['price'];
        $product['universe'] = $univers;
        $product['rank'] = $rank + 1;
        // ... 
        $products[] = $product;
    }
    return $products;
}
&lt;/pre&gt;


&lt;p&gt;Imaginons maintenant que la creation d'un &quot;blank product&quot; et la récupération des données brutes de la base soient déléguées à un objet &quot;adapter&quot; (i.e. délégué de la classe courante)&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
        $productsData = $this-&amp;gt;getAdapter()-&amp;gt;listRawProductsDataByUniversAndPriceInterval($univers, $minPrice, $maxPrice);
        ...
        $product = $this-&amp;gt;getAdapter()-&amp;gt;createABlankProduct();
        ...
&lt;/pre&gt;


&lt;p&gt;Imaginons que la classe courante propose un &quot;setter&quot; qui permette de &quot;changer&quot; l'objet adapter courant&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
    public function setAdapter(MySpecificAdapterInterface $adapter)
    {
        $this-&amp;gt;adapter = $adapter;
        return $this;
    }
&lt;/pre&gt;


&lt;p&gt;Nous avons donc un objet qui doit implémenter l'interface MySpecificAdapterInterface et qui doit donc implémenter une méthode createABlankProduct() qui doit renvoyer une structure qui doit être accessible via la notation array (pour l'instant nous n'utilisons pas la méthode qui fait le café)&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
class MySpecificAdapterStandardVersion implements MySpecificAdapterInterface
{
    public function createABlankProduct()
    {
        return new Product();
    }
    public function listRawProductsDataByUniversAndPriceInterval($univers, $minPrice, $maxPrice)
    {
        // faite ce que vous faisiez avant pour récupérer les données brutes en base ici
        // ...
        return $rows;
    }
}
&lt;/pre&gt;


&lt;p&gt;Imaginons maintenant que nous ayons une version &quot;simulateur&quot; de notre adapter qui l'on pourrait paramétrer pour lui indiquer ce qu'elle doit retourner lorsqu'on appelle une de ces 2 méthodes&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
class MySpecificAdapterMockVersion implements MySpecificAdapterInterface
{
    protected $expectedResult;

    public function createABlankProduct()
    {
        return array();
    }
    public function setExpectedResult($result)
    {
        $this-&amp;gt;expectedResult = $result;
        return $this;
    }

    public function listRawProductsDataByUniversAndPriceInterval($univers, $minPrice, $maxPrice)
    {
        return $this-&amp;gt;expectedResult;
    }
}
&lt;/pre&gt;


&lt;p&gt;Ainsi dans nos tests unitaires nous pourrons facilement &quot;tester&quot; la méthode listProductByUniversAndPriceInterval&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
class MyClassTest extends PHPUnit_Framework_TestCase
{
    public function testListProductByUniversAndPriceIntervalForExistingProductsInDatabaseReturnProductList()
    {
        $mock = new MySpecificAdapterMockVersion();
        $objectToTest = new MyClass();
        $objectToTest-&amp;gt;setAdapter($mock);
        $mock-&amp;gt;setExpectedResult(array(
            array('id'=&amp;gt;10, 'price'=&amp;gt;&amp;quot;11.99&amp;quot;),
            array('id'=&amp;gt;4, 'price'=&amp;gt;&amp;quot;15.99&amp;quot;),
            array('id'=&amp;gt;7, 'price'=&amp;gt;&amp;quot;27.99&amp;quot;),
        ));
        $products = $objectToTest-&amp;gt;listProductByUniversAndPriceInterval('games', 10.0, 30.0);

        // vérification des données du 1er produit

        $this-&amp;gt;assertEquals(11.99, $products[0]['price']);
        $this-&amp;gt;assertEquals(1, $products[0]['rank']);
        $this-&amp;gt;assertEquals(10, $products[0]['id']);
        $this-&amp;gt;assertEquals('games', $products[0]['univers']);

        // vérification des données du 2ème produit

        $this-&amp;gt;assertEquals(15.99, $products[1]['price']);
        $this-&amp;gt;assertEquals(2, $products[1]['rank']);
        $this-&amp;gt;assertEquals(4, $products[1]['id']);
        $this-&amp;gt;assertEquals('games', $products[1]['univers']);

        // vérification des données du 3ème produit

        $this-&amp;gt;assertEquals(27.99, $products[2]['price']);
        $this-&amp;gt;assertEquals(3, $products[2]['rank']);
        $this-&amp;gt;assertEquals(7, $products[2]['id']);
        $this-&amp;gt;assertEquals('games', $products[2]['univers']);
    }
}
&lt;/pre&gt;


&lt;p&gt;Grâce à l'utilisation de array() et de l'adapter mock, dans un premier temps, nous pouvons donc tester facilement la logique de la méthode listProductByUniversAndPriceInterval() sans devoir &quot;porter&quot; tout l'objet Product (qui vient de notre code legacy).&lt;br /&gt;
Maintenant que nous avons portez cette première méthode (voire d'autres), si nous avons besoin de porter d'autres méthodes &quot;qui font le café&quot; de l'entité product nous pouvons alors utiliser l'implémentation de l'interface ArrayAccess présenté au début et les tests unitaires et le contenu de la méthode déjà développé reste toujours valable, à savoir le fait d'utiliser un objet plutôt qu'un array n'a pas d'impact sur le code de la méthode listProductByUniversAndPriceInterval() car notre objet implémente l'interface ArrayAccess qui permet de l'utiliser, quand cela nous intéresse, comme un array() avec des crochets.&lt;/p&gt;


&lt;p&gt;En conclusion, nous pouvons retenir que l'utilisation de l'interface ArrayAccess peut nous permettre de commencer à travailler avec des structures de types array() notamment pour introduire des tests unitaires, puis de migrer sans douleur vers une structure plutôt objet qui sera enrichie par des méthodes &quot;qui font le café&quot; mais qui restera compatible avec la notation array()&lt;/p&gt;


&lt;p&gt;Et vous, dans quels cas utilisez-vous, ou non, l'interface ArrayAccess&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2010/04/09/Interface-ArrayAccess-%3A-ou-des-objets-que-l-on-utilise-comme-des-array#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2010/04/09/Interface-ArrayAccess-%3A-ou-des-objets-que-l-on-utilise-comme-des-array#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/79</wfw:commentRss>
      </item>
    
  <item>
    <title>Comment maîtriser la montée de version d'un framework dont votre application dépend ?</title>
    <link>http://blog.phppro.fr/?post/2008/11/20/Comment-maitriser-la-montee-de-version-d-un-framework-dont-votre-application-depend</link>
    <guid isPermaLink="false">urn:md5:571f90b7a52556ba12d6d67891d8916e</guid>
    <pubDate>Sat, 27 Mar 2010 21:05:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Méthodologie</category>
        <category>framework</category><category>include_path</category>    
    <description>&lt;p&gt;Vous utilisez très certainement un ou plusieurs framework comme fondations de votre projet / application (Symfony, Zend Framework, CakePHP...).
Une des premières évolutions majeures (d'un point de vue technique, mais pas fonctionnel) consiste en la montée de version des frameworks dont vous dépendez. ...&lt;/p&gt;    &lt;p&gt;En effet, les frameworks comme Zend Framework ou Symfony (entre autres) évoluent très vite et de nouvelles fonctionnalités voient le jour très régulièrement.
Vous pourriez être intéressés par utiliser la nouvelles version (ou une version plus récente que la version) d'un de vos frameworks, cependant qu'est-ce qui garantie que votre application continuera à fonctionner&amp;nbsp;?&lt;/p&gt;


&lt;p&gt;Réponse: les tests unitaires et l'include path.&lt;/p&gt;


&lt;p&gt;En effet, les tests unitaires nous permettent de construire petit à petit notre application et de vérifier la non régression aux cours des évolutions. Il est donc très facile d'exécuter les tests unitaires de votre application (si ils existent) en utilisant des versions différentes de votre framework. Pour cela il vous suffit de modifier l'include path.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;installez la version actuelle (utilisée par votre application) du framework dans le répertoire c:/dev/framework/v1&lt;/li&gt;
&lt;li&gt;installez la version plus récente du framework dans le répertoire c:/dev/framework/v2&lt;/li&gt;
&lt;li&gt;modifiez la directive include_path (dans le php.ini) pour y ajouter c:/dev/framework/v1&lt;/li&gt;
&lt;li&gt;exécutez vos tests unitaires&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Il est tout à fait envisageable d'automatiser l'exécution des tests unitaires sur différentes versions du framework si vous utilisez par exemple un serveur d'intégration continue. Ainsi vous pouvez créer un &quot;job&quot; (ou projet selon les outils d'intégration continue) par version de framework et déclencher l'exécution du &quot;build&quot; suite à un commit sur votre outil de gestion de version (i.e. SVN ou Git...), ainsi vous saurez en temps réel quels tests unitaires ne passent pas avec la nouvelle version du framework.&lt;/p&gt;


&lt;p&gt;Je vous conseille très fortement de mettre en place cette technique, à minima quelques temps avant de faire la migration, et au mieux dès le début ou la sortie d'une version plus récente de la librairie que vous utilisez.&lt;/p&gt;


&lt;p&gt;Bien sûr, il faut relativiser l'intérêt de cette technique qui devient totalement inutile si vous n'avez pas de tests unitaires ou si la couverture des tests est non significative (&amp;lt; 90%), il faudra donc travailler d'abord à cette couverture, avant de pouvoir tirer partie de cette technique. Vous pourrez cependant commencer par implémenter les tests unitaires liés aux fonctionnalités basées sur des composants connus pour avoir évolué dans les nouvelles versions du framework.&lt;/p&gt;


&lt;p&gt;Il existe bien entendu d'autres techniques utiles pour vérifier la compatibilité avec de nouvelles versions de frameworks, mais celle-ci est très facile à mettre en oeuvre si vous avez déjà des tests unitaires.&lt;/p&gt;


&lt;p&gt;Et vous quels sont vos techniques pour maîtriser les montées de versions des frameworks dont vous dépendez&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2008/11/20/Comment-maitriser-la-montee-de-version-d-un-framework-dont-votre-application-depend#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2008/11/20/Comment-maitriser-la-montee-de-version-d-un-framework-dont-votre-application-depend#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/22</wfw:commentRss>
      </item>
    
  <item>
    <title>Magento demystified : global picture de la séquence d'exécution d'une requête Magento</title>
    <link>http://blog.phppro.fr/?post/2010/03/24/Magento-demystified-%3A-global-picture-de-la-sequence-d-execution-d-une-requete-Magento</link>
    <guid isPermaLink="false">urn:md5:52ef0dca06fe4d561a17cedb6384c0fc</guid>
    <pubDate>Wed, 24 Mar 2010 09:00:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Architecture</category>
            
    <description>&lt;p&gt;Ces 6 derniers mois, j'ai fait pas mal de R&amp;amp;D autour du produit Magento.&lt;br /&gt;
Je ne vous livre pas ici mes convictions sur le produits, mais juste une cartographie globale de la séquence d'exécution d'une requête Magento.&lt;/p&gt;    &lt;p&gt;&lt;a href=&quot;http://blog.phppro.fr/public/projets/Magento.png&quot;&gt;&lt;img src=&quot;http://blog.phppro.fr/public/projets/.Magento_m.jpg&quot; alt=&quot;Global Picture Séquence Magento&quot; title=&quot;Global Picture Séquence Magento, fév 2010&quot; /&gt;&lt;/a&gt;&lt;/p&gt;


&lt;p&gt;(Analyse basée sur la version Magento Community Edition 1.3.2.4, qui n'est pas toute récente et qui date de 2009)&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2010/03/24/Magento-demystified-%3A-global-picture-de-la-sequence-d-execution-d-une-requete-Magento#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2010/03/24/Magento-demystified-%3A-global-picture-de-la-sequence-d-execution-d-une-requete-Magento#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/75</wfw:commentRss>
      </item>
    
  <item>
    <title>Don't reinvent the wheel ... invent the car !</title>
    <link>http://blog.phppro.fr/?post/2009/10/09/Don-t-reinvent-the-wheel-invent-the-car</link>
    <guid isPermaLink="false">urn:md5:c54d9b3d344dd4fb221edf6c3bf70020</guid>
    <pubDate>Sat, 20 Mar 2010 23:57:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Méthodologie</category>
        <category>adapter</category><category>développeur</category><category>equipe</category><category>pattern</category><category>projet</category><category>robustesse</category>    
    <description>&lt;p&gt;Vous avez déjà croisé des projets / équipes qui décident d'utiliser le dernier framework en vogue&amp;nbsp;? oui, certainement.&lt;br /&gt;
Par contre avez-vous déjà croisé des projets / équipes qui décident d'en changer (de framework) car ils se rendent compte que le choix n'est plus forcément pertinent ou le plus meilleur&amp;nbsp;? moi, non, pourtant c'est bien connu le la version parfaite d'un framework sur tous les sujets et ad vitam eternam n'existe pas...&lt;/p&gt;    &lt;p&gt;J'entends souvent&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;nous on fait du MVC&lt;/li&gt;
&lt;li&gt;nous on fait du design pattern&lt;/li&gt;
&lt;li&gt;nous on utilise des adapters&lt;/li&gt;
&lt;li&gt;nous on utilise des couches d'abstraction&lt;/li&gt;
&lt;li&gt;nous on utilise certains composant du framework X&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;li&gt;nous &amp;lt;whatever the generic term you want here&amp;gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mais quand on creuse, on se rend compte qu'il fallait plutôt entendre&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&quot;nous notre application est fortement couplée au / motorisée par le framework X&quot;&lt;/li&gt;
&lt;li&gt;&quot;nous on utilise l'implémentation MVC du framework X&quot;&lt;/li&gt;
&lt;li&gt;&quot;nous on utilise certaines implémentations de certains designs patterns proposés par le framework X&quot;&lt;/li&gt;
&lt;li&gt;&quot;nous on aimerait bien utiliser le composant XYZ du framework X mais on sait pas comment ca pourrait nous être utile concrètement&quot;&lt;/li&gt;
&lt;li&gt;&quot;nous on comprend pas tout dans le framework X mais il parait que c'est bien alors on l'utilise&quot;&lt;/li&gt;
&lt;li&gt;&quot;nous on utilise le mécanisme d'abstraction du composant W du framework X&quot;&lt;/li&gt;
&lt;li&gt;&quot;nous on met notre code dans un fichier Controller.php en utilisant les helpers et les vues du framework X&quot;&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;li&gt;nous &amp;lt;whatever the framework-specific implementation you want here&amp;gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Et les 40 lignes de code dans la méthode indexAction() ca vous rappelle quelque chose ?&lt;br /&gt;
Et les X heures / jours / semaines passés à se demander quand et comment on va faire pour migrer tout notre code application de la version 1.5.3 du framework X vers la toute nouvelle version 2.0.0 qui déchire et qui n'est plus totalement compatible sur bien des aspects mais tout le monde dit qu'il faut l'utiliser, ca ne vous rappelle pas quelque chose ?&lt;br /&gt;
Et la complexité intrinsèque de l'architecture et design patterns des composants du framework X que vous vous &quot;obligez&quot; à utiliser parce que tout le monde dit que maintenant c'est comme et c'est pas autrement en PHP, ca ne vous rappelle pas quelque chose&amp;nbsp;?
Vous êtes certainement comme moi, développeur, fier de montrer que je utiliser la nouvelle version du dernier framework à la mode sur mon projet. Mais pour les 10 autres projets sur lesquels mes collègues travaillent, tout le monde ne sera peut être pas aussi enthousiaste de perdre 1 semaine ou 2 semaine de développement pour réécrire tous les &quot;controller&quot; et retranscrire tous les &quot;formulaires&quot; avec le dernier composant à la mode...&lt;/p&gt;


&lt;p&gt;Plus concrètement mon point de vue est le suivant&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&quot;re-&quot; développer ce que d'autres on déjà développé, rendu mature et éprouvé, c'est bof&lt;/li&gt;
&lt;li&gt;les designs patterns c'est bien&lt;/li&gt;
&lt;li&gt;les frameworks c'est (presque) bien aussi&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;mais&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;utiliser un design pattern sans le comprendre c'est moins bien&lt;/li&gt;
&lt;li&gt;utiliser un framework sans comprendre les &quot;problématiques&quot; sous-jacentes auquel il répond c'est encore moins bien (j'ai pas dit &quot;c'est mal&quot;, mais j'aurais pu...)&lt;/li&gt;
&lt;li&gt;insérer 5 (ou 10, 50 ou 100 ...) lignes de code &quot;spécifique framework X&quot; pour implémenter un composant XYZ du framework X sans le cloisonner en dehors de votre code applicatif / métier ... ca c'est mal&amp;nbsp;!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Pourquoi être &quot;trop / très&quot; couplé avec un framework c'est mal&amp;nbsp;? (d'après moi)&lt;/h2&gt;


&lt;h3&gt;&quot;Moi, oui, je mélange les choux et les carottes&quot;&lt;/h3&gt;


&lt;p&gt;Un framework c'est souvent technique, votre logique applicative elle est souvent plutôt métier / fonctionnelle, le framework évoluera il faudra faire un effort pour savoir quel ligne concerne le framework et quelle ligne concerne de la logique métier invariante, cela peut être long&lt;/p&gt;


&lt;h3&gt;&quot;Moi, oui, j'ai peur de monter de version de framework&quot;&lt;/h3&gt;


&lt;p&gt;Un framework c'est souvent imparfait, en perpétuel évolution (surtout les Open Source, pour notre grand bonheur), donc souvent 3 mois après avoir implémenté la dernière version, une nouvelle version sort et vous êtes tiraillé entre migrer au bout de 6-9 mois vers cette version ou ... tout réécrire votre application en v2&amp;nbsp;! Pourquoi attendre 6-9 mois&amp;nbsp;? Pourquoi réécrire la logique métier de l'application alors qu'en 6-9 mois votre fonctionnel / métier n'a pas forcément évolué tant que ça, lui&amp;nbsp;?&lt;/p&gt;


&lt;h3&gt;&quot;Moi, je suis certifié framework X, je sais bien l'utiliser, je connais tous les composants, et je développe toutes mes applis avec&quot;&lt;/h3&gt;


&lt;p&gt;Et votre collègue aussi il connait aussi &quot;parfaitement&quot; ce truc si génial&amp;nbsp;? Et lui aussi il sait &quot;bien&quot; l'utiliser&amp;nbsp;? Et lui aussi il a compris tous les concepts complexes de patterns qui y sont implémentés&amp;nbsp;? Non&amp;nbsp;? Donc il a besoin de vous&amp;nbsp;? Oui&amp;nbsp;! Bravo, vous avez trouvé un moyen de ne pas vous faire virer, par contre pour ce qui est de la maintenabilité (i.e. par quelqu'un qui n'a pas vos connaissances), la simplicité (pour quelqu'un qui veut aller droit à la solution technique la plus simple pour répondre aux besoins)  c'est peut être moins rose...&lt;/p&gt;


&lt;h3&gt;&quot;Moi je fais des DAO et j'ai une couche d'abstraction de la base de données avec modélisation de mes tables en classe...&quot;&lt;/h3&gt;


&lt;p&gt;Et si votre source de données changent et vous fusionnez 2 tables en une ou scinder 1 table en 2&amp;nbsp;? Si vous décidez de stocker via l'appel de webservices plutôt que dans une base locale&amp;nbsp;? Si vous changez de moteur de base de données (mysql =&amp;gt; oracle, sqlite =&amp;gt; mysql, mysql =&amp;gt; sql server...), vous êtes sûr que votre couche d'abstraction sera si parfaite que ça&amp;nbsp;? Nous c'est vrai, à vous ca ne vous arrivera pas tout ça...&lt;/p&gt;


&lt;h3&gt;&quot;Moi, j'utilise a fond les composants du framework X, de toute façon il est bien fait, ca ne peut qu'être bon pour moi&quot;&lt;/h3&gt;


&lt;p&gt;Pensez-vous sincèrement que ce soit la qualité du framework ou la qualité de &quot;l'usage&quot; que vous en avez qui garantira maintenabilité / évolutivité / robustesse / performance / ...&amp;nbsp;? Personnellement, je ne parierai pas QUE sur la qualité du framework...&lt;/p&gt;


&lt;p&gt;Pour conclure, je dirais que les frameworks c'est bien, mais maîtriser l'usage que l'on a des frameworks et être conscient que l'ON DEVELOPPE DU CODE pour mettre en vie le framework c'est beaucoup mieux...
De nos jours, il n'est plus question de &quot;réinventer&quot; la façon d'implémenter tel ou tel design pattern (i.e. &quot;la roue / the wheel&quot; dans mon analogie) mais plutôt d'&quot;inventer&quot; le code applicatif et la façon dont tout ça va se mettre en jeu pour rendre une application fonctionnelle ET UTILE (i.e. &quot;la voiture / the car&quot;), ne perdez pas de vue, que comme des ingrédients et ustensiles pour faire un gâteau en cuisine, un framework reste un socle / outil / matériel, mais la réussite finale du gâteau (i.e. de l'appli) dépend du cuisinier (le développeur) et de sa capacité à être rigoureux, organisé, méthododiques, homogène, rationnel...&lt;/p&gt;


&lt;p&gt;Les frameworks, c'est bon&amp;nbsp;! mangez-en, mais sans que ça se voit&amp;nbsp;!&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/10/09/Don-t-reinvent-the-wheel-invent-the-car#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/10/09/Don-t-reinvent-the-wheel-invent-the-car#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/60</wfw:commentRss>
      </item>
    
  <item>
    <title>Retour d'expérience sur les métriques qualité d'une application Symfony d'un de mes clients</title>
    <link>http://blog.phppro.fr/?post/2010/03/17/Retour-d-experience-sur-les-metriques-qualite-d-une-application-Symfony-d-un-de-mes-clients</link>
    <guid isPermaLink="false">urn:md5:a748b1c00b0c7f0080148b9d31a2bbfd</guid>
    <pubDate>Sat, 20 Mar 2010 09:11:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Analyse</category>
        <category>audit</category><category>métriques</category><category>symfony</category>    
    <description>&lt;p&gt;En novembre dernier je suis intervenu chez un client pour améliorer les pratiques de développements sur une application basée sur Symfony.&lt;br /&gt;
A cette occasion j'ai réalisé un audit de l'application, voici quelques métriques à noter sur cette application (il ne s'agit ni d'une tendance ni d'une moyenne, je vous livre ici les métriques brutes pour que vous puissiez comparer).&lt;br /&gt;&lt;/p&gt;    &lt;p&gt;Il est à noter que le développement était relativement &quot;habituel&quot; pour du code applicatif Symfony, même si de nombreuses amélioration ont été envisagée par la suite dans la pratique lié au développement et notamment pour la maintenance et la réutilisabilité.&lt;/p&gt;


&lt;p&gt;&lt;a href=&quot;http://blog.phppro.fr/public/projets/symfony-client-project-metrics.jpg&quot;&gt;&lt;img src=&quot;http://blog.phppro.fr/public/projets/.symfony-client-project-metrics_m.jpg&quot; alt=&quot;Exemples de métriques qualité sur un projet client utilisant Symfony&quot; title=&quot;Exemples de métriques qualité sur un projet client utilisant Symfony, fév 2010&quot; /&gt;&lt;/a&gt;&lt;/p&gt;


&lt;p&gt;Et vous quelles sont les valeurs habituelles de métriques qualité que vous rencontrez sur vos différents projets&amp;nbsp;? (Symfony ou autre)&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2010/03/17/Retour-d-experience-sur-les-metriques-qualite-d-une-application-Symfony-d-un-de-mes-clients#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2010/03/17/Retour-d-experience-sur-les-metriques-qualite-d-une-application-Symfony-d-un-de-mes-clients#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/74</wfw:commentRss>
      </item>
    
  <item>
    <title>Marathon de l'industrialisation : où comment outiller vos troupes en les motivant</title>
    <link>http://blog.phppro.fr/?post/2010/03/13/Marathon-de-l-industrialisation-%3A-ou-comment-outiller-vos-troupes-en-les-motivant</link>
    <guid isPermaLink="false">urn:md5:37fbf7efa096eb1a1ee2b20e05160a0d</guid>
    <pubDate>Sat, 13 Mar 2010 09:00:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Méthodologie</category>
            
    <description>Depuis quelques temps j'ai mis en place une activité spéciale que je propose à mes clients : Le Marathon de l'Industrialisation.&lt;div&gt;L'idée: focaliser toute l'équipe de développement au même moment sur un temps court, pour améliorer significativement les pratiques, et ce grâce à l'adoption de nouveaux (variable en fonction des clients) outils et techniques de développement. Je vous livre ici un de mes supports utilisé en Juin 2009 chez un de mes clients.&lt;/div&gt;    &lt;div style=&quot;width:425px;text-align:left&quot; id=&quot;__ss_3240296&quot;&gt;&lt;a style=&quot;font:14px Helvetica,Arial,Sans-serif;display:block;margin:12px 0 3px 0;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/ohoareau/marathon-de-l-industrialisation&quot; title=&quot;Marathon De L Industrialisation&quot;&gt;Marathon De L Industrialisation&lt;/a&gt;&lt;object style=&quot;margin:0&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=marathon-de-l-industrialisation-100221161126-phpapp01&amp;amp;stripped_title=marathon-de-l-industrialisation&quot; /&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot; /&gt;&lt;param name=&quot;allowScriptAccess&quot; value=&quot;always&quot; /&gt;&lt;div style=&quot;font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;&quot;&gt;View more &lt;a style=&quot;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/&quot;&gt;presentations&lt;/a&gt; from &lt;a style=&quot;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/ohoareau&quot;&gt;PHPPRO&lt;/a&gt;.&lt;/div&gt;&lt;/object&gt;&lt;/div&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2010/03/13/Marathon-de-l-industrialisation-%3A-ou-comment-outiller-vos-troupes-en-les-motivant#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2010/03/13/Marathon-de-l-industrialisation-%3A-ou-comment-outiller-vos-troupes-en-les-motivant#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/73</wfw:commentRss>
      </item>
    
  <item>
    <title>Modèle PDF : où comment éviter de perdre son temps</title>
    <link>http://blog.phppro.fr/?post/2010/03/10/Modele-PDF-%3A-ou-comment-eviter-de-perdre-son-temps</link>
    <guid isPermaLink="false">urn:md5:9ba4c2d9bb58baebf86fcc134a1c78b6</guid>
    <pubDate>Wed, 10 Mar 2010 22:49:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Méthodologie</category>
        <category>enrichissement</category><category>pdf</category><category>xml</category><category>zend_pdf</category>    
    <description>&lt;p&gt;Chez nombre de mes clients la &quot;génération de PDF&quot; est un vrai sujet / enjeux. Mais que veux-t-on vraiment générer&amp;nbsp;?&lt;/p&gt;    &lt;p&gt;Dans nos applications, on doit souvent générer des PDF&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pour fournir une preuve / un récapitulatif (imprimable)&lt;/li&gt;
&lt;li&gt;pour transmettre une pièce justificative à un tiers&lt;/li&gt;
&lt;li&gt;pour archivage&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Il est très souvent hors de question d'afficher des données brutes dans un PDF, l'entreprise à une identité visuelle, le PDF doit forcément la respecter.&lt;br /&gt;
On se retrouve donc très très souvent à devoir fricoter avec le sujet des chartes graphiques, du design et autres images et couleurs dans nos PDF.&lt;/p&gt;


&lt;p&gt;Dans un nombre non négligeable de cas (ce n'est pas tout le temps vrai, mais souvent quand même), nous souhaitons formater un ensemble de type de données connues dans un format plus lisible via le PDF.&lt;br /&gt;
Voici ce que nous &quot;maitrisons&quot;&amp;nbsp;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;la charte graphique attendue (le gabarit, ...)&lt;/li&gt;
&lt;li&gt;la liste des données à formater&lt;/li&gt;
&lt;li&gt;l'emplacement des données, leur taille maximum et leur style graphique (police, taille, couleur...)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En dehors des données que nous n'avons pas à l'avance (celles qui arrivent au moment de la génération), il peut (dans un grand nombre de cas) être envisageable de créer un &quot;modèle&quot; de document (i.e. PDF) vide de toutes données (en tout cas celles qui sont dynamiques), et utiliser pour cela nos bons vieux logiciels de design graphique (ou équivalent), par exemple ... Word ;) ou OpenOffice Writer (il y a mieux, mais vous serez surpris de la facilité déconcertante à créer des documents au format A4 pour ce type de besoin avec word ou ooo writer), si vous utilisez une ancienne version de Word, un bon vieux PDFCreator (plugin gratuit pour windows) vous permettra d'&quot;imprimer&quot; le document au format PDF.&lt;br /&gt;
Nous reviendrons plus tard sur l'intérêt d'utiliser des outils bureautiques pour la génération du modèle.&lt;/p&gt;


&lt;p&gt;Une fois votre &quot;modèle&quot; disponible, il devient du coup tout de suite envisageable de le &quot;remplir&quot; avec les données dynamiques, on peut concevoir que cela est infiniment plus simple à réaliser que de créer l'ensemble du document PDF avec Zend_PDF ou autre qui fournissent uniquement que des primitives graphiques (dessiner une ligne, un rond, une image, du texte...).&lt;br /&gt;
Vous conviendrez que placer du texte ou une image d'une certaine taille à un certain endroit (positionnement absolu) est relativement simple avec n'importe quelle librairie PDF PHP.&lt;/p&gt;


&lt;p&gt;Du coup, pourquoi ne pas &quot;décrire&quot; le positionnement des données sur le document PDF dans un format moins &quot;technique&quot; que des instructions PHP&amp;nbsp;? On pourrait avoir par exemple une description XML du type&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;document&amp;gt;
	&amp;lt;styles&amp;gt;
		&amp;lt;font id=&amp;quot;default&amp;quot; family=&amp;quot;Helvetica&amp;quot; bold=&amp;quot;true&amp;quot; size=&amp;quot;10&amp;quot;/&amp;gt;
	&amp;lt;/styles&amp;gt;
	&amp;lt;pages&amp;gt;
		&amp;lt;page number=&amp;quot;0&amp;quot;&amp;gt;
			&amp;lt;!-- numéro de dossier --&amp;gt;
			&amp;lt;text top=&amp;quot;670&amp;quot; left=&amp;quot;260&amp;quot;&amp;gt;100001001&amp;lt;/text&amp;gt;
			&amp;lt;!-- nom / prénom --&amp;gt;
			&amp;lt;text top=&amp;quot;604&amp;quot; left=&amp;quot;180&amp;quot;&amp;gt;Hoareau, Olivier&amp;lt;/text&amp;gt;
			&amp;lt;!-- titre / civilité --&amp;gt;
			&amp;lt;text top=&amp;quot;588&amp;quot; left=&amp;quot;180&amp;quot;&amp;gt;M.&amp;lt;/text&amp;gt;
			&amp;lt;!-- date de naissance --&amp;gt;
			&amp;lt;text top=&amp;quot;572&amp;quot; left=&amp;quot;180&amp;quot;&amp;gt;07/12/1901&amp;lt;/text&amp;gt;
			&amp;lt;!-- adresse --&amp;gt;
			&amp;lt;text top=&amp;quot;505&amp;quot; left=&amp;quot;70&amp;quot;&amp;gt;8, rue de la colline
33540 GRADIGNAN
France&amp;lt;/text&amp;gt;
			&amp;lt;!-- mobile --&amp;gt;
			&amp;lt;text top=&amp;quot;395&amp;quot; left=&amp;quot;180&amp;quot;&amp;gt;0601020304&amp;lt;/text&amp;gt;
			&amp;lt;!-- email --&amp;gt;
			&amp;lt;text top=&amp;quot;380&amp;quot; left=&amp;quot;180&amp;quot;&amp;gt;olivier@phppro.fr&amp;lt;/text&amp;gt;
			&amp;lt;!-- optionnel: statut --&amp;gt;
			&amp;lt;image top=&amp;quot;650&amp;quot; left=&amp;quot;390&amp;quot; width=&amp;quot;190&amp;quot; height=&amp;quot;109&amp;quot; file=&amp;quot;refus.png&amp;quot;/&amp;gt;
		&amp;lt;/page&amp;gt;
	&amp;lt;/pages&amp;gt;
&amp;lt;/document&amp;gt;
&lt;/pre&gt;


&lt;p&gt;Grâce à SimpleXML et à, par exemple, Zend_PDF, il est très facile en quelques lignes de code de &quot;dessiner&quot; les éléments text et image définis dans la description xml sur un PDF existant (Zend_PDF permet de charger un PDF existant et de le modifier, ce que ne permettent pas toutes les librairies PDF, c'est une fonctionnalité essentielle dans cette technique).&lt;/p&gt;


&lt;p&gt;Pour permettre une génération avec des données dynamiques, on peut très bien imaginer la génération à la volée de la description xml à partir de données de la base de données via PHP/MySQL par exemple. Il devient ainsi tout à fait envisageable de créer une &quot;vue&quot; (voir Pattern MVC) qui contient un modèle dynamique de la description xml&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php

echo '&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; ?&amp;gt;'.&amp;quot;
&amp;quot;;
?&amp;gt;

&amp;lt;document&amp;gt;
	&amp;lt;styles&amp;gt;
		&amp;lt;font id=&amp;quot;default&amp;quot; family=&amp;quot;Helvetica&amp;quot; bold=&amp;quot;true&amp;quot; size=&amp;quot;10&amp;quot;/&amp;gt;
	&amp;lt;/styles&amp;gt;
	&amp;lt;pages&amp;gt;
		&amp;lt;page number=&amp;quot;0&amp;quot;&amp;gt;
			&amp;lt;!-- numéro de dossier --&amp;gt;
			&amp;lt;text top=&amp;quot;670&amp;quot; left=&amp;quot;260&amp;quot;&amp;gt;&amp;lt;?php echo $this-&amp;gt;dossier ?&amp;gt;&amp;lt;/text&amp;gt;
			&amp;lt;!-- nom / prénom --&amp;gt;
			&amp;lt;text top=&amp;quot;604&amp;quot; left=&amp;quot;180&amp;quot;&amp;gt;&amp;lt;?php echo strtoupper($this-&amp;gt;nom) ?&amp;gt;, &amp;lt;?php echo ucfirst($this-&amp;gt;prenom) ?&amp;gt;&amp;lt;/text&amp;gt;
			&amp;lt;!-- titre / civilité --&amp;gt;
			&amp;lt;text top=&amp;quot;588&amp;quot; left=&amp;quot;180&amp;quot;&amp;gt;&amp;lt;?php echo strtoupper($this-&amp;gt;titre) ?&amp;gt;&amp;lt;/text&amp;gt;
			&amp;lt;!-- date de naissance --&amp;gt;
			&amp;lt;text top=&amp;quot;572&amp;quot; left=&amp;quot;180&amp;quot;&amp;gt;&amp;lt;?php echo $this-&amp;gt;dateNaissance ?&amp;gt;&amp;lt;/text&amp;gt;
			&amp;lt;!-- adresse --&amp;gt;
			&amp;lt;text top=&amp;quot;505&amp;quot; left=&amp;quot;70&amp;quot;&amp;gt;&amp;lt;?php echo $this-&amp;gt;adresse . &amp;quot;
&amp;quot; . $this-&amp;gt;codePostal . &amp;quot; &amp;quot; . strtoupper($this-&amp;gt;ville) . &amp;quot;
&amp;quot; . strtoupper($this-&amp;gt;paysLogement) ?&amp;gt;&amp;lt;/text&amp;gt;
			&amp;lt;!-- mobile --&amp;gt;
			&amp;lt;text top=&amp;quot;395&amp;quot; left=&amp;quot;180&amp;quot;&amp;gt;&amp;lt;?php echo $this-&amp;gt;mobile?&amp;gt;&amp;lt;/text&amp;gt;
			&amp;lt;!-- email --&amp;gt;
			&amp;lt;text top=&amp;quot;380&amp;quot; left=&amp;quot;180&amp;quot;&amp;gt;&amp;lt;?php echo $this-&amp;gt;email?&amp;gt;&amp;lt;/text&amp;gt;
			&amp;lt;!-- adresse logement --&amp;gt;
			&amp;lt;text top=&amp;quot;305&amp;quot; left=&amp;quot;70&amp;quot;&amp;gt;&amp;lt;?php echo $this-&amp;gt;adresseLogement . &amp;quot;
&amp;quot; . $this-&amp;gt;codePostalLogement . &amp;quot; &amp;quot; . strtoupper($this-&amp;gt;villeLogement) . &amp;quot;
&amp;quot; . strtoupper($this-&amp;gt;paysLogement)?&amp;gt;&amp;lt;/text&amp;gt;
			&amp;lt;!-- optionnel: statut --&amp;gt;
			&amp;lt;required property=&amp;quot;statut&amp;quot;&amp;gt;
				&amp;lt;image top=&amp;quot;650&amp;quot; left=&amp;quot;390&amp;quot; width=&amp;quot;190&amp;quot; height=&amp;quot;109&amp;quot; file=&amp;quot;&amp;lt;?php echo dirname(__FILE__).'/images/'.($this-&amp;gt;statut==='refusé' ? 'ko' : ($this-&amp;gt;statut==='accepté' ? 'ok' : 'unknown')).'.png' ?&amp;gt;&amp;quot;/&amp;gt;
			&amp;lt;/required&amp;gt;
		&amp;lt;/page&amp;gt;
		&amp;lt;page&amp;gt;
			&amp;lt;text top=&amp;quot;400&amp;quot; left=&amp;quot;300&amp;quot;&amp;gt;Bonjour !&amp;lt;/text&amp;gt;
			&amp;lt;text top=&amp;quot;600&amp;quot; left=&amp;quot;200&amp;quot;&amp;gt;Hello &amp;lt;?php echo strtoupper($this-&amp;gt;nom)?&amp;gt; !&amp;lt;/text&amp;gt;
		&amp;lt;/page&amp;gt;
	&amp;lt;/pages&amp;gt;
&amp;lt;/document&amp;gt;
&lt;/pre&gt;


&lt;p&gt;Vous noterez au passage, la possibilité d'ajouter des pages sur un pdf, en rajoutant tout simplement des balises &amp;lt;page&amp;gt;, si la page n'existe pas dans le pdf, il est envisageable de la créer à la volée.&lt;/p&gt;


&lt;p&gt;En résumé, il peut être très intéressant d'&quot;enrichir&quot; un fichier PDF existant avec des données plutôt que de créer le fichier PDF depuis zéro avec la même librairie.&lt;br /&gt;
Enfin, revenons un peu sur &quot;la création avec word (and Co.)&quot; du modèle. Pourquoi utiliser un logiciel &quot;bureautique&quot; pour créer le modèle&amp;nbsp;? Pour le faire faire par les autres&amp;nbsp;! Si vous êtes développeur, vous n'êtes pas graphiste (certains le sont, pas la majorité loin de là), laissez le travail de création graphique à des spécialistes, voir à des fonctionnels qui savent ce qu'ils souhaitent (et surtout qui passeront du temps à positionner les éléments, à faire des compromis, à changer les couleurs...).&lt;/p&gt;


&lt;p&gt;Et vous quels sont vos techniques pour générer du PDF&amp;nbsp;? Vous passez par du HTML/CSS&amp;nbsp;? Quelles sont vos douleurs&amp;nbsp;?&lt;/p&gt;


&lt;p&gt;Bonus: faire des tests unitaires sur du XML, c'est très très facile, pensez-y quand vous souhaiterez faire des tests (unitaires) sur vos PDF...&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2010/03/10/Modele-PDF-%3A-ou-comment-eviter-de-perdre-son-temps#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2010/03/10/Modele-PDF-%3A-ou-comment-eviter-de-perdre-son-temps#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/72</wfw:commentRss>
      </item>
    
  <item>
    <title>Entretien avec un développeur et un chef de projet</title>
    <link>http://blog.phppro.fr/?post/2010/03/06/Entretien-avec-un-developpeur-et-un-chef-de-projet</link>
    <guid isPermaLink="false">urn:md5:c8283197caebdf3474e60fb7a4652eaa</guid>
    <pubDate>Sat, 06 Mar 2010 21:11:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Méthodologie</category>
        <category>client</category><category>développeur</category><category>entretien</category><category>roadmap</category>    
    <description>&lt;p&gt;Voici le déroulé d'un entretien que j'ai animé en fin d'année dernière dans une petite entreprise qui fait du développement Symfony.&lt;br /&gt;
Mon objectif était de comprendre leurs pratiques (de développement), les outils qu'ils utilisent et surtout d'avoir leur ressenti sur leur niveau d'industrialisation et les problèmes qu'ils rencontrent.&lt;br /&gt;
Cet entretien téléphonique m'a permi de bien préparé la journée &quot;marathon&quot; d'industrialisation que j'ai effectuée chez eux le lendemain.&lt;/p&gt;    &lt;ul&gt;
&lt;li&gt;A quel systèmes d'exploitation êtes vous confrontés (et sur quels environnements)&amp;nbsp;?&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Donnez-moi dans votre quotidien la part de (en 1/4, 1/3, 1/2):
&lt;ul&gt;
&lt;li&gt;développement&lt;/li&gt;
&lt;li&gt;tâches techniques&lt;/li&gt;
&lt;li&gt;réunions&lt;/li&gt;
&lt;li&gt;autres&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Quelle est l'activité qui vous prend le plus de temps au quotidien&amp;nbsp;?&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Citez moi les 3 logiciels que vous utilisez le plus au quotidien (notamment pour le développement)&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Décrivez moi en quelques mots l'utilité des applications symfony que vous développez&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Quelles sont vos douleurs actuellement pour mener à bien vos activités: ce qui vous fait perdre du temps en dev, en déploiement en prod...&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Ou en êtes vous dans la roadmap produit (début, milieu, fin)&amp;nbsp;?&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Votre sentiment sur la qualité du produit (d'abord du service rendu à vos clients, ensuite du code)&amp;nbsp;?&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Votre niveau de connaissance dans le framework Symfony:
&lt;ul&gt;
&lt;li&gt;0 = on fait faire les développements par d'autres&lt;/li&gt;
&lt;li&gt;1 = on fait en cherchant systématiquement dans la doc/internet&lt;/li&gt;
&lt;li&gt;2 = on connait pas encore tous les concepts mais globalement on s'en sort sans perdre trop de temps&lt;/li&gt;
&lt;li&gt;3 = on est experts&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Décrivez moi comment vous vous y prenez pour&amp;nbsp;: corriger un bug détecté en production, développer une nouvelle fonctionnalité planifiée, fournir le même service à un nouveau client&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Quelles opérations à réaliser pour mettre en production (depuis un dev jusqu'à l'utilisation possible par un client en production )&amp;nbsp;?&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Donnez moi la liste des 5 ou 10 commandes en ligne de commande que vous utilisez le plus au quotidien&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Quels sont les outils que vous avez envie d'utiliser et que n'utilisez pas encore&amp;nbsp;?&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Quels sont les outils que vous ne voulez pas utiliser, ou que vous fuyez volontairement&amp;nbsp;?&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Si vous deviez utiliser un outil pour les tests unitaires ce serait&amp;nbsp;? OK pour utiliser PHPUnit pour les tests unitaires&amp;nbsp;?&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Si vous deviez utiliser un outil pour le déploiement ce serait&amp;nbsp;? OK pour utiliser Phing pour le package et le déploiement, plutôt que les outils SF (trop liés à SF ?)&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Qu'attendez vous de mon intervention&amp;nbsp;?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Questions adressées au Patron&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Quelles sont les douleurs actuellement&amp;nbsp;?&lt;/li&gt;
&lt;li&gt;Où en êtes-vous dans la roadmap produit&amp;nbsp;?&lt;/li&gt;
&lt;li&gt;Votre sentiment sur la qualité du produit (d'abord du service rendu à vos clients, ensuite du code)&amp;nbsp;?&lt;/li&gt;
&lt;li&gt;Qelles sont les prochains grand axes d'évolutions&amp;nbsp;?&lt;/li&gt;
&lt;li&gt;Quid du besoin d'exposer des services web dans le futur&amp;nbsp;?&lt;/li&gt;
&lt;li&gt;Votre position par rapport à l'ajax ou a flash / flex&amp;nbsp;?&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Et vous comment menez-vous des entretiens de début de mission avec vos clients&amp;nbsp;? avez-vous d'autres pratiques pour appréhender le contexte&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2010/03/06/Entretien-avec-un-developpeur-et-un-chef-de-projet#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2010/03/06/Entretien-avec-un-developpeur-et-un-chef-de-projet#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/71</wfw:commentRss>
      </item>
    
  <item>
    <title>Le Mode Hébergé (ou Application Service Provider) : où comment changer le business model de votre client</title>
    <link>http://blog.phppro.fr/?post/2010/03/03/Le-Mode-Heberge-ou-Application-Service-Provider-%3A-ou-comment-changer-le-business-model-de-votre-client</link>
    <guid isPermaLink="false">urn:md5:9cb373ad8a505d1826a4cbb13d274e43</guid>
    <pubDate>Wed, 03 Mar 2010 09:00:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Architecture</category>
        <category>base de données</category><category>mysql</category><category>php</category><category>projet</category>    
    <description>&lt;p&gt;J'ai récemment répondu à un appel d'offres concernant la conception et le développement d'une base de données centralisée sur des données géographiques, je livre ici quelques pensées et remarques liées à cette réponse&lt;/p&gt;    &lt;p&gt;Mon (futur) client est un organisme qui réalise des cartographie de population.&lt;br /&gt;
Il utilise des données géographiques pour faire des filtrage, aggregation, recoupement et cartographie tout ceci est avec un SIG type ArcView.&lt;/p&gt;


&lt;p&gt;Récemment il a décidé de s'outiller un peu plus car il travaille malheureusement encore avec Access, entre autres, pour faire ses traitements et les fichiers de données peuvent atteindre 5 millions de lignes...&lt;/p&gt;


&lt;p&gt;Il s'est dit que monter une vraie base de données serait peut être plus qu'intéressant pour lui.&lt;br /&gt;
J'ai donc tout naturellement pensé au couple PHP/MySQL pour créer une application simple d'emploi qui propose un formulaire non technique (liste déroulante) de sélection/filtrage qui traduit et génère une requête SQL complexe.&lt;/p&gt;


&lt;p&gt;Qui dit PHP et MySQL dit serveur, idéalement pas sur un poste bureautique, surtout quand les traitements d'insertion en base doivent travailler sur 5 millions de lignes.&lt;br /&gt;
Cette structure n'étant pas spécialisé dans l'informatique, l'hébergement d'un serveur (qui aurait été sous Windows 2003 pour sur...) aurait dû être nécessaire sur site (pas d'informaticien de métier sur place a priori) avec les SLA que vous pouvez imaginer (non acceptable...).&lt;br /&gt;
J'ai donc tout naturellement pensé à l'hébergement de cette application sur un (en fait plusieurs) serveurs dédiés en location chez un hébergeur populaire sur le marché.&lt;br /&gt;
Jusque là, rien d'extraordinaire.&lt;/p&gt;


&lt;p&gt;Et bien si, en fait cela change tout pour mon client.&lt;br /&gt;
En effet, jusqu'alors les traitements étaient fait chez lui et donc utilisables par lui et ses collègues uniquement, mais il se trouve que ce type de traitement est réalisé par d'autres structures (pas vraiment concurrente mais travaillant sur des périmètre géographique différents), mais sur les mêmes données (c'est pour ça que ca fait 5 millions de lignes).&lt;br /&gt;
Si l'application est disponible via internet, mon client peut aisément y accéder depuis ses locaux ou en mobilité, mais ses partenaires et homologues aussi !&lt;br /&gt;
Le budget que mon client va dépenser pour la réalisation de cette application peut probablement être financé pour partie par une location qu'il proposerait à ses partenaires et homologues (évolution fonctionnelle pour eux qui pourront accéder plus régulièrement à l'information traitées et même faire baisser leur coût car ils n'auront plus à réaliser eux mêmes certaines de ces opérations).&lt;/p&gt;


&lt;p&gt;Si mon client avait choisi la solution &quot;serveur interne&quot;, il aurait eu plus de difficulté à &quot;monétiser&quot; ce service auprès de ces partenaires (problème de garantie de disponiblité de service et surtout d'accès puisqu'il faudrait adresse ip fixe et tout le toutim dans ces locaux, ce qui n'est pas impossible soit...).
Autre détails non négligeables, l'insertion des données peut prendre plusieurs heures, voire plus et la volumétrie est historisée tous les ans, ce qui peut engendrer de gros volumes (plusieurs dizaines de millions de lignes), et la capacité à passer de 1 à 2 puis 3 puis n serveurs (en passant sur Amazone EC2 ou autres) est un plus indéniable qui lui aurait couté beaucoup plus cher en interne et qui aurait été beaucoup moins réaliste (ou stocker ces serveurs ?).&lt;/p&gt;


&lt;p&gt;Pour les internautes avides de techniques, voici quelques schémas sur ma réponse (anonymisé), il s'agit d'une architecture relativement simple, le contexte n'en impose pas de plus compliqué à priori.&lt;/p&gt;


&lt;p&gt;&lt;a href=&quot;http://blog.phppro.fr/public/projets/bdd-archi.jpg&quot;&gt;&lt;img src=&quot;http://blog.phppro.fr/public/projets/.bdd-archi_s.jpg&quot; alt=&quot;Archi BDD&quot; title=&quot;Archi BDD, fév 2010&quot; /&gt;&lt;/a&gt;&lt;/p&gt;


&lt;p&gt;Le reverse proxy sera réalisé par HAProxy, qui est libre et très performant&lt;br /&gt;
La réplication de la base de données sur le serveur de sauvegarde par l'intermédiaire du mode réplication (simple) de MySQL.&lt;/p&gt;


&lt;p&gt;&lt;a href=&quot;http://blog.phppro.fr/public/projets/bdd-workflow.jpg&quot;&gt;&lt;img src=&quot;http://blog.phppro.fr/public/projets/.bdd-workflow_s.jpg&quot; alt=&quot;Workflow BDD&quot; title=&quot;Workflow BDD, fév 2010&quot; /&gt;&lt;/a&gt;&lt;/p&gt;


&lt;p&gt;Un mécanisme de plugin pour l'import et l'export sera mis en place pour permettre d'ajouter de nouvelles sources de données (i.e. formats de fichier, url http...), et d'exporter dans des formats facilitant l'intégration et permettant de faire gagner du temps (pourquoi pas ArcView, PDF, ...)&lt;/p&gt;


&lt;p&gt;Vos questions / remarques / retours d'expériences sur des problématiques similaires sont bien entendus les bienvenus&amp;nbsp;!&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2010/03/03/Le-Mode-Heberge-ou-Application-Service-Provider-%3A-ou-comment-changer-le-business-model-de-votre-client#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2010/03/03/Le-Mode-Heberge-ou-Application-Service-Provider-%3A-ou-comment-changer-le-business-model-de-votre-client#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/70</wfw:commentRss>
      </item>
    
  <item>
    <title>Complémentarité PHPUnderControl / Phing</title>
    <link>http://blog.phppro.fr/?post/2010/02/28/Complementarite-PHPUnderControl-/-Phing</link>
    <guid isPermaLink="false">urn:md5:82db8ddc0fbc85a59cda2bb5bb33d736</guid>
    <pubDate>Sun, 28 Feb 2010 22:56:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Outillage</category>
        <category>ant</category><category>cruisecontrol</category><category>java</category><category>phing</category><category>php</category><category>phpundercontrol</category><category>phpunit</category>    
    <description>&lt;p&gt;Suite à une demande d'un internaute, j'explique la complémentarité entre PHPUnderControl et Phing&lt;/p&gt;    &lt;h2&gt;PHPUnderControl&lt;/h2&gt;


&lt;p&gt;PHPUnderControl est un version adaptée pour PHP de l'outil open source CruiseControl qui est un logiciel serveur d'intégration continue développé en Java et initiallement pour la technologie Java (i.e. pour gérer des projets écrit en Java).&lt;br /&gt;
L'adaptation de PHPUnderControl consiste en la mise à disposition d'une IHM un peu plus contextualisée pour PHP avec des graphiques de métriques qualité récupérés depuis les outils disponibles avec PHP (phpunit notamment).&lt;/p&gt;


&lt;p&gt;Le principe de ce type de logiciel est de détecter des modifications dans un dépôt de code distant (par exemple un serveur Subversion - SVN) et de déclencher une commande suite à cette détection de modification. Il s'agit donc d'un espèce de méga-ordonnanceur qui est bête et méchant et ne sait finalement que &quot;déclencher&quot; des commandes sur des &quot;stimuli&quot; prédéfinis (i.e. la détection d'un commit sur SVN).&lt;/p&gt;


&lt;h2&gt;Phing&lt;/h2&gt;


&lt;p&gt;Phing est un port du logiciel Ant qui provient du monde Java. Ant est un outil pour écrire des scripts batchs en XML, les rendant ainsi multi-plateformes. Il n'est plus nécessaire de se soucier du format .bat de windows ou .sh des linux-like, les commandes sont écrites en XML&amp;nbsp;: on dispose par exemple de balises mkdir, copy, tar... et il est possible de créer ses propres balises de commande (task) voir ses propres séquences de commandes (target).&lt;br /&gt;
Phing est l'implémentation full-PHP de Ant, c'est à dire qu'il suit le même principe, met à disposition globalement les mêmes commandes selon la même syntaxe, mais l'implémentation est réalisée en PHP et est extensible en PHP. Il se base sur l'écriture d'un fichier de commandes nommés build.xml qu'il est coutume de mettre à la racine de votre projet (à subversionner) et qui doit contenir l'ensemble des commandes dont vous avez besoin pour gérer le code de votre projet (lancer les tests, packager le code, déployer le code, qualifier le code...)&lt;/p&gt;


&lt;h2&gt;PHPUnderControl + Phing et non PHPUnderControl = Phing&lt;/h2&gt;


&lt;p&gt;Phing est donc un moyen de décrire la ou les commandes à exécuter suite à un stimuli détecter par PHPUnderControl.
PHPUnderControl est &quot;compatible&quot; phing en standard ce qui permet de décrire/configurer facilement l'exécution de commande phing dans le fichier de configuration de PHPUnderControl (config.xml). Cependant, même si cette intégration n'existait pas, l'exécution d'une commande phing consiste en faite en l'exécution de la commande shell phing suivi des quelques paramètres, ce qui permet d'intégrer cet outils avec n'importe quel logiciel ordonnanceur, même les tâches planifiées windows.&lt;/p&gt;


&lt;p&gt;PHPUnderControl et Phing sont donc complémentaires mais indépendant et autonomes.&lt;/p&gt;


&lt;p&gt;Bien sûr il est possible de remplacer PHPUnderControl par un autre logiciel du même type (Hudson, Xinc, Continuum, ...), ainsi que Phing (makefile, shell script, script bat, ...)&lt;/p&gt;


&lt;p&gt;Pour obtenir plus de documentation, voici quelques liens&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PHPUnderControl&amp;nbsp;: http://phpundercontrol.sourceforge.net&lt;/li&gt;
&lt;li&gt;Phing&amp;nbsp;: http://phing.info&lt;/li&gt;
&lt;li&gt;CruiseControl&amp;nbsp;: http://www.cruisecontrol.net&lt;/li&gt;
&lt;li&gt;PHPUnit&amp;nbsp;: http://www.phpunit.de&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Avez-vous des questions complémentaires ou des remarques ?&lt;br /&gt;
Et vous, comment utilisez vous PHPUnderControl et Phing&amp;nbsp;? Avez-vous des exemples de build.xml que vous utilisez régulièrement&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2010/02/28/Complementarite-PHPUnderControl-/-Phing#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2010/02/28/Complementarite-PHPUnderControl-/-Phing#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/77</wfw:commentRss>
      </item>
    
  <item>
    <title>Les frameworks c'est bien, développer &quot;en dehors&quot; c'est mieux</title>
    <link>http://blog.phppro.fr/?post/2010/02/27/Les-frameworks-c-est-bien-developper-en-dehors-c-est-mieux</link>
    <guid isPermaLink="false">urn:md5:f1097bd82299f2ec993ab50547692d18</guid>
    <pubDate>Sat, 27 Feb 2010 09:00:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Méthodologie</category>
        <category>classe</category><category>framework</category><category>maintenabilité</category>    
    <description>&lt;p&gt;J'explique mon point de vue sur les frameworks et sur une erreur (de mon point de vue) qui coute cher aux entreprises...&lt;/p&gt;    &lt;p&gt;Les frameworks c'est génial&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;en 1 minute on a une application &quot;vierge&quot; super pro qui se connecte à notre base de données&lt;/li&gt;
&lt;li&gt;en 2 minutes je rajoute un écran sur mon appli&lt;/li&gt;
&lt;li&gt;en 3 minutes je rajoute un web service&lt;/li&gt;
&lt;li&gt;en 4 minutes je rajoute un mécanisme d'authentification SSO avec le plugin tartanpion&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ok&amp;nbsp;?&lt;/p&gt;


&lt;p&gt;On oublie aussi&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;en 2 mois j'apprends à coder AVEC le framework (i.e. maintenable, performant, ...)&lt;/li&gt;
&lt;li&gt;en 3 mois je réalise la migration de la v1.9 du framework à la v2.0 (ca fait mal hein ?)&lt;/li&gt;
&lt;li&gt;au bout de 4 mois je comprends vraiment comment ca marche et que je n'aurais pas développé telle classe comme ça si j'avais su avant :(&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;toujours ok&amp;nbsp;?&lt;/p&gt;


&lt;p&gt;Une erreur (ce n'est que mon point de vue) courante que je rencontre sur &quot;le terrain&quot; (i.e. chez mes clients), est l'inconditionnelle mixage entre du code applicatif (i.e. développé par vous) et du code technique (contraint par le framework).&lt;br /&gt;
On se retrouve alors souvent à avoir des classes MyPluginSpecificForZF par ci ou sfMonPluginParLa ... qui contient du code que vous développez qui se connecte à la base de données fait des traitements et renvoie OK ou NOK (exception, boolean, ce que vous voulez).&lt;/p&gt;


&lt;p&gt;Une question me vient alors à l'esprit&amp;nbsp;:&lt;/p&gt;


&lt;p&gt;Pourquoi diable avez vous créé une classe (ou une fonction) dont le nom est si framework-dependant avec un contenu qui ne l'ai pas forcément toujours, et qui surtout vous est tellement spécifique que vous auriez pu le développer même si vous utilisiez un autre framework&amp;nbsp;?&lt;/p&gt;


&lt;p&gt;Personnellement, j'aurai créé une classe autonome suivant mes conventions avec mes packages, dans mon arborescence, capitalisée dans mon dépôt de code, testé unitairement dans un contexte non framework-isé, c'est à dire beaucoup plus simplement en fait, puis, j'aurais créé la même classe que vous (enfin qu'eux), mais il n'y aurai que quelques lignes (uniquement technique, pas applicative)&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
try {
    MyPackage_MyClass::factory()
        -&amp;gt;mySpecificMethod($param1, ...);
} catch (Exception $e) {
    // do something with the exception if this plugin
}
&lt;/pre&gt;


&lt;p&gt;La même remarque me vient à l'esprit pour tous les &quot;adapters&quot; que l'on peut créer pour les différents frameworks.&lt;/p&gt;


&lt;p&gt;Tout ça manque &quot;d'illustration&quot;&amp;nbsp;? Pensez aux centaines de lignes de code que vous (ou les autres) écrivez pour réaliser des plugins Zend Framework, Symfony, Drupal, Magento, eZPublish, Joomla, &amp;lt;WhatEverFrameworkYouWant&amp;gt;et qu'il faut (difficilement) porter à chaque nouvelle version d'un framework ou carrément réécrire quand vous changez de framework...&lt;/p&gt;


&lt;p&gt;D'un point de vue maintenance vous y gagneriez, non&amp;nbsp;? Vous pourriez même faire des tests unitaires sur votre propre code sans devoir mettre en place des pseudo-artifices a base de XXXXControllerTest et autres, puisqu'au final vous n'auriez que votre classe à tester, pas le framework.&lt;br /&gt;
Mais si me dites-vous, car moi j'utilises massivement les &quot;features&quot; du framework dans mon code spécifique (par exemple un Mage::singleton('....')-&amp;gt;retrouveMoiMagiquementMesProduits(...) donc je ne peux pas extraire mon code de cet endroit la car il est trop &quot;dépendant&quot; au framework, je vous conseillerais alors d'aller faire un tour du côté des adapters et de ne plus utiliser en direct dans votre des méthodes que vous n'avez pas développé vous même, sauf si il s'agit d'une classe dite &quot;adapter&quot;, ...&lt;/p&gt;


&lt;p&gt;Que les auteurs de frameworks (que j'ai été et que je suis toujours aussi ;)) ne se formalisent pas, ce que je dénonce ce ne sont pas les frameworks, mais l'usage qui en est fait, qui pour moi est le plus important à long terme (surtout quand on voit que la majorité des frameworks sont réécrits au bout de 2 ou 3 ans)&lt;/p&gt;


&lt;p&gt;Donc, si je peux me permettre un conseil&amp;nbsp;:&lt;/p&gt;


&lt;p&gt;Ne placez jamais de code à vous dans une classe dont on vous contraint le nom ou l'héritage (extends).&lt;br /&gt;
Instanciez plutôt dans cette classe, une classe à vous qui est autonome et que vous avez développé par ailleurs.&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2010/02/27/Les-frameworks-c-est-bien-developper-en-dehors-c-est-mieux#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2010/02/27/Les-frameworks-c-est-bien-developper-en-dehors-c-est-mieux#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/69</wfw:commentRss>
      </item>
    
  <item>
    <title>SimpleTest, installation via mon PEAR</title>
    <link>http://blog.phppro.fr/?post/2010/02/25/SimpleTest-installation-via-mon-PEAR</link>
    <guid isPermaLink="false">urn:md5:3b98503606b6231c011184926ac8eb96</guid>
    <pubDate>Thu, 25 Feb 2010 17:18:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Outillage</category>
        <category>package</category><category>pear</category><category>simpletest</category><category>test unitaire</category>    
    <description>&lt;p&gt;Pour les besoins de l'AFUP, je dois me mettre un peu à SimpleTest (j'ai plus l'habitude sur PHPUnit).
Je n'ai pas trouvé de channel PEAR (a priori) fournissant un package installable via PEAR de SimpleTest et fournissant l'outil en ligne de commande (comme peux le proposer PHPUnit ou d'autres outils). Je vous propose donc celui de mon cru...&lt;/p&gt;    &lt;p&gt;Grâce à un de mes outils de packaging compatible PEAR, j'ai packagé la version 1.0.1 de SimpleTest pour un faire un package PEAR valide et installable via mon channel&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$ pear channel-discover pear.phppro.fr
$ pear upgrade phppro/simpletest
&lt;/pre&gt;


&lt;p&gt;ou un téléchargement puis une installation locale&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$ wget http://pear.phppro.fr/get/simpletest-1.0.1.tgz
$ pear upgrade simpletest-1.0.1.tgz
&lt;/pre&gt;


&lt;p&gt;Puis pour l'utilisation&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$ cd myproject
$ simpletest my_simpletest_test.php
&lt;/pre&gt;


&lt;p&gt;Pour l'implémentation de tests SimpleTest, je vous laisse bien entendu faire un tour sur le site officiel&amp;nbsp;: http://www.simpletest.org&lt;/p&gt;


&lt;p&gt;Dès qu'un channel / package officiel apparaît (ou que quelqu'un m'indique l'adresse) je supprimerai le mien ;)&lt;/p&gt;


&lt;p&gt;Et vous comment faisiez vous pour installer SimpleTest&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2010/02/25/SimpleTest-installation-via-mon-PEAR#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2010/02/25/SimpleTest-installation-via-mon-PEAR#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/76</wfw:commentRss>
      </item>
    
  <item>
    <title>Cloner une arborescence projet modèle</title>
    <link>http://blog.phppro.fr/?post/2010/02/24/Cloner-une-arborescence-projet-modele</link>
    <guid isPermaLink="false">urn:md5:a1a564458f5c07799e357004dfec9873</guid>
    <pubDate>Wed, 24 Feb 2010 09:00:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Trucs et astuces</category>
            
    <description>&lt;p&gt;Vous créez souvent de nouveaux projets pour vous ou pour les autres ?&lt;br /&gt;
Vous réalisez souvent des tâches répétitives de &quot;clonage&quot; d'arborescence en changeant certains noms de fichiers ou contenu pour les contextualiser ?&lt;br /&gt;
Ce script certainement peut vous aider&amp;nbsp;!&lt;/p&gt;    &lt;p&gt;Créer le fichier clone-tree.php avec le contenu suivant&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php

class TreeCloner
{
    public function cloneTree($src, $dest, $vars)
    {
        if (false === is_dir($dest)) {
            mkdir($dest);
        }
        foreach(scandir($src) as $item) {
            if ('.' === $item || '..' === $item) {
                continue;
            }
            $sPath = $src . '/' . $item;
            $dPath = $dest . '/' 
                . $this-&amp;gt;replace($item, $vars);

            if (true === is_dir($sPath)) {
                mkdir($dPath);
                $this-&amp;gt;cloneTree($sPath, $dPath, $vars);
            } else {
                $raw = file_get_contents($sPath);
                $generated = $this-&amp;gt;replace($raw, $vars);
                file_put_contents($dPath, $generated);
            }
        }
        return $this;
    }
    
    public function replace($txt, $vars)
    {
        $m = null;
        if (0 &amp;gt;= preg_match_all(
        	&amp;quot;|\%\{([^\}]+)\}|&amp;quot;,$txt,$m
            )) {
            return $txt;
        }
        
        for($i = 0; $i&amp;lt;count($m[0]);$i++) {
            $key  = $m[1][$i];
            $sPos = strpos($key, ':');
            $func = null;
            if (false !== $sPos) {
                list($func, $key) = explode(':', $key, 2);
            }
            if (null !== $func) {
                if ('php' === trim(strtolower($func))) {
                    $value = eval('return ('. $key . ');');
                } else {
                    if (false === isset($vars[$key])) {
                        continue;
                    }
                    $value = $vars[$key];
                    $value = $func($value);
                }
            } else {
                if (false === isset($vars[$key])) {
                    continue;
                }
                $value = $vars[$key];
            }
            $txt = str_replace($m[0][$i], $value, $txt);
        }
        return $txt;
    }
}

if (4 !== count($_SERVER['argv'])) {
    die(&amp;quot;Syntax: php &amp;quot;.basename(__FILE__).
    &amp;quot; &amp;lt;src&amp;gt; &amp;lt;dest&amp;gt; &amp;lt;variables.ini&amp;gt;
&amp;quot;
    );
}

$cloner = new TreeCloner();
$cloner-&amp;gt;cloneTree(
    $argv[1], $argv[2], parse_ini_file($argv[3])
);

&lt;/pre&gt;


&lt;p&gt;Vous pouvez maintenant utiliser le cloneur d'arborescence comme suit&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;créer une arborescence modèle dans un répertoire (appellons le 'source'), vous pouvez utiliser des &quot;variables&quot; comme suit: %{name} sera remplacé par la valeur de la variable name dans le fichier ini. Vous pouvez utiliser %{maVariable} dans le contenu d'un fichier (txt, php, xml, pptx, docx, csv, js, css ...) ou bien même dans le nom d'un fichier ou d'un répertoire&amp;nbsp;!&lt;/li&gt;
&lt;li&gt;créer un fichier de paramétrage avec les différentes variables et leur valeur (appellons le vars.ini)&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
name=Olivier
lastname=Hoareau
email=olivier@phppro.fr
...
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;exécutez la ligne de commande suivante pour générer l'arborescence à partir de votre modèle&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
$ php clone-tree.php source destination vars.ini
&lt;/pre&gt;


&lt;p&gt;Vous devriez avoir une belle arborescence finale dans le répertoire destination&amp;nbsp;!&lt;/p&gt;


&lt;p&gt;Et vous, comment faites vous pour générer des arborescences complexes pour vos projets&amp;nbsp;? Zend_Tool et autres Symfony et CakePHP pour les projets de ce type, mais pour les autres types de projets&amp;nbsp;?&lt;/p&gt;


&lt;p&gt;Une variante possible&amp;nbsp;: réaliser un .phar ou bien utiliser la directive / fonction __halt_compiler() (voir http://php.net/manual/fr/function.halt-compiler.php) pour embarquer l'ensemble de l'arborescence (zippée) dans votre fichier php (un peu le principe des .phar), ce qui permettra de ne fournir que le fichier de paramètres et le nom du répertoire de destination&amp;nbsp;!&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2010/02/24/Cloner-une-arborescence-projet-modele#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2010/02/24/Cloner-une-arborescence-projet-modele#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/68</wfw:commentRss>
      </item>
    
  <item>
    <title>Capitaliser, Améliorer et Rationnaliser les développements PHP en interne</title>
    <link>http://blog.phppro.fr/?post/2010/02/21/Capitaliser-SAmeliorer-et-Rationnaliser-les-developpements-PHP-en-interne</link>
    <guid isPermaLink="false">urn:md5:6a8b55dc0b9d9e5bb7a2a0c6563ce928</guid>
    <pubDate>Sun, 21 Feb 2010 17:36:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Outillage</category>
        <category>bonnes pratiques</category><category>intégration continue</category><category>phing</category><category>phpunit</category>    
    <description>Je vous livre ici un document de travail anonymisé sur la présentation d'un outil que je développe pour un client pour cadrer les pratiques de développements, ainsi que les enjeux et la démarche à adopter en interne.    &lt;div style=&quot;width:425px;text-align:left&quot; id=&quot;__ss_3238920&quot;&gt;&lt;a style=&quot;font:14px Helvetica,Arial,Sans-serif;display:block;margin:12px 0 3px 0;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/ohoareau/20100221-my-phingtool-blog&quot; title=&quot;20100221   my phingtool - blog&quot;&gt;20100221   my phingtool - blog&lt;/a&gt;&lt;object style=&quot;margin:0&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=20100221-myphingtool-blog-100221113115-phpapp02&amp;amp;stripped_title=20100221-my-phingtool-blog&quot; /&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot; /&gt;&lt;param name=&quot;allowScriptAccess&quot; value=&quot;always&quot; /&gt;&lt;div style=&quot;font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;&quot;&gt;View more &lt;a style=&quot;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/&quot;&gt;presentations&lt;/a&gt; from &lt;a style=&quot;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/ohoareau&quot;&gt;PHPPRO&lt;/a&gt;.&lt;/div&gt;&lt;/object&gt;&lt;/div&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2010/02/21/Capitaliser-SAmeliorer-et-Rationnaliser-les-developpements-PHP-en-interne#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2010/02/21/Capitaliser-SAmeliorer-et-Rationnaliser-les-developpements-PHP-en-interne#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/67</wfw:commentRss>
      </item>
    
  <item>
    <title>Utilisez PEAR pour gérer vos dépendances</title>
    <link>http://blog.phppro.fr/?post/2010/02/19/Utilisez-PEAR-pour-gerer-vos-dependances</link>
    <guid isPermaLink="false">urn:md5:49afe0c0b8b1177719abbc8672d00f57</guid>
    <pubDate>Fri, 19 Feb 2010 23:28:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Trucs et astuces</category>
        <category>configuration</category><category>dépendance</category><category>outils</category><category>pear</category>    
    <description>&lt;p&gt;PEAR (http://pear.php.net) est très mal connu dans les entreprises (en tout cas celle que je visite...).&lt;/p&gt;


&lt;p&gt;Je ne vais pas vous décrire ici tous les fondements et usages de cet outil, cependant, au delà d'une &quot;librairie&quot; vieillotte, il s'agit d'un outil puissant qui vous donne le pouvoir de réutiliser votre code et de gérer vos dépendances.&lt;/p&gt;    &lt;p&gt;Commençons par créer un projet vide dans le répertoire myproject&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
mkdir myproject
cd myproject
&lt;/pre&gt;


&lt;p&gt;Créons ensuite un &quot;repository&quot; pear local à notre projet grâce à l'utilisation d'un fichier de configuration personnalisé de PEAR&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
mkdir configs
mkdir dependencies dependencies/bin dependencies/doc dependencies/ext
mkdir dependencies/library dependencies/cache dependencies/cache
mkdir dependencies/data dependencies/download dependencies/temp
mkdir dependencies/test dependencies/www
pear -c configs/pear.conf -s
pear -c configs/pear.conf bin_dir `pwd`/dependencies/bin
pear -c configs/pear.conf doc_dir `pwd`/dependencies/doc
pear -c configs/pear.conf ext_dir `pwd`/dependencies/ext
pear -c configs/pear.conf php_dir `pwd`/dependencies/library
pear -c configs/pear.conf cache_dir `pwd`/dependencies/cache
pear -c configs/pear.conf data_dir `pwd`/dependencies/data
pear -c configs/pear.conf download_dir `pwd`/dependencies/download
pear -c configs/pear.conf temp_dir `pwd`/dependencies/temp
pear -c configs/pear.conf test_dir `pwd`/dependencies/test
pear -c configs/pear.conf www_dir `pwd`/dependencies/www
&lt;/pre&gt;


&lt;p&gt;Créons ensuite une commande raccourci pour éviter de devoir spécifier le fichier de configuration à chaque fois&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
vi mypear
---contenu de mypear---
#!/bin/sh

pear -c configs/pear.conf $*
---
chmod +x mypear
&lt;/pre&gt;


&lt;p&gt;Nous pouvons maintenant exécuter toutes les commandes pear habituelles qui auront pour effet de mettre à jour notre repository local i.e. le répertoire dependencies/ de notre projet&lt;br /&gt;
Il est maintenant tout à fait concevable d'installer vos propres dépendances localement&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
mypear channel-discover pear.mycompany.com
mypear upgrade mycompany/mypackage1
mypear upgrade mycompany/mypackage2
...
&lt;/pre&gt;


&lt;p&gt;(je ne décris pas ici comment créer vos propres package pear et installer votre propre channel pear, probablement pour de prochains billets ;) je vous conseille tout de même de regarder du côté de pirum, l'outil de Fabien Potencier qui est un must have pour votre channel pear: http://www.pirum-project.org)&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;Il ne vous reste plus qu'à mettre le répertoire dependencies/library dans l'include path (avec set_include_path()) de votre application, d'activer éventuellement le mécanisme d'autoloading (avec spl_autoload_register()) et les différentes classes de votre repository seront alors disponible dans votre application.&lt;/p&gt;


&lt;p&gt;Enfin, vous n'aurez plus qu'à &quot;zipper&quot; le contenu de votre répertoire projet pour avoir un package complet que vous pourrez déployer en production (il est bien sûr envisageable de construire programmatiquement ce package à déployer, mais cela est simplifié grâce au fait que vous avez toutes les classes nécessaires dans dependencies/library)&lt;/p&gt;


&lt;p&gt;Au passage, je conseille vivement l'utilisation de &quot;pear upgrade&quot; plutôt que &quot;pear install&quot;, en effet l'action upgrade va installer les dépendances du paquets (et mettre à jour les dépendances trop anciennes), alors que l'action install sortira en erreur si les des dépendances ne sont pas installées ou sont dans des versions trop anciennes.&lt;/p&gt;


&lt;p&gt;Cette technique (i.e. l'utilisation d'un fichier de configuration personnalisé de PEAR) peut vous permettre de gérer en parallèle plusieurs versions de packages et plusieurs versions de PHP. Notamment très utile si vous travaillez au quotidien avec plusieurs applications fonctionnant avec des versions différentes de dépendances ou d'outils (je pense par exemple à des plateformes d'intégration continue qui ont besoin de fonctionner avec plusieurs versions de phing / phpunit et php notamment).&lt;/p&gt;


&lt;p&gt;Et vous quels sont vos conseils d'utilisations de PEAR&amp;nbsp;?&lt;/p&gt;


&lt;p&gt;PS: Pyrus, la nouvelle version de PEAR est déjà disponible depuis quelques temps, elle n'est a priori cependant compatible qu'avec PHP 5.3.1, elle ne sera donc utilisé dans nos système d'information que dans quelques années lumières...&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2010/02/19/Utilisez-PEAR-pour-gerer-vos-dependances#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2010/02/19/Utilisez-PEAR-pour-gerer-vos-dependances#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/66</wfw:commentRss>
      </item>
    
  <item>
    <title>Incompatibilité Phing 2.3.3 et PHPUnit 3.4.x+</title>
    <link>http://blog.phppro.fr/?post/2010/01/09/Incompatibilite-Phing-233-et-PHPUnit-34x</link>
    <guid isPermaLink="false">urn:md5:17bc3ad3547d994abf9341054a44fd95</guid>
    <pubDate>Sat, 09 Jan 2010 10:04:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Trucs et astuces</category>
        <category>bug</category><category>phing</category><category>phpunit</category>    
    <description>&lt;p&gt;Phing, le port de Ant (java) en PHP, propose des tasks (balises xml) permettant d'exécuter des classes PHPUnit (tests unitaires en PHP). La version stable de Phing n'est maintenant plus compatible avec la dernière version stable de PHPUnit (3.4.3 actuellement) voir avec la branche 3.4.x, et on obtient l'erreur :&lt;br /&gt;&lt;/p&gt;
&lt;pre&gt;
[PHP Error] require_once(PHPUnit/Util/Log/XML.php): failed to open stream: No such file or directory [line 22 of C:\Program Files\PHP\PEAR\phing\tasks\ext\phpunit\phpunit3\XMLPHPUnit3ResultFormatter.php]
&lt;/pre&gt;

&lt;p&gt;Voici une piste pour y remédier&amp;nbsp;:&lt;/p&gt;    &lt;p&gt;Le problème vient du fait que les nouvelles versions de PHPUnit (a priori depuis la branche 3.4.x) ont supprimé notamment le fichier PHPUnit/Util/Log/XML.php suite à du refactoring. Ce fichier étant inclus par une classe de phing, le plantage est donc compréhensible (voir le &lt;a href=&quot;http://phing.info/trac/ticket/363&quot;&gt;ticket de rapport de bug&lt;/a&gt;).&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;Il existe 2 solutions&amp;nbsp;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;remplacer l'inclusion de XML.php par JUnit.php (telle que décrit dans le &lt;a href=&quot;http://phing.info/trac/ticket/415&quot;&gt;ticket de rapport de bug&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;mettre à jour la version de phing pour utiliser la branche 2.4.x encors en version Release Candidate (2.4.0RC3 actuellement).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Personnellement je préconise la deuxième solution, plus pérenne et plus propre. Voici comment faire&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
pear upgrade --alldeps --force phing/phing-2.4.0RC33
&lt;/pre&gt;


&lt;p&gt;(certaines dépendences de package seront mises à jour notamment VersionControl_SVN qui devra être à minima en version 0.3.3)&lt;br /&gt;
Vous devez ensuite remplacer toutes les utilisations de tasks &quot;phpunit2&quot; ou &quot;phpunit2report&quot; par &quot;phpunit&quot; dans vos build.xml.&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2010/01/09/Incompatibilite-Phing-233-et-PHPUnit-34x#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2010/01/09/Incompatibilite-Phing-233-et-PHPUnit-34x#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/65</wfw:commentRss>
      </item>
    
  <item>
    <title>Session &quot;Oui ! PHP est industriel !&quot; au forum PHP 2009 @Paris</title>
    <link>http://blog.phppro.fr/?post/2009/11/17/Session-Oui-PHP-est-industriel-au-forum-PHP-2009-Paris</link>
    <guid isPermaLink="false">urn:md5:0a9b552e056af6e88113f03466b16fe0</guid>
    <pubDate>Tue, 17 Nov 2009 11:33:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Evènements</category>
        <category>adapter</category><category>afup</category><category>agile</category><category>bouchon</category><category>build</category><category>conférence</category><category>convention</category><category>développeur</category><category>eclipse</category><category>entreprise</category><category>equipe</category><category>forum</category><category>intégration continue</category><category>mock</category><category>métrique</category><category>outils</category><category>pattern</category><category>phing</category><category>phpunit</category><category>service</category><category>subversion</category><category>test unitaire</category><category>webservices</category><category>zend framework</category><category>zend studio</category>    
    <description>Le forum PHP 2009 de Paris s'est déroulé la semaine dernière, je vous livre le support de session que nous avons utilisé avec Damien Seguy à l'occasion de la présentation de notre session sur l'industrialisation en PHP.    &lt;div style=&quot;width:425px;text-align:left&quot; id=&quot;__ss_2518174&quot;&gt;&lt;a style=&quot;font:14px Helvetica,Arial,Sans-serif;display:block;margin:12px 0 3px 0;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/ohoareau/afup-forum-php-2009-oui-php-est-industriel&quot; title=&quot;AFUP Forum PHP 2009 : Oui ! PHP est industriel !&quot;&gt;AFUP Forum PHP 2009 : Oui ! PHP est industriel !&lt;/a&gt;&lt;object style=&quot;margin:0&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=afup-forumphp2009-ouiphpestindustriel-091117052430-phpapp02&amp;amp;stripped_title=afup-forum-php-2009-oui-php-est-industriel&quot; /&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot; /&gt;&lt;param name=&quot;allowScriptAccess&quot; value=&quot;always&quot; /&gt;&lt;div style=&quot;font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;&quot;&gt;View more &lt;a style=&quot;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/&quot;&gt;presentations&lt;/a&gt; from &lt;a style=&quot;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/ohoareau&quot;&gt;PHPPRO&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Grâce à PHPTV, Vous pouvez aussi &lt;a href=&quot;http://www.phptv.fr/&quot;&gt;écouter le podcast de la session&lt;/a&gt; !&lt;/p&gt;
&lt;p&gt;Que vous ayez participé à la session ou non, vos retours / remarques / critiques sont les bienvenues !&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/11/17/Session-Oui-PHP-est-industriel-au-forum-PHP-2009-Paris#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/11/17/Session-Oui-PHP-est-industriel-au-forum-PHP-2009-Paris#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/64</wfw:commentRss>
      </item>
    
  <item>
    <title>Forum PHP AFUP : 12 et 13 novembre 2009, Paris : mon planning</title>
    <link>http://blog.phppro.fr/?post/2009/11/10/Forum-PHP-AFUP-%3A-12-et-13-novembre-2009-Paris-%3A-mon-planning</link>
    <guid isPermaLink="false">urn:md5:18e75d4421365bd5157b79c5409b7543</guid>
    <pubDate>Tue, 10 Nov 2009 11:02:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Evènements</category>
        <category>afup</category><category>forum</category><category>planning</category>    
    <description>&lt;p&gt;De retour de congé aux pays des requins, je vous livre mon planning prévisionnel pour le prochain évènement PHP parisien de cette semaine, si certains veulent venir discuter PHP / Agilité, ca peut aider&amp;nbsp;!&lt;/p&gt;    &lt;h3&gt;Jeudi 12 novembre 2009&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;07h00 - 09h30&amp;nbsp;: arrivée sur Paris, libre&lt;/li&gt;
&lt;li&gt;09h30 - 10h00&amp;nbsp;: Ouverture Forum&lt;/li&gt;
&lt;li&gt;10h00 - 11h00&amp;nbsp;: session &quot;Comment améliorer PHP&amp;nbsp;? avec un préprocesseur !&quot; =&amp;gt; Salle 2&lt;/li&gt;
&lt;li&gt;11h00 - 11h30&amp;nbsp;: libre&lt;/li&gt;
&lt;li&gt;11h30 - 12h30&amp;nbsp;: &quot;Un web ouvert avec PHP, Eric Daspet&quot;, Amphi, ou éventuellement libre&lt;/li&gt;
&lt;li&gt;12h30 - 14h00&amp;nbsp;: déjeuner, pour l'instant libre ;)&lt;/li&gt;
&lt;li&gt;14h30 - 15h00&amp;nbsp;: session &quot;Optimisation des performances Magento avec Zend Server&quot;, Amphi&lt;/li&gt;
&lt;li&gt;15h00 - 16h00&amp;nbsp;: &quot;Meilleur support Cloud et Open Source&amp;nbsp;: Le point sur PHP et Microsoft&quot;, Amphi, ou éventuellement libre&lt;/li&gt;
&lt;li&gt;16h00...&amp;nbsp;: libre&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Vendredi 13 novembre 2009&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;09h00 - 10h00&amp;nbsp;: Jouons avec PHP 5.3, Amphi&lt;/li&gt;
&lt;li&gt;10h00 - 11h00&amp;nbsp;: Motiver ses développeurs, Amphi&lt;/li&gt;
&lt;li&gt;11h00 - 14h00&amp;nbsp;: libre&lt;/li&gt;
&lt;li&gt;14h00 - 15h00&amp;nbsp;: Je présenterai ma conférence &quot;Oui&amp;nbsp;! PHP est industriel !&quot; avec Damien Seguy, Amphi, venez nombreux&amp;nbsp;!&lt;/li&gt;
&lt;li&gt;15h00 - 16h00&amp;nbsp;: Bonnes pratiques de développement en PHP, Salle 2&lt;/li&gt;
&lt;li&gt;16h00 - 16h30: libre&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tout au long des 2 jours, vous pouvez me joindre par email (olivier@phppro.fr) ou téléphone (0631207479), n'hésitez pas, je serais ravi de discuter PHP / Agilité avec vous&amp;nbsp;!&lt;/p&gt;


&lt;p&gt;Bon forum à tous&amp;nbsp;!&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/11/10/Forum-PHP-AFUP-%3A-12-et-13-novembre-2009-Paris-%3A-mon-planning#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/11/10/Forum-PHP-AFUP-%3A-12-et-13-novembre-2009-Paris-%3A-mon-planning#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/63</wfw:commentRss>
      </item>
    
  <item>
    <title>Analyse de dépendances classes/fichiers</title>
    <link>http://blog.phppro.fr/?post/2009/10/25/Analyse-de-dependances-classes/fichiers</link>
    <guid isPermaLink="false">urn:md5:8df1db07976ce34ba08356c53b7397a8</guid>
    <pubDate>Sun, 25 Oct 2009 11:48:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Outillage</category>
        <category>dependance</category><category>métrique</category><category>outils</category><category>pattern</category><category>pear</category><category>php</category><category>require</category>    
    <description>&lt;p&gt;Dans mon précédent post sur &quot;Zend Framework 1.8.3 demystified, Act I&quot;, je vous avais livré un schéma de dépendances entre packages générés grâce à un de mes outils... Je vous livre maintenant l'outils.&lt;/p&gt;    &lt;p&gt;WDependency est un outil réalisé en PHP et utilisant le programme GraphViz/Dot pour générer des graphes de dépendances entre classes , fichiers, projets, packages...&lt;br /&gt;&lt;/p&gt;


&lt;h2&gt;Installation&lt;/h2&gt;

&lt;pre&gt;
pear channel-discover pear.phppro.fr
pear install --alldeps phppro/wdependency
&lt;/pre&gt;


&lt;p&gt;(Un package requis nommé 'WFramework' sera aussi installé, il s'agit d'un framework d'abstraction que je développe avec mes conventions pour illustrer des pratiques / conventions chez mes clients)&lt;/p&gt;


&lt;p&gt;Vérifiez que l'installation c'est bien passée&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
wdependency
&lt;/pre&gt;


&lt;p&gt;(Vous devriez voir l'aide repris ci-dessous)&lt;/p&gt;


&lt;p&gt;Il est ensuite nécessaire d'installer GraphViz/Dot (le binaire 'dot' doit être accessible en ligne de commande)&lt;br /&gt;
Vous pouvez vérifiez si il est installé sur votre poste&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
wdependency :check-dot-support
&lt;/pre&gt;


&lt;p&gt;Si la version de dot ne s'affiche pas, c'est que vous devez l'installer, rendez-vous sur http://www.graphviz.org rubrique downloads, téléchargez et installez l'outil dot.&lt;br /&gt;
Une fois installé, re-vérifiez que wdependency est bien cablé avec dot en relançant un&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
wdependency :check-dot-support
&lt;/pre&gt;


&lt;p&gt;Ensuite pour utiliser l'outil&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
cd my-source-directory
wdependency :help
&lt;/pre&gt;


&lt;p&gt;Vous obtiendrez l'aide de l'outil ci-dessous&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;

 ***** [Syntax] ***************************************************************

   $ wdependency [commands] [options] [&amp;lt;directory&amp;gt;[,&amp;lt;directory&amp;gt;,...]]

 ***** [Available commands] ***************************************************

   use: :&amp;lt;command&amp;gt; , ':' is required

   :help                  display this help
   :instances             computes the instances (use of new operator) dependencies
   :cache                 parses directory content and caches result in all.php for further use
   :clean-cache           cleans existing cache file
   :often-used            top rank node that heavily depend on others
   :classes               computes the classes dependencies (children =&amp;gt; mother)
   :package0-inheritances  computes the classes inheritances reduced to first package level
   :package1              computes the classes dependencies reduced to second package level
   :inheritances          computes the inheritances dependencies
   :includes              computes the includes dependencies
   :packages              computes the packages dependencies
   :dependent-nodes       list all mostly dependent nodes (top ranked)
   :check-dot-support     display the version of dot binary if available (required for dot/picture export)
   :popo                  list Plain Old PHP Objects (no extends / no implements)
   :supers                computes the hierarchy dependency (mother =&amp;gt; children)
   :testcases             list all classes that extends PHPUnit_Framework_TestCase
   :untestedpackages      list all packages that does not contain phpunit test cases
   :testedpackages        list all packages that contains phpunit test cases
   :hubs                  list all classes that are very used by others
   :self-dependent        list all classes that are self-dependent (depend on themself)
   :dependent             list all classes that are dependent to others (more than 4 links to the same)
   :very-dependent        list all classes that are very dependent to others (more than 10 links to the same)
   :extra-dependent       list all classes that are extra dependent to others (more than 20 links to the same)
   :describe-X            display content of specific interface, where X is:
         * listener     for displaying W_Dependency_Listener_Interface
         * exporter     for displaying W_Dependency_Exporter_Interface
         * analyzer     for displaying W_Dependency_Analyzer_Interface
         * filter       for displaying W_Dependency_Filter_Interface
         * aggregator   for displaying W_Dependency_Aggregator_Interface
         * source       for displaying W_Dependency_Source_Interface
         * action       for displaying W_Dependency_Action_Interface

 ***** [Available options] ****************************************************

   use: -&amp;lt;option&amp;gt; , no value (no '=...')

   -v      verbose mode, debug information logged in ./wdependency.log
   -h      display this help
   -c      activate/use cache file for parsing optimization (generate/use ./wdependency.cache file)
   -cc     clean existing cache file

   use: --&amp;lt;option&amp;gt;=&amp;lt;value&amp;gt; , no default value, '=&amp;lt;value&amp;gt;' is required

   --action      load custom action(s) (list separator: ,), specified by class name
   --aggregator  load custom aggregator(s) (list separator: ,), specified by class name
   --analyzer    load custom analyzer(s) (list separator: ,), specified by class name
   --bottom      keep only nodes that are in the specified bottom rank (top X) when computing weight from links
   --by          aggregate nodes using the specified algorithm:
         * dir          aggregate by directories (dirname on / of node)
         * package      aggregate by package (dirname on _ of node)
         * package#X    aggregate by package at position X
         * dir#X        aggregate by directory at position X
   --class       keep only nodes that are used by specified class
   --dir         keep only nodes that are used by file in specified directory
   --exporter    load custom exporter(s) (list separator: ,), specified by class name
   --file        keep only nodes that are used by specified file
   --filter      load custom filter(s) (list separator: ,), specified by class name
   --format      list of export format:
         * png          PNG picture file
         * jpg          JPG picture file
         * gif          GIF picture file
         * dot          GraphViz DOT text format
         * graphml      GraphML xml format
         * json         JavaScript Object Notation text format
         * php          PHP file (return array(...);)
         * svg          Scalable Vector Graphics picture format (xml)
   --has         keep only nodes that have links
   --layout      graphviz (dot) layout to use, by default 'dot', type dot -K? to see available list
   --link        keep only nodes that have link to specified node
   --linked-to   keep only links to specified dependency
   --listener    load custom listener(s) (list separator: ,), specified by class name
   --load        loads the specified file as a data source (from previous analyzes, PHP format supported only)
   --max         keep only nodes that have a maximum of links specified
   --max-link-weight  keep only links that have less than specified weight
   --min         keep only nodes that have at least the minimum links specified
   --min-link-weight  keep only links that have more than specified weight
   --n           keep only nodes that have number of nodes specified
   --node        keep only nodes that are used by specified node
   --nolink      keep only nodes that have not link to specified node
   --none        keep only nodes that have no links
   --package     keep only nodes that are used by class in specified package
   --pattern     keep only nodes that have name matching specified pattern
   --prefix      keep only nodes that have name beginning with specified prefix
   --reverse     reverse link order (user become used and used become user)
   --source      load custom source(s) (list separator: ,), specified by class name
   --subdirs-as-projects  use subdirectories as autonomous projects
   --template    php file template to render export (file exporter only), use $options and $data
   --top         keep only nodes that are in the specified top rank (top X) when computing weight from links

 ***** [Additional information] ***********************************************

   * additional options could be available dependending on the renderer
     template (i.e. phtml view file if used)

   * for schema / graph export in dot, png, jpg, ... you must have the
     graphviz binaries installed (dot binary)

 ***** [Software information] *************************************************

   wdependency v0.0.7, built on 2009-10-25
   Under BSD License, Olivier Hoareau &amp;lt;olivier a t phppro.fr&amp;gt; - PHPPRO

   PEAR package

&lt;/pre&gt;


&lt;p&gt;Le principe est simple&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;vous vous positionnez dans le répertoire racine à analyser&lt;/li&gt;
&lt;li&gt;vous indiquez l'action a effectuer et le format d'export voulu&lt;/li&gt;
&lt;li&gt;vous indiquez éventuellement les &quot;filtres / tri&quot; et les &quot;aggrégations&quot; voulues&lt;/li&gt;
&lt;li&gt;vous lancez wdependency avec tous ces paramètres&lt;/li&gt;
&lt;li&gt;wdependency va analyser chaque fichier PHP de répertoires du répertoire courant (récursif)&lt;/li&gt;
&lt;li&gt;il va construire un modèle de donnée en mémoire pour réprésenter les dépendances&lt;/li&gt;
&lt;li&gt;il va trier/filtrer les données&lt;/li&gt;
&lt;li&gt;il va exporter en jpg/png/... (i.e. le(s) format(s) que vous avez choisis)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Exemples&lt;/h2&gt;


&lt;p&gt;Vous pourrez ensuite générer le graphiques de dépendances entre vos répertoires de second niveau par exemple grâce à la commande&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
wdependency :package1
&lt;/pre&gt;


&lt;p&gt;un fichier png devrait apparaître dans le répertoire courant&lt;/p&gt;


&lt;p&gt;Ou bien générer le graphe d'héritage de vos classes&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
wdependency :inheritances
&lt;/pre&gt;


&lt;p&gt;Le graphe d'héritage d'une classe en particulier&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
wdependency :inheritances --node=MyPackage_MyClass
&lt;/pre&gt;


&lt;p&gt;...&lt;/p&gt;


&lt;h2&gt;Mise en cache du parsing pour aller plus vite&lt;/h2&gt;


&lt;p&gt;Il est possible de parser une fois et de mettre en cache le modèle de données résultant pour ensuite faire plusieurs &quot;requêtes&quot; sans devoir tout reparser à chaque fois&amp;nbsp;:&lt;/p&gt;


&lt;p&gt;Commencez par vider le cache et regénérer le fichier de cache (./wdependency.cache):&lt;/p&gt;

&lt;pre&gt;
wdependency -cc
wdependency :cache
&lt;/pre&gt;


&lt;p&gt;Puis exécutez n'importe laquelle des commandes comme d'habitude, vous verrez c'est plus rapide ;)&lt;/p&gt;


&lt;p&gt;Bien sûr il s'agit d'une version alpha certainement buggée, merci par avance pour vos retours sur le gestionnaire d'issues&amp;nbsp;: http://code.google.com/p/wdependency/issues/list&lt;/p&gt;


&lt;p&gt;Bon usage&amp;nbsp;!&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/10/25/Analyse-de-dependances-classes/fichiers#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/10/25/Analyse-de-dependances-classes/fichiers#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/62</wfw:commentRss>
      </item>
    
  <item>
    <title>Zend Framework demystified, act I...</title>
    <link>http://blog.phppro.fr/?post/2009/10/18/Zend-Framework-demystified-act-I</link>
    <guid isPermaLink="false">urn:md5:65e853ad658a124406357a64b356f687</guid>
    <pubDate>Sun, 18 Oct 2009 18:48:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Outillage</category>
        <category>code mort</category><category>dependance</category><category>dépendance</category><category>outils</category>    
    <description>&lt;p&gt;Voici une vue des dépendances entre package dans Zend Framework 1.8.3 réalisé avec un de mes outils d'analyse de code.&lt;/p&gt;    &lt;p&gt;&lt;a href=&quot;http://blog.phppro.fr/public/projects.png&quot;&gt;&lt;img src=&quot;http://blog.phppro.fr/public/./.projects_m.jpg&quot; alt=&quot;Graphes de dépendances entre packages ZF 1.8.3&quot; title=&quot;Graphes de dépendances entre packages ZF 1.8.3, oct 2009&quot; /&gt;&lt;/a&gt;&lt;/p&gt;


&lt;p&gt;Je travaille actuellement sur l'analyse des dépendances entre classes, packages, applications / frameworks pour notamment cartographier les zones mortes des applis (i.e. code non utilisé), les zones de complexité ou de dette technique (i.e. point central ayant beaucoup de liaison...).&lt;br /&gt;
Après plusieurs heures de recherches d'une librairie de visualisation temps réel sur un graphe, je n'ai toujours pas trouvé mon bonheur pour parcourir en temps réel le graphe, je vous livre donc pour l'instant ma version graphviz (dot)&lt;/p&gt;


&lt;p&gt;Avez-vous d'autres techniques pour représenter les dépendances classes/applications/frameworks ?&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;PS: le noeud &lt;strong&gt;ROOT&lt;/strong&gt; correspond aux classes située à la racine du répertoire library/Zend, les autres noeuds correspondent aux répertoires situés dans library/Zend/&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/10/18/Zend-Framework-demystified-act-I#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/10/18/Zend-Framework-demystified-act-I#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/61</wfw:commentRss>
      </item>
    
  <item>
    <title>PHP : Une plateforme industrialisable au service de l'Agilité</title>
    <link>http://blog.phppro.fr/?post/2009/09/28/PHP-%3A-Une-plateforme-industrialisable-au-service-de-l-Agilite</link>
    <guid isPermaLink="false">urn:md5:98e8b9342a4df7f0bf1b0f04a1c8116c</guid>
    <pubDate>Mon, 28 Sep 2009 19:51:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Méthodologie</category>
        <category>afup</category><category>agile</category><category>développeur</category><category>eclipse pdt</category><category>equipe</category><category>hudson</category><category>intégration continue</category><category>pear</category><category>phpunit</category><category>plateforme</category><category>projet</category><category>subversion</category><category>test unitaire</category>    
    <description>Je publie sur slideshare une version  refactorée d'une présentation draft que j'avais réalisé il y a quelques mois pour l'Université du S.I avant de préférer avec Damien Seguy une version plus light... Au programme, PHP et Agilité, encore et toujours ;)    &lt;div style=&quot;width:425px;text-align:left&quot; id=&quot;__ss_2083732&quot;&gt;&lt;a style=&quot;font:14px Helvetica,Arial,Sans-serif;display:block;margin:12px 0 3px 0;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/ohoareau/php-une-technologie-au-service-de-lagilit&quot; title=&quot;PHP : Une Technologie Au Service De L&amp;#39;Agilité&quot;&gt;PHP : Une Technologie Au Service De L&amp;#39;Agilité&lt;/a&gt;&lt;object style=&quot;margin:0px&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=php-unetechnologieauservicedelagilit-090928134735-phpapp01&amp;stripped_title=php-une-technologie-au-service-de-lagilit&quot; /&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;/&gt;&lt;param name=&quot;allowScriptAccess&quot; value=&quot;always&quot;/&gt;&lt;embed src=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=php-unetechnologieauservicedelagilit-090928134735-phpapp01&amp;stripped_title=php-une-technologie-au-service-de-lagilit&quot; type=&quot;application/x-shockwave-flash&quot; allowscriptaccess=&quot;always&quot; allowfullscreen=&quot;true&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style=&quot;font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;&quot;&gt;View more &lt;a style=&quot;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/&quot;&gt;presentations&lt;/a&gt; from &lt;a style=&quot;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/ohoareau&quot;&gt;PHPPRO&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Vu que je n'ai jamais joué cette présentation pour l'instant, vos critiques et remarques sont, encore et toujours, les bienvenus !&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/09/28/PHP-%3A-Une-plateforme-industrialisable-au-service-de-l-Agilite#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/09/28/PHP-%3A-Une-plateforme-industrialisable-au-service-de-l-Agilite#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/59</wfw:commentRss>
      </item>
    
  <item>
    <title>Test unitaire: comment bouchonner ?</title>
    <link>http://blog.phppro.fr/?post/2009/09/26/Test-unitaire%3A-comment-bouchonner</link>
    <guid isPermaLink="false">urn:md5:f0ddb4a4e92f9372eb357841ac9eb6f0</guid>
    <pubDate>Sat, 26 Sep 2009 16:59:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Méthodologie</category>
        <category>adapter</category><category>bouchon</category><category>factory</category><category>mock</category><category>phpunit</category><category>service</category><category>test unitaire</category><category>webservices</category>    
    <description>&lt;p&gt;L'exécution d'un test unitaire nécessite en règle générale la mise en place de bouchon (mock en anglais) qui permettent de simuler les appels systèmes ou d'api sous jacent.&lt;br /&gt;
En effet, il peut être difficile de tester une partie de votre code qui a des impacts sur le système externe (enregistrement sur disque, appels réseaux / webservices, stockage mémoire vive, appels base de données...), le principe du bouchon est alors bien pratique pour tester la logique sans déclencher toutes les opérations.&lt;/p&gt;    &lt;p&gt;Le schéma ci-dessous décrit un exemple de séquence d'exécution permettant de mettre en oeuvre un test unitaire (test avec bouchon) sur une méthode d'une classe (appellée &quot;Service&quot;) qui elle même utilise une méthode d'une autre classe pour récupérer la liste non triée brute des commandes. Il est ici nécessaire de bouchonner car la récupération de la liste des commandes brutes est ici réalisées par l'appel d'un webservice SOAP, qu'il est hors de question de déclencher lors de l'exécution des tests unitaires (nous utiliserons alors le bouchon)&amp;nbsp;:&lt;/p&gt;


&lt;p&gt;&lt;a href=&quot;http://blog.phppro.fr/public/sequence-test-unitaire.jpg&quot;&gt;&lt;img src=&quot;http://blog.phppro.fr/public/./.sequence-test-unitaire_m.jpg&quot; alt=&quot;Séquence d&amp;#039;appel lors de l&amp;#039;exécution d&amp;#039;un test unitaire (test avec bouchon)&quot; title=&quot;Séquence d&amp;#039;appel lors de l&amp;#039;exécution d&amp;#039;un test unitaire (test avec bouchon), sep 2009&quot; /&gt;&lt;/a&gt;&lt;/p&gt;


&lt;p&gt;Voici un exemple de code que l'on pourrait associer à ce schéma&amp;nbsp;:&lt;/p&gt;


&lt;h4&gt;La classe de test&lt;/h4&gt;

&lt;pre class=&quot;php&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;?php&lt;/span&gt;
&amp;nbsp;
&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;class&lt;/span&gt; MyApp_Order_ServiceTest &lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;extends&lt;/span&gt; PHPUnit_Framework_TestCase
&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#123;&lt;/span&gt;
	&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;function&lt;/span&gt; setUp&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;
	&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#123;&lt;/span&gt;
		&lt;span style=&quot;color: #ff0000&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #006600;&quot;&gt;fixture&lt;/span&gt; &lt;span style=&quot;color: #66cc66;&quot;&gt;=&lt;/span&gt; &lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;new&lt;/span&gt; MyApp_Order_Service&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;;&lt;/span&gt;
		&lt;span style=&quot;color: #ff0000&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #006600;&quot;&gt;fixture&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #006600;&quot;&gt;setAdapter&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;
			&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;new&lt;/span&gt; MyApp_Order_Adapter_Mock&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;;&lt;/span&gt;
		&lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;/**
		 * ou bien:
		 *
		 * $this-&amp;gt;fixture =
		 * 	Myapp_Order_Service::factory('mock');
		 */&lt;/span&gt;
	&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
	&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;function&lt;/span&gt;
	 test_listOrdersForDate_forNoOrders_returnEmptyArray&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;
	&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#123;&lt;/span&gt;
		&lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;// 1. Paramétrer le mock&lt;/span&gt;
		&lt;span style=&quot;color: #ff0000&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #006600;&quot;&gt;fixture&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #006600;&quot;&gt;getAdapter&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;
			&lt;span style=&quot;color: #66cc66;&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #006600;&quot;&gt;addMethodExpectedResult&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;
				&lt;span style=&quot;color: #ff0000;&quot;&gt;'listForDate'&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;,&lt;/span&gt;
				&lt;span style=&quot;color: #000066;&quot;&gt;array&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;;&lt;/span&gt;
&amp;nbsp;
		&lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;/**
		 * 2,3,4,5,6 et 7. Appeller la méthode du
		 *                 service que l'on teste
		 */&lt;/span&gt;
		&lt;span style=&quot;color: #ff0000&quot;&gt;$result&lt;/span&gt; &lt;span style=&quot;color: #66cc66;&quot;&gt;=&lt;/span&gt; &lt;span style=&quot;color: #ff0000&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #006600;&quot;&gt;fixture&lt;/span&gt;
			&lt;span style=&quot;color: #66cc66;&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #006600;&quot;&gt;listOrdersByDate&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;'2009-09-26'&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;;&lt;/span&gt;
&amp;nbsp;
		&lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;/**
		 * 8 et 9. Demander au mock si il a été
		 *         appellé et avec quels paramètres
		 */&lt;/span&gt;
		&lt;span style=&quot;color: #ff0000&quot;&gt;$params&lt;/span&gt; &lt;span style=&quot;color: #66cc66;&quot;&gt;=&lt;/span&gt; &lt;span style=&quot;color: #ff0000&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #006600;&quot;&gt;fixture&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #006600;&quot;&gt;getAdapter&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;
			&lt;span style=&quot;color: #66cc66;&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #006600;&quot;&gt;getLastMethodCallParams&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;'listForDate'&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;;&lt;/span&gt;
&amp;nbsp;
		&lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;/**
		 * 10. Vérifier (tester) le résultat du
		 *     service
		 */&lt;/span&gt;
		&lt;span style=&quot;color: #ff0000&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #006600;&quot;&gt;assertEquals&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000066;&quot;&gt;array&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #ff0000&quot;&gt;$result&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;;&lt;/span&gt;
&amp;nbsp;
		&lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;/**
		 * 11. Vérifier (tester) les paramètres
		 *     envoyés par le service à l'adapter
		 */&lt;/span&gt;
		&lt;span style=&quot;color: #ff0000&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #006600;&quot;&gt;assertEquals&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000066;&quot;&gt;array&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;'2009-09-26'&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #ff0000&quot;&gt;$params&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;;&lt;/span&gt;
	&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#125;&lt;/span&gt;
        &lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;// ...&lt;/span&gt;
&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;


&lt;h4&gt;Le &quot;Service&quot;&lt;/h4&gt;

&lt;pre class=&quot;php&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;?php&lt;/span&gt;
&amp;nbsp;
&lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;/**
 * Fonctionnalités liées aux &amp;quot;Commandes&amp;quot;
 *
 * La classe parent (MyApp_Abstract_Service) fournis
 * toutes les méthodes génériques pour les services:
 *
 * setAdapter(), getAdapter()...
 */&lt;/span&gt;
&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;class&lt;/span&gt; MyApp_Order_Service &lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;extends&lt;/span&gt; MyApp_Abstract_Service
&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#123;&lt;/span&gt;
	&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;function&lt;/span&gt; listOrdersForDate&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #ff0000&quot;&gt;$date&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;
	&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#123;&lt;/span&gt;
		&lt;span style=&quot;color: #b1b100;&quot;&gt;if&lt;/span&gt; &lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;false&lt;/span&gt; &lt;span style=&quot;color: #66cc66;&quot;&gt;===&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;preg_match&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;
			&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;|^20[0-9]{2}&lt;span style=&quot;color: #000099; font-weight: bold;&quot;&gt;\-&lt;/span&gt;[0-9]{2}&lt;span style=&quot;color: #000099; font-weight: bold;&quot;&gt;\-&lt;/span&gt;[0-3][0-9]$|&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;,&lt;/span&gt;
			&lt;span style=&quot;color: #ff0000&quot;&gt;$date&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#123;&lt;/span&gt;
			throw &lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;new&lt;/span&gt; RuntimeException&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;
			 &lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;Bad date format &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;.&lt;/span&gt;
			 &lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;(expected YYYY-MM-DD, got: '$date')&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;;&lt;/span&gt;
		&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#125;&lt;/span&gt;
		try &lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#123;&lt;/span&gt;
			&lt;span style=&quot;color: #ff0000&quot;&gt;$rawList&lt;/span&gt; &lt;span style=&quot;color: #66cc66;&quot;&gt;=&lt;/span&gt; &lt;span style=&quot;color: #ff0000&quot;&gt;$this&lt;/span&gt;
				&lt;span style=&quot;color: #66cc66;&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #006600;&quot;&gt;getAdapter&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;
					&lt;span style=&quot;color: #66cc66;&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #006600;&quot;&gt;listForDate&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #ff0000&quot;&gt;$date&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;;&lt;/span&gt;
		&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#125;&lt;/span&gt; catch &lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;Exception &lt;span style=&quot;color: #ff0000&quot;&gt;$e&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#123;&lt;/span&gt;
			MyApp_Logger&lt;span style=&quot;color: #66cc66;&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color: #006600;&quot;&gt;logException&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #ff0000&quot;&gt;$e&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;;&lt;/span&gt;
			throw &lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;new&lt;/span&gt; RuntimeException&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;
			&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;An error occured when retrieving &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;.&lt;/span&gt;
			&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;orders list for date &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;.&lt;/span&gt;
			&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;'$date': &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #ff0000&quot;&gt;$e&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #006600;&quot;&gt;getMessage&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;;&lt;/span&gt;
		&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#125;&lt;/span&gt;
		&lt;span style=&quot;color: #ff0000&quot;&gt;$orders&lt;/span&gt; &lt;span style=&quot;color: #66cc66;&quot;&gt;=&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;array&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;;&lt;/span&gt;
		&lt;span style=&quot;color: #b1b100;&quot;&gt;foreach&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #ff0000&quot;&gt;$rawList&lt;/span&gt; &lt;span style=&quot;color: #b1b100;&quot;&gt;as&lt;/span&gt; &lt;span style=&quot;color: #ff0000&quot;&gt;$order&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#123;&lt;/span&gt;
			&lt;span style=&quot;color: #ff0000&quot;&gt;$orders&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span style=&quot;color: #ff0000&quot;&gt;$order&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;'id'&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span style=&quot;color: #66cc66;&quot;&gt;=&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;array&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;
				&lt;span style=&quot;color: #ff0000;&quot;&gt;'nbItems'&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;=&amp;gt;&lt;/span&gt;count&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #ff0000&quot;&gt;$order&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;'items'&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;,&lt;/span&gt;
				&lt;span style=&quot;color: #ff0000;&quot;&gt;'amount'&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #ff0000&quot;&gt;$order&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;'price'&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;,&lt;/span&gt;
			&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;;&lt;/span&gt;
		&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#125;&lt;/span&gt;
		&lt;span style=&quot;color: #000066;&quot;&gt;asort&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #ff0000&quot;&gt;$orders&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;;&lt;/span&gt;
		&lt;span style=&quot;color: #b1b100;&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color: #ff0000&quot;&gt;$orders&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;;&lt;/span&gt;
	&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#125;&lt;/span&gt;
        &lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;// ...&lt;/span&gt;
&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;


&lt;h4&gt;L'Adapter Mock (bouchon)&lt;/h4&gt;

&lt;pre class=&quot;php&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;?php&lt;/span&gt;
&amp;nbsp;
&lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;/**
 * Bouchon pour les fonctionnalités &amp;quot;Commandes&amp;quot;
 *
 * La classe parent (MyApp_Abstract_Mock) fournis
 * toutes les méthodes génériques pour les bouchons:
 *
 * getLastMethodCallParams(), 
 * markMethodExecutionAndReturnExpectedResult(), ...
 */&lt;/span&gt;
&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;class&lt;/span&gt; MyApp_Order_Adapter_Mock &lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;extends&lt;/span&gt; Myapp_Abstract_Mock
	implements MyApp_Order_Adapter_Interface
&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#123;&lt;/span&gt;
	&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;function&lt;/span&gt; listForDate&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #ff0000&quot;&gt;$date&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;
	&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#123;&lt;/span&gt;
		&lt;span style=&quot;color: #ff0000&quot;&gt;$args&lt;/span&gt; &lt;span style=&quot;color: #66cc66;&quot;&gt;=&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;func_get_args&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;;&lt;/span&gt;
		&lt;span style=&quot;color: #b1b100;&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color: #ff0000&quot;&gt;$this&lt;/span&gt;
		 &lt;span style=&quot;color: #66cc66;&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #006600;&quot;&gt;markMethodExecutionAndReturnExpectedResult&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;
			&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;__FUNCTION__&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;,&lt;/span&gt;
			&lt;span style=&quot;color: #ff0000&quot;&gt;$args&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;;&lt;/span&gt;
	&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#125;&lt;/span&gt;
        &lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;// ...&lt;/span&gt;
&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;


&lt;h4&gt;L'Adapter standard (celui qui appelle le SOAP, mais qui n'est pas utilisé dans les tests unitaires)&lt;/h4&gt;

&lt;pre class=&quot;php&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;?php&lt;/span&gt;
&amp;nbsp;
&lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;/**
 * Implémentation native pour les fonctionnalités &amp;quot;Commandes&amp;quot;
 *
 * L'interface MyApp_Order_Adapter_Interface définit la liste
 * des méthodes à implémenter dans cet adapter
 */&lt;/span&gt;
&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;class&lt;/span&gt; Myapp_Order_Adapter_Default
	implements MyApp_Order_Adapter_Interface
&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#123;&lt;/span&gt;
	&lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;/**
	 * @var SoapClient
	 */&lt;/span&gt;
	&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;private&lt;/span&gt; &lt;span style=&quot;color: #ff0000&quot;&gt;$soapClient&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;;&lt;/span&gt;
&amp;nbsp;
	&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;function&lt;/span&gt; __construct&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;
	&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#123;&lt;/span&gt;
		&lt;span style=&quot;color: #ff0000&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #006600;&quot;&gt;soapClient&lt;/span&gt; &lt;span style=&quot;color: #66cc66;&quot;&gt;=&lt;/span&gt; &lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;new&lt;/span&gt; SoapClient&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;;&lt;/span&gt;
	&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
	&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;function&lt;/span&gt; listForDate&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #ff0000&quot;&gt;$date&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;
	&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#123;&lt;/span&gt;
		&lt;span style=&quot;color: #b1b100;&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color: #ff0000&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #006600;&quot;&gt;soapClient&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #006600;&quot;&gt;listForDate&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #ff0000&quot;&gt;$date&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;;&lt;/span&gt;
	&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
        &lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;// ...&lt;/span&gt;
&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;


&lt;h3&gt;La séquence&lt;/h3&gt;


&lt;p&gt;La séquence d'appel est donc la suivante&amp;nbsp;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Paramétrage du mock&amp;nbsp;: on dit au bouchon quel est le résultat qu'il doit renvoyer quand on va l'appeller&lt;/li&gt;
&lt;li&gt;Appel de la méthode testée par le test unitaire: on exécute la méthode avec les paramètres en dur depuis le test unitaire&lt;/li&gt;
&lt;li&gt;Pré-logique du service&amp;nbsp;: la méthode commence par traiter les arguments et se préparer avant l'appel de l'adapter (mock dans notre cas)&lt;/li&gt;
&lt;li&gt;Appel de l'adapter par le service: le mock est alors appelé par le service avec d'éventuels arguments préparés&lt;/li&gt;
&lt;li&gt;Renvoie du résultat par l'adapter: le mock renvoie &quot;bêtement&quot; le résultat qu'on lui a demandé de renvoyer lors du paramétrage (étape 1)&lt;/li&gt;
&lt;li&gt;Post-logique: le service traite le résultat de l'adapter (filtrage, trie, transformation...)&lt;/li&gt;
&lt;li&gt;Renvoie du résultat attendue par le test: le service renvoie le résultat final au test unitaire&lt;/li&gt;
&lt;li&gt;Le test vérifie que le mock a bien été appelé par le service (pour être sur que tout s'est bien passé, n'oublions pas le service est une boite noire, lorsque l'on fait un test on n'est pas censé savoir comment le service est implémenté, mais uniquement que son comportement)&lt;/li&gt;
&lt;li&gt;Le test récupère les paramètres d'appel du mock par le service&lt;/li&gt;
&lt;li&gt;Vérification du résultat attendu: 1ère assertion sur le résultat final, on vérifie que la liste retournée par le service est celle attendue dans le cadre de ce test&lt;/li&gt;
&lt;li&gt;Vérification des paramètres fournis par le service à l'adapter (mock): 2ème assertion sur l'appel du mock par le service, on vérifie que le service a bien eu le comportement attendu sur l'adapter (mock)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Les étapes 8, 9 et 11 sont souvent oubliés, elles sont cependant de mon point de vue nécessaire pour garantir que le test unitaire met en oeuvre un véritable bouchon et que seul la logique que l'on souhaite testée est exécutée (sinon, il ne s'agit pas d'un test unitaire, mais plutôt d'un test d'intégration...).&lt;br /&gt;
Cette technique est bien entendu a utiliser pour bouchonner les appels à la base de données...&lt;/p&gt;


&lt;p&gt;Et vous, comment faites vous pour bouchonner votre code, notamment dans le cadre des tests unitaires&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/09/26/Test-unitaire%3A-comment-bouchonner#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/09/26/Test-unitaire%3A-comment-bouchonner#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/58</wfw:commentRss>
      </item>
    
  <item>
    <title>Agilité, Tests et Industrialisation en PHP</title>
    <link>http://blog.phppro.fr/?post/2009/09/23/Agilite-Tests-et-Industrialisation-en-PHP</link>
    <guid isPermaLink="false">urn:md5:06a49eee549a9b5691aac9a5f9173c23</guid>
    <pubDate>Wed, 23 Sep 2009 16:50:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Méthodologie</category>
            
    <description>Je publie sur slideshare la présentation faite ce jour. Je parle de méthodologie agile au travers de l'histoire d'une équipe, et d'industrialisation des développements PHP (Intégration Continue, Tests, ...)    &lt;div style=&quot;width:425px;text-align:left&quot; id=&quot;__ss_2049536&quot;&gt;&lt;a style=&quot;font:14px Helvetica,Arial,Sans-serif;display:block;margin:12px 0 3px 0;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/ohoareau/agilit-tests-et-industrialisation&quot; title=&quot;Agilité, Tests Et Industrialisation&quot;&gt;Agilité, Tests Et Industrialisation&lt;/a&gt;&lt;object style=&quot;margin:0&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=agilittestsetindustrialisation-090923101902-phpapp02&amp;amp;stripped_title=agilit-tests-et-industrialisation&quot; /&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot; /&gt;&lt;param name=&quot;allowScriptAccess&quot; value=&quot;always&quot; /&gt;&lt;div style=&quot;font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;&quot;&gt;View more &lt;a style=&quot;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/&quot;&gt;presentations&lt;/a&gt; from &lt;a style=&quot;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/ohoareau&quot;&gt;ohoareau&lt;/a&gt;.&lt;/div&gt;&lt;/object&gt;&lt;/div&gt;
&lt;p&gt;Vos retours, remarques, critiques sont les bienvenues !&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/09/23/Agilite-Tests-et-Industrialisation-en-PHP#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/09/23/Agilite-Tests-et-Industrialisation-en-PHP#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/57</wfw:commentRss>
      </item>
    
  <item>
    <title>PS : c'est vraiment pas mal : [...], me suis bien éclaté aujourd'hui ;-)</title>
    <link>http://blog.phppro.fr/?post/2009/09/22/PS-%3A-cest-vraiment-pas-mal-%3A-%5B%5D-me-suis-bien-eclate-aujourdhui-</link>
    <guid isPermaLink="false">urn:md5:fe6172e82cba8552f553602430ee50c3</guid>
    <pubDate>Tue, 22 Sep 2009 18:14:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Méthodologie</category>
        <category>agile</category><category>code</category><category>développeur</category><category>equipe</category>    
    <description>&lt;p&gt;Retours sur la satisfaction d'un développeur.&lt;/p&gt;    &lt;p&gt;Récemment j'ai mis en place une pratique un peu particulière pour suivre des développeurs à distance et garder le contact&amp;nbsp;: &quot;La méthode du jour&quot;.&lt;br /&gt;
Le principe est simple, je leur demande de m'envoyer chaque soir par email un extrait du code développé dans la journée, une méthode de leur choix&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;une méthode du code applicatif&lt;/li&gt;
&lt;li&gt;une méthode de test&lt;/li&gt;
&lt;li&gt;un fragment de méthode&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;L'idée est de faire en sorte, que le lendemain matin, ils aient un feedback extérieur sur leur développement de la veille. Par mes soins (ils pourraient l'envoyer à d'autres développeur d'une autre équipe, ca marcherait aussi pour cet aspect). Un autre aspect intéressant est que je garde le contact avec le code en ne regardant que les échantillons &quot;sélectionnés&quot; par l'équipe (un par jour ET par développeur tout de même).&lt;/p&gt;


&lt;p&gt;Au delà de la revue de code technique, je peux capter les humeurs / ressentis / comportements, et proposer mon aide sur des sujets qui peuvent paraître moins triviaux&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;isolement d'un développeur&lt;/li&gt;
&lt;li&gt;sentiment de non confiance en soi&lt;/li&gt;
&lt;li&gt;divergence de pratiques&lt;/li&gt;
&lt;li&gt;baisse de la vélocité&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Je voulais vous faire partager cette pratique qui m'est bénéfique, et qui doit l'être à mes développeurs ;)&lt;/p&gt;


&lt;p&gt;La petite citation du jour d'un de mes développeurs illustre bien cet aspect &quot;behind the scene&quot; que l'on peut évaluer au delà de la réalité du code technique&amp;nbsp;:&lt;/p&gt;



&lt;blockquote&gt;&lt;p&gt;PS&amp;nbsp;: c'est vraiment pas mal: &lt;a href=&quot;http://blog.phppro.fr/?post/2009/09/22/...&quot; title=&quot;...&quot;&gt;...&lt;/a&gt;, me suis bien éclaté aujourd'hui :-)&lt;/p&gt;&lt;/blockquote&gt;


&lt;p&gt;Et vous avez vous d'autres pratiques pour &quot;faire&quot; parler vos développeurs et/ou entretenir des relations de suivi et d'échange à distance avec eux&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/09/22/PS-%3A-cest-vraiment-pas-mal-%3A-%5B%5D-me-suis-bien-eclate-aujourdhui-#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/09/22/PS-%3A-cest-vraiment-pas-mal-%3A-%5B%5D-me-suis-bien-eclate-aujourdhui-#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/56</wfw:commentRss>
      </item>
    
  <item>
    <title>La loi de Demeter où le principe de la connaissance minimale</title>
    <link>http://blog.phppro.fr/?post/2009/09/21/La-loi-de-Demeter-ou-le-principe-de-la-connaissance-minimale</link>
    <guid isPermaLink="false">urn:md5:d2212c82a734b530a8e141353d65605f</guid>
    <pubDate>Mon, 21 Sep 2009 10:17:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Méthodologie</category>
            
    <description>&lt;p&gt;Vous avez certainement déjà fait&amp;nbsp;: $this-&amp;gt;getFirstObject()-&amp;gt;getSecondObject()-&amp;gt;doSomething()&amp;nbsp;? Vous avez déjà certainement eu des soucis a repasser à différents endroits de votre code quand l'interface du &quot;SecondObject&quot; change, voici un principe de développement logiciel qui alors nous être utile...&lt;/p&gt;    &lt;p&gt;Le principe de Demeter dit qu'un objet a le droit d'accéder aux méthodes/opérations d'un autre objet, mais pas à celles d'un objet encapsulé dans un autre objet.
Concrètement, nous avons le droit de faire&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$this
    -&amp;gt;getFirstObject()
        -&amp;gt;doSomething(...);
&lt;/pre&gt;


&lt;p&gt;Mais nous n'avons pas le droit de faire&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$this
    -&amp;gt;getFirstObject()
        -&amp;gt;getSecondObject()
            -&amp;gt;doSomething();
&lt;/pre&gt;


&lt;p&gt;Ce qui nous oblige donc dans le &quot;first object&quot; à fournir autant d'opérations que nécessaire pour éviter que la classe (représentée par $this dans notre cas) ne sache quel objet réalise réellement l'opération.&lt;/p&gt;


&lt;p&gt;Par exemple, dans la ligne suivante&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$this
    -&amp;gt;getAdapter()
        -&amp;gt;getTypeDispositifService()
            -&amp;gt;contientDesPhases(
                $this
                    -&amp;gt;getAdapter()
                        -&amp;gt;getIdTypeDispositifConcours($concours));
&lt;/pre&gt;


&lt;p&gt;Le principe de Demeter n'est pas respecté car depuis le service (représenté par $this) nous n'avons pas le droit d'appeller une méthode sur le &quot;TypeDispositifService&quot;, nous n'avons le droit d'appeller que des méthodes de l'adapter, nous devons donc créer une méthode dans l'adapter qui encapsule tout ca, par exemple&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$this
    -&amp;gt;getAdapter()
        -&amp;gt;contientDesPhases(
             $this
                 -&amp;gt;getAdapter()
                     -&amp;gt;getIdTypeDispositifConcours($concours));
&lt;/pre&gt;


&lt;p&gt;avec l'implémentation suivante&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
class ...._Adapter_Default implements ..._Adapter_Interface
{
    ...
    /**
     * @return boolean
     */
    public function contientDesPhases($typeDispositif)
    {
        return $this
                         -&amp;gt;getTypeDispositifService()
                             -&amp;gt;contientDesPhases($typeDispositif);
    }
    ...
}
&lt;/pre&gt;


&lt;p&gt;Une des conséquences positives de l'application de la loi de Demeter est l'augmentation des degrés d'adaptabilité, évolutivité, maintenabilité du code / de l'application.
Une inconvénient est la nécessité d'encapsuler systématiquement les appels de second niveau, augmentant le nombre d'appel de méthodes à réaliser pour exécuter un code, ce point pouvant être relativement maîtrisé en mettant en oeuvre une architecture de type services la plus applatie possible (i.e. en évitant tant que possible les dépendances de dépendances de dépendances ... d'objets).&lt;/p&gt;


&lt;p&gt;J'applique cette loi sur tout mes développements et je la conseille à mes développeurs, et personnellement nous avons un retour d'expériences positifs.&lt;/p&gt;


&lt;p&gt;Pour une définition plus théorique et complète de la loi de Demeter, je vous propose de consulter &lt;a href=&quot;http://fr.wikipedia.org/wiki/Loi_de_D%C3%A9m%C3%A9ter&quot;&gt;Wikipédia&lt;/a&gt;.&lt;/p&gt;


&lt;p&gt;Et vous, est-ce une règle que vous appliquez&amp;nbsp;? En appliquez vous d'autres contradictoires / complémentaires&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/09/21/La-loi-de-Demeter-ou-le-principe-de-la-connaissance-minimale#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/09/21/La-loi-de-Demeter-ou-le-principe-de-la-connaissance-minimale#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/55</wfw:commentRss>
      </item>
    
  <item>
    <title>Faire le ménage dans le code : mettre le code mort à la poubelle ou le garder au cas où ?</title>
    <link>http://blog.phppro.fr/?post/2009/09/14/Faire-le-menage-dans-le-code-%3A-mettre-le-code-mort-a-la-poubelle-ou-le-garder-au-cas-ou</link>
    <guid isPermaLink="false">urn:md5:e9f322e9003c10bb4873e79de27d03b8</guid>
    <pubDate>Mon, 14 Sep 2009 09:30:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Méthodologie</category>
        <category>code mort</category><category>ménage</category><category>subversion</category>    
    <description>&lt;p&gt;Une question d'un développeur: &lt;br /&gt;
&lt;code&gt;La prochaine itération va comporter un bon nombre de tâches techniques et nous avons pensé «&amp;nbsp;faire le ménage&amp;nbsp;» dans notre code, mais que faire du code mort&amp;nbsp;? Faut-il supprimer les sections inutilisées &lt;a href=&quot;http://blog.phppro.fr/?post/2009/09/14/...&quot; title=&quot;...&quot;&gt;...&lt;/a&gt; en partant du principe qu’elles polluent les classes &lt;a href=&quot;http://blog.phppro.fr/?post/2009/09/14/...&quot; title=&quot;...&quot;&gt;...&lt;/a&gt; ou bien les conserver si un jour elles venaient à resservir ?&quot;&lt;/code&gt;
Ma réponse...&lt;/p&gt;    &lt;p&gt;Mon avis: supprimer sans état d'âme le code dit &quot;mort&quot;, à partir du moment ou il a été committer au moins une fois dans subversion.&lt;/p&gt;


&lt;p&gt;Il peut être envisageable de noter le numéro de révision ou la date ou de créer un tag dans subversion avant tout nettoyage de printemps. Ce qui vous permettra de retrouver plus facilement/rapidement le code ensuite.&lt;/p&gt;


&lt;p&gt;Rien ne vous empêche de garder une version en local du trunk à la révision actuelle, en plus du trunk sur lequel vous travaillé. Ce qui vous permettra d'avoir directement sous la main le code source &quot;legacy&quot; qui pourrait être de nouveau nécessaire.&lt;/p&gt;


&lt;p&gt;&lt;code&gt;Ne laissez pas de code mort dans le trunk, &quot;la mort amène la gangrène&quot; ;)&lt;/code&gt;&lt;/p&gt;


&lt;p&gt;Si vous pensez que les développements réalisés mettent en oeuvre un concept qui pourra être réutilisable, par contre le code lui non, alors écrivez un article wiki pour décrire ce pattern / concept / technique, ca vous prendra 15-30 minutes et ce sera facilement accessible pour tout le monde.&lt;/p&gt;


&lt;p&gt;N'oubliez pas qu'un de vos objectifs utopique est d'avoir le moins possible de lignes de code, et qu'elles soient le plus simple possible.&lt;/p&gt;


&lt;p&gt;Ma maxime préférée&amp;nbsp;: &lt;code&gt;&quot;L'excellence attire l'excellence&quot;&lt;/code&gt;&amp;nbsp;: si votre code est propre, enviables, beau, clean, smart (whatever you want), les nouveaux développements auront beaucoup plus de chances de l'être.&lt;/p&gt;


&lt;p&gt;Et vous qu'auriez vous répondu&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/09/14/Faire-le-menage-dans-le-code-%3A-mettre-le-code-mort-a-la-poubelle-ou-le-garder-au-cas-ou#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/09/14/Faire-le-menage-dans-le-code-%3A-mettre-le-code-mort-a-la-poubelle-ou-le-garder-au-cas-ou#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/54</wfw:commentRss>
      </item>
    
  <item>
    <title>Service/Adapter : mes adapters sont trop gros mon capitaine !</title>
    <link>http://blog.phppro.fr/?post/2009/09/11/Service/Adapter-%3A-mes-adapters-sont-trop-gros-mon-capitaine</link>
    <guid isPermaLink="false">urn:md5:e9bb9e68c65f98304482061dd65e5b9f</guid>
    <pubDate>Fri, 11 Sep 2009 15:17:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Méthodologie</category>
        <category>adapter</category><category>pattern</category><category>service</category>    
    <description>&lt;p&gt;Si vous implémentez le pattern Adapter, ou bien si vous suivez mes posts, vous avez entendu parler de la notion d'Adapter. Mon point de vue sur la question est qu'ils doivent être minimals (1 à 2 lignes par méthodes, aucune logique autre que l'appel à une méthode native php / extension). On arrive alors de temps en temps à une problématique&amp;nbsp;: les adapters ont tendance à vouloir &quot;grossir&quot;, quand on veux &quot;généraliser&quot; la logique dans le service (et qu'on relègue l'implémentation dans l'adapter). La contrainte des 1 à 2 lignes est alors difficile à respecter... ou presque&amp;nbsp;!&lt;/p&gt;    &lt;p&gt;Quand vous développez ou refactorez un morceau de code (Service, Adapter, ...) posez vous la question suivante&amp;nbsp;:&lt;/p&gt;


&lt;p&gt;&quot;Si je devais tester unitairement ce morceau de code quels seraient les impacts extérieurs (appels de méthodes externes à la classe, utilisation de singleton ou d'objet externes non passés en arguments et par exemple instancié dans le code...) ?&quot;&lt;/p&gt;


&lt;p&gt;Si la réponse est &quot;aucun&quot;, alors vous êtes en face d'un service.
Si la réponse n'est pas &quot;aucun&quot; alors vous êtes AUSSI en face d'un service si vous utilisez if, while, for, try, foreach, ... (i.e. si vous avez de la logique qui &quot;créé, modifie, contrôle l'information)&lt;/p&gt;


&lt;p&gt;Ensuite, imaginez que vous êtes un &quot;parser&quot; technique bête et méchant (je sais que ce n'est pas le cas), parcourez alors votre morceau de code, instruction par instruction, ligne par ligne de haut en bas. A chaque fois que vous tombez sur une utilisation d'une fonction native php qui a un impact sur le système (réseau, système de fichiers, mémoire vive / partagée, base de données, extension c...) alors vous êtes en face d'une méthode d'un adapter à fournir potentiellement.&lt;/p&gt;


&lt;p&gt;Souvent quand on me demande de regarder des adapters déjà développés, ils ont beau s'appeller ..../Adapter/....php, il s'agit de service&amp;nbsp;! car ils ont  une succession de méthodes qui sont volumineuses (beaucoup plus qu'une ligne chacune) avec de la logique de if, while, for, try, foreach...&lt;/p&gt;


&lt;p&gt;Par contre, je suis d'accord que certaines au moins de ces méthodes doivent être dans l'adapter et pas dans le service (peut être pas toutes cependant).&lt;/p&gt;


&lt;p&gt;Quand vous êtes en face d'un problème comme cela, vous devez au moins vous poser 2 questions&amp;nbsp;:&lt;/p&gt;


&lt;p&gt;1/ Est-il possible de déplacer cette méthode de cet adapter dans son service (au dessus), en fournissant éventuellement de nouvelle méthodes dans l'adapter qui sont plus unitaires et qui sont orchestrées par cette logique qui arrive dans le service&amp;nbsp;?
2/ Est-il plus pertinent de garder cette méthode dans l'adapter (bien placée) et de créer un nouveau service, dit &quot;technique&quot;, qui sera utilisé par l'adapter en une seule ligne, ce nouveau service proposant une ou plusieurs méthodes, et ayant lui même un adapter&amp;nbsp;?&lt;/p&gt;


&lt;p&gt;Si S = Service, A = Adapter, le schéma habituel / commun est le suivant&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
S1 =&amp;gt; A1.1  (i.e. Le service 1 utilise son adapter 1)
&lt;/pre&gt;


&lt;p&gt;Mais on peut aussi chainer les utilisations de services&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
S1 =&amp;gt; A1.1 =&amp;gt; S2 =&amp;gt; A2.1  (i.e. Le service 1 utilise son adapter 1 qui lui même devant réaliser des opérations complexes, utilise un second service (2) qui lui même utilise son adapter 1)
&lt;/pre&gt;


&lt;p&gt;Par exemple,dans le cas simple (avec beaucoup de lignes dans l'adapter), l'implémentation est&amp;nbsp;:&lt;/p&gt;


&lt;p&gt;Service(ex: AccuseReception) =&amp;gt; Adapter(ex: Feed)&lt;/p&gt;


&lt;p&gt;dans le but de simplifier l'adapter Feed, de rendre le code plus testable et plus module on peut avoir&amp;nbsp;:&lt;/p&gt;


&lt;p&gt;Service(ex: AccuseReception) =&amp;gt; Adapter(ex: Feed) =&amp;gt; Service(ex: XMLParser) =&amp;gt; Adapter(ex: Native ou SimpleXMLElement)&lt;/p&gt;


&lt;p&gt;Ce qui nécessite la création d'un service technique 'XMLParser' avec sa batterie d'adapter (Default/Native, Mock, ...)&lt;/p&gt;


&lt;p&gt;Le service XMLParser devient utilisable à son tour directement en tant que service depuis un contrôleur (MVC), ou un adapter.&lt;/p&gt;


&lt;p&gt;L'intérêt est alors que l'on peut facilement tester unitairement (PHPUnit, SimpleTest) les services (i.e AccuseReception et XMLParser dans notre exemple), alors qu'il est difficile (personnellement je le déconseille) de tester les adapter (et donc l'implémentation initiale Feed qui faisait plusieurs centaines de lignes).&lt;/p&gt;


&lt;p&gt;Attention cependant à respecter la règle suivante dans la mesure du possible&amp;nbsp;:&lt;/p&gt;


&lt;p&gt;&quot;Un service a le droit uniquement d'appeller ses propres méthodes ou celle de son adapter, mais pas celle d'un autre service&quot;.&lt;/p&gt;


&lt;p&gt;Idéalement il faut donc créer une méthode dans l'adapter et passer par cette méthode pour appeller le XMLParser, et non utiliser le XMLParser en direct.&lt;/p&gt;


&lt;p&gt;Il peut être envisageable d'avoir &quot;plusieurs&quot; adapter en même temps pour un même service (adapter read, adapter write...), cela est potentiellement intéressant car permet une réutilisabilité accrue, attention par contre à la complexité de gestion. Je recommande de n'avoir qu'un seul adapter par service, cela est plus facile à gérer, et permet de garantir qu'un adapter est 100% adapté au service qui l'utilise (on ne se retrouve pas dans des situations d'interblocage dans lesquels l'adapter générique de lecture dans un fichier qu'on utilise n'a pas la méthode toto qui est utile par un des services qui l'utilise (mais uniquement que par celui la !))&lt;/p&gt;


&lt;p&gt;Tout cela est un peut théorique, j'en conviens, dans la forme.&lt;/p&gt;


&lt;p&gt;Je peux tenter d'illustrer mon propos sur des exemples précis / ciblés si vous me le demandez.&lt;/p&gt;


&lt;p&gt;Et vous quelles sont vos pratiques concernant le découpage Service/Adapter&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/09/11/Service/Adapter-%3A-mes-adapters-sont-trop-gros-mon-capitaine#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/09/11/Service/Adapter-%3A-mes-adapters-sont-trop-gros-mon-capitaine#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/53</wfw:commentRss>
      </item>
    
  <item>
    <title>Tests d'intégration, quézako ?</title>
    <link>http://blog.phppro.fr/?post/2009/08/14/Tests-d-integration-quezako</link>
    <guid isPermaLink="false">urn:md5:b413670be73525b6eceb954267432043</guid>
    <pubDate>Fri, 14 Aug 2009 11:17:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Méthodologie</category>
        <category>api</category><category>bouchon</category><category>phpunit</category><category>test</category><category>tests d intégration</category><category>webservices</category>    
    <description>&lt;p&gt;Les tests unitaires c'est bien, mais ce n'est pas suffisant, surtout quand on a besoin de tester que notre application s'intègre effectivement bien avec un webservice d'un de nos fournisseurs...
Parlons de tests d'intégration, donc.&lt;/p&gt;    &lt;h2&gt;Qu'est ce q'un test d'intégration&amp;nbsp;?&lt;/h2&gt;


&lt;p&gt;De mon point de vue, un test d'intégration sert à tester l'intégration (ca paraît trivial mais il est important de le rappeller).&lt;br /&gt;
Je ne pense donc pas qu'il soit nécessaire de tester TOUTE l'application, ou TOUTES les fonctionnalités de l'application, ou toutes les lignes de code de l'application grâce aux tests d'intégrations, mais uniquement que les &quot;intégrations&quot; (i.e. &quot;les communications / intéractions de notre code avec un autre code que nous n'avons pas développé&quot;) qui sont effectivement réalisées dans notre application.&lt;/p&gt;


&lt;h2&gt;Lister et catégoriser les intégrations&lt;/h2&gt;


&lt;p&gt;Il faut donc commencer à lister les &quot;intégrations&quot; de notre application. Elles peuvent être multiples&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;utilisation de base de données type SQL&lt;/li&gt;
&lt;li&gt;utilisation de webservices (soap, rest, xml, http, ...)&lt;/li&gt;
&lt;li&gt;import / export de fichiers&lt;/li&gt;
&lt;li&gt;intéractions avoir d'autres applications grâces à des protocoles non web standard ou propriétaire (ssh, ftp, scp, cft, ldap...)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;et pour les plus puristes d'entre nous&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;utilisation du système de fichier&lt;/li&gt;
&lt;li&gt;utilisation de toute fonction native à la plateforme ayant un impact sur le système ou l'éco-système (réseau, disque, mémoire vive...)&lt;/li&gt;
&lt;li&gt;utilisation de librairies spécifiques (génération de pdf, génération d'images et de graphiques...)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En résumé, une &quot;intégration&quot; concerne l'utilisation de tout code ou librairie que vous N'AVEZ PAS développé vous même et qui est utilisé par votre application.&lt;/p&gt;


&lt;p&gt;On peut les classer en deux types d'intégration&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;les intégrations distantes&amp;nbsp;: i.e. &quot;ressources utilisées / accédées via un protocole réseau&quot;, sont les plus &quot;visibles&quot; et facile à tester, elles concernent les utilisations de ressources externes telles que des webservices ou du ldap par exemple.&lt;/li&gt;
&lt;li&gt;les intégrations locales&amp;nbsp;: i.e. &quot;API intégrées dans le langage du code développé&quot;, sont les plus vicieuses et les plus difficile à tester, elles concernent l'utilisation de librairies (notamment PHP), d'extension / module, de framework, de ressources locales (disque dur, mémoire vive...)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Les types de tests d'intégration&lt;/h2&gt;


&lt;p&gt;Basiquement, on distingue 2 types de test d'intégration&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;les tests qui coûtent cher (long ou financièrement coutêux)&lt;/li&gt;
&lt;li&gt;les tests qui ne coûtent pas cher (rapide et sans / peu d'impact notable sur notre éco-système)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Une chose est sûr, tout test d'intégration qui est (facilement) automatisable doit être automatisé.&lt;br /&gt;
Attention cependant, en fonction du type de test (cher / pas cher), on peut décider d'exécuter régulièrement ou non le test d'intégration, tout n'est pas nécessairement candidat à l'exécution régulière (imaginez un test d'envoi de bulk massif de 100 sms via un broker, cela pourrait vite vous coûter cher en temps (lire les 100 sms) et en argent (à 0,07 centimes minimum le sms, ca fait quand même 7€ par exécution du test qui partent en fumée !, imaginez si vous exécutez ce test automatiquement à chaque commit, cela va vite vous faire mal d'appuyer sur le bouton &quot;commit&quot; !).&lt;br /&gt;
De mon point de vue, il y a donc une différence entre automatisation et exécution régulière. Je distingue donc 3 niveaux d'automatisation de tests d'intégration&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;test d'intégration manuel&amp;nbsp;: i.e. procédure de contrôle décrite mais réalisée manuellement (via des clics et des saisies clavier par exemple)&lt;/li&gt;
&lt;li&gt;test d'intégration semi-automatique&amp;nbsp;: i.e. test qui a été codé, est reproductible et déclenchable via un programme pré-programmé, mais nécessite une action humaine comme déclencheur. L'action humaine peut être un clic, une commande console à exécuter ou une url à appeller via un navigateur, par exemple.&lt;/li&gt;
&lt;li&gt;test d'intégration automatique&amp;nbsp;: i.e. idem semi-automatique mais déclenché sans l'intervention de l'homme par un évènement pré-programmé (ex: commit subversion, build nocturne ...)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;&quot;Quel outil choisir pour mes tests d'intégration ?&quot; où la question dont la réponse n'est pas (la plus) importante&lt;/h2&gt;


&lt;p&gt;Je vous voit tous à attendre avec une impatience une liste d'outils populaires pour rédiger et exécuter vos tests d'intégrations. Donc, la voici&amp;nbsp;!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PHP.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Trève de plaisanterie, de mon point de vue, le vrai sujet au-delà de la question de savoir si vous allez pouvoir utiliser tel ou tel outils meilleur que son voisin pour vos tests d'intégrations, est plutôt de savoir et de comprendre les enjeux des tests d'intégration, et les problèmes que vous allez devoir résoudre.&lt;br /&gt;
Je vous conseille d'utiliser des outils que vous maîtrisez et qui permettent de faire des rapports d'erreurs d'exécutions tels que les outils de type xUnit (PHPUnit, voire SimpleTest) qui sont adaptés pour exécuter du code PHP et faire un rapport sur des assertions bonnes ou fausses sur le résultat.&lt;/p&gt;


&lt;h2&gt;Les contexts d'intégration&amp;nbsp;: où le niveau de cloisonnement des tests&lt;/h2&gt;


&lt;p&gt;Qu'est ce qui se cache derrière le terme barbare de &quot;cloisonnement des tests d'intégrations&quot;&amp;nbsp;? Tout simplement le niveau d' &quot;impact&quot; que vous avez sur l'application avec laquelle vous êtes intégré, on peut en effet distinguer différents niveaux d'impact en fonction des environnements mis à disposition pour l'intégration&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;bouchon local&amp;nbsp;: cloisonnement total car pas de persistence côté application distante, mais vous devez installer localement et avec vos compétences le bouchon éventuellement fourni par l'équipe de l'application à laquelle vous souhaitez vous intégrer&lt;/li&gt;
&lt;li&gt;bouchon distant&amp;nbsp;: cloisonnement total car pas de persistence côté application distante&lt;/li&gt;
&lt;li&gt;environnement de test dédié&amp;nbsp;: cloisonnement fort car votre application est la seule à appeler cette instance de l'application distante&lt;/li&gt;
&lt;li&gt;environnement de test mutualisé&amp;nbsp;: cloisonnement faible mais impact minime (instance de test)&lt;/li&gt;
&lt;li&gt;environnement de préproduction&amp;nbsp;: cloisonnement faible voir inexistant mais impact non critique (instance de préproduction)&lt;/li&gt;
&lt;li&gt;environnement de production&amp;nbsp;: aucun cloisonnement, impact maximal (instance de production)&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Plus vous aurez un cloisonnement fort, plus vos tests seront faciles et peu coûteux pour une exécution (très) régulière, et inversement.&lt;/p&gt;


&lt;h2&gt;L'idée reçue&amp;nbsp;: un test d'intégration est un test de bout en bout graphique ou fonctionnel&lt;/h2&gt;


&lt;p&gt;Il sera toujours plus difficile d'automatiser (et de rejouer régulièrement) le test de bout en bout qui vise à vérifier que la valeur 2 est bien présente dans la case 3 du tableau de la deuxième page (sachant que la valeur 2 est récupérée via un appel de webservice) plutôt que d'appeller en direct le webservice via une API développée dans votre code.&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;Attention donc, de mon point de vue un test d'intégration n'est pas un test bout en bout graphique ou fonctionnel, mais plutôt un moyen de tester que les couches basses de votre application savent communiquer avec l'application distante selon le ou les contrats de services définis.&lt;/p&gt;


&lt;h2&gt;L'API locale&amp;nbsp;: La clé pour rédiger facilement vos tests d'intégration&lt;/h2&gt;


&lt;p&gt;Pour tester &quot;uniquement&quot; l'intégration, il faut donc privilégier le développement d'API interne à votre code qui permet de &quot;cacher / envelopper&quot; l'appel à l'application distante à laquelle vous vous intégrée. Ce qui vous permettra de facilement développer des scripts php de tests d'intégration en utilisant PHPUnit par exemple.
Cette API locale devra fournir l'&quot;enrobage&quot; de l'appel à l'application distante en fournissant à votre code des fonctionnalités &quot;locales&quot; faciles à utiliser et faciles à bouchonner (notamment pour vos tests unitaires).&lt;br /&gt;
Ainsi lors de vos développements, vous devriez à minima avoir 2 types de fonctions / méthodes / classes&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;les fonctions / méthodes / classes du coeur de votre application&lt;/li&gt;
&lt;li&gt;les fonctions / méthodes / classes fournissant une API d'accès aux applications et fonctionnalités distantes à intégrer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Une fois développé cette couche / API locale, il vous sera plus facile de réaliser des scripts PHP de tests d'intégration utilisant cette API en direct et manipulant l'API pour tester les différentes fonctionnalités disponibles.&lt;br /&gt;
Une piste pour la réalisation de cette API locale serait la création systématique d'une classe &amp;lt;ApplicationAIntegrer&amp;gt;Service qui fourni les différentes fonctionnalités à intégrer sous forme de méthode, et de créer pour ce service un adapter sous-jacent par défault (réalisant effectivement les appels distants) et à minima un adapter bouchon (mock) utile pour les tests unitaires par exemple et le développement. Je vous invite à lire mes billets sur le concept de service et d'adapter pour plus de précision et d'exemple sur comment s'y prendre en PHP via un exemple.&lt;/p&gt;


&lt;p&gt;Cette technique d'API locale (peu importe le protocole réseau ou non) est systématiquement utilisée par les grands fournisseurs de webservices de nos jours (eBay / PayPal / Google ...) avec de plus en plus une intégration dans les frameworks populaires alla Zend Framework ou Symfony.&lt;/p&gt;


&lt;h2&gt;Où placer mes tests d'intégrations&amp;nbsp;?&lt;/h2&gt;


&lt;p&gt;Dans le répertoire locale de votre application&amp;nbsp;: ./tests/integration, par exemple ;)&lt;br /&gt;
Vous pouvez créer un répertoire par &quot;intégration&quot; et un fichier par cas de test d'intégration, ou bien si vous utilisez PHPUnit par exemple, une classe de test par &quot;intégration&quot; et une méthode de test par cas d'intégration.&lt;/p&gt;


&lt;h2&gt;Pour la suite, à vous de me dire ce que vous voulez...&lt;/h2&gt;


&lt;p&gt;Je vais m'arrêter là pour l'instant, si vous souhaitez des précisions ou des exemples sur des aspects ou des points concrets, vous n'avez qu'à demander ;)&lt;/p&gt;


&lt;p&gt;Et sinon, vous comment faite vous pour écrire vos tests d'intégration&amp;nbsp;?&lt;/p&gt;


&lt;p&gt;PS: Bien sûr il manque encore au moins un test, le test de bout en bout ;)&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/08/14/Tests-d-integration-quezako#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/08/14/Tests-d-integration-quezako#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/52</wfw:commentRss>
      </item>
    
  <item>
    <title>Tests Unitaires : ma philosophie via un exemple</title>
    <link>http://blog.phppro.fr/?post/2009/08/10/Tests-Unitaires-%3A-ma-philosophie-via-un-exemple</link>
    <guid isPermaLink="false">urn:md5:745113614016ef7377f5af80234fd87e</guid>
    <pubDate>Mon, 10 Aug 2009 08:38:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Méthodologie</category>
        <category>adapter</category><category>bouchon</category><category>mock</category><category>phpunit</category><category>service</category><category>test</category><category>test unitaire</category>    
    <description>&lt;p&gt;Je vois souvent des tests dit unitaires chez mes clients, mais qui se connectent à la base de données, font des requêtes réseaux...&lt;br /&gt;
Voici une petite présentation de ce que personnellement j'appelle tests unitaires, par l'exemple.&lt;/p&gt;    &lt;h2&gt;Qu'est ce qu'un test&amp;nbsp;?&lt;/h2&gt;


&lt;p&gt;Un test est un morceau de code écrit dans un langage quelconque (pas forcément celui du code de votre application, peut être le langage naturel, un document word...), qui&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;permet de vérifier que le comportement de l'application (ou d'un morceau de l'application) est conforme au comportement attendu&lt;/li&gt;
&lt;li&gt;permet de décrire une façon reproductible de réaliser une vérification précise sur l'application&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Il existe plusieurs typologies de tests et outils associés en PHP&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tests unitaires (ex: PHPUnit, SimpleTest, ...)&lt;/li&gt;
&lt;li&gt;tests fonctionnels (ex: GreenPepper, Fitnesse, ...)&lt;/li&gt;
&lt;li&gt;tests d'intégrations (ex: PHPUnit, ...)&lt;/li&gt;
&lt;li&gt;tests graphiques (ex: Selenium, ...)&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Qu'est ce qu'un test unitaire&amp;nbsp;?&lt;/h2&gt;


&lt;h3&gt;Définition générale&lt;/h3&gt;


&lt;p&gt;Un test unitaire est un morceau de code de préférence minimaliste qui a pour but de valider le bon fonctionnement de l'utilisation d'une routine / fonction / méthode dans un CAS PRECIS (et non dans tous les cas d'utilisation possible de la routine).&lt;br /&gt;
L'objectif d'un test unitaire étant de tester la logique d'une routine en particulier, il est nécessaire de ne pas être parasité par les éventuels bugs ou mauvaises utilisations de méthodes/fonctions/routines dont la méthode testées aurait besoin.&lt;br /&gt;
C'est pour cela qu'on met en oeuvre un mécanisme de bouchonnage des dépendances visant à ne pas déclencher tout code logique qui ne doit pas être testé dans ce test unitaire.&lt;/p&gt;


&lt;h3&gt;Implémentation commune en PHP&lt;/h3&gt;


&lt;p&gt;Un test unitaire PHP est un test unitaire écrit en PHP en utilisant dans la plupart des cas un framework de test unitaire de type xUnit.&lt;br /&gt;
Le plus populaire étant PHPUnit développé par Sebastian Bergmann depuis plusieurs années et écrit 100% en PHP.&lt;/p&gt;


&lt;p&gt;Le principe est simple&amp;nbsp;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;on écrit une classe de test qui hérite d'une classe du framework&lt;/li&gt;
&lt;li&gt;on écrit autant de méthode à l'intérieur de cette classe que de tests unitaire à réaliser&lt;/li&gt;
&lt;li&gt;en général on écrit une classe de tests pour tester une classe (ou une fonction native) du code applicatif (relation 1&amp;lt;=&amp;gt;1)&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Exemple de fichier de test tests/phpunit/StrlenTest.php&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;

&amp;lt;?php
class StrlenTest extends PHPUnit_Framework_TestCase
{
    public function testStrlenForEmptyStringReturn0()
    {
        $this-&amp;gt;assertEquals(0,strlen(''));
    }
    public function testStrlenForNotEmptyStringReturnStringLength()
    {
        $this-&amp;gt;assertEquals(3, strlen('php'));
        $this-&amp;gt;assertEquals(14,strlen('rasmus lerdorf'));
    }
    public function testStrlenForArrayReturn1()
    {
        $this-&amp;gt;assertEquals(1,strlen(array());
    }
}
&lt;/pre&gt;


&lt;h3&gt;Implémentation sur un projet exemple&lt;/h3&gt;


&lt;p&gt;Les tests unitaires dans le projet exemple, sont des tests unitaires dont l'objectif est de bouchonner un maximum les dépendances.&lt;/p&gt;


&lt;h4&gt;Que ne dois-je pas tester avec les tests unitaires&amp;nbsp;?&lt;/h4&gt;


&lt;p&gt;On pourrait rephraser le titre de ce paragraphe en&amp;nbsp;: &quot;Que ne dois-je pas tester/exécuter/déclencher/appeller avec les tests unitaires ?&quot;, la réponse serait donc&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;les connexions / requêtes sur la base de données depuis PHP&lt;/li&gt;
&lt;li&gt;les appels réseaux depuis PHP&lt;/li&gt;
&lt;li&gt;l'utilisation de fonctions / classes/méthodes natives depuis PHP (sauf à des fins didactiques)&lt;/li&gt;
&lt;li&gt;l'utilisation d'api / librairies / frameworks / code source non développées expressément dans la classe que je suis en train de tester&lt;/li&gt;
&lt;li&gt;le contenu du bootstrap ou de l'initialisation de votre application&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Que dois-je tester avec les tests unitaires&amp;nbsp;?&lt;/h4&gt;


&lt;p&gt;Autrement dit quand vous faites un tests unitaires, vous n'avez le droit d'exécuter (au sens &quot;passer par les lignes php&quot;) que&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;le code écrit dans la méthode de test&lt;/li&gt;
&lt;li&gt;le contenu (intégral) de la méthode en cours de test&lt;/li&gt;
&lt;li&gt;le contenu des bouchons nécessaires au fonctionnement de la méthode testée&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;c'est tout.&lt;/p&gt;


&lt;p&gt;Aucun appel&amp;nbsp;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;système/disque&lt;/li&gt;
&lt;li&gt;réseau&lt;/li&gt;
&lt;li&gt;mémoire vive (autre que l'exécution des lignes de code listées ci-dessus)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;n'est autorisé dans un test unitaire.&lt;/p&gt;


&lt;p&gt;Comme toujours il existe des exceptions&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;vous avez le droit de faire un appel système/disque pour lire un fichier qui contient des données de tests, si ce fichier est utilisé par le bouchon&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Comment détecter ce qu'il faut tester de ce qu'il faut bouchonner&amp;nbsp;?&lt;/h3&gt;


&lt;p&gt;Prenons un exemple, on souhaite développer une fonctionnalité de listing filtrée des accusés réception de bordereaux de dépôt de plis.&lt;br /&gt;
Notre application contacte un webservice pour récupérer la liste des bordereaux de dépôt de plis sur une date précise et tris cette liste par numéros de plis:&lt;/p&gt;


&lt;p&gt;Voici notre démarche&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;nous choisissons le nom de notre service: library/Bordereaux/Service.php (classe Bordereaux_Service)&amp;nbsp;:&lt;/li&gt;
&lt;li&gt;nous créons un fichier/classe de test pour notre nouveau service: tests/phpunit/Bordereaux/ServiceTest.php (classe Bordereaux_ServiceTest)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
&amp;lt;?php

require_once dirname(__FILE__).'/../../../contexts/phpunit.php';

class Bordereaux_ServiceTest extends PHPUnit_Framework_TestCase 
{
    /**
     * Service testé
     * 
     * @var Bordereaux_Service
     */
    private $f;
    /**
     * Adapter bouchon (mock)
     *
     * @var Bordereaux_Adapter_Mock
     */
    private $m;

    public function setUp()
    {
        $this-&amp;gt;f = new Bordereaux_Service();
        $this-&amp;gt;m = new Bordereaux_Adapter_Mock();

        // bouchonne le service testé
        $this-&amp;gt;f-&amp;gt;setAdapter($this-&amp;gt;m);
    }
}
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;nous créons la première méthode de test unitaire pour le cas en erreur (plantage du webservice)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
&amp;lt;?php
...
    public function testListSortedBordereauxForNetworkErrorThrowException()
    {
        $this-&amp;gt;m-&amp;gt;addExpectedResult('listBordereaux',new RuntimeException('This is my network exception'));
        try {
            $this-&amp;gt;f-&amp;gt;listSortedBordereaux('2009-08-03');
            $this-&amp;gt;fail('No network exception thrown');
        } catch (RuntimeException $e) {
            $this-&amp;gt;assertEquals('This is my network exception',$e-&amp;gt;getMessage());
        }
    }
...
&lt;/pre&gt;


&lt;pre&gt;* nous créons l'adapter &quot;bouchon&quot; (ou mock) dans le fichier library/Bordereaux/Adapter/Mock.php&lt;/pre&gt;

&lt;pre&gt;
&amp;lt;?php

class Bordereaux_Adapter_Mock implements Bordereaux_Adapter_Interface
{
    /**
     * List of results/exceptions to return/throw
     *
     * @var array
     */
    private $methods;

    public function addExpectedResult($method,$result)
    {
        if (!isset($this-&amp;gt;methods[$method])) {
            $this-&amp;gt;methods[$method] = array();
        }
        $this-&amp;gt;methods[$method][] = $result;
        return $this;
    }

    protected function processMethod($method)
    {
        if (!isset($this-&amp;gt;methods[$method])) {
            throw new RuntimeException(&amp;quot;No result set for method '$method'&amp;quot;);
        }
        $r = array_shift($this-&amp;gt;methods[$method]);
        if (true === ($r instanceof Exception)) {
            throw $r;
        }
        return $r;
    }

    public function listBordereaux($day)
    {
        return $this-&amp;gt;processMethod(__FUNCTION__);
    }
}

&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;nous créons/développons l'adapter par défault qui réalisera le travaille réel d'appeler le webservice dans le fichier library/Bordereaux/Adapter/Default.php&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
&amp;lt;?php

class Bordereaux_Adapter_Default implements Bordereaux_Adapter_Interface
{
    /**
     * à noter l'utilisation d'un service Rest_Service, à développer.
     * Comme nous en avons besoin maintenant, nous devons arrêter temporairement
     * le développement en cours, pour développer (avec la même pratique !)
     * ce service. Une fois totalement testé et développé, nous pourrons revenir
     * à ce développement.
     *
     * Il est important de noter ici, que dans la méthode ci-dessous :
     * - il est INTERDIT de faire plus d'une ligne de code
     * - il est INTERDIT de faire un try/catch ou de lever explicitement une exception
     */
    public function listBordereaux($day)
    {
        return Rest_Service::factory()-&amp;gt;get(str_replace(array('%{day}'),array($day),cfgGet('ws.bordereaux.url')));
    }
}

&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;nous créons l'interface des adapter dans le fichier library/Bordereaux/Adapter/Interface.php&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
&amp;lt;?php

interface Bordereaux_Adapter_Interface
{
    /**
     * List all bordereaux picked up from the webservice
     * 
     * @return array of bordereaux
     * @throws Exception if network error
     */
    public function listBordereaux($day);
}

&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;nous créons le fichier et la classe (minimale) du service avec la méthode (vide) dans le fichier library/Bordereaux/Service.php&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;

class Bordereaux_Service
{
    /**
     * Underlying adapter
     *
     * @var Bordereaux_Adapter_Interface
     */
    private $adapter;

    public function __construct()
    {
        $this-&amp;gt;adapter = new Bordereaux_Adapter_Default();
    }

    public function listSortedBordereaux($day)
    {
        $bordereaux = $this-&amp;gt;adapter-&amp;gt;listBordereaux($day);
        // les deux lignes qui suivent ne seront présentes qu'à partir du 2ième test unitaire
        usort($bordereaux,array($this,'compareBordereauxByPliNumber'));
        return $bordereaux;
    }
    /**
     * Compare pli id and return -1, 0, 1 (helper method used by usort())
     *
     * @return integer
     */
    public function compareBordereauxByPliNumber($bordereau1,$bordereau2)
    {
        return -1;
        // à partir du deuxième test unitaire nous pourrons faire des tests sur cette méthode, qui contiendra alors (commentez alors la ligne ci-dessus) :
        return $bordereau1['pliId'] &amp;lt; $bordereau2['pliId'] ? 1 : ($bordereau1['pliId'] &amp;gt; $bordereau2['pliId'] ? -1 : 0);
    }
}
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;nous pouvons maintenant faire nos tests unitaires pour les autres cas et notamment le cas nominal qui renvoie des bordereaux précis pour une date précise&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
&amp;lt;?php
...
    public function testListSortedBordereauxForDateContainingBordereauxReturnBordereauxListAsArray()
    {
        $expected = json_decode('[{&amp;quot;pliId&amp;quot;:&amp;quot;903&amp;quot;},{&amp;quot;pliId&amp;quot;:&amp;quot;1001&amp;quot;},{&amp;quot;pliId&amp;quot;:&amp;quot;1354&amp;quot;}]',true);

        $this-&amp;gt;m-&amp;gt;addExpectedResult('listBordereaux',json_decode('[{&amp;quot;pliId&amp;quot;:&amp;quot;1001&amp;quot;},{&amp;quot;pliId&amp;quot;:&amp;quot;903&amp;quot;},{&amp;quot;pliId:&amp;quot;1354&amp;quot;}]',true));
        
        $result = $this-&amp;gt;f-&amp;gt;listSortedBordereaux('2009-08-03');

        $this-&amp;gt;assertEquals($expected,$result);
    }
}
&lt;/pre&gt;


&lt;p&gt;Dans ces tests unitaires le webservices n'est JAMAIS appelé&amp;nbsp;! Pourtant nous réalisons quand même des tests et notre application fonctionne correctement.
Nous avons bouchonné l'appel réseau en interceptant la méthode listBordereaux() de l'adapter et en faisant en sorte qu'elle renvoie ce que nous souhaitons pour nos tests.&lt;/p&gt;


&lt;p&gt;Bien entendu il manque un test (non unitaire cette fois-ci) qui nous permet de vérifier que l'intégration avec le webservices est fonctionnelle.&lt;/p&gt;


&lt;p&gt;Et vous qu'est ce que l'expression &quot;test unitaire&quot; signifie pour vous&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/08/10/Tests-Unitaires-%3A-ma-philosophie-via-un-exemple#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/08/10/Tests-Unitaires-%3A-ma-philosophie-via-un-exemple#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/51</wfw:commentRss>
      </item>
    
  <item>
    <title>subversion + fichier .ini + include_path = gestion dynamiques des dépendances (librairies,...)</title>
    <link>http://blog.phppro.fr/?post/2009/08/07/subversion-fichier-ini-include_path-gestion-dynamiques-des-dependances-librairies</link>
    <guid isPermaLink="false">urn:md5:b787e882008881d5783ee3d2e324fc4a</guid>
    <pubDate>Fri, 07 Aug 2009 08:50:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Trucs et astuces</category>
        <category>contexte</category><category>dependance</category><category>include_path</category><category>ini</category><category>subversion</category>    
    <description>&lt;p&gt;Votre application utilise certainement le tout dernier framework dernier cri, ou encore LA librairie ultime pour faire du PDF ou de l'ODT en PHP..
Ou encore, votre application dépend d'une autre application que vous avez réalisé ou interne à votre entreprise, elle a besoin de certaines classes par exemple.&lt;/p&gt;


&lt;p&gt;Voici une méthode (il en existe certainement plein d'autres !) vous permettant de gérer vos dépendances pour être sûr de travailler toujours sur la bonne version d'une librairie, et pas sur une version modifiée en local sur votre poste et qui serait différente de celle sur votre serveur de production&amp;nbsp;!&lt;/p&gt;    &lt;h2&gt;Subversion&amp;nbsp;: la base de tout&lt;/h2&gt;


&lt;p&gt;Subversion (ou CVS, même si je conseille de l'abandonner au profit de SVN) est un outil de gestion de source/version qui permet de centraliser une copie du code source de votre application sur un serveur central.&lt;br /&gt;
Cet outil permet entre autre de gérer la synchronisation des développement entre plusieurs développeurs d'une équipe, et de garder la version officielle du code source de votre application, ainsi qu'éventuellement toutes les releases officielles que vous auriez sorties.&lt;/p&gt;


&lt;p&gt;Au delà de &quot;maintenir&quot; le code source de votre application, SVN, de son petit nom, peut aussi maintenir le code source de plusieurs applications, chacune ayant une adresse&amp;nbsp;:&lt;/p&gt;


&lt;p&gt;(exemple d'une installation de subversion en mode DAV/Svn avec Apache)&lt;/p&gt;

&lt;pre&gt;
Mon application 1 =&amp;gt; http://mon-serveur-svn/mon-application-1/trunk
Mon application 2 =&amp;gt; http://mon-serveur-svn/mon-application-2/trunk
&lt;/pre&gt;


&lt;p&gt;Imaginons que l'application 1 ait besoin de certaines classes de l'application 2 pour fonctionner, il est nécessaire lorsque l'on récupère le code source de l'application 1 de récupérer aussi le code source de l'application 2.&lt;/p&gt;


&lt;p&gt;Ces opérations peuvent être réalisées grâce à un &quot;checkout&quot; via subversion (i.e. &quot;récupération en local du code source de l'application en restant lié au serveur central), ou bien une mise à jour via un &quot;update&quot; (i.e. &quot;je synchronise la copie locale du code source que j'ai sur mon ordinateur avec celle du serveur en récupérant toutes les différences sur mon ordinateur local&quot;)&lt;/p&gt;


&lt;p&gt;En règle général, ces opérations sont manuelles, on utilise souvent un client SVN pour cela, comme par exemple TortoiseSVN ou bien Eclipse/Subclipe/Subversive (entre autres).&lt;/p&gt;


&lt;h2&gt;Dépot de dépendances local&lt;/h2&gt;


&lt;p&gt;Quand je développe en local, j'ai donc besoin&amp;nbsp;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;du code source de mon application (en cours de développement&amp;nbsp;: trunk)&lt;/li&gt;
&lt;li&gt;du code source de toutes les dépendances dont mon application à besoin (versions précises)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si j'ai récupéré en local les différentes dépendances de mon application, il faut maintenant que je paramètre mon application pour qu'elle trouve les fichiers en fonction de leur emplacement.&lt;br /&gt;
Typiquement les répertoires qui contiennent les classes de l'application 2 dont j'ai besoin dans l'application 1 doivent être disponible dans l'include path de mon application&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Exemple dans le fichier contexts/@common.php (inclus par tous les fichiers de mon application)&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
&amp;lt;?php

set_include_path(get_include_path().PATH_SEPARATOR.dirname(__FILE__).'/../dependencies/mon-application-2/');

// ...
&lt;/pre&gt;


&lt;p&gt;Imaginons que mon-application-2 contiennent entre autres des classes mais aussi des fichiers d'autres types, en règle général on va plutôt isoler dans un répertoire les classes php, par exemple library/, nous aurons donc&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Exemple dans le fichier contexts/@common.php (inclus par tous les fichiers de mon application)&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
&amp;lt;?php

set_include_path(get_include_path().PATH_SEPARATOR.dirname(__FILE__).'/../dependencies/mon-application-2/library');

// ...
&lt;/pre&gt;


&lt;p&gt;Vous n'êtes pas développeur de l'application 2, donc elle peut être en cours de développement. Vous devez être sûr que la version que vous utilisez est compatible avec vos développements dans votre application (les signatures de méthodes doivent être les bonnes, les fonctionnalités doivent être celles que vous attendez...).
Pour garantir ces points, il est nécessaire que vous récupériez une VERSION précise de la dépendance que vous savez compatible avec le mode de fonctionnement que vous attendez.
Vous allez donc récupérer un &quot;tag&quot; subversion de la dépendance 2&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
Mon application 2 dans la version 0.1.1 (compatible avec mes développements) : http://mon-serveur-svn/mon-application-2/tags/0.1.1
&lt;/pre&gt;


&lt;p&gt;En local, vous allez mettre le code source de cette version dans un répertoire qui porte le numéro de version pour que ce soit plus explicite&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Exemple dans le fichier contexts/@common.php (inclus par tous les fichiers de mon application)&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
&amp;lt;?php

set_include_path(get_include_path().PATH_SEPARATOR.dirname(__FILE__).'/../dependencies/mon-application-2/0.1.1/library');

// ...
&lt;/pre&gt;


&lt;p&gt;Vous avez maintenant dans votre répertoire dependencies/ l'ensemble de vos dépendances qui proviennent de tags subversion (i.e. de versions précises).&lt;/p&gt;


&lt;h2&gt;Détection de dépendances et modification de l'include path automatisé&lt;/h2&gt;


&lt;p&gt;Au cours du temps vous serez amenez à utiliser des versions successives de vos dépendances, au rythme de leur développement.&lt;br /&gt;
Vous passerez peut être à la version 0.1.2 de mon-application-2 (votre dépendance) puis 0.1.5 puis 0.2 puis ... puis 1.0.&lt;/p&gt;


&lt;p&gt;N'oubliez pas alors de modifier votre fichier contexts/@common.php (ou équivalent) qui spécifie l'include_path&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php

set_include_path(get_include_path().PATH_SEPARATOR.dirname(__FILE__).'/../dependencies/mon-application-2/1.0.0/library');

// ...
&lt;/pre&gt;


&lt;p&gt;Un moyen plus pratique, surtout quand il y a plusieurs dépendances, est d'utiliser un fichier de configuration que vous chargez et qui vous permet de modifier l'include path.&lt;br /&gt;
Ce fichier peut être au format php, ini, xml ... au choix, voici un exemple en utilisant un fichier ini (utilisable par phing)&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fichier config/dependencies.ini&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;

;; liste des dépendances

dependencies.list = &amp;quot;ma2&amp;quot;

;; dépendance mon-application-2

dependencies.ma2.name    = &amp;quot;mon-application-2&amp;quot;
dependencies.ma2.version = &amp;quot;1.0.0&amp;quot;
dependencies.ma2.base    = &amp;quot;library&amp;quot;

&lt;/pre&gt;


&lt;p&gt;Vous pouvez maintenant ajouter la lecture et l'analyse de ce fichier dans votre &quot;bootstrap&quot;&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fichier contexts/@common.php (inclus par tous les scripts php)&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
&amp;lt;?php

$paths = array();
$depsInfo = parse_ini_file(dirname(__FILE__).'/../config/dependencies.ini');
foreach(explode(',',$depsInfo['dependencies.list']) as $depName) {
    $depKeyPrefix = 'dependencies.'.$depName.'.';
    $paths[] = dirname(__FILE__).'/../dependencies/'.$depsInfo[$depKeyPrefix.'name'].'/'.$depsInfo[$depKeyPrefix.'base'];
}

set_include_path(get_include_path().PATH_SEPARATOR.implode(PATH_SEPARATOR,$paths));

// ...
&lt;/pre&gt;


&lt;p&gt;Grâce à ce fichier config/dependencies.ini et à ces quelques lignes de code PHP, vous n'avez plus besoin de vous préoccuper de votre include_path, il sera automatiquement paramétré à l'exécution de votre application.
Si vous souhaiter changer la version d'une dépendance, commencer par faire un checkout dans le répertoire dependencies/&amp;lt;nom-dependence&amp;gt;/&amp;lt;version&amp;gt;/ puis modifier le fichier config/dependencies.ini pour y paramétrer le numéro de version ou ajouter la dépendance (utilisez la &quot;,&quot; pour spécifier plusieurs dépendances dans la liste).&lt;/p&gt;


&lt;h2&gt;Subversion&amp;nbsp;: maintenir le code source des dépendances&lt;/h2&gt;


&lt;p&gt;Au delà de 'maintenir' le code source de VOS applications (i.e. de vos développements), le serveur Subversion (même local dans votre entreprise) peut aussi maintenir une copie du code source des dépendances externes (i.e. open source, publique...) que pourraient avoir vos applications.&lt;br /&gt;
Par exemple, je développe souvent avec Zend Framework (entre autres), et je peux donc stocker le code source de ces librairies chez moi, sur mon serveur subversion par exemple dans les urls suivantes&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
Zend Framework 1.8.3 =&amp;gt; http://mon-serveur-svn/ZendFramework/tags/1.8.3
Zend Framework 1.9.0 =&amp;gt; http://mon-serveur-svn/ZendFramework/tags/1.9.0
...
&lt;/pre&gt;


&lt;p&gt;Il me suffit alors de faire un checkout de l'url http://mon-serveur-svn/ZendFramework/tags/1.8.3 et de modifier mon fichier de configuration des dépendances&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fichier config/dependencies.ini&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;

;; liste des dépendances

dependencies.list = &amp;quot;ma2,zf&amp;quot;

;; dépendance mon-application-2

dependencies.ma2.name    = &amp;quot;mon-application-2&amp;quot;
dependencies.ma2.version = &amp;quot;1.0.0&amp;quot;
dependencies.ma2.base    = &amp;quot;library&amp;quot;

;; dépendance zend-framework

dependencies.zf.name    = &amp;quot;zend-framework&amp;quot;
dependencies.zf.version = &amp;quot;1.8.3&amp;quot;
dependencies.zf.base    = &amp;quot;library&amp;quot;

&lt;/pre&gt;


&lt;h3&gt;Dépendances de type ressources&lt;/h3&gt;


&lt;p&gt;Certaines dépendances concernent des fichiers images, css, javascript, ... comme par exemple les frameworks javascript tels que Dojo, ExtJS, JQuery...&lt;br /&gt;
Il est tout a fait possible de gérer ces dépendances via ce même canal en altérant un peu le principe.&lt;br /&gt;
L'idée est de stocker les dépendances en local au même endroit que précédemment, cependant il ne faut pas forcément ajouter le répertoire à l'include path.&lt;br /&gt;
Par contre il faut faire en sorte que les fichier ressources (images, css, js...) soit accessible dans le document root (htdocs/public/html) du serveur web&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fichier config/dependencies.ini&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;

;; liste des dépendances

dependencies.list = &amp;quot;ma2,zf,ej&amp;quot;

;; dépendance mon-application-2

dependencies.ma2.name    = &amp;quot;mon-application-2&amp;quot;
dependencies.ma2.version = &amp;quot;1.0.0&amp;quot;
dependencies.ma2.base    = &amp;quot;library&amp;quot;

;; dépendance zend-framework

dependencies.zf.name    = &amp;quot;zend-framework&amp;quot;
dependencies.zf.version = &amp;quot;1.8.3&amp;quot;
dependencies.zf.base    = &amp;quot;library&amp;quot;

;; dépendance ext-js

dependencies.ej.name    = &amp;quot;ext-js&amp;quot;
dependencies.ej.version = &amp;quot;1.0.0&amp;quot;
dependencies.ej.public  = yes
dependencies.ej.path    = no
dependencies.ej.htdocs  = &amp;quot;&amp;quot;

&lt;/pre&gt;


&lt;p&gt;On subversionne ensuite la dépendance ext-js dans svn&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
ExtJS 1.0.0 =&amp;gt; http://mon-serveur-svn/ExtJS/tags/1.0.0
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Fichier contexts/@common.php (inclus par tous les scripts php)&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
&amp;lt;?php

$paths = array();
$depsInfo = parse_ini_file(dirname(__FILE__).'/../config/dependencies.ini');
foreach(explode(',',$depsInfo['dependencies.list']) as $depName) {
    $depKeyPrefix = 'dependencies.'.$depName.'.';
    if (!isset($depsInfo[$depKeyPrefix.'path'])) {
        $depsInfo[$depKeyPrefix.'path'] = 'yes';
    }
    if ('yes' === $depsInfo[$depKeyPrefix.'path']) {
        $paths[] = dirname(__FILE__).'/../dependencies/'.$depsInfo[$depKeyPrefix.'name'].'/'.$depsInfo[$depKeyPrefix.'base'];
    }
}

set_include_path(get_include_path().PATH_SEPARATOR.implode(PATH_SEPARATOR,$paths));

// ...
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Fichier .htaccess ou fichier de configuration apache (pour le virtual host par exemple)&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;

# ...
Alias /extjs &amp;lt;chemin-vers-la-racine-de-votre-application&amp;gt;/dependencies/ext-js/1.0.0

# ...
&lt;/pre&gt;


&lt;h2&gt;Conclusion&lt;/h2&gt;


&lt;p&gt;Cette technique vous permet de gérer un include_path dynamique et notamment&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;utiliser plusieurs versions différentes de la même dépendance pour des applications différentes sur le même serveur / poste&lt;/li&gt;
&lt;li&gt;maîtriser le paramétrage de vos dépendances&lt;/li&gt;
&lt;li&gt;éviter de mettre l'url subversion de votre projet les différentes dépendances comme si il s'agissait de code source que vous aviez développé (cela alourdi considérablement le dev)&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Une amélioration possible, testée, est l'automatisation avec phing du checkout/update des dépendances, ainsi vous laissez à phing le soin de récupérer les dépendances en fonction du fichier dependencies.ini, notamment juste avant d'exécuter les tests unitaires ou de faire un déploiement.&lt;/p&gt;


&lt;p&gt;Et vous quels sont vos pratiques pour gérer vos dépendances&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/08/07/subversion-fichier-ini-include_path-gestion-dynamiques-des-dependances-librairies#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/08/07/subversion-fichier-ini-include_path-gestion-dynamiques-des-dependances-librairies#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/50</wfw:commentRss>
      </item>
    
  <item>
    <title>Les fichiers de contextes : où comment utiliser la même appli sur le web, les tests unitaires, fonctionnels, la ligne de commande, ...</title>
    <link>http://blog.phppro.fr/?post/2009/08/05/Les-fichiers-de-contextes-%3A-ou-comment-utiliser-la-meme-appli-sur-le-web-les-tests-unitaires-fonctionnels-la-ligne-de-commande</link>
    <guid isPermaLink="false">urn:md5:62027e03b8195383a5e0599e6c2f03b0</guid>
    <pubDate>Wed, 05 Aug 2009 09:03:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Trucs et astuces</category>
        <category>batch</category><category>contexte</category><category>greenpepper</category><category>gtk</category><category>phpunit</category>    
    <description>&lt;p&gt;Votre application peut être exécutée via&amp;nbsp;:&lt;/p&gt;


&lt;pre&gt;* un navigateur (web)
* une ligne de commande (batch)
* PHPUnit (tests unitaires ou tests d'intégration)
* GreenPepper / Fitnesse (tests fonctionnels)
* ...&lt;/pre&gt;    &lt;h2&gt;Les points d'entrées&lt;/h2&gt;


&lt;p&gt;Chacun de ces &quot;points d'entrées&quot; de votre application a besoin d'un contexte de travail particulier, par exemple&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;en mode web, l'uri de la requête et les paramètres GET/POST... sont nécessaire à l'application&lt;/li&gt;
&lt;li&gt;en mode batch, les paramètres saisies en ligne de commande sont nécessaire à l'application&lt;/li&gt;
&lt;li&gt;en mode tests unitaires, les classes du frameworks PHPUnit ainsi que les classes de tests doivent être disponible&lt;/li&gt;
&lt;li&gt;en mode tests fonctionnels, les classes de Fixtures doivent être disponible&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bien que chacun de ces &quot;contextes&quot; a des spécificités particulières, l'ensemble de ces contextes ont besoin de&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;l'ensemble des classes développées pour la logique de l'application&lt;/li&gt;
&lt;li&gt;de la connexion à la base de données&lt;/li&gt;
&lt;li&gt;des paramétrage éventuels dans le fichier de configuration&lt;/li&gt;
&lt;li&gt;d'écrire dans un fichier de log&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;L'objectif a atteindre est donc de faire en sorte que le maximum de code développé pour l'application soit fonctionnel et ce peut importe le point d'entrée (i.e. le contexte) par lequel l'application a été lancée.&lt;/p&gt;


&lt;h2&gt;Les fichiers contextes&lt;/h2&gt;


&lt;p&gt;Si nous résumons, nous avons besoin de&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;un fichier spécifique à chacun des contextes&lt;/li&gt;
&lt;li&gt;un fichier commun inclus par chacun des contextes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Voici donc un exemple de fichiers contextes&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fichier contexts/http.php&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
&amp;lt;?php

$context = 'http';

require_once dirname(__FILE__).'/@common.php';

// traitement spécifique: traite la requête HTTP, exemple avec Zend Framework :

$controller = Zend_Controller_Front::getInstance();

// ...

$controller-&amp;gt;dispatch();

&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Fichier contexts/phpunit.php&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
&amp;lt;?php

$context = 'phpunit';

require_once dirname(__FILE__).'/@common.php';

// ligne suivante pas nécessaire si PHPUnit est dans l'include path et que l'autoload est actif
require_once 'PHPUnit/Framework/TestCase.php';

// traitement spécifique: paramètre l'include path pour trouver les classes de tests, injecte les données de tests

set_include_path(get_include_path().PATH_SEPARATOR.dirname(__FILE__).'/../tests/phpunit');

// injecte les données de tests situées dans config/db/phpunit/create-schema.sql et create-data.sql
// ...

&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Fichier contexts/greenpepper.php&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
&amp;lt;?php

$context = 'greenpepper';

require_once dirname(__FILE__).'/@common.php';

// traitement spécifique: paramètre l'include path pour trouver les fixtures, injecte les données de références

set_include_path(get_include_path().PATH_SEPARATOR.dirname(__FILE__).'/../tests/greenpepper');

// injecte les données de références situées dans config/db/greenpepper/create-schema.sql et create-data.sql
// ...

// ajoute la fonction adhoc de logging dans le fichier greenpepper.log

function gpLog($txt)
{
    $fp = fopen(dirname(__FILE__).'/../logs/greenpepper.log','a+');
    $msg = '['.date('Y-m-D H:i:s').'] '.$txt.PHP_EOL;
    fputs($fp,$msg);
    fclose($fp);
}

&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Fichier contexts/batch.php&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
&amp;lt;?php

$context = 'batch';

require_once dirname(__FILE__).'/@common.php';

// traitement spécifique: aucun, ils sont réalisés dans les scripts qui inclus le contexte batch

&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Fichier contexts/@common.php&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
&amp;lt;?php

// sur quel environnement (prod, preprod, ...) sommes nous ?
$env     = getenv('PHP_ENV');

// sets the include path : library/ ...

// load configuration file
// ...

// load database connection
// ...

// load file logger
// ...

// activate autloader (pour l'include automatique des
// fichiers contenant la définition des classes instanciées)
// ...

&lt;/pre&gt;


&lt;h2&gt;L'utilisation des fichiers contextes&lt;/h2&gt;


&lt;p&gt;Maintenant que nous avons nos différents contextes prêt à l'emploi il faut faire le branchement pour qu'ils soient appeller au bon moment.&lt;/p&gt;


&lt;h3&gt;Contexte HTTP&lt;/h3&gt;


&lt;p&gt;Le fichier contexte http doit être le premier fichier appellé dans une application de type MVC(2), voici comment faire&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fichier public/index.php&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
&amp;lt;?php

require dirname(__FILE__).'/../contexts/http.php';
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Fichier public/.htaccess (compatible Apache 2 uniquement)&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
RewriteEngine On

RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_URI} !-f
RewriteRule ^.*$ /index.php [L}
&lt;/pre&gt;


&lt;h3&gt;Contexte Batch&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Fichier bin/mon-batch1-qui-fait-quelque-chose-de-precis.php&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
&amp;lt;?php

require_once dirname(__FILE__).'/../contexts/batch.php';

// écrire ici la logique spécifique du batch 1
// ...

&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Fichier bin/mon-batch2-qui-fait-quelque-chose-de-precis.php&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
&amp;lt;?php

require_once dirname(__FILE__).'/../contexts/batch.php';

// écrire ici la logique spécifique du batch 2
// ...

&lt;/pre&gt;


&lt;h3&gt;Contexte Tests Unitaires (PHPUnit)&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Fichier tests/phpunit/MonPackage/MonServiceTest.php&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
&amp;lt;?php

require_once dirname(__FILE__).'/../../../contexts/phpunit.php';

class MonPackage_MonServiceTest extends PHPUnit_Framework_TestCase
{
    public function setUp()
    {
        $this-&amp;gt;f = new MonPackage_MonService();
    }

    public function testSomethingForNormalCaseReturnTrue()
    {
        $this-&amp;gt;assertTrue($this-&amp;gt;f-&amp;gt;someting());
    }

    // ...
}

&lt;/pre&gt;


&lt;h3&gt;Contexte Tests Fonctionnels (GreenPepper)&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Fichier tests/greenpepper/MonPackage/MonServiceFixture.php&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
&amp;lt;?php

require_once dirname(__FILE__).'/../../../contexts/greenpepper.php';

class MonPackage_MonServiceFixture
{
    public function something()
    {
        $r = null;
        try {
            $s = new MonPackage_MonService();
            $r = $s-&amp;gt;something();
        } catch (Exception $e) {
            $r = 'erreur: '.$e-&amp;gt;getMessage();
            return $r;
        }

        // for debug only
        gpLog(__METHOD__.' =&amp;gt; '.var_export($r,true));

        return $r;
    }

    // ...
}

&lt;/pre&gt;


&lt;h2&gt;Ajouter un nouveau contexte d'appel&lt;/h2&gt;


&lt;p&gt;Si vous avez besoin d'ajouter un nouveau contexte d'appel à votre application, par exemple PHP-GTK (si votre application doit tourner en mode desktop client lourd PHP-GTK), vous pouvez créer le fichier suivant par exemple&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fichier contexts/php-gtk.php&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
&amp;lt;?php

$context = 'gtk';

require_once dirname(__FILE__).'/@common.php';

// traitement spécifique à GTK : ajout du répertoire des classes graphiques à l'include path, initialisation des évènements, ...

set_include_path(get_include_path().PATH_SEPARATOR.dirname(__FILE__).'/../library-gtk');

// ...

&lt;/pre&gt;


&lt;p&gt;Il vous suffit maintenant de faire en sorte que vos script d'appel GTK utilise votre nouveau fichier de contexte&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fichier bin/gtk-main.php&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
&amp;lt;?php

require_once dirname(__FILE__).'/../contexts/php-gtk.php';

// lancement de l'application avec les spécificités du script GTK...
// ...

&lt;/pre&gt;


&lt;h2&gt;Conclusion&lt;/h2&gt;


&lt;p&gt;Il est important de distinguer le code commun et le code spécifique aux contextes d'appel de votre application, cela vous permettra entre autre&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;de factoriser votre code&lt;/li&gt;
&lt;li&gt;de capitaliser sur un coeur applicatif pouvant être réutiliser dans d'autres mode de lancement (ligne de commande, web, graphique...)&lt;/li&gt;
&lt;li&gt;de ne pas charger l'ensemble de toutes les configurations nécessaires pour tous les contextes à chaque fois (ajout des répertoires de tests à l'include path...)&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Et vous quels sont vos pratiques pour rendre une application multi-contextuelle&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/08/05/Les-fichiers-de-contextes-%3A-ou-comment-utiliser-la-meme-appli-sur-le-web-les-tests-unitaires-fonctionnels-la-ligne-de-commande#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/08/05/Les-fichiers-de-contextes-%3A-ou-comment-utiliser-la-meme-appli-sur-le-web-les-tests-unitaires-fonctionnels-la-ligne-de-commande#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/49</wfw:commentRss>
      </item>
    
  <item>
    <title>include_path + conventions + autoload  = chargement automatique des classes PHP</title>
    <link>http://blog.phppro.fr/?post/2009/08/03/include_path-conventions-autoload-chargement-automatique-des-classes-PHP</link>
    <guid isPermaLink="false">urn:md5:66915bb3c4800c0f19eafeee76820fcf</guid>
    <pubDate>Mon, 03 Aug 2009 11:52:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Trucs et astuces</category>
        <category>autoload</category><category>convention</category><category>include</category><category>include_path</category><category>php</category><category>require</category><category>spl</category>    
    <description>&lt;p&gt;Vous avez des dizaines de require/include en début de chacun de vos script ?&lt;br /&gt;
Pire dans vos scripts PHP, un peu partout ?&lt;br /&gt;
Et si l'autoload vous changeait la vie&amp;nbsp;!&lt;/p&gt;    &lt;h2&gt;Les fonctions d'inclusion en PHP&lt;/h2&gt;


&lt;p&gt;Les fonctions include et require permettent à tout endroit d'un script PHP d'inclure le contenu d'un autre fichier php à un endroit précis.
Voici un exemple d'utilisation&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
&amp;lt;?php
require 'MonEntreprise/Xml/Service.php';
&lt;/pre&gt;


&lt;p&gt;Plus que des fonctions, il s'agit de directives PHP, elles peuvents donc être appelées avec ou sans leur parenthèses&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php
require ('MonEntreprise/Xml/Service.php');
require  'MonEntreprise/Xml/Service.php' ;
&lt;/pre&gt;


&lt;h3&gt;Les 4 fonctions disponibles&lt;/h3&gt;


&lt;p&gt;Il existe 4 fonctions principales pour inclure un fichier PHP dans un autre&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;require $file&amp;nbsp;: inclusion systématique du fichier PHP avec erreur fatale si $file n'existe pas&lt;/li&gt;
&lt;li&gt;include $file&amp;nbsp;: inclusion systématique du fichier PHP avec warning si $file n'existe pas (mais le script continue)&lt;/li&gt;
&lt;li&gt;require_once $file&amp;nbsp;: inclusion du fichier $file uniquement que si il n'a jamais été inclus (une seule inclusion même si plusieurs appels), même comportement d'erreur que require&lt;/li&gt;
&lt;li&gt;include_once $file&amp;nbsp;: inclusion du fichier $file uniquement que si il n'a jamais été inclus (une seule inclusion même si plusieurs appels), même comportement d'erreur que include&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Attention, dans le cas de l'utilisation des inclusions en *_once, le chemin transmis en tant que paramètre doit être strictement le même pour que l'inclusion ne se fasse qu'une seule fois&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php
/**
  * les deux lignes suivantes sont exécutées et le fichier
  * est inclus 2 fois, bien que ce soit physiquement le même fichier :
  */

require_once 'contexts/http.php';
require_once 'c:/dev/workspace/depot-silo/contexts/http.php';
&lt;/pre&gt;


&lt;h3&gt;Chemin du fichier à inclure&lt;/h3&gt;


&lt;p&gt;Il existe plusieurs possibilités pour indiquer le nom du fichier&amp;nbsp;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;un chemin absolu, ex: c:/dev/workspace/mon-projet/library/MonEntreprise/Xml/service.php&lt;/li&gt;
&lt;li&gt;un chemin relatif, ex: ../../library/MonEntreprise/Xml/service.php&lt;/li&gt;
&lt;li&gt;un chemin basé sur l'include path, ex: MonEntreprise/Xml/Service.php&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;l'Include path&lt;/h2&gt;


&lt;p&gt;L'include path en PHP est une notion similaire au concept de class path en Java.&lt;br /&gt;
Il s'agit d'une liste de répertoire dans lesquels PHP tentera de chercher les ressources (fichiers) à inclure.&lt;/p&gt;


&lt;p&gt;Les fonctions d'inclusions (require/include) sont donc massivement basées sur l'include path et tente de cherche les fichiers indiqués dans ces répertoires.&lt;/p&gt;


&lt;p&gt;D'autres fonctions PHP (ex: file_get_contents) peuvent utiliser l'include_path.&lt;/p&gt;


&lt;p&gt;L'include path est donc une &quot; chaîne de caractère &quot; listant un ensemble de répertoires, séparés par un séparateur dépendant de la plateforme (: pour *nix,&amp;nbsp;; pour windows)&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
Exemple sous windows: .;c:/php/pear
Exemple sous linux:   .:/usr/share/pear
&lt;/pre&gt;


&lt;h3&gt;Paramétrer l'include path&lt;/h3&gt;


&lt;p&gt;Il existe plusieurs possibilités pour positionner/modifier l'include path PHP&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;soit pour tout les scripts / requêtes&lt;/li&gt;
&lt;li&gt;soit pour l'exécution précise / requête en cours&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Paramétrage global pour tout les scripts&lt;/h4&gt;


&lt;p&gt;Il suffit de modifier la directive &quot;include_path&quot; du fichier php.ini&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
include_path = .:/usr/share/pear:/opt/mon-repertoire-perso
&lt;/pre&gt;


&lt;h4&gt;Paramétrage local à l'exécution du script en cours&lt;/h4&gt;


&lt;p&gt;Il suffit d'utiliser la fonction &quot;set_include_path()&quot; n'importe où dans votre script&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php
set_include_path(get_include_path() . PATH_SEPARATOR
     . '/opt/mon-repertoire-perso');
&lt;/pre&gt;


&lt;p&gt;L'utilisation de get_include_path() permet de récupérer l'include_path actuel (pour éviter de perdre les répertoires déjà listés).&lt;br /&gt;
La constante PATH_SEPARATOR est automatiquement positionnée par PHP avec &quot;:&quot; sous linux et &quot;;&quot; sous windows.&lt;/p&gt;


&lt;h4&gt;Exemple d'utilisation peut importe le type de paramétrage&lt;/h4&gt;


&lt;p&gt;Si le répertoire /opt/mon-repertoire-perso a été ajouté à l'include_path, et si le fichier /opt/mon-repertoire-perso/mon-script.php existe,
alors les lignes suivantes permettront d'inclure le fichier&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php
// par rapport à /opt/mon-repertoire-perso/
include 'mon-script.php';

// par rapport à /opt/mon-repertoire-perso/
require 'mon-script.php'; 

// par rapport à /opt/mon-repertoire-perso/
include '../mon-repertoire-perso/mon-script.php'; 

// par rapport à /usr/share/pear/
include '../../../opt/mon-repertoire-perso/mon-script.php'; 
&lt;/pre&gt;


&lt;h2&gt;L'instanciation de classes&lt;/h2&gt;


&lt;p&gt;PHP est compatible programmation orientée objet. Il est possible de développer en utilisant classes, méthodes, interfaces...&lt;/p&gt;


&lt;p&gt;Voici un exemple de classe php qui hérite de la classe standard Exception, avec un constructeur (attention pas de polymorphisme en php)&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php
// définie la classe d'exception

class MonException extends Exception
{
    private $params;

    public function __construct($message,$code,$params)
    {
        parent::__construct($message,$code);
        $this-&amp;gt;params = $params;
    }

    public function getParams()
    {
        return $this-&amp;gt;params;
    }
}

// instancie l'exception

$exception = new MonException();

// lève l'exception instanciée

throw $exception;

&lt;/pre&gt;


&lt;p&gt;Il est aussi possible, et recommandé de déplacer la définition des classes dans des fichiers bien distinct portant le nom de la classe&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fichier library/MonException.php&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
&amp;lt;?php
class MonException extends Exception
{
    private $params;

    public function __construct($message,$code,$params)
    {
        parent::__construct($message,$code);
        $this-&amp;gt;params = $params;
    }

    public function getParams()
    {
        return $this-&amp;gt;params;
    }
}
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Script principal mon-script.php&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
&amp;lt;?php
set_include_path(get_include_path().PATH_SEPARATOR
    .dirname(__FILE__).'/library');

require_once 'MonException.php';

throw new MonException();
&lt;/pre&gt;


&lt;p&gt;L'utilisation de set_include_path() permet d'ajouter le répertoire library/ dans l'include path et permet ainsi aux fonctions d'inclusions de trouver le fichier contenant la classe.&lt;br /&gt;
L'utilisation de require_once permet de ne charger qu'une seule fois le fichier MonException.php, au cas où il aurait été déjà chargé si le script mon-script.php est lui même inclus par un autre script qui avait déjà chargé le fichier MonException.php, et donc permet de ne charger qu'une seule fois la définition de la classe MonException (sinon PHP lève une erreur fatale dès que 2 définitions de classes portent le même nom).&lt;/p&gt;


&lt;h2&gt;Autoload: où chargement automatique des classes en PHP&lt;/h2&gt;


&lt;p&gt;Lors de la première utilisation d'une classe dans un script, il est possible d'inclure automatiquement sa définition si elle placée dans un fichier respectant certaines conventions.&lt;br /&gt;
En effet, le script mon-script.php suivant&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php
class MaClass
{
    public function maMethod()
    {
       return 'result';
    }
}

$o = new MaClass();
echo $o-&amp;gt;maMethod();
&lt;/pre&gt;


&lt;p&gt;peut être remplacé par 2 fichier&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;library/MaClass.php&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
&amp;lt;?php
class MaClass
{
    public function maMethod()
    {
       return 'result';
    }
}
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;mon-script.php&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
&amp;lt;?php
set_include_path(get_include_path().PATH_SEPARATOR.dirname(__FILE__).'/library');
function  __autoload($className)
{
    require_once $className.'.php';
}

$o = new MaClass();
echo $o-&amp;gt;maMethod();
&lt;/pre&gt;


&lt;p&gt;En effet, PHP propose le mécanisme d'autoload permettant de définir une fonction qui sera automatiquement appelée dès que PHP détectera l'utilisation d'une classe inconnue.&lt;br /&gt;
Dans notre cas, dans le fichier mon-script.php, la première utilisation de la classe MaClass est faite lors de l'utilisation de l'opérateur d'instanciation (new)&amp;nbsp;: nous tentons d'instancier la classe sans avoir inclus le fichier de définition de la classe. PHP déclenche alors le mécanisme d'autoload CAR nous avons définit avant une fonction callback spéciale nommée __autoload($className), charge à notre fonction d'inclure le fichier qui porte le nom de la classe en espérant qu'il existe dans l'include path.&lt;/p&gt;


&lt;h3&gt;Autoload, classes et packages&lt;/h3&gt;


&lt;p&gt;Avec PHP &amp;lt; 5.3, le concept de package/namespace n'existe pas réellement et nous sommes obligés d'avoir recours à certains artifices pour simuler le concept de package (comme nous pourrions l'avoir dans d'autres langages objet)&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;par exemple la classe MaClass dans le package MonPackage situé dans le fichier library/MonPackage/MaClass.php&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
&amp;lt;?php
class MonPackage_MaClass
{
    public function maMethod()
    {
        return 'result';
    }
}
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;et le script mon-script.php qui utilise cette classe grâce à un autoload adapté&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
&amp;lt;?php
set_include_path(get_include_path().PATH_SEPARATOR
    .dirname(__FILE__).'/library');
function  __autoload($className)
{
    require_once str_replace('_','/',$className).'.php';
}

$o = new MonPackage_MaClass();
echo $o-&amp;gt;maMethod();
&lt;/pre&gt;


&lt;h3&gt;Autoload à la mode SPL (Standard PHP Library)&lt;/h3&gt;


&lt;p&gt;la fonction magique &lt;strong&gt;autoload() est un mécanisme standard php. Cependant l'extension SPL, devenu standard depuis quelques temps en PHP 5 permet de fournir plusieurs fonctions &lt;/strong&gt;autoload selon une syntaxe légèrement différente (le principe est le même)&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php

function maFonctionAutoload1($className)
{
    if ('Pkg1_' !== substr($className,0,5)) {
        return false;
    }
    require_once 'repertoire1/'
        .str_replace('_','/',$className).'.php';
}

function maFonctionAutoload2($className)
{
    if ('Pkg2_' !== substr($className,0,5)) {
        return false;
    }
    require_once 'repertoire2/'
        .str_replace('_','/',$className).'.php';
}

spl_autoload_register('maFonctionAutoload1');
spl_autoload_register('maFonctionAutoload2');

// ...
&lt;/pre&gt;


&lt;h3&gt;Classe d'autoload&lt;/h3&gt;


&lt;p&gt;Pour ceux qui développe 100% en objet, il est possible d'utiliser une méthode d'autoload plutôt qu'une fonction via SPL&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fichier library/Autoloader.php&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
&amp;lt;?php
class Autoloader
{
    public function __construct()
    {
        // ajout du répertoire parent (library/) à l'include path

        set_include_path(get_include_path()
            .PATH_SEPARATOR.realpath(dirname(__FILE__).'/..'));
        spl_autoload_register(array('Autoloader','autoload'));
    }
    public function autoload($className)
    {
        require_once str_replace('_','/',$className).'.php';
    }
}
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Script mon-script.php&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;

/**
  * inclusion de l'autoloader avec chemin absolu
  * car l'include path n'est pas encore positionné
  */

include dirname(__FILE__).'/library/Autoloader.php';

$o = new MaClass();

echo $o-&amp;gt;maMethod();
&lt;/pre&gt;


&lt;h2&gt;Conclusion&lt;/h2&gt;


&lt;p&gt;Grâce à cette technique vous n'aurez plus besoin d'utiliser les require/include  dans vos scripts, si vous développez objet et si vous nommez correctement vos classes/fichiers
Les frameworks Zend Framework, Symfony, ... proposent des modules d'auto-chargement basés sur ces principes (dans les grandes lignes).&lt;/p&gt;


&lt;p&gt;Attention cependant à certains points&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;plus il y a de répertoires listés dans l'autoload et plus vous aurez potentiellement un impact sur les performances&lt;/li&gt;
&lt;li&gt;les répertoires contenant les classes les plus &quot;populaires&quot; (i.e. les plus utilisées dans votre code) doivent se situer au début de l'include path, pour optimiser les performances, notamment les répertoires des frameworks&lt;/li&gt;
&lt;li&gt;si vous développez un framework ou une librairie, privilégiez l'utilisation explicite des require_once plutôt que l'autoload, car votre librairie sera incluse dans une application qui aura peut être son propre mécanisme d'include path potentiellement incompatible avec le votre&lt;/li&gt;
&lt;li&gt;vous devez avoir une rigueur à toute épreuve pour le nommage et le placement de vos classes/fichiers&amp;nbsp;: 1 fichier par classe, 1 classe par fichier, les fichiers portent le même nom que les classes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Et vous quelles sont vos pratiques pour le nommage / rangement et auto-chargement de vos classes&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/08/03/include_path-conventions-autoload-chargement-automatique-des-classes-PHP#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/08/03/include_path-conventions-autoload-chargement-automatique-des-classes-PHP#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/48</wfw:commentRss>
      </item>
    
  <item>
    <title>DNS + Proxy + Header, où comment gérer le déplacement d'un site internet en douceur</title>
    <link>http://blog.phppro.fr/?post/2009/07/24/DNS-Proxy-Header-ou-comment-gerer-le-deplacement-d-un-site-internet-en-douceur</link>
    <guid isPermaLink="false">urn:md5:9a49cf907877a378e45724cde12cddcc</guid>
    <pubDate>Fri, 24 Jul 2009 18:15:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Trucs et astuces</category>
            
    <description>&lt;p&gt;Cela vous est probablement déjà arrivé de devoir déplacement un site internet sur un autre serveur. L'adresse IP du serveur n'étant plus la même, il faut alors modifier l'entrée DNS (souvent A, mais peut être CNAME) pour que le nom de domaine pointe maintenant sur l'adresse IP du nouveau serveur. Mais cela peut prendre du temps pour se propager...&lt;br /&gt;&lt;/p&gt;    &lt;p&gt;Si vous aviez prévu votre opération depuis longtemps, vous aviez certainement &quot;abaissé&quot; les TTL (i.e. Time To Live), à 10 minutes par exemple, qui permettent d'indiquer aux différents équipements internets combien de temps ils doivent garder en cache la correspondance DNS&amp;lt;=&amp;gt;IP récupérée depuis le serveur DNS primaire.&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;Que vous ayez prévu ou pas de baisser les TTL, certains (vieux) équipements ne suivent pas forcéments à la lettre ce paramétrage, et alors c'est le drame, tout une partie de vos utilisateurs accèdent toujours à l'adresse IP de l'ancien serveur pendant un peu plus longtemps.&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;Voici une technique que je mets en place pour pallier à ce problème&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;dupliquer / (ré-)installer le site sur le nouveau serveur&lt;/li&gt;
&lt;li&gt;tester le nouveau serveur en mettant la correspondance DNS &amp;lt;=&amp;gt; IP dans votre fichier etc/hosts de votre ordinateur personnelle (portable,...) temporairement&lt;/li&gt;
&lt;li&gt;une fois le test ok, modifier les DNS chez votre registrar/serveurs dns en migrant le DNS vers la nouvelle IP&lt;/li&gt;
&lt;li&gt;sur l'ancien serveur, modifier la configuration apache pour que le serveur apache fasse proxy vers le nouveau serveur apache&lt;/li&gt;
&lt;li&gt;ajouter la même ligne que vous aviez précédemment mise dans votre etc/hosts perso dans celui de l'ancien serveur&lt;/li&gt;
&lt;li&gt;remplacer la directive DocumentRoot de votre virtual host de l'ancien serveur par ces lignes&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
ProxyPass / http://mon.domaine.com/
ProxyPassReverse / http://mon.domaine.com/
Header add Old-Server yes
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;redémarrer votre ancien serveur apache&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Le principe est simple&amp;nbsp;: tant que l'équipement internet utilisé par l'internaute n'a pas mis à jour son cache, il pointera toujours sur l'ancienne IP, mais ce serveur fera proxy vers le nouveau serveur pour servir exactement le même contenu et surtout, si vous avez une base de données derrière votre site internet, ira mettre à jour la nouvelle base de données (pas besoin de faire une seconde migration&amp;nbsp;! Lorsque l'équipement utilisé par l'internaute sera à jour il pointera directement sur l'adresse IP du nouveau serveur et ne passera donc plus par le proxy.&lt;br /&gt;
Si vous utilisez Firefox, vous pourrez par exemple vous servir du plugin Firebug pour vérifier que le header &quot;Old-Server: yes&quot; n'est plus transmis, car tant qu'il est transmis, vous pointez sur l'ancien serveur.&lt;/p&gt;


&lt;h3&gt;Comment savoir quand je pourrais désinstaller / supprimer l'ancien serveur&amp;nbsp;?&lt;/h3&gt;


&lt;p&gt;Il vous suffira de regarder dans les logs de l'ancien serveur web, si vous site internet est très utilisé / populaire, vous allez pouvoir observer une baisse significative des logs à un moment donné. Lorsque plus aucune ligne de log n'apparaîtra depuis plusieurs heures/jours, vous pourrez sereinement supprimer cet ancien serveur.&lt;/p&gt;


&lt;p&gt;Et vous, quelles sont vos pratiques&amp;nbsp;? Il s'agit d'une &quot;bidouille&quot; pour être plus serein, je suis sûr que vous en avez d'autres&amp;nbsp;!&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/07/24/DNS-Proxy-Header-ou-comment-gerer-le-deplacement-d-un-site-internet-en-douceur#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/07/24/DNS-Proxy-Header-ou-comment-gerer-le-deplacement-d-un-site-internet-en-douceur#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/47</wfw:commentRss>
      </item>
    
  <item>
    <title>10 conseils pour réussir son projet de développement en équipe</title>
    <link>http://blog.phppro.fr/?post/2009/07/03/10-conseils-pour-reussir-son-projet-de-developpement-en-equipe</link>
    <guid isPermaLink="false">urn:md5:b677a6aa0143051551c2885128524f89</guid>
    <pubDate>Wed, 22 Jul 2009 09:50:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Méthodologie</category>
        <category>agile</category><category>intégration continue</category><category>métrique</category><category>outils</category><category>projet</category><category>tests</category>    
    <description>&lt;p&gt;Voici 10 conseils pragmatiques que j'utilise au quotidien chez mes clients...&lt;/p&gt;    &lt;p&gt;Si parmi vos enjeux vous avez :&lt;br /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;développer votre business / sortir un service concurrentiel&lt;/li&gt;
&lt;li&gt;montrer rapidement votre avancement à vos commanditaires&lt;/li&gt;
&lt;li&gt;rationnaliser les développements pour éviter les dispersions, pertes de temps et hétérogénéité du code&lt;/li&gt;
&lt;li&gt;utiliser au mieux les compétences de chacun, faire en sorte que les meilleurs ne s'ennuient pas et que les meilleurs poussent les autres à l'excellence&lt;/li&gt;
&lt;li&gt;capitaliser des compétences / briques techniques / logiciels&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Voici 10 conseils pour réussir et performer dans votre/vos projet(s) :&lt;br /&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Gardez le contrôle sur le code (mesurer, vérifier, tester)&lt;/li&gt;
&lt;li&gt;L'excellence attire l'excellence&amp;nbsp;: (innover, exprimer, montrer l'exemple)&lt;/li&gt;
&lt;li&gt;rationnalisez les pratiques (documenter, vérifier)&lt;/li&gt;
&lt;li&gt;rationnalisez les outils (choisisser, installer, former)&lt;/li&gt;
&lt;li&gt;utilisez l'intégration continue (installer, paramétrer)&lt;/li&gt;
&lt;li&gt;mettez en place une méthodologie itérative courte (raccourcir, itérer)&lt;/li&gt;
&lt;li&gt;utilisez la technique de l'entonnoir (laissez-vivre =&amp;gt; rationnalisez, laissez-vivre =&amp;gt; rationnalisez, ...)&lt;/li&gt;
&lt;li&gt;prenez en compte le feedback à tous les niveaux (observer, apprendre, corriger)&lt;/li&gt;
&lt;li&gt;KISS&amp;nbsp;: Keep It Short and Simple (faire TOUJOURS simple)&lt;/li&gt;
&lt;li&gt;DRY&amp;nbsp;: Don't Repeat Yourself, ou factorisez / réutilisez (faire TOUJOURS 1 seule fois)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Bien sûr, ces conseils ne sont pas exhaustifs, vous en avez probablement d'autres vous aussi, quels sont-ils&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/07/03/10-conseils-pour-reussir-son-projet-de-developpement-en-equipe#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/07/03/10-conseils-pour-reussir-son-projet-de-developpement-en-equipe#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/44</wfw:commentRss>
      </item>
    
  <item>
    <title>AgileTour 2009 @ Bordeaux : 3 sessions proposées</title>
    <link>http://blog.phppro.fr/?post/2009/07/22/AgileTour-2009-Bordeaux-%3A-3-sessions-proposees</link>
    <guid isPermaLink="false">urn:md5:be9ae3f0879407e5227500bee13c7f6f</guid>
    <pubDate>Wed, 22 Jul 2009 08:15:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Evènements</category>
        <category>agile</category><category>conférence</category><category>outils</category><category>phpunit</category><category>tests</category>    
    <description>&lt;p&gt;Cette année l'AgileTour passe à Bordeaux le 29 Octobre, l'occasion pour moi de proposer quelques sujets de sessions...&lt;/p&gt;    &lt;p&gt;Pour ceux qui ne connaissent pas l'AgileTour, il s'agit d'une &quot;tournée&quot; dans plusieurs villes de france (mais pas uniquement), pour :&lt;br /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;promouvoir massivement l'Agilité&lt;/li&gt;
&lt;li&gt;partager nos visions de l'Agilité&lt;/li&gt;
&lt;li&gt;fédérer&lt;/li&gt;
&lt;li&gt;soutenir&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Le 29 octobre 2009, l'AgileTour 2009 s'arrêtera à Bordeaux, aux quelques rues de chez moi ;), à cette occasion, je vous propose quelques sujets de sessions autour de l'agilité et de PHP :&lt;br /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TDR&amp;nbsp;: Tests Fonctionnels Automatisables / Exécutables, où l'outil qui redonne la maîtrise au MOA (30min, Dojo)&lt;/li&gt;
&lt;li&gt;TDD&amp;nbsp;: Test Driven Development en PHP (1h, Dojo)&lt;/li&gt;
&lt;li&gt;Le quotidien d'une équipe Agile, retours d'expériences (30min, conférence)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Venez nombreux si ces sujets ou bien d'autres vous intéressent&amp;nbsp;!&lt;/p&gt;


&lt;p&gt;Le site internet de l'évènement&amp;nbsp;: http://agiletour.org&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/07/22/AgileTour-2009-Bordeaux-%3A-3-sessions-proposees#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/07/22/AgileTour-2009-Bordeaux-%3A-3-sessions-proposees#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/46</wfw:commentRss>
      </item>
    
  <item>
    <title>Intégration PHPUnit dans Eclipse</title>
    <link>http://blog.phppro.fr/?post/2009/07/08/Integration-PHPUnit-dans-Eclipse</link>
    <guid isPermaLink="false">urn:md5:b4c5c9b6f24ecf226c18e22f4aae0882</guid>
    <pubDate>Wed, 08 Jul 2009 23:18:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Outillage</category>
        <category>eclipse</category><category>eclipse pdt</category><category>outils</category><category>phpunit</category><category>tests</category><category>zend studio</category>    
    <description>&lt;p&gt;Une nouvelle intégration de PHPUnit 3.x dans Eclipse à la mode JUnit&lt;/p&gt;    &lt;p&gt;J'utilise Zend Studio et Eclipse PDT au quotidien chez mes clients.&lt;br /&gt;
Adepte du TDD (Test Driven Development), le support de PHPUnit dans Zend Studio est devenu plus qu'un confort pour moi, une véritable nécessité.&lt;br /&gt;
Pour mes clients qui choisissent Eclipse PDT plutôt que Zend Studio, j'étais jusqu'alors résigné à créer des boutons external tools dans Eclipse pour lancer en ligne de commande phpunit (efficace, mais rendu non graphique en mode console qui peut rebuter certains).&lt;br /&gt;
Sur ma demande d'aide, un de mes anciens collègues, Java-iste dans l'âme, et PHP-iste à ses heures perdues, a développé une intégration de PHPUnit 3.x dans Eclipse en open source.
Il s'agit d'un wrapper au plugin JUnit 4 existant dans Eclipse JDT permettant de lancer les tests unitaires PHPUnit (via la ligne de commande en fait) et d'afficher les résultats tel que JUnit le fait (et tel que le plugin PHPUnit de Zend Studio le fait).
Bien sûr il s'agit d'un premier jet, vos idées / remarques / demandes d'amélioration sont les bienvenues&amp;nbsp;!&lt;/p&gt;


&lt;p&gt;Sans plus attendre le site du projet open source&amp;nbsp;: http://code.google.com/p/phpunit4eclipse/wiki/j2phpUnitWrapper&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/07/08/Integration-PHPUnit-dans-Eclipse#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/07/08/Integration-PHPUnit-dans-Eclipse#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/45</wfw:commentRss>
      </item>
    
  <item>
    <title>La question est posée : &quot;PHP : POO ou (exclusif) Procédural ?&quot;</title>
    <link>http://blog.phppro.fr/?post/2009/06/25/La-question-est-posee-%3A-PHP-%3A-POO-ou-exclusif-Procedural</link>
    <guid isPermaLink="false">urn:md5:9fc27de47709cc8005d1be49927af3db</guid>
    <pubDate>Thu, 25 Jun 2009 23:12:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Analyse</category>
        <category>entreprise</category><category>optimisation</category><category>pattern</category><category>php</category><category>tests</category><category>zend framework</category>    
    <description>&lt;p&gt;Un de mes clients se pose la question &quot;PHP&amp;nbsp;: Faire du POO ou du Procédural ?&quot;&lt;/p&gt;    &lt;p&gt;Certains clients sont à juste titre souvent réticent à passer à du tout objet, notamment en PHP.&lt;/p&gt;


&lt;p&gt;De mon point de vue, le débat n'est pas forcément POO ou Procédural (même si, je l'avoue je suis un POO-convaincu), mais plutôt, industrialisés/rationnalisés/maîtrisés/testables/bouchonnables versus &quot;non homogènes, non-bonnes pratiques,non cadrés, non-testable&quot;.&lt;/p&gt;


&lt;p&gt;L'implémentation POO que je propose en règle général de mettre en place à mes clients correspond dans une certaine mesure à une compilation de fonctions rangées par groupes qui correspondent à des classes et des packages (répertoires).&lt;br /&gt;
Concrètement il ne s'agit pas de POO pure mais plutôt d'une forme de POO &quot;allégée&quot; avec des fonctionnalités de procédural (ex: une classe &quot;PliService&quot; qui contient une liste de méthodes listPlis(), getPlis(), deletePli($pliId), addPli($pliInfos), ...) en POO pur on aurait plutôt eu une classe &quot;Pli&quot; avec les méthodes &quot;save(), delete(), check(), send()...&quot;.&lt;br /&gt;
Les avantages induits, sont qu'étant en programmation objet (même non puriste), nous pouvons bénéficier d'un certains nombre de design patterns pertinents tels que Adapter, Mock, Singleton, MVC... et bien sûr ne pas réinventer la roue et utiliser des composants tout fait tels que ceux proposer par ZF (ou d'autres frameworks) et pouvoir les étendre facilement pour les adapter à nos besoins (pas possible en procédural).&lt;/p&gt;


&lt;p&gt;D'autre part, je suis loin d'être omniscient, mais je ne connais aucune librairie ou framework, développée récemment ou activement développée (en dehors de produit sur étagère bien spécifique) qui soient full procédural, tous sont POO-enabled, a priori, cela marque bien la mouvance actuelle que prend PHP pour sa professionnalisation.&lt;/p&gt;


&lt;p&gt;Au delà de POO versus procédural, les questions concrètes auxquelles il faut répondre sont&amp;nbsp;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;comment j'organise mes fichiers&lt;/li&gt;
&lt;li&gt;comment je factorise mes fonctionnalités&lt;/li&gt;
&lt;li&gt;comment je rend testable / bouchonnable mes fonctionnalités&lt;/li&gt;
&lt;li&gt;comment je séparer la logique, du rendu, comment j'utilise une seule fois la logique pour plusieurs rendu différent&lt;/li&gt;
&lt;li&gt;comment j'interface les outils de tests fonctionnels exécutables (une NECESSITE pour les fonctionnels et donc pour ceux qui motivent les développements)&lt;/li&gt;
&lt;li&gt;comment je garanti qu'un nouveau développeur pourra prendre en main n'importe quelle partie du code sans trop de douleurs&lt;/li&gt;
&lt;li&gt;comment je garantie qu'il n'y aura pas de collision de nommage entre mes fonctionnalités&lt;/li&gt;
&lt;li&gt;comment je garantie que mes développeurs ne seront indépendants et ne s'interbloqueront pas mutuellement dans leur développement (i.e. travailleront sur des fichiers différents)&lt;/li&gt;
&lt;li&gt;comment je package mon application pour qu'elle soit facilement déployable sur un environnement de production différent de l'environnement de développement&lt;/li&gt;
&lt;li&gt;comment j'intègre mon code à une usine de développement de type intégration continue&lt;/li&gt;
&lt;li&gt;comment je garantie que mes développeurs peuvent à tout moment exercer leurs connaissances précédemment acquises sans forcément avoir assimilé tout les concepts, frameworks, librairies, techniques, méthodes (what ever you want), qui composent la stack applicative et de pratiques mises en place pour les développement.&lt;/li&gt;
&lt;li&gt;comment je capitalise sur mes développements pour tout mon parc applicatif (pas seulement que pour une seule application)&lt;/li&gt;
&lt;li&gt;... (j'ai de multiples questions de ce type encore...)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;De mon point de vue, POO-allégée (celle que je propose au quotidien à mes clients), permet avec tout un tas de conventions (que je propose aussi à mes clients) de répondre à la majorité de ces questions.
Une utilisation procédurale-exclusive de PHP avec des conventions adaptées pourraient je pense répondre positivement à pas mal de ces questions, toutes je ne sais pas (admettons, je n'ai pas essayé), elle irait juste à rebrousse-poil de tout ce que la communauté PHP met en oeuvre depuis l'arrivée de PHP 5 (et des iterators, des exceptions, des interfaces, des classes, ...).&lt;/p&gt;


&lt;p&gt;Petit rappel, en PHP, contrairement à Java, TOUT N'EST PAS OBJET, ce qui veut dire que finalement si on fait des classes et objets, à l'intérieur de leurs méthodes, ce sont des fonctions (procédurales) qui sont utilisées (toutes la batteries de fonctions très fournies que PHP propose en standard). La structuration en classe, est donc une coquille qui permet de s'organiser et de rendre plus clair la modélisation et la séparation de responsabilité (MVC et autres joyeusetées).&lt;/p&gt;


&lt;p&gt;Zend Framework (ou Symfony dans une certaine mesure) n'est finalement qu'une implémentation de tout un tas de design patterns (i.e. de réponses à des problèmes connus, traités une bonne fois pour toute), ne pas utiliser ni Zend Framework, ni tout autre framework, reviendrait à faire une croix sur les solutions aux problèmes communs déjà trouvés par toutes les communautés.&lt;br /&gt;
Une limitation cependant, tout framework &quot;généraliste&quot; prend sa force dans la généralité, pas dans l'adéquation à des besoins spécifiques (tels que la performance, la finesse, l'exhaustivité...). Il est donc très souvent nécessaire de se poser la question &quot;Pour ce besoin précis, dois-je prendre tel composant de tel framework ou dois-je mettre en oeuvre mon propre développement ?&quot;, la réponse dépend du contexte, 8 fois sur 10, il ne sera pas nécessaire de redévelopper votre propre composant, ceux déjà existant suffiront (et seront éprouvés / matures / stables / documentés / utilisés contrairement à vos éventuels développements à mener), mais 2 fois sur 10, les composants spécifiques montreront leurs limites et vous devrez créer le vôtre aux petits oignions. Faire une croix sur la POO et/ou les frameworks existants vous pénalisera dans 8 cas sur 10, et ne vous aidera pas non plus dans les 2 autres cas sur 10 (c'est dommage, vous êtes dans le monde de l'open source avec PHP, vous auriez pu prendre/étendre/réutiliser/améliorer) il faut en être conscient.  Il est possible alors de créer son propre framework (c'est peut être ce que certains ont fait), pourquoi serait-il mieux qu'un framework téléchargé plus de x millions de fois, financé par Google, Adobe, IBM,... et utilisé chez de plus en plus de grand comptes entre autres français&amp;nbsp;?&lt;/p&gt;


&lt;p&gt;Le grand débat dans les grands comptes est souvent ZF ou Symfony, jamais POO ou Procédural (en tout cas chez ceux que j'ai eu la chance de croiser jusqu'alors), mais bon pourquoi pas, c'est peut être les autres qui se trompent me dira-t-on...&lt;/p&gt;


&lt;p&gt;Et vous, la communauté, quel est votre point de vue&amp;nbsp;?&lt;/p&gt;


&lt;p&gt;PS: PHP 4 n'est plus supporté depuis plus d'un an, si vous développez procédural only, n'oubliez pas de mettre à jour vos pratiques pour être au moins compatibles avec les bonnes pratiques de développement procédural en PHP 5 ;)&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/06/25/La-question-est-posee-%3A-PHP-%3A-POO-ou-exclusif-Procedural#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/06/25/La-question-est-posee-%3A-PHP-%3A-POO-ou-exclusif-Procedural#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/43</wfw:commentRss>
      </item>
    
  <item>
    <title>Installer HUDSON sur Ubuntu 8.10 sur le port 80</title>
    <link>http://blog.phppro.fr/?post/2009/06/05/Installer-HUDSON-sur-Ubuntu-810-sur-le-port-80</link>
    <guid isPermaLink="false">urn:md5:b7945ba4379e9587599275c6831f12bf</guid>
    <pubDate>Fri, 05 Jun 2009 19:02:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Trucs et astuces</category>
        <category>apt</category><category>hudson</category><category>intégration continue</category><category>java</category><category>lighttpd</category><category>pear</category><category>php</category><category>proxy</category><category>ubuntu</category>    
    <description>&lt;p&gt;Voici la procédure que j'ai déroulée pour installer l'outil d'intégration continue Hudson (v1.3.09) sur un serveur sous Ubuntu 8.10 (64 bits) complètement vierge.&lt;/p&gt;    &lt;ul&gt;
&lt;li&gt;Connexion SSH sur le serveur en mode root&lt;/li&gt;
&lt;li&gt;Création d'un utilisateur ayant les droits sudo&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
adduser ohoareau
adduser ohoareau admin
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Nommer à la machine&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
echo nomdelamachine &amp;gt; /etc/hostname
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Redémarrage pour prendre en compte le nom de machine&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
reboot
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Connexion en SSH avec l'utilisateur nouvellement créé (ohoareau)&lt;/li&gt;
&lt;li&gt;Mise à jour de APT&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
sudo apt-get update
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Installation de PHP et PEAR&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
sudo apt-get install php5
sudo apt-get install php5-pear
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Paramétrage de l'horloge réseau&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
sudo ntpdate fr.pool.ntp.org
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Installation d'un serveur de mail pour l'envoi des mails vers l'extérieur&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
sudo apt-get install postfix
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Installation des channels et paquets PEAR nécessaire pour le build avec Phing&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
sudo pear upgrade pear
sudo pear channel-discover pear.phpunit.de
sudo pear channel-discover pear.xplib.de
sudo pear channel-discover pear.pdepend.org
sudo pear channel-discover pear.phing.info
sudo pear channel-discover pear.phppro.fr
sudo pear update-channels
sudo pear upgrade pear
sudo pear install --force VersionControl_SVN Console_ProgressBar XML_Serializer
sudo pear install --alldeps phpunit/PHPUnit
sudo pear install phpunit/phpcpd
sudo pear install --alldeps PHP_CodeSniffer
sudo pear install --alldeps --force pdepend/PHP_Depend
sudo pear install --alldeps --force PHP_UML
sudo pear install PHP_CompatInfo
sudo pear install --alldeps phing/phing
sudo pear install phppro/phpcr
sudo pear install PhpDocumentor
sudo pear install XML_Beautifier
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Installation de XDebug pour PHP (pour la couverture de code)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
sudo apt-get install php5-xdebug
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Mise à jour du paramétrage par défaut de PHP&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
sudo vi /etc/php5/cli/php.ini
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Modifiez la ligne suivante dans le fichier /etc/php5/cli/php.ini&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
memory_limit = 128M
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Installation de Java (JDK/JRE) pour Hudson&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
sudo apt-get install openjdk-6-jdk openjdk-6-jre
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Ajout du repo hudson distant&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
vi /etc/apt/sources.list
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Ajoutez la ligne suivante dans le fichier /etc/apt/sources.list&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
deb http://hudson-ci.org/debian binary/
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Installation d'Hudson (par défaut sur le port 8080)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
sudo apt-get install hudson
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Installation de LightHTTPD sur le port 80 pour faire proxy vers Hudson&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
sudo apt-get install lighttpd
sudo vi /etc/lighttpd/conf-available/10-hudson.conf
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Copiez le contenu suivant dans le fichier /etc/lighttpd/conf-available/10-hudson.conf&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
## Let lighttpd act as a proxy server for special file types, hosts etc
##
## Documentation: /usr/share/doc/lighttpd-doc/proxy.txt
##                http://www.lighttpd.net/documentation/proxy.html

server.modules   += ( &amp;quot;mod_proxy&amp;quot; )

## Balance algorithm, possible values are: &amp;quot;hash&amp;quot;, &amp;quot;round-robin&amp;quot; or &amp;quot;fair&amp;quot; (default)
# proxy.balance     = &amp;quot;hash&amp;quot;


$HTTP[&amp;quot;host&amp;quot;] == &amp;quot;nomdelamachine&amp;quot; {
  proxy.balance = &amp;quot;hash&amp;quot;
  proxy.server  = ( &amp;quot;&amp;quot; =&amp;gt; ( ( &amp;quot;host&amp;quot; =&amp;gt; &amp;quot;nomdelamachine&amp;quot;, &amp;quot;port&amp;quot; =&amp;gt; 8080 ) ) )
}
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Rédemarrage de Lighttpd&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
sudo /etc/init.d/lighttpd restart
&lt;/pre&gt;


&lt;p&gt;Normalement, vous pouvez accédez à l'interface d'Hudson via l'url http://nomdelamachine (attention à mettre à jour votre etc/hosts sur le poste du navigateur si il s'agit d'un nom qui n'est pas renseigné sur les dns publique internet) &lt;br /&gt;&lt;/p&gt;


&lt;p&gt;Il est possible que des erreurs ce soient glissées n'hésitez pas à le dire&amp;nbsp;!&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/06/05/Installer-HUDSON-sur-Ubuntu-810-sur-le-port-80#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/06/05/Installer-HUDSON-sur-Ubuntu-810-sur-le-port-80#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/42</wfw:commentRss>
      </item>
    
  <item>
    <title>Exemple de fichier build.xml pour un projet utilisant l'intégration continue</title>
    <link>http://blog.phppro.fr/?post/2009/06/05/Exemple-de-fichier-buildxml-pour-un-projet-utilisant-l-integration-continue</link>
    <guid isPermaLink="false">urn:md5:11e028e58140c183d870dbae43551270</guid>
    <pubDate>Fri, 05 Jun 2009 16:08:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Outillage</category>
        <category>build</category><category>build.xml</category><category>intégration continue</category><category>phing</category>    
    <description>&lt;p&gt;Vous utiliser PHP, Phing et vous souhaitez utiliser hudson, Xinc ou PHPUnderControl avec Phing&lt;/p&gt;    &lt;p&gt;Voici un fichier build.xml qui me sert souvent de base pour automatiser mes builds php en intégration continue.
Il n'est pas parfait, n'hésitez pas à proposer des améliorations ou proposer vos variantes / exemples, ils sont les bienvenus&amp;nbsp;!&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;

&amp;lt;!-- vim: set expandtab tabstop=4 shiftwidth=4: --&amp;gt;

&amp;lt;project name=&amp;quot;php&amp;quot; default=&amp;quot;help&amp;quot;&amp;gt;

    &amp;lt;!-- ================================================================== --&amp;gt;
    &amp;lt;!-- Properties                                                         --&amp;gt;
    &amp;lt;!-- ================================================================== --&amp;gt;
    
    &amp;lt;!-- *** Project General Properties *********************************** --&amp;gt;

    &amp;lt;property name=&amp;quot;home&amp;quot;         value=&amp;quot;${project.basedir}&amp;quot;/&amp;gt;
    &amp;lt;property name=&amp;quot;src.dir&amp;quot;      value=&amp;quot;library&amp;quot;/&amp;gt;
    &amp;lt;property name=&amp;quot;test.dir&amp;quot;     value=&amp;quot;tests&amp;quot;/&amp;gt;
    &amp;lt;property name=&amp;quot;report.dir&amp;quot;   value=&amp;quot;reports&amp;quot;/&amp;gt;
    &amp;lt;property name=&amp;quot;doc.dir&amp;quot;      value=&amp;quot;docs&amp;quot;/&amp;gt;
    &amp;lt;property name=&amp;quot;context.dir&amp;quot;  value=&amp;quot;contexts&amp;quot;/&amp;gt;
    &amp;lt;property name=&amp;quot;config.dir&amp;quot;   value=&amp;quot;config&amp;quot;/&amp;gt;
    &amp;lt;property name=&amp;quot;convention&amp;quot;   value=&amp;quot;PEAR&amp;quot;/&amp;gt;

    &amp;lt;!-- *** Project Custom Properties ************************************ --&amp;gt;

    &amp;lt;property file=&amp;quot;${home}/${config.dir}/build.properties&amp;quot;   override=&amp;quot;true&amp;quot;/&amp;gt;
    &amp;lt;property file=&amp;quot;${home}/${config.dir}/${conf}.properties&amp;quot; override=&amp;quot;true&amp;quot;/&amp;gt;

    &amp;lt;!-- *** Project Common Properties ************************************ --&amp;gt;

    &amp;lt;property name=&amp;quot;package.name&amp;quot;   value=&amp;quot;${application.name}&amp;quot;/&amp;gt;

    &amp;lt;!-- ================================================================== --&amp;gt;
    &amp;lt;!-- Targets                                                            --&amp;gt;
    &amp;lt;!-- ================================================================== --&amp;gt;

    &amp;lt;!-- *** $ phing help ************************************************* --&amp;gt;

    &amp;lt;target name        = &amp;quot;help&amp;quot;
            depends     = &amp;quot;&amp;quot;
            description = &amp;quot;Information about this build file&amp;quot;&amp;gt;

        &amp;lt;echo&amp;gt;${package.name} Phing Build commands&amp;lt;/echo&amp;gt;

    &amp;lt;/target&amp;gt;

    &amp;lt;!-- *** $ phing inits ************************************************ --&amp;gt;

    &amp;lt;target name        = &amp;quot;inits&amp;quot;
            depends     = &amp;quot;&amp;quot;
            description = &amp;quot;Initializes the application environment&amp;quot;&amp;gt;

        &amp;lt;!-- /library --&amp;gt;
        &amp;lt;mkdir dir=&amp;quot;${home}/${src.dir}&amp;quot;/&amp;gt;

        &amp;lt;!-- /tests --&amp;gt;
        &amp;lt;mkdir dir=&amp;quot;${home}/${test.dir}&amp;quot;/&amp;gt;
        &amp;lt;mkdir dir=&amp;quot;${home}/${test.dir}/phpunit&amp;quot;/&amp;gt;
        &amp;lt;mkdir dir=&amp;quot;${home}/${test.dir}/fitnesse&amp;quot;/&amp;gt;
        &amp;lt;mkdir dir=&amp;quot;${home}/${test.dir}/selenium&amp;quot;/&amp;gt;
        &amp;lt;mkdir dir=&amp;quot;${home}/${test.dir}/jsunit&amp;quot;/&amp;gt;

        &amp;lt;!-- /reports --&amp;gt;
        &amp;lt;mkdir dir=&amp;quot;${home}/${report.dir}&amp;quot;/&amp;gt;
        &amp;lt;mkdir dir=&amp;quot;${home}/${report.dir}/phpunit&amp;quot;/&amp;gt;
        &amp;lt;mkdir dir=&amp;quot;${home}/${report.dir}/coverage&amp;quot;/&amp;gt;
        &amp;lt;mkdir dir=&amp;quot;${home}/${report.dir}/codesniffer&amp;quot;/&amp;gt;

        &amp;lt;!-- /docs --&amp;gt;
        &amp;lt;mkdir dir=&amp;quot;${home}/${doc.dir}&amp;quot;/&amp;gt;
        &amp;lt;mkdir dir=&amp;quot;${home}/${doc.dir}/api&amp;quot;/&amp;gt;

        &amp;lt;!-- /contexts --&amp;gt;
        &amp;lt;mkdir dir=&amp;quot;${home}/${context.dir}&amp;quot;/&amp;gt;

    &amp;lt;/target&amp;gt;

    &amp;lt;!-- *** $ phing tests-unit ******************************************* --&amp;gt;

    &amp;lt;target name        = &amp;quot;tests-unit&amp;quot;
            depends     = &amp;quot;inits&amp;quot;
            description = &amp;quot;Executes unit tests (PHPUnit)&amp;quot;&amp;gt;

        &amp;lt;php expression=&amp;quot;require_once '${home}/${context.dir}/phpunit.php'&amp;quot;/&amp;gt;

        &amp;lt;phpunit2 haltonfailure=&amp;quot;false&amp;quot; printsummary=&amp;quot;true&amp;quot;&amp;gt;
            &amp;lt;batchtest classpath=&amp;quot;${home}/${src.dir}&amp;quot;&amp;gt;
                &amp;lt;fileset dir=&amp;quot;${home}&amp;quot;&amp;gt;
                    &amp;lt;include name=&amp;quot;${test.dir}/phpunit/**/*Test.php&amp;quot; /&amp;gt;
                &amp;lt;/fileset&amp;gt;
            &amp;lt;/batchtest&amp;gt;
            &amp;lt;formatter type    = &amp;quot;xml&amp;quot;
                       todir   = &amp;quot;${home}/${report.dir}/phpunit&amp;quot;
                       outfile = &amp;quot;tests-report.xml&amp;quot; /&amp;gt;
        &amp;lt;/phpunit2&amp;gt;

        &amp;lt;phpunit2report
            infile = &amp;quot;${home}/${report.dir}/phpunit/tests-report.xml&amp;quot;
            format = &amp;quot;frames&amp;quot;
            todir  = &amp;quot;${home}/${report.dir}/phpunit&amp;quot; /&amp;gt;

    &amp;lt;/target&amp;gt;

    &amp;lt;!-- *** $ phing tests-unit-with-coverage ***************************** --&amp;gt;

    &amp;lt;target name        = &amp;quot;tests-unit-with-coverage&amp;quot;
            depends     = &amp;quot;inits&amp;quot;
            description = &amp;quot;Executes unit tests (PHPUnit), coverage (XDebug)&amp;quot;&amp;gt;

        &amp;lt;php expression=&amp;quot;require_once '${home}/${context.dir}/phpunit.php'&amp;quot;/&amp;gt;

        &amp;lt;coverage-setup database=&amp;quot;${home}/${report.dir}/coverage/coverage.db&amp;quot;&amp;gt;
            &amp;lt;fileset dir=&amp;quot;${home}&amp;quot;&amp;gt;
                &amp;lt;include name=&amp;quot;${src.dir}/**/*.php&amp;quot; /&amp;gt;
                &amp;lt;exclude name=&amp;quot;${src.dir}/**/*Test.php&amp;quot; /&amp;gt;
            &amp;lt;/fileset&amp;gt;
        &amp;lt;/coverage-setup&amp;gt;

        &amp;lt;phpunit2 haltonfailure = &amp;quot;false&amp;quot;
                  printsummary  = &amp;quot;true&amp;quot;
                  codecoverage  = &amp;quot;true&amp;quot;&amp;gt;
            &amp;lt;batchtest classpath=&amp;quot;${home}/${src.dir}&amp;quot;&amp;gt;
                &amp;lt;fileset dir=&amp;quot;${home}&amp;quot;&amp;gt;
                    &amp;lt;include name=&amp;quot;${test.dir}/**/*Test.php&amp;quot; /&amp;gt;
                &amp;lt;/fileset&amp;gt;
            &amp;lt;/batchtest&amp;gt;
            &amp;lt;formatter type    = &amp;quot;xml&amp;quot;
                       todir   = &amp;quot;${home}/${report.dir}/phpunit&amp;quot;
                       outfile = &amp;quot;tests-report.xml&amp;quot; /&amp;gt;
        &amp;lt;/phpunit2&amp;gt;

        &amp;lt;coverage-report outfile=&amp;quot;${home}/${report.dir}/coverage/coverage.db&amp;quot;&amp;gt;
            &amp;lt;report todir=&amp;quot;${home}/${report.dir}/coverage&amp;quot; /&amp;gt;
        &amp;lt;/coverage-report&amp;gt;

        &amp;lt;phpunit2report
            infile = &amp;quot;${home}/${report.dir}/phpunit/tests-report.xml&amp;quot;
            format = &amp;quot;frames&amp;quot;
            todir  = &amp;quot;${home}/${report.dir}/phpunit&amp;quot; /&amp;gt;

    &amp;lt;/target&amp;gt;

    &amp;lt;!-- *** $ phing tests-functional ************************************* --&amp;gt;

    &amp;lt;target name        = &amp;quot;tests-functional&amp;quot;
            depends     = &amp;quot;inits&amp;quot;
            description = &amp;quot;Executes functional tests (Fitnesse)&amp;quot;&amp;gt;


    &amp;lt;/target&amp;gt;

    &amp;lt;!-- *** $ phing tests-javascript ************************************* --&amp;gt;

    &amp;lt;target name        = &amp;quot;tests-javascript&amp;quot;
            depends     = &amp;quot;inits&amp;quot;
            description = &amp;quot;Executes javascript tests (JSUnit)&amp;quot;&amp;gt;


    &amp;lt;/target&amp;gt;

    &amp;lt;!-- *** $ phing tests-gui ******************************************** --&amp;gt;

    &amp;lt;target name        = &amp;quot;tests-gui&amp;quot;
            depends     = &amp;quot;inits&amp;quot;
            description = &amp;quot;Executes graphical user interface tests (Selenium)&amp;quot;&amp;gt;


    &amp;lt;/target&amp;gt;

    &amp;lt;!-- *** $ phing tests ************************************************ --&amp;gt;

    &amp;lt;target name        = &amp;quot;tests&amp;quot;
            depends     = &amp;quot;tests-unit-with-coverage,tests-functional,tests-javascript,tests-gui&amp;quot;
            description = &amp;quot;Generates all tests reports for project&amp;quot;&amp;gt;



    &amp;lt;/target&amp;gt;

    &amp;lt;!-- *** $ phing checks-convention ************************************ --&amp;gt;

    &amp;lt;target name        = &amp;quot;checks-convention&amp;quot;
            depends     = &amp;quot;inits&amp;quot;
            description = &amp;quot;Checks the quality of the code towards convention&amp;quot;&amp;gt;
            
        &amp;lt;php expression=&amp;quot;$cwd = getcwd()&amp;quot; returnProperty=&amp;quot;cwd&amp;quot;/&amp;gt;

        &amp;lt;phpcs  outputFile   = &amp;quot;${home}/${report.dir}/codesniffer/checks-report.xml&amp;quot;
                standard     = &amp;quot;${convention}&amp;quot;
                showWarnings = &amp;quot;true&amp;quot;
                tabWidth     = &amp;quot;4&amp;quot;
                format       = &amp;quot;checkstyle&amp;quot;&amp;gt;
                &amp;lt;fileset dir=&amp;quot;${home}/${src.dir}&amp;quot;&amp;gt;
                &amp;lt;include name=&amp;quot;**/*.php&amp;quot; /&amp;gt;
                &amp;lt;include name=&amp;quot;**/*.phtml&amp;quot; /&amp;gt;
            &amp;lt;/fileset&amp;gt;
        &amp;lt;/phpcs&amp;gt;

        &amp;lt;php expression=&amp;quot;chdir('${cwd}')&amp;quot;/&amp;gt;
        
        &amp;lt;xslt file   = &amp;quot;${home}/${report.dir}/codesniffer/checks-report.xml&amp;quot;
              tofile = &amp;quot;${home}/${report.dir}/codesniffer/index.html&amp;quot;
              style  = &amp;quot;${home}/${config.dir}/codesniffer.xsl&amp;quot; /&amp;gt;

    &amp;lt;/target&amp;gt;

    &amp;lt;!-- *** $ phing checks-syntax **************************************** --&amp;gt;

    &amp;lt;target name        = &amp;quot;checks-syntax&amp;quot;
            depends     = &amp;quot;inits&amp;quot;
            description = &amp;quot;Checks the syntax of the code (Lint)&amp;quot;&amp;gt;

        &amp;lt;phplint haltonfailure=&amp;quot;true&amp;quot;&amp;gt;
            &amp;lt;fileset dir=&amp;quot;${home}&amp;quot;&amp;gt;
                &amp;lt;include name=&amp;quot;${src.dir}/**/*.php&amp;quot;/&amp;gt;
                &amp;lt;include name=&amp;quot;${src.dir}/**/*.phtml&amp;quot;/&amp;gt;
            &amp;lt;/fileset&amp;gt;
        &amp;lt;/phplint&amp;gt;

    &amp;lt;/target&amp;gt;

    &amp;lt;!-- *** $ phing checks *********************************************** --&amp;gt;

    &amp;lt;target name        = &amp;quot;checks&amp;quot;
            depends     = &amp;quot;checks-convention,checks-syntax&amp;quot;
            description = &amp;quot;Executes checking operations (convention,syntax)&amp;quot;&amp;gt;



    &amp;lt;/target&amp;gt;

    &amp;lt;!-- *** $ phing builds-on-commit ************************************* --&amp;gt;

    &amp;lt;target name        = &amp;quot;builds-on-commit&amp;quot;
            depends     = &amp;quot;tests-unit&amp;quot;
            description = &amp;quot;Continous integration Light-build on svn commit&amp;quot;&amp;gt;



    &amp;lt;/target&amp;gt;

    &amp;lt;!-- *** $ phing builds-nightly *************************************** --&amp;gt;

    &amp;lt;target name        = &amp;quot;builds-nightly&amp;quot;
            depends     = &amp;quot;tests,checks,docs&amp;quot;
            description = &amp;quot;Continuous integration full nightly-build&amp;quot;&amp;gt;



    &amp;lt;/target&amp;gt;

    &amp;lt;!-- *** $ phing docs-api ********************************************* --&amp;gt;

    &amp;lt;target name        = &amp;quot;docs-api&amp;quot;
            depends     = &amp;quot;inits&amp;quot;
            description = &amp;quot;Generates the PHPDocumentor API Documentation&amp;quot;&amp;gt;

        &amp;lt;phpdoc title               = &amp;quot;${package.name} API Documentation&amp;quot;
                destdir             = &amp;quot;${home}/${doc.dir}/api&amp;quot;
                sourcecode          = &amp;quot;yes&amp;quot;
                output              = &amp;quot;HTML:Smarty:PHP&amp;quot;
                defaultcategoryname = &amp;quot;${package.name}&amp;quot;
                defaultpackagename  = &amp;quot;${package.name}&amp;quot;&amp;gt;
            &amp;lt;fileset dir=&amp;quot;${home}&amp;quot;&amp;gt;
                &amp;lt;include name=&amp;quot;${src.dir}/**&amp;quot; /&amp;gt;
                &amp;lt;include name=&amp;quot;${test.dir}/phpunit/**&amp;quot; /&amp;gt;
                &amp;lt;include name=&amp;quot;${test.dir}/fitnesse/**&amp;quot; /&amp;gt;
            &amp;lt;/fileset&amp;gt;
            &amp;lt;projdocfileset dir=&amp;quot;${home}/${src.dir}&amp;quot;&amp;gt;
            &amp;lt;/projdocfileset&amp;gt;
        &amp;lt;/phpdoc&amp;gt;

    &amp;lt;/target&amp;gt;

    &amp;lt;!-- *** $ phing docs ************************************************* --&amp;gt;

    &amp;lt;target name        = &amp;quot;docs&amp;quot;
            depends     = &amp;quot;docs-api&amp;quot;
            description = &amp;quot;Generates all documentation for project&amp;quot;&amp;gt;



    &amp;lt;/target&amp;gt;
    
    &amp;lt;!-- *** $ phing deploys-testfunc ************************************* --&amp;gt;

    &amp;lt;target name        = &amp;quot;deploys-testfunc&amp;quot;
            depends     = &amp;quot;tests-unit&amp;quot;
            description = &amp;quot;Executes unit tests and deploys a tag of the application on functional tests platform&amp;quot;&amp;gt;

        &amp;lt;echo&amp;gt;Deploying on functional tests platform...&amp;lt;/echo&amp;gt;

        &amp;lt;echo&amp;gt;@todo: specific function tests platform pre-deployment tasks...&amp;lt;/echo&amp;gt;

        &amp;lt;phingcall target=&amp;quot;_template-deploys&amp;quot;&amp;gt;
            &amp;lt;property name=&amp;quot;${deploy.platform}&amp;quot; value=&amp;quot;testfunc&amp;quot;/&amp;gt;
        &amp;lt;/phingcall&amp;gt;
        
        &amp;lt;echo&amp;gt;@todo: specific functional tests platform post-deployment tasks...&amp;lt;/echo&amp;gt;

        &amp;lt;echo&amp;gt;Application deployed on functional tests platform.&amp;lt;/echo&amp;gt;

    &amp;lt;/target&amp;gt;

    &amp;lt;!-- *** $ phing deploys-integ **************************************** --&amp;gt;

    &amp;lt;target name        = &amp;quot;deploys-integ&amp;quot;
            depends     = &amp;quot;tests-unit&amp;quot;
            description = &amp;quot;Executes unit tests and deploys a tag of the application on integration&amp;quot;&amp;gt;

        &amp;lt;echo&amp;gt;Deploying on integration...&amp;lt;/echo&amp;gt;

        &amp;lt;echo&amp;gt;@todo: specific integration pre-deployment tasks...&amp;lt;/echo&amp;gt;

        &amp;lt;phingcall target=&amp;quot;_template-deploys&amp;quot;&amp;gt;
            &amp;lt;property name=&amp;quot;${deploy.platform}&amp;quot; value=&amp;quot;integ&amp;quot;/&amp;gt;
        &amp;lt;/phingcall&amp;gt;
        
        &amp;lt;echo&amp;gt;@todo: specific integration post-deployment tasks...&amp;lt;/echo&amp;gt;

        &amp;lt;echo&amp;gt;Application deployed on integration.&amp;lt;/echo&amp;gt;

    &amp;lt;/target&amp;gt;

    &amp;lt;!-- *** $ phing deploys-preprod ************************************** --&amp;gt;

    &amp;lt;target name        = &amp;quot;deploys-preprod&amp;quot;
            depends     = &amp;quot;tests-unit&amp;quot;
            description = &amp;quot;Executes unit tests and deploys a tag of the application on pre-production&amp;quot;&amp;gt;

        &amp;lt;echo&amp;gt;Deploying on preproduction...&amp;lt;/echo&amp;gt;

        &amp;lt;echo&amp;gt;@todo: specific preproduction pre-deployment tasks...&amp;lt;/echo&amp;gt;

        &amp;lt;phingcall target=&amp;quot;_template-deploys&amp;quot;&amp;gt;
            &amp;lt;property name=&amp;quot;${deploy.platform}&amp;quot; value=&amp;quot;preprod&amp;quot;/&amp;gt;
        &amp;lt;/phingcall&amp;gt;

        &amp;lt;echo&amp;gt;@todo: specific preproduction post-deployment tasks...&amp;lt;/echo&amp;gt;

        &amp;lt;echo&amp;gt;Application deployed on preproduction.&amp;lt;/echo&amp;gt;

    &amp;lt;/target&amp;gt;

    &amp;lt;!-- *** $ phing deploys-prod ***************************************** --&amp;gt;

    &amp;lt;target name        = &amp;quot;deploys-prod&amp;quot;
            depends     = &amp;quot;tests-unit&amp;quot;
            description = &amp;quot;Executes unit tests and deploys a tag of the application on production&amp;quot;&amp;gt;

        &amp;lt;echo&amp;gt;Deploying on production...&amp;lt;/echo&amp;gt;

        &amp;lt;echo&amp;gt;@todo: specific production pre-deployment tasks...&amp;lt;/echo&amp;gt;

        &amp;lt;phingcall target=&amp;quot;_template-deploys&amp;quot;&amp;gt;
            &amp;lt;property name=&amp;quot;${deploy.platform}&amp;quot; value=&amp;quot;prod&amp;quot;/&amp;gt;
        &amp;lt;/phingcall&amp;gt;
        
        &amp;lt;echo&amp;gt;@todo: specific production post-deployment tasks...&amp;lt;/echo&amp;gt;

        &amp;lt;echo&amp;gt;Application deployed on production.&amp;lt;/echo&amp;gt;

    &amp;lt;/target&amp;gt;

    &amp;lt;!-- *** internal task ************************************************ --&amp;gt;

    &amp;lt;target name        = &amp;quot;_template-deploys&amp;quot;
            depends     = &amp;quot;tests-unit&amp;quot;
            description = &amp;quot;Template - Executes unit tests and deploys a tag of the application on a platform&amp;quot;&amp;gt;

        &amp;lt;if&amp;gt;
            &amp;lt;isnotset property=&amp;quot;deploy.platform&amp;quot;/&amp;gt;
            &amp;lt;then&amp;gt;
                &amp;lt;fail&amp;gt;You must set the deploy.platform property with the name of the platform to deploy on&amp;lt;/fail&amp;gt;
            &amp;lt;/then&amp;gt;
        &amp;lt;/if&amp;gt;

        &amp;lt;if&amp;gt;
            &amp;lt;isnotset property=&amp;quot;tag&amp;quot;/&amp;gt;
            &amp;lt;then&amp;gt;
                &amp;lt;fail&amp;gt;You must provide a svn tag in order to deploy this tag on ${deploy.platform}&amp;lt;/fail&amp;gt;
            &amp;lt;/then&amp;gt;
        &amp;lt;/if&amp;gt;

        &amp;lt;property file=&amp;quot;${home}/${config.dir}/platforms/${deploy.platform}.properties&amp;quot; override=&amp;quot;true&amp;quot; /&amp;gt;

        &amp;lt;property name=&amp;quot;deploy.src.dir&amp;quot; value=&amp;quot;${home}/${build.dir}/${deploy.platform}/${tag}&amp;quot; /&amp;gt;
        &amp;lt;property name=&amp;quot;svn.url&amp;quot;        value=&amp;quot;${svn.repo}/tags/${tag}&amp;quot; /&amp;gt;

        &amp;lt;svnexport svnpath       = &amp;quot;${svn.bin}&amp;quot;
                   username      = &amp;quot;${svn.username}&amp;quot;
                   password      = &amp;quot;${svn.password}&amp;quot;
                   force         = &amp;quot;true&amp;quot;
                   nocache       = &amp;quot;true&amp;quot;
                   repositoryurl = &amp;quot;${svn.url}&amp;quot;
                   todir         = &amp;quot;${deploy.src.dir}&amp;quot;/&amp;gt;
        
        &amp;lt;echo&amp;gt;@todo : deployment (zip ${deploy.src.dir, then ftp, ...) &amp;lt;/echo&amp;gt;

        &amp;lt;fail&amp;gt;@remove: Not yet implemented&amp;lt;/fail&amp;gt;

    &amp;lt;/target&amp;gt;

&amp;lt;/project&amp;gt;
&lt;/pre&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/06/05/Exemple-de-fichier-buildxml-pour-un-projet-utilisant-l-integration-continue#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/06/05/Exemple-de-fichier-buildxml-pour-un-projet-utilisant-l-integration-continue#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/41</wfw:commentRss>
      </item>
    
  <item>
    <title>Des valeurs par défaut ... qui cachent des bugs !</title>
    <link>http://blog.phppro.fr/?post/2009/05/30/Des-valeurs-par-defaut-qui-cachent-des-bugs</link>
    <guid isPermaLink="false">urn:md5:3ca5dd5456d8159ec8623b000c43823b</guid>
    <pubDate>Sat, 30 May 2009 16:18:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Trucs et astuces</category>
        <category>bug</category>    
    <description>&lt;pre&gt;
function mafunction($param1,$param2='yes') { ... }
&lt;/pre&gt;

&lt;p&gt;Que se passe-t-il si vous faites cet appel&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
mafunction(&amp;quot;monparam1&amp;quot;,null);
&lt;/pre&gt;

&lt;p&gt;ou bien&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
mafunction(&amp;quot;monparam1&amp;quot;,$value2);
&lt;/pre&gt;

&lt;p&gt;avec $value2 = null&amp;nbsp;?&lt;/p&gt;    &lt;p&gt;Nous sommes souvent tenté de créer des fonctions et/ou des méthodes pour réaliser nos tâches et logique de code. C'est normal&lt;br /&gt;
Dans bien des cas, nous revenons sur notre code plusieurs jours/semaines/mois après pour améliorer certaines fonctionnalités ou tout bonnement en ajouter. C'est normal&lt;br /&gt;
&lt;br /&gt;
Il nous arrive alors de rajouter des paramètres facultatifs (avec ces fameuses valeurs par défaut) pour ajouter une fonctionnalité tout en restant compatible avec l'existant&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
/**
 * Lists enabled items of specified type
 *
 * @param string $type type of items to return
 *
 * @return array of enabled items
 */
function listItems($type) {
    // ...
    return $items;
}
&lt;/pre&gt;

&lt;p&gt;vers par exemple&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
/**
 * Lists items of specified type and specified status
 *
 * @param string $type type of items to return
 * @param string $status optional status (default is 'enabled')
 *
 * @return array of items
 */
function listItems($type,$status = 'enabled') {
    // ....
    if ('enabled' === $status) {
        // ...
        $items = ...
    } else {
        // ...
        $items = ...
    }
    return $items;
}
&lt;/pre&gt;

&lt;p&gt;Puis nous appellons notre fonction par exemple comme ceci&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
$items = listItems('users');
$items = listItems('users','enabled');
$items = listItems('users','disabled');
&lt;/pre&gt;

&lt;p&gt;Jusque là tout va bien... enfin vous êtes sûr&amp;nbsp;? Finalement peut être pas tant que ça, en effet, que se passe-t-il si nous faisons ces appels&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
$items = listItems('users',null);
$items = listItems('users',$status); // isset($status) === false
$items = listItems('users',$status); // $status = null
&lt;/pre&gt;

&lt;p&gt;Et bien le deuxième paramètre étant fourni (null dans les 3 cas), la valeur par défaut ne sera pas prise en compte&amp;nbsp;! et la variable locale '$status' dans votre fonction 'listItems' aura la valeur ... null !&lt;br /&gt;
Vous saisissez le problème&amp;nbsp;? En fait, au lieu d'avoir la valeur 'enabled', valeur que vous vous attendez à avoir si le paramètre $status est non fournie (et par abus de langage, null - ce qui est donc faux), vous aurez la valeur null.&lt;br /&gt;
Il est donc nécessaire systématiquement de rajouter un test à l'intérieur de la fonction&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
function listItems($type,$status = 'enabled') {
    if (!$status) {
        $status = 'enabled';
    }
    // ....
    if ('enabled' === $status) {
        // ...
        $items = ...
    } else {
        // ...
        $items = ...
    }
    return $items;
}
&lt;/pre&gt;

&lt;p&gt;voir même si vous êtes parano&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
function listItems($type,$status = 'enabled') {
    if (!$status || !in_array($status,array('enabled','disabled'))) {
        $status = 'enabled';
    }
    // ....
    if ('enabled' === $status) {
        // ...
        $items = ...
    } else {
        // ...
        $items = ...
    }
    return $items;
}
&lt;/pre&gt;

&lt;p&gt;Vous me direz, oui mais au lieu d'utiliser une string ('enabled') on aurait pu utiliser un boolean et le problème était réglé pas besoin de cette verrue. Vous aurez raison, mais du coup pour tous les paramètres qui ont des valeurs par défaut autre que string (regardez votre code je pense qu'il y en plein ;) ) on fait comment sinon&amp;nbsp;? ;)&lt;/p&gt;


&lt;p&gt;Alors, gare aux bugs&amp;nbsp;! L'utilisation des valeurs par défaut pour les  paramètres de fonctions/méthodes, ne vous dispense pas de vérifier leur valeur&amp;nbsp;!&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/05/30/Des-valeurs-par-defaut-qui-cachent-des-bugs#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/05/30/Des-valeurs-par-defaut-qui-cachent-des-bugs#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/40</wfw:commentRss>
      </item>
    
  <item>
    <title>LivePHP.net : tester les différentes versions de PHP !</title>
    <link>http://blog.phppro.fr/?post/2009/05/18/LivePHPnet-%3A-tester-les-differentes-versions-de-PHP</link>
    <guid isPermaLink="false">urn:md5:642d4cae46628c46239fbe322320fcec</guid>
    <pubDate>Mon, 18 May 2009 08:00:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Outillage</category>
            
    <description>&lt;p&gt;PHP.net est notre référence à tous pour trouver LA documentation sur PHP. Mais si vous êtes comme moi, vous êtes peut être souvent confronté à plusieurs versions de PHP sur vos projets ou chez vos clients. Comment être sûr qu'une fonction PHP se comporte de la même façon dans une version plus ancienne que la doc PHP.net&amp;nbsp;? suivez le guide ;)&lt;/p&gt;    &lt;p&gt;Voici un nouveau service en ligne qui vous permettra de tester une bonne partie (toutes les fonctions ne sont pas couvertes, pour raisons de sécurité ;)) des fonctions PHP sous forme d'une API Web services (format XML, json...).&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;Le principe est simple vous pouvez récupérer le résultat d'une fonction php en appelant l'url&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
http://livephp.net/functions/&amp;lt;nom-de-la-fonction&amp;gt;/&amp;lt;param1&amp;gt;/&amp;lt;param2&amp;gt;/....?format=&amp;lt;format&amp;gt;&amp;amp;version=&amp;lt;liste-des-versions-php&amp;gt;
&lt;/pre&gt;


&lt;p&gt;Voici quelques exemples&amp;nbsp;:&lt;/p&gt;


&lt;h3&gt;Liste des services disponibles&lt;/h3&gt;
&lt;pre&gt;
http://livephp.net

&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; ?&amp;gt;

&amp;lt;response&amp;gt;
	&amp;lt;code&amp;gt;200&amp;lt;/code&amp;gt;
	&amp;lt;message&amp;gt;success&amp;lt;/message&amp;gt;
	&amp;lt;results&amp;gt;
		&amp;lt;service id=&amp;quot;http://livephp.net/functions&amp;quot;&amp;gt;array (
  0 =&amp;gt; 'Call http://livephp.net/functions to get the list of available function for specified PHP version (version= parameter, or default PHP version)',
  1 =&amp;gt; 'Call http://livephp.net/functions/[function_name]/[arg1]/[arg2]/... to call function_name(arg1,arg2,...) and get the result back',
  2 =&amp;gt; 'You can use parameters : format=[format], version=[version], reference=[version,version,...|all]',
)&amp;lt;/service&amp;gt;
		&amp;lt;service id=&amp;quot;http://livephp.net/formats&amp;quot;&amp;gt;Call http://livephp.net/formats to get the list of available formats to use with the 'format=' parameter&amp;lt;/service&amp;gt;

		&amp;lt;service id=&amp;quot;http://livephp.net/versions&amp;quot;&amp;gt;Call http://livephp.net/versions to get the list of available PHP versions with the 'version=' parameter&amp;lt;/service&amp;gt;
	&amp;lt;/results&amp;gt;
&amp;lt;/response&amp;gt;
&lt;/pre&gt;

&lt;h3&gt;Liste des versions disponibles&lt;/h3&gt;
&lt;pre&gt;
http://livephp.net/versions

&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; ?&amp;gt;

&amp;lt;response&amp;gt;
	&amp;lt;code&amp;gt;200&amp;lt;/code&amp;gt;
	&amp;lt;message&amp;gt;success&amp;lt;/message&amp;gt;
	&amp;lt;results&amp;gt;
		&amp;lt;version&amp;gt;4.4.9&amp;lt;/version&amp;gt;
		&amp;lt;version&amp;gt;5.0.5&amp;lt;/version&amp;gt;

		&amp;lt;version&amp;gt;5.1.0&amp;lt;/version&amp;gt;
		&amp;lt;version&amp;gt;5.1.6&amp;lt;/version&amp;gt;
		&amp;lt;version&amp;gt;5.2.0&amp;lt;/version&amp;gt;
		&amp;lt;version&amp;gt;5.2.1&amp;lt;/version&amp;gt;
		&amp;lt;version&amp;gt;5.2.2&amp;lt;/version&amp;gt;
		&amp;lt;version&amp;gt;5.2.3&amp;lt;/version&amp;gt;

		&amp;lt;version&amp;gt;5.2.4&amp;lt;/version&amp;gt;
		&amp;lt;version&amp;gt;5.2.5&amp;lt;/version&amp;gt;
		&amp;lt;version&amp;gt;5.2.6&amp;lt;/version&amp;gt;
		&amp;lt;version&amp;gt;5.2.8&amp;lt;/version&amp;gt;
		&amp;lt;version&amp;gt;5.2.9&amp;lt;/version&amp;gt;
		&amp;lt;version&amp;gt;5.3-dev&amp;lt;/version&amp;gt;

		&amp;lt;version&amp;gt;6.0-dev&amp;lt;/version&amp;gt;
	&amp;lt;/results&amp;gt;
&amp;lt;/response&amp;gt;
&lt;/pre&gt;

&lt;h3&gt;Liste des formats disponibles&lt;/h3&gt;
&lt;pre&gt;
http://livephp.net/formats

&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; ?&amp;gt;

&amp;lt;response&amp;gt;
	&amp;lt;code&amp;gt;200&amp;lt;/code&amp;gt;
	&amp;lt;message&amp;gt;success&amp;lt;/message&amp;gt;
	&amp;lt;results&amp;gt;
		&amp;lt;format&amp;gt;xml&amp;lt;/format&amp;gt;
		&amp;lt;format&amp;gt;json&amp;lt;/format&amp;gt;

		&amp;lt;format&amp;gt;php&amp;lt;/format&amp;gt;
		&amp;lt;format&amp;gt;js&amp;lt;/format&amp;gt;
		&amp;lt;format&amp;gt;raw&amp;lt;/format&amp;gt;
		&amp;lt;format&amp;gt;csv&amp;lt;/format&amp;gt;
	&amp;lt;/results&amp;gt;
&amp;lt;/response&amp;gt;
&lt;/pre&gt;

&lt;h3&gt;Liste des fonctions disponibles pour la dernière release stable de PHP&lt;/h3&gt;
&lt;pre&gt;
http://livephp.net/functions

&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; ?&amp;gt;

&amp;lt;response&amp;gt;
	&amp;lt;code&amp;gt;200&amp;lt;/code&amp;gt;
	&amp;lt;message&amp;gt;success&amp;lt;/message&amp;gt;
	&amp;lt;results&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.9&amp;quot; duration=&amp;quot;0.00588488578796&amp;quot;&amp;gt;array (
  'internal' =&amp;gt; 
  array (
    0 =&amp;gt; 'zend_version',
    1 =&amp;gt; 'func_num_args',
    2 =&amp;gt; 'func_get_arg',
    3 =&amp;gt; 'func_get_args',
    4 =&amp;gt; 'strlen',
    5 =&amp;gt; 'strcmp',
    6 =&amp;gt; 'strncmp',
    7 =&amp;gt; 'strcasecmp',
    ...
  ),
  'user' =&amp;gt; 
  array (
  ),
)&amp;lt;/return&amp;gt;
	&amp;lt;/results&amp;gt;
&amp;lt;/response&amp;gt;
&lt;/pre&gt;

&lt;h3&gt;Ex: substr('test',0,3) =&amp;gt; 'tes'&lt;/h3&gt;
&lt;pre&gt;
http://livephp.net/functions/substr/test/0/3?version=all&amp;amp;format=csv

type,value,version,duration
return,tes,4.4.9,0.0035560131073
return,tes,5.0.5,0.00384306907654
return,tes,5.1.0,0.00425505638123
return,tes,5.1.6,0.00440287590027
return,tes,5.2.0,0.00467205047607
return,tes,5.2.1,0.0046968460083
return,tes,5.2.2,0.00472688674927
return,tes,5.2.3,0.00475788116455
return,tes,5.2.4,0.00470304489136
return,tes,5.2.5,0.00474095344543
return,tes,5.2.6,0.00479793548584
return,tes,5.2.8,0.00478100776672
return,tes,5.2.9,0.00476813316345
return,tes,5.3-dev,0.00529599189758
return,tes,6.0-dev,0.00888299942017
&lt;/pre&gt;

&lt;h3&gt;Ex: array_merge(array(1,2,3),1) =&amp;gt; comportement différent entre PHP 4 et PHP 5&lt;/h3&gt;
&lt;pre&gt;
http://livephp.net/functions/array_merge/[1,2,3]/1?version=all

&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; ?&amp;gt;

&amp;lt;response&amp;gt;
	&amp;lt;code&amp;gt;200&amp;lt;/code&amp;gt;
	&amp;lt;message&amp;gt;success&amp;lt;/message&amp;gt;
	&amp;lt;results&amp;gt;
		&amp;lt;return version=&amp;quot;4.4.9&amp;quot; duration=&amp;quot;0.00359106063843&amp;quot;&amp;gt;array (
  0 =&amp;gt; 1,
  1 =&amp;gt; 2,
  2 =&amp;gt; 3,
  3 =&amp;gt; '1',
)&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.0.5&amp;quot; duration=&amp;quot;0.00379920005798&amp;quot;&amp;gt;false&amp;lt;/return&amp;gt;

		&amp;lt;return version=&amp;quot;5.1.0&amp;quot; duration=&amp;quot;0.00418400764465&amp;quot;&amp;gt;false&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.1.6&amp;quot; duration=&amp;quot;0.00437688827515&amp;quot;&amp;gt;false&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.0&amp;quot; duration=&amp;quot;0.00474405288696&amp;quot;&amp;gt;false&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.1&amp;quot; duration=&amp;quot;0.00470590591431&amp;quot;&amp;gt;false&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.2&amp;quot; duration=&amp;quot;0.004723072052&amp;quot;&amp;gt;false&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.3&amp;quot; duration=&amp;quot;0.00476813316345&amp;quot;&amp;gt;false&amp;lt;/return&amp;gt;

		&amp;lt;return version=&amp;quot;5.2.4&amp;quot; duration=&amp;quot;0.00476694107056&amp;quot;&amp;gt;false&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.5&amp;quot; duration=&amp;quot;0.00478100776672&amp;quot;&amp;gt;false&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.6&amp;quot; duration=&amp;quot;0.00486397743225&amp;quot;&amp;gt;false&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.8&amp;quot; duration=&amp;quot;0.00476217269897&amp;quot;&amp;gt;false&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.9&amp;quot; duration=&amp;quot;0.0047550201416&amp;quot;&amp;gt;false&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.3-dev&amp;quot; duration=&amp;quot;0.00532984733582&amp;quot;&amp;gt;false&amp;lt;/return&amp;gt;

		&amp;lt;return version=&amp;quot;6.0-dev&amp;quot; duration=&amp;quot;0.00882506370544&amp;quot;&amp;gt;false&amp;lt;/return&amp;gt;
	&amp;lt;/results&amp;gt;
&amp;lt;/response&amp;gt;
&lt;/pre&gt;

&lt;h3&gt;md5() non dispo dans PHP 6 (pour l'instant ?)&lt;/h3&gt;
&lt;pre&gt;
http://livephp.net/functions/md5/aaaaaaaaaaaaaga?version=6.0-dev,5.2.9&amp;amp;reference=5.2.9

&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; ?&amp;gt;

&amp;lt;response&amp;gt;
	&amp;lt;code&amp;gt;200&amp;lt;/code&amp;gt;
	&amp;lt;message&amp;gt;success&amp;lt;/message&amp;gt;
	&amp;lt;results&amp;gt;
		&amp;lt;return version=&amp;quot;6.0-dev&amp;quot; duration=&amp;quot;0.00935697555542&amp;quot; regression=&amp;quot;true&amp;quot;&amp;gt;false&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.9&amp;quot; duration=&amp;quot;0.00510597229004&amp;quot;&amp;gt;a2543fd1f2af3fac6b0e273a18bbe219&amp;lt;/return&amp;gt;

	&amp;lt;/results&amp;gt;
&amp;lt;/response&amp;gt;
&lt;/pre&gt;

&lt;h3&gt;Lister les numéros de versions&lt;/h3&gt;
&lt;pre&gt;
http://livephp.net/functions/phpversion?version=all

&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; ?&amp;gt;

&amp;lt;response&amp;gt;
	&amp;lt;code&amp;gt;200&amp;lt;/code&amp;gt;
	&amp;lt;message&amp;gt;success&amp;lt;/message&amp;gt;
	&amp;lt;results&amp;gt;
		&amp;lt;return version=&amp;quot;4.4.9&amp;quot; duration=&amp;quot;0.00354194641113&amp;quot;&amp;gt;4.4.9&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.0.5&amp;quot; duration=&amp;quot;0.00374889373779&amp;quot;&amp;gt;5.0.5&amp;lt;/return&amp;gt;

		&amp;lt;return version=&amp;quot;5.1.0&amp;quot; duration=&amp;quot;0.00420808792114&amp;quot;&amp;gt;5.1.0&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.1.6&amp;quot; duration=&amp;quot;0.00435781478882&amp;quot;&amp;gt;5.1.6&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.0&amp;quot; duration=&amp;quot;0.00470304489136&amp;quot;&amp;gt;5.2.0&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.1&amp;quot; duration=&amp;quot;0.00473499298096&amp;quot;&amp;gt;5.2.1&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.2&amp;quot; duration=&amp;quot;0.00475382804871&amp;quot;&amp;gt;5.2.2&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.3&amp;quot; duration=&amp;quot;0.00475478172302&amp;quot;&amp;gt;5.2.3&amp;lt;/return&amp;gt;

		&amp;lt;return version=&amp;quot;5.2.4&amp;quot; duration=&amp;quot;0.00473499298096&amp;quot;&amp;gt;5.2.4&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.5&amp;quot; duration=&amp;quot;0.0047550201416&amp;quot;&amp;gt;5.2.5&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.6&amp;quot; duration=&amp;quot;0.004802942276&amp;quot;&amp;gt;5.2.6&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.8&amp;quot; duration=&amp;quot;0.00480198860168&amp;quot;&amp;gt;5.2.8&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.9&amp;quot; duration=&amp;quot;0.00478005409241&amp;quot;&amp;gt;5.2.9&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.3-dev&amp;quot; duration=&amp;quot;0.00531506538391&amp;quot;&amp;gt;5.3.0RC3-dev&amp;lt;/return&amp;gt;

		&amp;lt;return version=&amp;quot;6.0-dev&amp;quot; duration=&amp;quot;0.00883913040161&amp;quot;&amp;gt;false&amp;lt;/return&amp;gt;
	&amp;lt;/results&amp;gt;
&amp;lt;/response&amp;gt;
&lt;/pre&gt;

&lt;h3&gt;Récupérer la valeur de retour de la fonction au format brut&lt;/h3&gt;
&lt;pre&gt;
http://livephp.net/functions/substr/my-test/0/2?format=raw

my
&lt;/pre&gt;

&lt;h3&gt;Format CSV&amp;nbsp;!&lt;/h3&gt;
&lt;pre&gt;
http://livephp.net/functions/ucfirst/my test?version=all&amp;amp;format=csv&amp;amp;sep=;

type;value;version;duration
return;My test;4.4.9;0.00356388092041
return;My test;5.0.5;0.00379014015198
return;My test;5.1.0;0.0042290687561
return;My test;5.1.6;0.00434899330139
return;My test;5.2.0;0.00467896461487
return;My test;5.2.1;0.00471687316895
return;My test;5.2.2;0.004723072052
return;My test;5.2.3;0.00476217269897
return;My test;5.2.4;0.00476408004761
return;My test;5.2.5;0.00472712516785
return;My test;5.2.6;0.0047709941864
return;My test;5.2.8;0.00476121902466
return;My test;5.2.9;0.00476288795471
return;My test;5.3-dev;0.00525689125061
return;My test;6.0-dev;0.00884604454041
&lt;/pre&gt;

&lt;h3&gt;Certaines fonctions sont désactivées&lt;/h3&gt;
&lt;pre&gt;
http://livephp.net/functions/exec/ls

&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; ?&amp;gt;

&amp;lt;response&amp;gt;
	&amp;lt;code&amp;gt;403&amp;lt;/code&amp;gt;
	&amp;lt;message&amp;gt;Function 'exec' has not been enabled&amp;lt;/message&amp;gt;
	&amp;lt;results&amp;gt;
	&amp;lt;/results&amp;gt;
&amp;lt;/response&amp;gt;
&lt;/pre&gt;

&lt;h3&gt;Utilisez la notation json pour passer un argument tableau ou objet&lt;/h3&gt;
&lt;pre&gt;
http://livephp.net/functions/json_encode/[1,2,3]?version=all

&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; ?&amp;gt;

&amp;lt;response&amp;gt;
	&amp;lt;code&amp;gt;200&amp;lt;/code&amp;gt;
	&amp;lt;message&amp;gt;success&amp;lt;/message&amp;gt;
	&amp;lt;results&amp;gt;
		&amp;lt;return version=&amp;quot;4.4.9&amp;quot; duration=&amp;quot;0.00357604026794&amp;quot;&amp;gt;Error: Function 'json_encode' does not exist in PHP version '4.4.9'&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.0.5&amp;quot; duration=&amp;quot;0.00391697883606&amp;quot;&amp;gt;Error: Function 'json_encode' does not exist in PHP version '5.0.5'&amp;lt;/return&amp;gt;

		&amp;lt;return version=&amp;quot;5.1.0&amp;quot; duration=&amp;quot;0.00429391860962&amp;quot;&amp;gt;Error: Function 'json_encode' does not exist in PHP version '5.1.0'&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.1.6&amp;quot; duration=&amp;quot;0.0044469833374&amp;quot;&amp;gt;Error: Function 'json_encode' does not exist in PHP version '5.1.6'&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.0&amp;quot; duration=&amp;quot;0.00477600097656&amp;quot;&amp;gt;[1,2,3]&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.1&amp;quot; duration=&amp;quot;0.00473999977112&amp;quot;&amp;gt;[1,2,3]&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.2&amp;quot; duration=&amp;quot;0.00473594665527&amp;quot;&amp;gt;[1,2,3]&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.3&amp;quot; duration=&amp;quot;0.00478887557983&amp;quot;&amp;gt;[1,2,3]&amp;lt;/return&amp;gt;

		&amp;lt;return version=&amp;quot;5.2.4&amp;quot; duration=&amp;quot;0.00479698181152&amp;quot;&amp;gt;[1,2,3]&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.5&amp;quot; duration=&amp;quot;0.00476408004761&amp;quot;&amp;gt;[1,2,3]&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.6&amp;quot; duration=&amp;quot;0.00474905967712&amp;quot;&amp;gt;[1,2,3]&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.8&amp;quot; duration=&amp;quot;0.00475907325745&amp;quot;&amp;gt;[1,2,3]&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.9&amp;quot; duration=&amp;quot;0.00477600097656&amp;quot;&amp;gt;[1,2,3]&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.3-dev&amp;quot; duration=&amp;quot;0.00535202026367&amp;quot;&amp;gt;[1,2,3]&amp;lt;/return&amp;gt;

		&amp;lt;return version=&amp;quot;6.0-dev&amp;quot; duration=&amp;quot;0.00893187522888&amp;quot;&amp;gt;[1,2,3]&amp;lt;/return&amp;gt;
	&amp;lt;/results&amp;gt;
&amp;lt;/response&amp;gt;

http://livephp.net/functions/json_decode/\[1,2,3]?version=all

&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; ?&amp;gt;

&amp;lt;response&amp;gt;
	&amp;lt;code&amp;gt;200&amp;lt;/code&amp;gt;
	&amp;lt;message&amp;gt;success&amp;lt;/message&amp;gt;
	&amp;lt;results&amp;gt;
		&amp;lt;return version=&amp;quot;4.4.9&amp;quot; duration=&amp;quot;0.00359201431274&amp;quot;&amp;gt;Error: Function 'json_decode' does not exist in PHP version '4.4.9'&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.0.5&amp;quot; duration=&amp;quot;0.00375604629517&amp;quot;&amp;gt;Error: Function 'json_decode' does not exist in PHP version '5.0.5'&amp;lt;/return&amp;gt;

		&amp;lt;return version=&amp;quot;5.1.0&amp;quot; duration=&amp;quot;0.00430989265442&amp;quot;&amp;gt;Error: Function 'json_decode' does not exist in PHP version '5.1.0'&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.1.6&amp;quot; duration=&amp;quot;0.00447916984558&amp;quot;&amp;gt;Error: Function 'json_decode' does not exist in PHP version '5.1.6'&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.0&amp;quot; duration=&amp;quot;0.00474095344543&amp;quot;&amp;gt;array (
  0 =&amp;gt; 1,
  1 =&amp;gt; 2,
  2 =&amp;gt; 3,
)&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.1&amp;quot; duration=&amp;quot;0.00470018386841&amp;quot;&amp;gt;array (
  0 =&amp;gt; 1,
  1 =&amp;gt; 2,
  2 =&amp;gt; 3,
)&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.2&amp;quot; duration=&amp;quot;0.00474286079407&amp;quot;&amp;gt;array (
  0 =&amp;gt; 1,
  1 =&amp;gt; 2,
  2 =&amp;gt; 3,
)&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.3&amp;quot; duration=&amp;quot;0.00478100776672&amp;quot;&amp;gt;array (
  0 =&amp;gt; 1,
  1 =&amp;gt; 2,
  2 =&amp;gt; 3,
)&amp;lt;/return&amp;gt;

		&amp;lt;return version=&amp;quot;5.2.4&amp;quot; duration=&amp;quot;0.00489497184753&amp;quot;&amp;gt;array (
  0 =&amp;gt; 1,
  1 =&amp;gt; 2,
  2 =&amp;gt; 3,
)&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.5&amp;quot; duration=&amp;quot;0.00485587120056&amp;quot;&amp;gt;array (
  0 =&amp;gt; 1,
  1 =&amp;gt; 2,
  2 =&amp;gt; 3,
)&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.6&amp;quot; duration=&amp;quot;0.00483107566833&amp;quot;&amp;gt;array (
  0 =&amp;gt; 1,
  1 =&amp;gt; 2,
  2 =&amp;gt; 3,
)&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.8&amp;quot; duration=&amp;quot;0.0047619342804&amp;quot;&amp;gt;array (
  0 =&amp;gt; 1,
  1 =&amp;gt; 2,
  2 =&amp;gt; 3,
)&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.2.9&amp;quot; duration=&amp;quot;0.00485610961914&amp;quot;&amp;gt;array (
  0 =&amp;gt; 1,
  1 =&amp;gt; 2,
  2 =&amp;gt; 3,
)&amp;lt;/return&amp;gt;
		&amp;lt;return version=&amp;quot;5.3-dev&amp;quot; duration=&amp;quot;0.00533699989319&amp;quot;&amp;gt;array (
  0 =&amp;gt; 1,
  1 =&amp;gt; 2,
  2 =&amp;gt; 3,
)&amp;lt;/return&amp;gt;

		&amp;lt;return version=&amp;quot;6.0-dev&amp;quot; duration=&amp;quot;0.00902605056763&amp;quot;&amp;gt;array (
  0 =&amp;gt; 1,
  1 =&amp;gt; 2,
  2 =&amp;gt; 3,
)&amp;lt;/return&amp;gt;
	&amp;lt;/results&amp;gt;
&amp;lt;/response&amp;gt;

&lt;/pre&gt;


&lt;p&gt;A quand un tel service intégrer à la documentation en ligne de PHP.net&amp;nbsp;? ;)&lt;/p&gt;


&lt;p&gt;Bonne utilisation&lt;/p&gt;


&lt;p&gt;PS: vous souhaitez voir d'autres fonctionnalités&amp;nbsp;? Envoyer un email à features@livephp.net&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/05/18/LivePHPnet-%3A-tester-les-differentes-versions-de-PHP#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/05/18/LivePHPnet-%3A-tester-les-differentes-versions-de-PHP#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/39</wfw:commentRss>
      </item>
    
  <item>
    <title>Installer son propre channel pear en local (par exemple chez un client...)</title>
    <link>http://blog.phppro.fr/?post/2009/05/12/Installer-son-propre-channel-pear-en-local-par-exemple-chez-un-client</link>
    <guid isPermaLink="false">urn:md5:29a6e814e97ddc67204e400ba5fd7a7c</guid>
    <pubDate>Tue, 12 May 2009 13:44:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Trucs et astuces</category>
        <category>configuration</category><category>déploiement</category><category>mysql</category><category>outils</category><category>pear</category>    
    <description>&lt;p&gt;Un petit tutorial rapide pour vous faire un retour d'expérience sur l'installation de Chiara PEAR Server (aka un channel PEAR perso)&lt;/p&gt;    &lt;p&gt;Juste pour partager avec vous ma dernière installation du Chiara PEAR Server sur une machine windows avec wamp (désolé).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;installation de apache / mysql / php (par exemple via wamp, zend server CE, ...)&lt;/li&gt;
&lt;li&gt;ajout du répertoire racine de php dans le PATH&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
$ pear config-set http_proxy &amp;lt;my-proxy-host&amp;gt;:&amp;lt;my-proxy-port&amp;gt;
$ pear update-channels
$ pear upgrade pear
$ pear channel-discover pear.chiaraquartet.net 
$ pear install --alldeps MDB2-2.5.0b2
$ pear install --alldeps MDB2_Schema-0.8.5
$ pear install --alldeps MDB2_Driver_mysqli-1.5.0b2
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;créer la base mysql pear accessible pour le user pear, pour le host %&lt;/li&gt;
&lt;li&gt;ajouter le user mysql pear avec le mot de passe pear pour le host localhost&lt;/li&gt;
&lt;li&gt;flusher les privileges mysql&lt;/li&gt;
&lt;li&gt;créer le répertoire c:/wamp/apps/pear-channel (il s'agit du document root, vous pouvez le mettre ailleurs !)&lt;/li&gt;
&lt;li&gt;créer le fichier c:/wamp/alias/pear-channel.conf&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
NameVirtualHost *:80
&amp;lt;VirtualHost *:80&amp;gt;
	ServerName pear
	DocumentRoot &amp;quot;c:/wamp/apps/pear-channel/&amp;quot;
	&amp;lt;Directory &amp;quot;c:/wamp/apps/pear-channel/&amp;quot;&amp;gt;
		Options All
		AllowOverride all
        Order Deny,Allow
		Allow from all
	&amp;lt;/Directory&amp;gt;
	DirectoryIndex admin.php
&amp;lt;/VirtualHost&amp;gt;
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;ajouter l'alias de host dans etc/hosts (et dans le etc/hosts de tout vos postes bureautiques devant accéder au channel, ou bien via le contrôleur de domaine windows):&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
127.0.0.1	mypear
$ pear install --alldeps chiara/Chiara_PEAR_Server-0.19.0
$ pear run-scripts chiara/Chiara_PEAR_Server
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;modifier l'admin channel (6) et mettre 'admin'&lt;/li&gt;
&lt;li&gt;modifier la channel uri (7) et mettre 'mypear' (nom de la machine)&lt;/li&gt;
&lt;li&gt;tapez entrée&lt;/li&gt;
&lt;li&gt;modifier le suggested channel alias et mettre '&amp;lt;votre-alias-ici&amp;gt;' (par exemple 'mypear')&lt;/li&gt;
&lt;li&gt;mettre 'My PHP Pear channel' pour le channel description&lt;/li&gt;
&lt;li&gt;mettre 'pear' comme mot de passe&lt;/li&gt;
&lt;li&gt;tapez entrée&lt;/li&gt;
&lt;li&gt;modifier le Path to document root et mettre 'C:\wamp\apps\pear-channel'&lt;/li&gt;
&lt;li&gt;modifier le temporary path et mettre 'c:/tmp'&lt;/li&gt;
&lt;li&gt;tapez entrée&lt;/li&gt;
&lt;li&gt;reporter l'include path du fichier c:/wamp/bin/php/php5.2.6/php.ini dans tous les fichiers php.ini notamment celui d'apache (c:/wamp/bin/apache/...)&lt;/li&gt;
&lt;li&gt;redémarrer le serveur web apache&lt;/li&gt;
&lt;li&gt;accéder à http://mypear/, login=admin, pass=pear&lt;/li&gt;
&lt;li&gt;commencez à créer les mainteneurs, les catégories, les packages et à uploader des versions de package.&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;pour ajouter le &quot;channel&quot; sur un poste développeur, faire&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
$ pear -u http_proxy channel-discover mypear
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;ensuite pour installer une release d'un package situé sur le channel&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
$ pear -u http_proxy install --alldeps mypear/&amp;lt;nom-du-package&amp;gt;
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;redémarrer le serveur apache (wamp)&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Pour ajouter des packages et des releases, je vous laisse prendre connaissance du fonctionnement de Chiara PEAR Server. En gros, vous commencez par ajouter des mainteneurs, vous créez des catégories, ensuite vous créer un package que vous affectez à une catégorie. Vous ajouter alors des mainteneurs pour ce package. Le mainteneur doit alors se connecter à l'admin avec son compte et peut alors &quot;uploader&quot; une version (release) du package, préalablement packagée dans un fichier .tgz avec un fichier package.xml à la racine et un répertoire &amp;lt;nom-du-package&amp;gt;-&amp;lt;version&amp;gt; qui contient tous les fichiers. Un fois la release uploadée, elle est directement disponible à l'installation / l'update via pear&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$ pear install mypear/&amp;lt;my-package&amp;gt;
&lt;/pre&gt;


&lt;p&gt;ou bien&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$ pear update mypear/&amp;lt;my-package&amp;gt;
&lt;/pre&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/05/12/Installer-son-propre-channel-pear-en-local-par-exemple-chez-un-client#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/05/12/Installer-son-propre-channel-pear-en-local-par-exemple-chez-un-client#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/38</wfw:commentRss>
      </item>
    
  <item>
    <title>PHP Tree Match : Vérifiez que vos arborescences projets sont standardisées !</title>
    <link>http://blog.phppro.fr/?post/2009/05/11/PHP-Tree-Match-%3A-Verifiez-que-vos-arborescence-projets-sont-standardisees</link>
    <guid isPermaLink="false">urn:md5:3fbc6478949245d5e81878deda6cf58e</guid>
    <pubDate>Mon, 11 May 2009 18:41:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Outillage</category>
            
    <description>&lt;p&gt;&quot;Et alors, mes 18 projets ont tous la même arborescence&amp;nbsp;? Ils sont tous standard (i.e. respectent &quot;nos&quot; standards) et sont tous &quot;faits&quot; de la même façon ?&quot;
Certains d'entre vous se sont déjà vus poser cette question&amp;nbsp;? Si, c'est la cas, PHP Tree Match pourra probablement vous aider...&lt;/p&gt;    &lt;p&gt;PHP Tree Match, ou PHPTM de son petit nom, est un nouvel outil &quot;basique&quot; (dans la série des phpcpd, phpmd...) qui permet de vérifier que l'arborescence de votre/vos projets respectent bien une norme que vous vous donnez.
Par exemple, vous avez l'habitude mettre une arborescence du type&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
/
    application/
        default/
            controllers/
            models/
            views/
    config/
         application.ini
    html/
        index.php
        .htaccess
    ...
&lt;/pre&gt;


&lt;p&gt;Vous devez gérer 1, 5, 10 ... projets similaires et vous voulez vérifier d'un seul coup que tous vos projets respectent bien vos conventions, que le fichier config/application.ini contient bien la section &quot;&lt;a href=&quot;http://blog.phppro.fr/?post/2009/05/11/production&quot; title=&quot;production&quot;&gt;production&lt;/a&gt;&quot;, que le fichier .project n'existe pas en prod...&lt;/p&gt;


&lt;h2&gt;Installation&lt;/h2&gt;


&lt;h3&gt;à partir du Channel PEAR&lt;/h3&gt;

&lt;pre&gt;
$ pear channel-discover pear.phppro.fr
$ pear install phppro/phptm
&lt;/pre&gt;


&lt;h3&gt;mise à jour&lt;/h3&gt;

&lt;pre&gt;
$ pear upgrade phppro/phptm
&lt;/pre&gt;


&lt;h3&gt;à partir du package compatible PEAR en téléchargement sur Google Code&lt;/h3&gt;

&lt;pre&gt;
$ wget http://phptreematch.googlecode.com/files/phptm-0.1.0.tgz
$ pear install phptm-0.1.0.tgz
&lt;/pre&gt;


&lt;h2&gt;Usage&lt;/h2&gt;


&lt;p&gt;Avant d'utiliser phptm vous devez &quot;décrire&quot; votre arborescence projet dans un fichier de description au format xml. Voici un exemple&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;
&amp;lt;tree&amp;gt;
    &amp;lt;item name=&amp;quot;.project&amp;quot; present=&amp;quot;false&amp;quot;/&amp;gt;
    &amp;lt;item name=&amp;quot;bin/phptm.bat&amp;quot; hash=&amp;quot;8c8d25cde4142e4b945792c2ca9a31cc&amp;quot;/&amp;gt;
    &amp;lt;item name=&amp;quot;bin/phptm.php&amp;quot; hash=&amp;quot;635f67483f575310a904c02a2068d0dd&amp;quot;/&amp;gt;
    &amp;lt;item name=&amp;quot;build/package.xml&amp;quot; hash=&amp;quot;ca34d98facbc5ff461976d5f18910cbe&amp;quot;/&amp;gt;
    &amp;lt;item name=&amp;quot;build.xml&amp;quot; hash=&amp;quot;a25ebe507f99623d266aeee4d1ed4463&amp;quot;/&amp;gt;
    &amp;lt;item name=&amp;quot;config/build.properties&amp;quot;&amp;gt;
        &amp;lt;contains&amp;gt;version=&amp;lt;/contains&amp;gt;
        &amp;lt;contains&amp;gt;name=&amp;lt;/contains&amp;gt;
    &amp;lt;/item&amp;gt;
    &amp;lt;item name=&amp;quot;config/package.xml&amp;quot;/&amp;gt;
    &amp;lt;item name=&amp;quot;docs/README.markdown&amp;quot;/&amp;gt;
    &amp;lt;item name=&amp;quot;library/PHPTM/Analyzer.php&amp;quot;/&amp;gt;
    &amp;lt;item name=&amp;quot;library/PHPTM/Log/XML/XML.php&amp;quot;/&amp;gt;
    &amp;lt;item name=&amp;quot;library/PHPTM/Log/XML.php&amp;quot;/&amp;gt;
    &amp;lt;item name=&amp;quot;library/PHPTM/TextUI/Command.php&amp;quot;/&amp;gt;
    &amp;lt;item name=&amp;quot;library/PHPTM/TextUI/Getopt.php&amp;quot;/&amp;gt;
    &amp;lt;item name=&amp;quot;library/PHPTM/TextUI/ResultPrinter.php&amp;quot;/&amp;gt;
    &amp;lt;item name=&amp;quot;library/PHPTM/Util/FilterIterator.php&amp;quot;/&amp;gt;
&amp;lt;/tree&amp;gt;
&lt;/pre&gt;


&lt;p&gt;Le fichier ci-dessus permet notamment de vérifier que certains fichier sont bien absent(present=false), que certains fichiers existent et ont un contenu précis (calculé à partir de &quot;hash&quot;), que certains fichiers existent et contiennent une chaîne de caractère précise (&quot;contains&quot;, mais vous pouvez aussi utiliser &quot;notcontains&quot;)...&lt;/p&gt;


&lt;p&gt;Inspirez vous de l'exemple ci-dessus pour construire votre fichier de description, ou bien exécutez la commande suivante sur une arborescence type&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$ phptm --build-tree my-typical-directory &amp;gt; typical-tree.xml
&lt;/pre&gt;


&lt;p&gt;Pour &quot;passer&quot; ensuite phptm sur votre application, exécutez&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$ phptm --expected-tree=typical-tree.xml my-directory
&lt;/pre&gt;


&lt;p&gt;ou bien pour exporter aussi le résultat sous forme xml&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$ phptm --log-xml=phptm-report.xml --expected-tree=typical-tree.xml my-directory
&lt;/pre&gt;


&lt;h2&gt;Description du rapport calculé&lt;/h2&gt;


&lt;p&gt;Le rapport en ligne de commande ou bien le rapport xml vous indique alors si des erreurs de cohérences sont détectées et si oui, lesquelles&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;missing&amp;nbsp;: absence de fichier listé&lt;/li&gt;
&lt;li&gt;present&amp;nbsp;: présence de fichier listé comme devant être absent&lt;/li&gt;
&lt;li&gt;hash&amp;nbsp;: contenu de fichier non identique à celui attendu&lt;/li&gt;
&lt;li&gt;contains&amp;nbsp;: fichier contenant une chaîne marquée comme devant ne pas être dans le fichier&lt;/li&gt;
&lt;li&gt;not-contains&amp;nbsp;: fichier ne contenant pas une chaîne attendue&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Limitations&lt;/h2&gt;


&lt;p&gt;PHPTM est loin d'être un outil parfait et exhaustif, n'hésitez pas à remonter des éventuels bugs et des demandes d'évolutions sur l'espace du projet sur Google Code &lt;a href=&quot;http://code.google.com/p/phptreematch/issues/list&quot;&gt;http://code.google.com/p/phptreematch/issues/list&lt;/a&gt;&amp;nbsp;! ou à partir la documentation sur &lt;a href=&quot;http://code.google.com/p/phptreematch&quot;&gt;http://code.google.com/p/phptreematch&lt;/a&gt;&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;
&lt;strong&gt;Bonne utilisation !&lt;/strong&gt;&lt;br /&gt;&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/05/11/PHP-Tree-Match-%3A-Verifiez-que-vos-arborescence-projets-sont-standardisees#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/05/11/PHP-Tree-Match-%3A-Verifiez-que-vos-arborescence-projets-sont-standardisees#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/37</wfw:commentRss>
      </item>
    
  <item>
    <title>PHP Code Ratio : calculez le ratio de commentaires dans vos fichiers !</title>
    <link>http://blog.phppro.fr/?post/2009/05/05/PHP-Code-Ratio-%3A-calculez-le-ratio-de-commentaires-dans-vos-fichiers</link>
    <guid isPermaLink="false">urn:md5:5f6702ccd96ae3e800a27f9601aa7d46</guid>
    <pubDate>Tue, 05 May 2009 11:23:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Outillage</category>
        <category>métrique</category><category>outils</category>    
    <description>&lt;p&gt;&quot;Et alors, quel est le pourcentage de commentaires dans mon appli ?&quot;
Certains d'entre vous se sont déjà vus poser cette question&amp;nbsp;? Si, c'est la cas, PHP Code Ratio pourra probablement vous aider...&lt;/p&gt;    &lt;p&gt;PHP Code Ratio, ou PHPCR de son petit nom, est un nouvel outil &quot;basique&quot; (dans la série des phpcpd, phpmd...) qui permet de connaître le pourcentage de commentaire dans vos fichiers php.&lt;/p&gt;


&lt;h2&gt;Installation&lt;/h2&gt;


&lt;h3&gt;à partir du Channel PEAR&lt;/h3&gt;

&lt;pre&gt;
$ pear channel-discover pear.phppro.fr
$ pear install phppro/phpcr
&lt;/pre&gt;


&lt;h3&gt;mise à jour&lt;/h3&gt;

&lt;pre&gt;
$ pear upgrade phppro/phpcr
&lt;/pre&gt;


&lt;h3&gt;à partir du package compatible PEAR en téléchargement sur Google Code&lt;/h3&gt;

&lt;pre&gt;
$ wget http://phpcoderatio.googlecode.com/files/phpcr-0.1.0.tgz
$ pear install phpcr-0.1.0.tgz
&lt;/pre&gt;


&lt;h2&gt;Usage&lt;/h2&gt;


&lt;p&gt;Pour &quot;passer&quot; phpcr sur votre application, exécutez&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$ phpcr my-directory
&lt;/pre&gt;


&lt;p&gt;ou bien pour exporter aussi le résultat sous forme xml&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$ phpcr --log-xml=phpcr-report.xml my-directory
&lt;/pre&gt;


&lt;h2&gt;Description des métriques calculées&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Metrique 'code'&amp;nbsp;: pourcentage de code php sans les espaces, sans les commentaires et sans le HTML &quot;inline&quot;&lt;/li&gt;
&lt;li&gt;Metrique 'code-with-space'&amp;nbsp;: pourcentage de code php avec les espaces mais sans commentaires et sans HTML &quot;inline&quot;&lt;/li&gt;
&lt;li&gt;Metrique 'code-with-space-and-html'&amp;nbsp;: pourcentage de code php avec espaces, HTML &quot;inline&quot; mais sans commentaire&lt;/li&gt;
&lt;li&gt;Metrique 'html'&amp;nbsp;: pourcentage de HTML &quot;inline&quot; dans les fichiers PHP&lt;/li&gt;
&lt;li&gt;Metrique 'whitespace'&amp;nbsp;: pourcentage d'espace dans les fichiers php&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Extensions parsées&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;php&lt;/li&gt;
&lt;li&gt;php3&lt;/li&gt;
&lt;li&gt;php4&lt;/li&gt;
&lt;li&gt;php5&lt;/li&gt;
&lt;li&gt;phtml&lt;/li&gt;
&lt;li&gt;inc&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;mais vous pouvez modifier cette liste en utilisant le paramètre ligne de commande &quot;--suffixes=&quot;&lt;/p&gt;


&lt;h2&gt;Limitations&lt;/h2&gt;


&lt;p&gt;PHPCR est loin d'être un outil parfait et exhaustif, n'hésitez pas à remonter des éventuels bugs et des demandes d'évolutions sur l'espace du projet sur Google Code &lt;a href=&quot;http://code.google.com/p/phpcoderatio/issues/list&quot;&gt;http://code.google.com/p/phpcoderatio/issues/list&lt;/a&gt;&amp;nbsp;! ou à partir la documentation sur &lt;a href=&quot;http://code.google.com/p/phpcoderatio&quot;&gt;http://code.google.com/p/phpcoderatio&lt;/a&gt;&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;
&lt;strong&gt;Bonne utilisation !&lt;/strong&gt;&lt;br /&gt;&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/05/05/PHP-Code-Ratio-%3A-calculez-le-ratio-de-commentaires-dans-vos-fichiers#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/05/05/PHP-Code-Ratio-%3A-calculez-le-ratio-de-commentaires-dans-vos-fichiers#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/36</wfw:commentRss>
      </item>
    
  <item>
    <title>Pourquoi Eclipse et les annotations ont révolutionné ma façon de coder</title>
    <link>http://blog.phppro.fr/?post/2009/04/20/Pourquoi-Eclipse-et-les-annotations-ont-revolutionne-ma-facon-de-coder</link>
    <guid isPermaLink="false">urn:md5:5b9626aee0911b0724781b8a047b35ee</guid>
    <pubDate>Tue, 21 Apr 2009 08:55:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Outillage</category>
        <category>annotation</category><category>build</category><category>eclipse</category><category>factory</category><category>pattern</category><category>refactoring</category><category>zend studio</category>    
    <description>&lt;p&gt;Comme beaucoup d'entre vous, &quot;vi&quot; (prononcez &quot;vi-aille&quot;) a longtemps été mon ami pour développer mon code PHP.&lt;br /&gt;
Il me plaisait bien, j'avais l'air d'être un geek et tout allait bien.&lt;br /&gt;&lt;/p&gt;    &lt;p&gt;Et puis il y a quelques temps (2-3 ans), j'ai du travailler chez un client avec Eclipse + Plugin PHP (que je connaissais en Java mais qui ne m'attirait pas à l'époque pour PHP au vu du peu de fonctionnalités que je pensais disponible pour cette plateforme).&lt;br /&gt;
Ce type d'outils généère un bloc de commentaire au dessus d'une fonction ou méthode quand on en créé une (après le passage à la ligne par l'appui sur la touche entrée).&lt;br /&gt;
J'ai vu qu'il rajoutait des annotations ou doclet&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
/**
 * ...
 * 
 * @return unknown
 */
&lt;/pre&gt;


&lt;p&gt;Je me suis dit que j'allais alors remplacer unknown par le type retourné quand il s'agissait d'un objet, par exemple&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
/**
 * ...
 * 
 * @return Sms
 */
&lt;/pre&gt;


&lt;p&gt;(en ce temps là, j'avais encore le temps de perdre mon temps à faire des choses d'apparences inutiles dans le code...).&lt;br /&gt;
Jusque là, rien de transcendant. Et puis dans un autre fichier, j'ai utilisé ma méthode nouvellement créé, et là, surprise&amp;nbsp;! Eclipse me propose une autocomplétion avec la liste de toutes les méthodes de la classe Sms&amp;nbsp;! Cool !&lt;br /&gt;
Je me suis dit alors que j'allais essayer de systématiquement mettre les @return, et que ca m'aiderait à aller plus vite (plutôt que de chercher le nom des méthodes dispos en switchant entre les fichiers).&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;Puis je remarque aussi qu'il me met des @param au-dessus des méthodes / fonctions, pour décrire les paramètres de mes fonctions.&lt;br /&gt;&lt;/p&gt;

&lt;pre&gt;
/**
 * ...
 * 
 * @param unknown $sms
 * @param unknown $xmlelement
 *
 * ...
 */
&lt;/pre&gt;


&lt;p&gt;Je décide alors d'essayer de mettre des types, par exemple&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
/**
 * ...
 * 
 * @param Sms $sms le sms à envoyer
 * @param SimpleXMLElement $xmlelement le document xml
 *
 * ...
 */
&lt;/pre&gt;


&lt;p&gt;Et je me rends compte qu'il m'indique ces types dans l'autocompletion, ce qui m'aide dans de nombreux cas pour savoir quoi passer à la fonction / méthode.&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;Par contre, j'ai souvent plein de méthodes qui ne renvoient rien, donc je ne bénéficie d'aucune complétion automatique dans ces cas là.&lt;br /&gt;
Je me dis alors, et si je renvoyait $this ?&lt;br /&gt;
Je mets alors des&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
...
{
    ...
    return $this;
}
&lt;/pre&gt;


&lt;p&gt;à la fin de chaque méthode qui n'est pas censée renvoyer un résultat (par exemple les setters !) et je rajoute l'annotation @return &amp;lt;la-classe-de-$this&amp;gt;.&lt;br /&gt;
Du coup, je peux chainer certains de mes appels&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$sms = new Sms();
$sms-&amp;gt;setSender(...)
          -&amp;gt;addRecipient(...)
          -&amp;gt;addRecipient(...)
          -&amp;gt;setMessage(...)
          -&amp;gt;send();
&lt;/pre&gt;


&lt;p&gt;Cool&amp;nbsp;! Pas mal, mon code devient plus concis et du coup, j'essaye de nommer mes méthodes pour que leur &quot;enchainement&quot; soit compréhensible et lisible. En plus, j'ai l'auto-complétion pour chacune des lignes grâce à mon @return !&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;La cerise sur le gâteau&amp;nbsp;: j'utilise alors le pattern Factory (avec une méthode statique)&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
    /**
      * Returns a new instance of this class
      *
      * @return Sms
      */
    public final static function getInstance()
    {
        return new self;
    }
&lt;/pre&gt;


&lt;p&gt;Ce qui me permet de simplifier encore mon utilisation de mes classes&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
Sms::getInstance()
    -&amp;gt;setSender(...)
    -&amp;gt;addRecipient(...)
    -&amp;gt;addRecipient(...)
    -&amp;gt;setMessage(...)
    -&amp;gt;send();
&lt;/pre&gt;


&lt;p&gt;On pourrait même mettre tout sur une ligne&amp;nbsp;! (attention à la lisibilité).&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;Si je résume, grâce à Eclipse et aux annotations&amp;nbsp;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;j'ai tendance à utiliser plutôt les structures objet pour avoir l'auto-complétion (plutôt que de multiple paramètre string)&lt;/li&gt;
&lt;li&gt;je fais des return $this systématiques pour les méthodes qui ne renvoient rien&lt;/li&gt;
&lt;li&gt;j'ai simplifié et rendu plus lisible les noms de mes méthodes pour qu'elles s'inscrivent harmonieusement dans un enchainement&lt;/li&gt;
&lt;li&gt;j'ai typé mes paramètres de fonctions / méthodes ce qui me permet d'avoir l'auto-complétion sur les paramètres dans mes fonctions&lt;/li&gt;
&lt;li&gt;je crée quasi systématiquement des factories pour éviter de passer par une variable inutile&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Une vraie révolution, à l'époque, pour moi !&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;Et vous, quels sont les petits trucs qui ont révolutionné votre manière de coder&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/04/20/Pourquoi-Eclipse-et-les-annotations-ont-revolutionne-ma-facon-de-coder#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/04/20/Pourquoi-Eclipse-et-les-annotations-ont-revolutionne-ma-facon-de-coder#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/32</wfw:commentRss>
      </item>
    
  <item>
    <title>Générer un document OpenXML à partir d'un modèle (pptx, xlsx,docx) et de variables: où comment générer vos documents à la volée simplement</title>
    <link>http://blog.phppro.fr/?post/2009/04/18/Generer-un-document-OpenXML-a-partir-d-un-modele-pptx-xlsxdocx-et-de-variables%3A-ou-comment-generer-vos-document-a-la-volee-simplement</link>
    <guid isPermaLink="false">urn:md5:411502baf0e0cecaa67e65392488c01e</guid>
    <pubDate>Sat, 18 Apr 2009 10:08:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Trucs et astuces</category>
        <category>openxml</category><category>projet</category><category>regexp</category><category>zip</category>    
    <description>&lt;p&gt;Vous avez produit pour un client un document powerpoint vraiment bien, avec l'entête de votre entreprise, votre logo... sur un sujet qui revient souvent pour vous.&lt;br /&gt;
Le temps passe, et voilà qu'on vous redemande le même sujet ou très similaire mais avec quelques informations différentes ou bien à actualiser.&lt;br /&gt;&lt;/p&gt;    &lt;p&gt;Si vous êtes capable d'extraire dans un fichier toute les informations qui ont changé, vous trouverez ci-dessous un script qui peut vous faire gagner du temps pour re-générer le document mis à jour.&lt;/p&gt;


&lt;h2&gt;Principe du script&lt;/h2&gt;


&lt;p&gt;Les fichiers OpenXML (*.pptx, *.docx, *.xlsx) sont en fait des fichiers zippés qui contient des répertoires et des fichiers xml (entre autres). Après avoir extrait le zip (avec l'extension PHP Zip, qui est donc nécessaire !), il est donc facile de remplacer des mots ou &quot;variables&quot; dans les fichiers xml (texte). Le script ci-dessous utilise la fonction preg_match_all() pour trouver les occurences de l'expression '${*}' ou '*' est le nom d'une variable. Si vous mettez donc '${mavariable}' n'importe où dans votre document (avec votre bien vieux microsoft Office), elle sera trouvée et remplacée par la valeur que vous aurez indiquée pour cette variable dans un fichier de configuration (ini).&lt;/p&gt;


&lt;p&gt;Le principe est simple, limité, mais fonctionnel. Abusez-en&amp;nbsp;!&lt;/p&gt;


&lt;h2&gt;Création du fichier template&lt;/h2&gt;


&lt;p&gt;Commencez par créer / modifier votre fichier pptx (ou docx ou xlsx) en ajoutant des '${mavariable}' à la place des mots, phrases, ou valeurs que voulez modifier dynamiquement, appellons ce fichier 'template.pptx' (par exemple)&lt;/p&gt;


&lt;h2&gt;Création du fichier de propriétés&lt;/h2&gt;


&lt;p&gt;Créer un fichier 'properties.ini' (par exemple) qui contient la liste de toute les variables que vou voulez utiliser dans votre document&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
doc.title = Le titre du document ici
doc.author.name = Mon nom ici
...
&lt;/pre&gt;


&lt;h2&gt;Création du script de génération&lt;/h2&gt;


&lt;p&gt;Copier le script ci-dessous dans le fichier 'openxml.php'&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php
/**
 * Content of the openxml.php file
 *
 * @usage php openxml.php &amp;lt;iniFile&amp;gt; &amp;lt;templateFile&amp;gt; &amp;lt;finalFile&amp;gt;
 *
 * @copyright none.
 */

/**
 * Replaces all ${variable.name} by the value of 'variable.name' of your configuration file in the specified file
 *
 * @param string $file the source file
 * @param array  $vars the list of variable to replace
 *
 * @return void
 */
function parseFile($file,$vars)
{
    $content = file_get_contents($file);

    $matches = null;
    if(preg_match_all('/\$\{([^\}]+)\}/',$content,$matches)&amp;gt;0) {
        for($i=0;$i&amp;lt;count($matches[0]);$i++){
            $v = '$vars';
            $vl='';
            $found = trim($matches[1][$i]);
            foreach(explode('.',$found) as $t){
                $v.= '[&amp;quot;'.strip_tags($t).'&amp;quot;]';
                $vl.=($vl?'.':'').strip_tags($t);
            }
            eval('$value = isset('.$v.') ? '.$v.' : (isset($vars[&amp;quot;'.$vl.'&amp;quot;]) ? $vars[&amp;quot;'.$vl.'&amp;quot;] : &amp;quot;[Variable not found: \'$vl\']&amp;quot;);');
            $content = str_replace('${'.$matches[1][$i].'}',$value,$content);
        }
    }

    file_put_contents($file,$content);
}

/**
 * Lists all files in specified location
 *
 * @param string $location the location to list files in
 * 
 * @return array
 */
function listFiles($location,$root=null)
{
    if(null==$root) {
        $root = $location;
    }
    $files = array();
    if (is_dir($location)){
        $dh = opendir($location);
        while(false !== ($file = readdir($dh))){
            if ('.'===$file || '..' === $file) continue;
            $files = array_merge($files,listFiles($location.'/'.$file,$root));
        }
        closedir($dh);
    }
        $files[] = $location;
    sort($files);
    if($root === $location) {
        array_shift($files);
    }
    return $files;
}

/**
 * Deletes all files in specified locaton
 *
 * @param string $location
 *
 * @return void
 */
function delete($location)
{
    if (is_dir($location)){
        $dh = opendir($location);
        while(false !== ($file = readdir($dh))){
            if ('.'===$file || '..' === $file) continue;
            delete($location.'/'.$file);
        }
        closedir($dh);
    }
    @unlink($location);
}

$iniFile = $_SERVER['argv'][1];
$templateFile =  $_SERVER['argv'][2];
$finalFile = $_SERVER['argv'][3];

// 1. imports ini file

$vars = parse_ini_file($iniFile);

// 2. unzips the file into a temporary directory

$tempDir = sys_get_temp_dir().'/openxml';
if(!file_exists($tempDir)){
    mkdir($tempDir,0777,true);
}

$zip = new ZipArchive();
if (true !== $zip-&amp;gt;open($templateFile)) {
    throw new RuntimeException(&amp;quot;Unable to read zip file '$templateFile'&amp;quot;);
}
$zip-&amp;gt;extractTo($tempDir);
$zip-&amp;gt;close();

// 3. parses extracted files to replace variables

$files = listFiles($tempDir);

foreach($files as $file) {
    if(substr($file,strlen($file)-4)!=='.xml') continue;
    parseFile($file,$vars);
}

// 4. Re-zip all files into the final pptx file

$zip = new ZipArchive();

if(file_exists($finalFile)){
    unlink($finalFile);
}
if ($zip-&amp;gt;open($finalFile, ZIPARCHIVE::CREATE)!==TRUE) {
    throw new RuntimeException(&amp;quot;Unable to create zip archive '$finalFile'&amp;quot;);
}

foreach($files as $file) {
    $fileName = str_replace('\','/',substr($file,strlen($tempDir)+1));
    $zip-&amp;gt;addFile($file,$fileName);
}
$zip-&amp;gt;close();

// 5. Deletes all artifacts

delete($tempDir);

echo &amp;quot;Your openxl document has been generated in '$finalFile' from template '$templateFile' and variables set in configuration file '$iniFile'
&amp;quot;;

&lt;/pre&gt;


&lt;h2&gt;Exécutez le script&amp;nbsp;!&lt;/h2&gt;

&lt;pre&gt;
$ php openxml.php properties.ini template.pptx document.pptx
&lt;/pre&gt;


&lt;p&gt;Vous devriez avoir un fichier 'document.pptx' créé à partir du fichier template (modèle) et dont les variables ont été remplacé par leur valeur trouvée dans le fichier de configuation.&lt;/p&gt;


&lt;h2&gt;Pour aller plus loin&lt;/h2&gt;


&lt;p&gt;Ce script n'est pas parfait, mais il fonctionne très bien pour les cas simples. Sous excel, vous aurez peut être quelques petits soucis car les valeurs remplacées sont considérées comme du texte et pas des chiffres, ce qu posent problèmes ou formule (il y a un palliatif)&lt;/p&gt;


&lt;p&gt;Je vous laisse le soin de faire évoluer ce script pour&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ajouter des images générées dynamiquement par php&lt;/li&gt;
&lt;li&gt;réaliser une api objet à intégrer dans vos scripts et applications/sites.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;N'hésitez pas à poster vos commentaires surtout si vous trouvez des outils plus complets ailleurs, ou que vous faites évoluer ce script&amp;nbsp;!&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/04/18/Generer-un-document-OpenXML-a-partir-d-un-modele-pptx-xlsxdocx-et-de-variables%3A-ou-comment-generer-vos-document-a-la-volee-simplement#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/04/18/Generer-un-document-OpenXML-a-partir-d-un-modele-pptx-xlsxdocx-et-de-variables%3A-ou-comment-generer-vos-document-a-la-volee-simplement#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/35</wfw:commentRss>
      </item>
    
  <item>
    <title>dirname(__FILE__), où comment éviter d'utiliser define('ROOT',...);</title>
    <link>http://blog.phppro.fr/?post/2009/04/14/dirname__FILE__-ou-comment-eviter-d-utiliser-define-ROOT</link>
    <guid isPermaLink="false">urn:md5:4ce91b15e6bdfee3d29f3fdedfce4246</guid>
    <pubDate>Tue, 14 Apr 2009 09:26:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Trucs et astuces</category>
        <category>inclusion</category><category>projet</category>    
    <description>&lt;p&gt;Comment récupérer le répertoire courant du fichier dans lequel on se trouve ...&lt;/p&gt;    &lt;h2&gt;La technique du dirname(&lt;strong&gt;FILE&lt;/strong&gt;)&lt;/h2&gt;

&lt;pre&gt;
/**
 * Exemples:
 *
 * /home/moi-meme/public_html/index.php =&amp;gt; /home/moi-meme/public_html
 * c:\public_html\index.php =&amp;gt; c:\public_html
 */
$cwd = dirname(__FILE__);
&lt;/pre&gt;


&lt;p&gt;Si vous voulez le chemin réel sur la machine (enlever les lien symboliques et les ../), vous pouvez aussi faire&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
/**
 * Exemples:
 *
 * /home/moi-meme/public_html/index.php =&amp;gt; /var/users/moi-meme/public_html (avec lien symbolique de /home vers /var/users)
 * c:\public_html\index.php =&amp;gt; c:/public_html
 */
$cwd = realpath(dirname(__FILE__));
&lt;/pre&gt;


&lt;p&gt;Pour obtenir un chemin normalisé avec des / au lieu de \ sur windows (utile pour le cross plateforme windows/linux ou pour la comparaison de sous-chaîne sans se soucier de la plateforme)&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
/**
 * Exemples:
 *
 * /home/moi-meme/public_html/index.php =&amp;gt; /home/moi-meme/public_html
 * c:\public_html\index.php =&amp;gt; c:/public_html
 */
$cwd = str_replace(DIRECTORY_SEPARATOR,'/',realpath(dirname(__FILE__)));
&lt;/pre&gt;


&lt;h2&gt;La technique du dirname(&lt;strong&gt;FILE&lt;/strong&gt;).'/../'&lt;/h2&gt;


&lt;p&gt;Comme lorsque vous êtes dans un fichier vous savez où il se trouve relativement par rapport aux autres (par exemple mon fichier ./includes/functions.php, je sais qu'il est dans le sous-répertoire includes à partir de la racine de mon application), vous pouvez ensuite vous servir de se &quot;positionnement&quot; relatif pour include des fichiers&amp;nbsp;:&lt;/p&gt;


&lt;p&gt;Dans le fichier ./includes/all.php&lt;/p&gt;

&lt;pre&gt;
require_once dirname(__FILE__).'/functions/database.php';

...
&lt;/pre&gt;


&lt;p&gt;Imaginez que vous ayiez un répertoire ./html/ qui contient index.php et un répertoire ./includes/ qui contient le fichier functions.php (de sorte que le fichier functions.php ne soit pas directement accessible dans l'arborescence du serveur web qui pointe sur ./html) .&lt;br /&gt;
Vous pouvez donc include le fichier functions.php de la manière suivante dans le fichier index.php&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
require_once dirname(__FILE__).'/../includes/functions.php';

...
&lt;/pre&gt;

&lt;p&gt;même si vous déplacez votre application dans un autre répertoire du système ou sur un autre serveur, tant que l'arborescence des fichiers de votre application ne change pas, il n'y a aucun impact sur votre code source !&lt;br /&gt;
Plus besoin de tenir (pour ceux qui n'utilisaient pas déjà cette technique) un fichier de configuration avec des define qui est a modifié à chaque installation.&lt;br /&gt;
&lt;br /&gt;
Et vous comment gérez vous les chemins absolus (inclusions de fichiers ou lecture de fichier sur le disque) dans vos applications&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/04/14/dirname__FILE__-ou-comment-eviter-d-utiliser-define-ROOT#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/04/14/dirname__FILE__-ou-comment-eviter-d-utiliser-define-ROOT#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/34</wfw:commentRss>
      </item>
    
  <item>
    <title>&quot;Injectabilité / Mockabilité&quot; : Un indicateur simple de la qualité de votre design de code</title>
    <link>http://blog.phppro.fr/?post/2009/04/14/Injectabilite-/-Mockabilite-%3A-Un-indicateur-simple-de-la-qualite-de-votre-design-de-code</link>
    <guid isPermaLink="false">urn:md5:405d74f9a51e30956fa3e3b8d7a1148e</guid>
    <pubDate>Tue, 14 Apr 2009 08:30:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Analyse</category>
        <category>application</category><category>dépendance</category><category>métrique</category><category>phpunit</category><category>tests</category>    
    <description>&lt;p&gt;On parle souvent d'indicateur de qualité de code avec nombre de tests unitaires qui passent au vert, nombre d'erreurs détectées pour le non respect des standards, nombre de lignes de code...&lt;br /&gt;
Tous ces indicateurs donnent une information intéressante sur la qualité du code mais pas sur la qualité de l'architecture ou design du code, c'est à dire aucune évaluation de la conception de votre code.&lt;br /&gt;&lt;/p&gt;    &lt;p&gt;Voici un indicateur simple que vous pouvez mettre en place (par exemple avec un petit développement complémentaire à l'outil PHP_CodeSniffer ou avec 2 ou 3 expressions régulières) pour avoir une indication de la qualité de votre design: &lt;strong&gt;l'indicateur d'injectabilité / mockabilité&lt;/strong&gt;&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;Concrètement, il s'agit de mesurer la capacité que votre code a de se faire injecter ses dépendances, et sa capacité à être bouchonné (&quot;mock&quot; en anglais).&lt;br /&gt;
Plus un code est bouchonnable, plus il est testable unitairement et modulaire.&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;Pour mesurer cet indicateur &quot;i&quot; simplement, il suffit de comparer le nombre de getter/setter de propriété de classe, au nombre de propriété de classe, une formule (proposition) pourrait être par exemple:&lt;br /&gt;&lt;/p&gt;

&lt;pre&gt;
i = (nb getters + nb setters) / (2 * nb propriétés)

avec 0 &amp;lt;= i &amp;lt;= 1
&lt;/pre&gt;


&lt;p&gt;Plus i est proche de 1, plus votre classe est &quot;mockable&quot; et donc plus facile à tester unitairement...&lt;br /&gt;
Attention, comme tout indicateur, il a des limites, il ne s'agit que d'un outil de plus pour vous aider à qualifier votre code ou votre application !&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;A quand cet indicateur dans PHPUnderControl, ou ailleurs !&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;Et vous, quels sont vos indicateurs de qualité de design de code&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/04/14/Injectabilite-/-Mockabilite-%3A-Un-indicateur-simple-de-la-qualite-de-votre-design-de-code#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/04/14/Injectabilite-/-Mockabilite-%3A-Un-indicateur-simple-de-la-qualite-de-votre-design-de-code#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/31</wfw:commentRss>
      </item>
    
  <item>
    <title>Le lien symbolique : ou comment maîtriser son déploiement</title>
    <link>http://blog.phppro.fr/?post/2009/04/10/Le-lien-symbolique-%3A-ou-comment-maitriser-son-deploiement</link>
    <guid isPermaLink="false">urn:md5:9fc7eb57543ff6b8832ed20e70a4283d</guid>
    <pubDate>Sat, 11 Apr 2009 08:12:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Trucs et astuces</category>
        <category>application</category><category>déploiement</category>    
    <description>&lt;p&gt;Vous avez déjà fait des mises en production ou mises en ligne.&lt;br /&gt;
Vous savez que les problèmes arrivent, que l'on peut faire des erreurs, par exemple supprimer malencontreusement un répertoire au moment du remplacement par la nouvelle version, etc...&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;Il existe une technique simple qui permet de simplifier les mises en production, de faciliter le retour en arrière et de gérer l'archivage&amp;nbsp;: &lt;strong&gt;le lien symbolique&lt;/strong&gt;.&lt;br /&gt;&lt;/p&gt;    &lt;p&gt;La technique est triviale: vous ajoutez dans un répertoire racine petit à petit toutes les nouvelles versions (par exemple /opt)&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
/
    opt/
        mon-appli-1.0/
        mon-appli-1.1/
        ...
        mon-appli-1.7/
        ...
        mon-appli-2.2/
&lt;/pre&gt;


&lt;p&gt;A chaque fois, une fois le répertoire de la nouvelle version ajouté, vous créez un lien symbolique qui pointe vers la version (à priori la dernière) qui vous intéresse:&lt;/p&gt;

&lt;pre&gt;
$ rm -f /opt/mon-appli
$ ln -s /opt/mon-appli-2.2 /opt/mon-appli
&lt;/pre&gt;


&lt;p&gt;La première fois, vous faites pointez le DOCUMENT_ROOT de votre application PHP sur /opt/mon-appli/...&lt;br /&gt;
Lors de la prochaine mise en production, vous n'aurez qu'à créer un répertoire portant le nom de la nouvelle version, y mettre les fichiers de la nouvelle version, et modifier le lien symbolique (rm -f, puis ln -s) lorsque vous serez prêt vers le nouveau répertoire.&lt;br /&gt;
Il n'y aura aucune indisponibilité de l'application et si vous détectez un problème vous n'aurez qu'à rechanger le lien symbolique pour remettre l'ancienne version !&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;Bien sûr cette technique ne gère pas votre base de données. Si des changements de schémas ou de données interviennent entre les différentes versions il faudra gérer cela.&lt;/p&gt;


&lt;p&gt;Et vous quelles sont vos pratiques&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/04/10/Le-lien-symbolique-%3A-ou-comment-maitriser-son-deploiement#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/04/10/Le-lien-symbolique-%3A-ou-comment-maitriser-son-deploiement#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/30</wfw:commentRss>
      </item>
    
  <item>
    <title>Université du S.I 2009 : Oui ! PHP est industriel !</title>
    <link>http://blog.phppro.fr/?post/2009/04/09/Universite-du-SI-2009-%3A-Oui-PHP-est-industriel</link>
    <guid isPermaLink="false">urn:md5:0256f5e4bec1400c0794d755f715d145</guid>
    <pubDate>Thu, 09 Apr 2009 14:55:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Evènements</category>
        <category>conférence</category><category>equipe</category><category>intégration continue</category><category>usi</category>    
    <description>&lt;p&gt;Vous connaissez l'Université du Système d'Information&amp;nbsp;? ou USI&amp;nbsp;!&lt;/p&gt;


&lt;p&gt;Organisé par OCTO Technology (Paris), ce séminaire mélange Geek et Boss en proposant 4 thématiques de conférences&amp;nbsp;:&lt;/p&gt;    &lt;ul&gt;
&lt;li&gt;Gouvernance&lt;/li&gt;
&lt;li&gt;Technologies&lt;/li&gt;
&lt;li&gt;Méthodologies&lt;/li&gt;
&lt;li&gt;Usabilité&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Côté Technologies, a côté de Java, .NET, Grails, Ruby..., nous aurons cette année ... PHP&amp;nbsp;!&lt;/p&gt;


&lt;p&gt;Lors de la prochaine édition, 1 et 2 juillet 2009 à Paris, nous essaierons avec Damien Seguy (Alterway/Nexen) de démontrer que PHP est aujourd'hui une technologie / plateforme / environnement industriel.&lt;br /&gt;
Parmi les sujets abordés dans notre session&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frameworks PHP professionnels&lt;/li&gt;
&lt;li&gt;Intégration continue&lt;/li&gt;
&lt;li&gt;Environnement de développement&lt;/li&gt;
&lt;li&gt;Outillage&lt;/li&gt;
&lt;li&gt;Test Driven Development&lt;/li&gt;
&lt;li&gt;Tests fonctionnels automatisables&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Plus d'info sur l'évènement&amp;nbsp;? &lt;a href=&quot;http://usi2009.universite-du-si.com/Olivier-Hoareau-1-171.html&quot;&gt;Allez voir sur le site&lt;/a&gt;&amp;nbsp;!&lt;/p&gt;


&lt;p&gt;Venez nombreux&amp;nbsp;!&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/04/09/Universite-du-SI-2009-%3A-Oui-PHP-est-industriel#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/04/09/Universite-du-SI-2009-%3A-Oui-PHP-est-industriel#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/33</wfw:commentRss>
      </item>
    
  <item>
    <title>Constuire une librairie &quot;maison&quot; : ou comment capitaliser à moyen terme</title>
    <link>http://blog.phppro.fr/?post/2009/04/07/Constuire-une-librairie-maison-%3A-ou-comment-capitaliser-a-moyen-terme</link>
    <guid isPermaLink="false">urn:md5:358f507e04446960f9d2917f085d65cb</guid>
    <pubDate>Tue, 07 Apr 2009 14:53:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Méthodologie</category>
        <category>application</category><category>integration</category><category>pattern</category><category>phpunit</category><category>projet</category><category>service</category>    
    <description>&lt;p&gt;Vous avez déjà développé des dizaines de milliers de ligne de code.&lt;br /&gt;
Vous avez déjà contribué à plusieurs (2, 5, 10 ...) projets d'applications PHP dans votre entreprise.&lt;br /&gt;
Vous avez déjà, comme moi, eu ce sentiment quand vous étiez sur le développement d'un morceau de code, que vous aviez déjà codé cela une fois, mais impossible de vous souvenir &quot;comment&quot; et &quot;où&quot; retrouver le code.&lt;br /&gt;
&quot;Ah, si j'avais un listing bien organisé de tout ce que j'ai codé rangé par fonctionnalité...&quot;&lt;br /&gt;&lt;/p&gt;    &lt;p&gt;Vous connaissez certainement le KISS (Keep It Simple and Stupid), mais connaissez-vous le DRY (Do no Repeat Yourself) ?&lt;br /&gt;
Voyons ensemble une méthode simple pour commencer à capitaliser sur vos développements.&lt;/p&gt;


&lt;h2&gt;Centraliser le code&lt;/h2&gt;


&lt;p&gt;Commencez par créer un repository SVN pour votre &quot;librairie&quot; de code &quot;commun&quot;. Appellons la &quot;phpcommon&quot; (vous pouvez utiliser CVS ou un Git-like).&lt;/p&gt;


&lt;h2&gt;Organisez la librairie&lt;/h2&gt;


&lt;p&gt;Créez l'arborescence suivante à la racine de phpcommon :&lt;br /&gt;&lt;/p&gt;

&lt;pre&gt;
/
    library/
    tests/
        unit/
&lt;/pre&gt;


&lt;h2&gt;Installez votre librairie (pour l'instant vide)&lt;/h2&gt;


&lt;p&gt;Faites un checkout de votre projet PHPCommon sur votre serveur dans un répertoire permanent par exemple /opt/phpcommon ou c:/php/phpcommon.&lt;br /&gt;
Puis ajoutez ce chemin /opt/phpcommon/library dans la directive include_path de votre fichier php.ini (si vous utilisez des serveurs pré-packagés tels que Wamp, EasyPHP ou Zend Server, veuillez à modifier tous les fichiers php.ini: mode CLI, mode CGI...).&lt;br /&gt;&lt;/p&gt;


&lt;h2&gt;Pensez &quot;réutilisation&quot; / &quot;PHPCommon&quot; avant tout&lt;/h2&gt;


&lt;p&gt;Dorénavant, lorsque vous avez un besoin de développement, posez-vous en premier la question :&lt;br /&gt;
&lt;br /&gt;
&lt;strong&gt;Pour ce développement, que puis-je mettre dans ma librairie centrale, et que dois-je laisser dans cette application ?&lt;/strong&gt;&lt;br /&gt;
&lt;br /&gt;
Prenons un exemple concret:&lt;br /&gt;
&lt;em&gt;Je dois ajouter la fonctionnalité d'envoi de SMS lorsque l'utilisateur réalise une action de modification sur le site / l'application.&lt;/em&gt;&lt;br /&gt;
Dans ce cas, que puis-je mettre dans ma librairie centrale ?&lt;br /&gt;
&lt;strong&gt;Réponse&lt;/strong&gt;: peut être une fonctionnalité (nous parlerons de &quot;service&quot;) d'envoi d'un SMS à un numéro donné.&lt;br /&gt;
Que dois-je laisser dans l'application ?&lt;br /&gt;
&lt;strong&gt;Réponse&lt;/strong&gt;: la collecte des paramètres d'envoi du SMS (expéditeur, destinataire, message) ainsi que l'affichage de l'information à l'utilisateur (&quot;message envoyé&quot;, &quot;erreur survenue&quot;, ...)&lt;/p&gt;


&lt;h2&gt;Implémentez 1 fois dans PHPCommon / Utilisez plusieurs fois dans vos applications&lt;/h2&gt;


&lt;p&gt;Pour continuer sur l'exemple du SMS, nous pouvons alors coder un &quot;service&quot; d'envoi de SMS dans notre librairie en faisant par exemple 1 classe &quot;Sms&quot; qui nous propose une méthode constructeur qui prend les paramètres du SMS (expéditeur, destinataire, message...) et une méthode send() qui est censée envoyer le SMS (en utilisant par exemple une API HTTP fournie par un broker SMS), tout cela dans le fichier library/Sms.php&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;Une fois cette classe &quot;codée&quot; nous pourrons l'utiliser dans n'importe laquelle de nos applications en faisant :&lt;br /&gt;&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php

...

require_once 'Sms.php';

$sms = new Sms($exp,$dest,$message);
$r = $sms-&amp;gt;send();

... // tester $r pour savoir si il y a eu une erreur
&lt;/pre&gt;


&lt;p&gt;Notre fonctionnalité d'envoi de SMS est devenue &quot;utilisable&quot; dans n'importe laquelle de nos applications, nous avons commencé à capitaliser sur nos &quot;services&quot; techniques &quot;transverses&quot;.&lt;/p&gt;


&lt;h2&gt;Attention aux collisions&amp;nbsp;!&lt;/h2&gt;


&lt;p&gt;En nommant votre classe Sms, vous prenez le risque de rentrer en collision de nommage avec une autre classe d'une autre librairie qui porterait le même nom. Il y aurait alors une erreur fatale à l'exécution du scripts.&lt;br /&gt;
Pour pallier à ce problème, il vous suffit de préfixer vos classes par le nom de votre entreprise ou un nom arbitraire, par exemple:&lt;/p&gt;

&lt;pre&gt;
class Phpcommon_Sms
{
    ...
}
&lt;/pre&gt;


&lt;p&gt;et mettre le fichier dans librairie/Phpcommon/Sms.php&lt;/p&gt;


&lt;h2&gt;Organisez vos fonctionnalités: Soyez modulaire&lt;/h2&gt;


&lt;p&gt;Vous allez donc pouvoir centraliser vos développements &quot;communs&quot; pour pouvoir les réutiliser. Mais petit à petit vous allez avoir de plus en plus de fichiers dans votre répertoire library et cela va devenir très vite chaotique car vous ne saurez plus quels fichiers vont ensemble...&lt;br /&gt;
Une possibilité serait d'avoir une organisation modulaire de vos fichiers en sous-répertoire &quot;fonctionnels&quot;. Vous allez donc créer des &quot;packages&quot; (cette notion n'existe pas en PHP &amp;lt; 5.3 mais est simulable, PHP 5.3 apporte les namespaces).&lt;br /&gt;
Voici un exemple d'arborescence typique de votre répertoire librairie avec quelques exemples de briques de service:&lt;/p&gt;

&lt;pre&gt;
/
    library/
        Phpcommon/
            Mail/
                Interface.php
                Service.php
            Sms/
                Adapter/
                    Orange.php
                    Sfr.php
                    Smsbox.php
                Interface.php
                Service.php
            Soap/
                Interface.php
                Service.php
            ...
&lt;/pre&gt;


&lt;p&gt;De cette façon vous retrouverez facilement les classes nécessaires à telle ou telle fonctionnalité.&lt;/p&gt;


&lt;h2&gt;Testez et documentez votre code&amp;nbsp;!&lt;/h2&gt;


&lt;p&gt;Tous vos développements &quot;communs&quot; ont de fortes chances d'être réutilisés dans plusieurs de vos applications. S'ils contiennent des bugs, des regressions au cours du temps ou bien si ils sont écrits trop rapidement, vous fragiliserez toutes les applications qui les utilisent et inversement, si vous développez avec soins dans cette librairie, que vous réalisez des tests unitaires et faites attention à la qualité de votre code, vous rendrez plus robuste et plus maintenable toutes les applications qui l'utilise.&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;Investissez sur votre librairie commune&amp;nbsp;!&lt;/p&gt;


&lt;p&gt;Et vous quelles sont vos pratiques pour factoriser votre code&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/04/07/Constuire-une-librairie-maison-%3A-ou-comment-capitaliser-a-moyen-terme#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/04/07/Constuire-une-librairie-maison-%3A-ou-comment-capitaliser-a-moyen-terme#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/29</wfw:commentRss>
      </item>
    
  <item>
    <title>Typer vos exceptions: les types d'exception génériques mais utiles</title>
    <link>http://blog.phppro.fr/?post/2009/04/04/Types-vos-exceptions%3A-les-types-d-exception-generiques-mais-utiles</link>
    <guid isPermaLink="false">urn:md5:c9c039ee9ca77a9132bf7b917693639a</guid>
    <pubDate>Sat, 04 Apr 2009 13:53:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Trucs et astuces</category>
        <category>exception</category>    
    <description>&lt;p&gt;Si vous utilisez le mécanisme d'exception de PHP, vous devez savoir qu'il est possible de créer vos propres exceptions en héritant des classes Exception ou RuntimeException. (SPL), par exemple comme ceçi&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
class MyException extends Exception
{
}
&lt;/pre&gt;


&lt;p&gt;...&lt;/p&gt;    &lt;p&gt;Si vous utilisez le mécanisme d'exception de PHP, vous devez savoir qu'il est possible de créer vos propres exceptions en héritant des classes Exception ou RuntimeException. (SPL), par exemple comme ceçi&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
class MyException extends Exception
{
}
&lt;/pre&gt;


&lt;p&gt;A l'usage vous vous êtes certainement rendu compte que vous levez des exceptions dans un nombre de cas limité, par exemple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;si une donnée nécessaire est nulle&lt;/li&gt;
&lt;li&gt;si un enregistrement en base existe déjà&lt;/li&gt;
&lt;li&gt;si une information fournie n'est pas au bon format&lt;/li&gt;
&lt;li&gt;si l'information demandée n'a pas été trouvée&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vous pourriez créer une exception pour chaque cas précis de chacune des classes, mais cela vous obligerait à la longue à maintenir des dizaines de classes d'exceptions. Une autre solution est de rationnaliser vos exceptions en créant une liste limité et &quot;immuable&quot; (enfin presque) de types d'exceptions. Vous obligeant ainsi, quand vous souhaitez lever une exception à piocher dans les types d'exceptions déjà existant.&lt;/p&gt;


&lt;p&gt;Voici la liste de classes d'exceptions que j'utilise personnellement régulièrement&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adapter&amp;nbsp;: type générique pour toute sous-couche d'implémentation technique&lt;/li&gt;
&lt;li&gt;AlreadyExists&amp;nbsp;: à utiliser pour indiquer qu'une opération de création d'une entité n'a pas aboutie car l'entité existe déjà (un fichier, une ligne en base, ...)&lt;/li&gt;
&lt;li&gt;AlreadySet&amp;nbsp;: à utiliser pour indiquer qu'une propriété modifiable une seule fois à déjà été modifié et qu'elle n'est plus modifiable&lt;/li&gt;
&lt;li&gt;Assertion&amp;nbsp;: à utiliser pour indiquer qu'un test systématique a échoué&lt;/li&gt;
&lt;li&gt;BadFormat&amp;nbsp;: à utiliser si le format de l'information fourni n'est pas correcte (email non valide, entier ou lieu de boolean ...)&lt;/li&gt;
&lt;li&gt;Database&amp;nbsp;: à utiliser comme surcouche aux exceptions PDOException par exemple, pour indiquer qu'une requête ou qu'une opération sur une base de données a échoué&lt;/li&gt;
&lt;li&gt;Denied&amp;nbsp;: à utiliser pour indiquer que l'accès à la fonctionnalité ou à la ressource n'est pas permi dans ce contexte (par exemple en fonction de la session ou d'un login ou profil d'utilisateur)&lt;/li&gt;
&lt;li&gt;Missing&amp;nbsp;: à utiliser pour indiquer qu'une information est manquante pour terminer l'opération&lt;/li&gt;
&lt;li&gt;Multiple&amp;nbsp;: à utiliser pour indiquer qu'il n'est pas possible de retourner l'information précise demandé car plusieurs entités existent (en base de données par exemple)&lt;/li&gt;
&lt;li&gt;NotFound&amp;nbsp;: à utiliser pour indiquer qu'une ressource ou information demandée n'a pas été trouvé (par exemple la lecture d'un fichier de configuration...)&lt;/li&gt;
&lt;li&gt;NotYetImplemented&amp;nbsp;: à utiliser pour indiquer un endroit du code non terminé (non implémenté). Cette exception est assez utile, mais si vous faite du TDD, vous ne devriez pas l'utiliser ;)&lt;/li&gt;
&lt;li&gt;Null&amp;nbsp;: à utiliser pour indiquer qu'une information d'entrée requise est nulle ou non fournie (par exemple une clé d'un array fourni, un objet normalement setté via un setter dans la classe...)&lt;/li&gt;
&lt;li&gt;Timeout&amp;nbsp;: à utiliser pour indiquer que le temps d'attente maximum à été dépassé (par exemple dans le cas d'une connexion réseau...)&lt;/li&gt;
&lt;li&gt;TooMany&amp;nbsp;: à utiliser pour indiquer que le nombre d'information à retourner est trop important&lt;/li&gt;
&lt;li&gt;Unexpected&amp;nbsp;: à utiliser en dernier recours pour indiquer une branche du code qui ne devrait jamais être déclenché (en règle générale je l'utilise dans un morceau de code compliqué pour lequel je ne maîtrise pas toute la logique...)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cette liste n'est pas forcément exhaustive, vous pouvez en rajouter/supprimé bien sûr&amp;nbsp;!
Par contre, je vous conseille de ne pas dépasser 15-20 types d'exceptions maximum, Ce qui vous permettra de bien les connaître et les avoir en tête et de ne pas dupliquer certains cas très similaires car trop précis.&lt;/p&gt;


&lt;p&gt;Bien sûr pour éviter que les noms de classes rentrent en collision avec d'autres classes de votre code ou des librairies externe que vous utilisz, vous pouvez les mettre dans un pseudo package (avant php 5.3, en préfixant le nom de classe par Xxx_Exception_&amp;lt;Nom-de-l'exception&amp;gt;) ou un namespace (php 5.3+).&lt;/p&gt;


&lt;p&gt;Il peut être judicieux de créer une classe centrale (par exemple Xxx_Exception) de laquelle toutes vos exceptions héritent. Ce qui vous permettra, de faire vos try/catch uniquement que sur l'ensemble des exceptions &quot;normalisées&quot; et laisser ainsi passer les autres exception non typées, très utile quand vous faites des tests unitaires ou pour détecter si votre code est robuste ou pas.&lt;/p&gt;


&lt;p&gt;Une possibilité complémentaire est d'indiquer un code et/ou un message spécifique à vos exceptions, ce qui vous permettra par exemple de manipuler ce code dans des réponses AJAX ou HTML pour renvoyer un message d'erreur dans la langue de l'utilisateur. Sachant que vous maîtrisez les types d'exceptions (un maximum de 20 par exemple) vous pouvez leur donner des codes d'erreurs précis&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
class Xxx_Exception_NotFound extends Exception
{
    const CODE = 1001;
    const MSG    = 'Resource not found';

    public function __construct($message=null)
    {
        parent::__construct(null === $message ?
               self::MSG : $message,self::CODE);
    }
}
&lt;/pre&gt;


&lt;p&gt;Et vous quels sont vos usages en terme de typage d'exception&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/04/04/Types-vos-exceptions%3A-les-types-d-exception-generiques-mais-utiles#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/04/04/Types-vos-exceptions%3A-les-types-d-exception-generiques-mais-utiles#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/27</wfw:commentRss>
      </item>
    
  <item>
    <title>Salon Solutions Linux 2009 : Massacre d'éléPHPants !</title>
    <link>http://blog.phppro.fr/?post/2009/04/03/Salon-Solutions-Linux-2009-%3A-Massacre-d-elePHPants</link>
    <guid isPermaLink="false">urn:md5:6c71736a7b1cc2b160ea121e00fdc6bf</guid>
    <pubDate>Fri, 03 Apr 2009 14:11:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Evènements</category>
        <category>afup</category>    
    <description>&lt;p&gt;Le salon Solutions Linux 2009 s'est déroulé du 31/03 au 02/04 au Parc des Expositions de Paris Porte de Versailles (Hall 2.2). Retour sur ma journée du Jeudi sur place...&lt;/p&gt;    &lt;p&gt;Le salon Solutions Linux 2009 s'est déroulé du 31/03 au 02/04 au Parc des Expositions de Paris Porte de Versailles (Hall 2.2).&lt;/p&gt;


&lt;p&gt;J'ai pu y participé le Jeudi (02/04), au programme, il y avait entre autre une journée avec pas mal de sessions PHP, notamment sur des retours d'expériences (20minute.fr) et des présentations de frameworks (ZF ...).
Le stand AFUP, placé dans le village associatif, a été pris d'asso par les amateurs d'ElePHPant (vous avez ce petit éléphant bleu avec un gros logo PHP !), plusieurs dizaines d'ElePHPant ont été sacrifié par l'AFUP au profit de leur nouveau maître...&lt;/p&gt;


&lt;p&gt;Parmi les points que j'ai noté sur le sujet PHP, ont a pu y voir&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Le stand AFUP ayant permis d'échangé avec des entreprises qui utilisent PHP sur leur sites ou applications à forte volumétrie (plusieurs millions de visiteurs par mois)&lt;/li&gt;
&lt;li&gt;Des débats sur &quot;Alors je prends ZF ou Symfony ?&quot;&lt;/li&gt;
&lt;li&gt;Alterway et son stand d'essai de certification PHP&lt;/li&gt;
&lt;li&gt;Zend et son nouveau Zend Server&lt;/li&gt;
&lt;li&gt;L'AFUP qui réglait des bugs PHP en live&lt;/li&gt;
&lt;li&gt;Bull et sa forge (usine de développement) Novaforge pour PHP (entre autres)&lt;/li&gt;
&lt;li&gt;La dédicace du livre sur PDO de notre trésorier préféré de l'AFUP (Christophe V.)&lt;/li&gt;
&lt;li&gt;FAASCAPE qui lance son service de Feature As A Service sur le nuage (avec des devs en PHP peut être à venir)&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Une journée forte intéressante&amp;nbsp;! J'attends l'année prochaine avec impatience&amp;nbsp;!&lt;/p&gt;


&lt;p&gt;En attendant vous pouvez voir &lt;a href=&quot;http://www.flickr.com/groups/1072472@N21/&quot;&gt;quelques photos prises pour l'occasion&lt;/a&gt; par J. Pauli.&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/04/03/Salon-Solutions-Linux-2009-%3A-Massacre-d-elePHPants#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/04/03/Salon-Solutions-Linux-2009-%3A-Massacre-d-elePHPants#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/28</wfw:commentRss>
      </item>
    
  <item>
    <title>Chasser les dépendances implicites : où comment rendre votre code moins obscur</title>
    <link>http://blog.phppro.fr/?post/2009/02/10/Chasser-les-dependances-implicites-%3A-ou-comment-rendre-votre-code-moins-obscur</link>
    <guid isPermaLink="false">urn:md5:fbbd9baed2dbc6a1058a97066ca722b3</guid>
    <pubDate>Tue, 10 Feb 2009 09:04:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Méthodologie</category>
        <category>application</category><category>configuration</category><category>dépendance</category><category>exception</category><category>refactoring</category>    
    <description>&lt;p&gt;En PHP, comme dans beaucoup d'autres langages, nous pouvons faire des fonctions et des méthodes qui prennent des arguments.&lt;/p&gt;


&lt;p&gt;Prenons la fonction suivante&amp;nbsp;: ...&lt;/p&gt;    &lt;p&gt;En PHP, comme dans beaucoup d'autres langages, nous pouvons faire des fonctions et des méthodes qui prennent des arguments.&lt;/p&gt;


&lt;p&gt;Prenons la fonction suivante&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
function loadConfig()
{
    global $CFG;
    $CFG = parse_ini_file(dirname(__FILE__).'/config/main.ini',true);
}
&lt;/pre&gt;


&lt;p&gt;Cette fonction peut être appelée comme suit dans un fichier index.php&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
...
loadConfig();
...
&lt;/pre&gt;


&lt;p&gt;Mais que se passe-t-il si&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;vous déplacez votre fichier index.php (relativement par rapport au fichier config/main.ini), ou inversement&amp;nbsp;?&lt;/li&gt;
&lt;li&gt;vous changez le nom de la variabe $CFG dans votre script index.php&amp;nbsp;?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ainsi la fonction loadConfig() a une double dépendance&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;elle dépend de l'existence d'un fichier de configuration config/main.ini au format ini&lt;/li&gt;
&lt;li&gt;elle dépend de l'utilisation de la variable globale $CFG&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pourtant rien n'indique clairement ces dépendances dans son utilisation ou même sa déclaration (la signature de la fonction).
Si vous développez cette fonction, vous vous souviendrez qu'elle a ces 2 dépendances; par contre, si vous la développez et que quelqu'un d'autres l'utilise dans son script (vous êtes en équipe par exemple et des nouveaux arrivent régulièrement) alors à moins de lire le code source de cette fonction rien ne lui indiquera ces 2 dépendances, ce qui peut occasionner des erreurs (renommage de fichiers ou de variable).&lt;/p&gt;


&lt;h2&gt;Comment faire&amp;nbsp;?&lt;/h2&gt;


&lt;p&gt;Expliciter systématiquement les dépendances externes de vos fonctions ou de vos méthodes / classes. Pour cela, vous pourriez faire une variante de votre fonction loadConfig() comme suit&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
function loadIniConfigFileIntoArray($configFile,&amp;amp;$cfg)
{
    if(false===file_exists($configFile)) {
        throw new RuntimeException(&amp;quot;Configuration file '$configFile' does not exist&amp;quot;);
    }
    $cfg = parse_ini_file($configFile);
}
&lt;/pre&gt;


&lt;p&gt;ou alors en retournant une référence (on pourrait aussi renvoyer un objet sans faire de référence en PHP5, ce qui est automatique)&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
function &amp;amp;loadIniConfigFile($configFile)
{
    if(false===file_exists($configFile)) {
        throw new RuntimeException(&amp;quot;Configuration file '$configFile' does not exist&amp;quot;);
    }
    $cfg = parse_ini_file($configFile);
    return $cfg;
}
&lt;/pre&gt;


&lt;p&gt;Vous pourriez ainsi indiquer dans les commentaires de la fonction&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
/**
 * Parses the specified configuration file (ini format) and returns an array
 *
 * @param string $configFile the full path of the configuration file to parse
 * 
 * @return array the data structure containing the configuration
 *
 * @throws RuntimeException if file does not exist
 */
&lt;/pre&gt;


&lt;p&gt;Dans le même principe, veuillez à rendre plus explicite les dépendances externes de vos fonctions envers&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;les variables globales (ou de registre si utilisation de framework tel que Zend Framework)&lt;/li&gt;
&lt;li&gt;les connexion base de données ou les ressources (fichiers,...)&lt;/li&gt;
&lt;li&gt;les constantes&lt;/li&gt;
&lt;li&gt;les fichiers&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/02/10/Chasser-les-dependances-implicites-%3A-ou-comment-rendre-votre-code-moins-obscur#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/02/10/Chasser-les-dependances-implicites-%3A-ou-comment-rendre-votre-code-moins-obscur#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/26</wfw:commentRss>
      </item>
    
  <item>
    <title>De la nécessité de vérifier l'état des connexions/requêtes à la base</title>
    <link>http://blog.phppro.fr/?post/2009/02/04/De-la-necessite-de-verifier-l-etat-des-connexions/requetes-a-la-base</link>
    <guid isPermaLink="false">urn:md5:b2fe08560e9bf6f4113c84e72db6fe12</guid>
    <pubDate>Wed, 04 Feb 2009 11:41:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Méthodologie</category>
        <category>base de données</category><category>exception</category><category>mysql</category><category>robustesse</category><category>transaction</category>    
    <description>&lt;p&gt;Imaginez un site marchand sur lequel olivier@email.fr a un compte et sur lequel il fait un achat d'ordinateur, au moment de la validation et du paiement, nous écrivons en base de données&amp;nbsp;: ...&lt;/p&gt;    &lt;p&gt;Imaginez un site marchand sur lequel olivier@email.fr a un compte et sur lequel il fait un achat d'ordinateur, au moment de la validation et du paiement, nous écrivons en base de données&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
mysql_query(&amp;quot;INSERT INTO commandes (produit,prix,compte) VALUES ('Ordinateur',699,'olivier@email.fr')&amp;quot;);
...
mysql_query(&amp;quot;UPDATE comptes SET montant = montant + 699 WHERE email = 'olivier@email.fr')&amp;quot;);
&lt;/pre&gt;


&lt;p&gt;Question&amp;nbsp;: que se passe-t-il si la connexion à la base de données est perdu lors de la première requête (problème réseau par exemple) ou bien que le nombre de visiteurs sur votre site marchand est tellement important que le nombre de connexion base de données maximum est atteint et que PHP retourne une erreur sur la première requête SQL (du style &quot;unable to connect to ...)&amp;nbsp;?&lt;/p&gt;


&lt;p&gt;Ce qui va se passer&amp;nbsp;: vous allez avoir une erreur de type warning (si vous les avez désactivées en prod, elle sera passée sous silence et juste inscrite dans le fichier de log de php), la deuxième requête va alors être exécutée, et par chance, la connexion refonctionne (les problèmes de charge peuvent être très volatile). Vous allez alors vous retrouver avec un problème d'intégrité en base de données (la commande n'aura pas été ajouté mais le montant des achats du compte aura été mis à jour). Les effets de bord peuvent être alors multiples et dépendent de la logique qui suit. Si vous affichez dans l'espace client de l'utilisateur le montant de ses achats, vous aurez des valeurs erronées et il manquera une des commandes de l'utilisateur.&lt;/p&gt;


&lt;p&gt;Comment faire&amp;nbsp;?&lt;/p&gt;


&lt;p&gt;Il est nécessaire de vérifier systématiquement le statut de l'exécution de la requête que vous avez effectuée&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$result = mysql_query(&amp;quot;INSERT INTO commandes (produit,prix,compte) VALUES ('Ordinateur',699,'olivier@email.fr')&amp;quot;);
if (false === $result) {
    // une erreur d'exécution de la requête SQL a été détecté, il est nécessaire de ne pas continuer le processus et de traiter l'erreur
    // une façon apropriée est de lancer une exception, qui sera &amp;quot;catchée&amp;quot; par la méthode ou la fonction appelante
    throw new RuntimeException(&amp;quot;Unable to add commande to database: &amp;quot;.mysql_error());
}
...
$result = mysql_query(&amp;quot;UPDATE comptes SET montant = montant + 699 WHERE email = 'olivier@email.fr')&amp;quot;);
if (false === $result) {
    // une erreur d'exécution de la requête SQL a été détecté, il est nécessaire de ne pas continuer le processus et de traiter l'erreur
    // une façon apropriée est de lancer une exception, qui sera &amp;quot;catchée&amp;quot; par la méthode ou la fonction appelante
    throw new RuntimeException(&amp;quot;Unable to update comptes in database: &amp;quot;.mysql_error());
}
...
&lt;/pre&gt;


&lt;p&gt;D'accord, mais que se passe-t-il si l'erreur survient sur la deuxième requête SQL&amp;nbsp;? nous allons bien détecter l'erreur mais la première requête en base (l'insertion) aura déjà été effectuée et nous aurons donc une inconsistance en base de données si nous ne réalisons pas la seconde requête.
Il est donc nécessaire d'introduire la notion de transaction, c'est à dire d'une séquence de requêtes considérées comme atomique (indissociable)&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$result = mysql_query(&amp;quot;BEGIN&amp;quot;);
if (false === $result) {
    // impossible de commencer la transaction, on doit afficher une page d'erreur générique au client par exemple
    throw new RuntimeException(&amp;quot;Unable to begin transaction: &amp;quot;.mysql_error());
}

$result = mysql_query(&amp;quot;INSERT INTO commandes (produit,prix,compte) VALUES ('Ordinateur',699,'olivier@email.fr')&amp;quot;);
if (false === $result) {
    // une erreur d'exécution de la requête SQL a été détecté, il est nécessaire de ne pas continuer le processus et de traiter l'erreur
    // une façon apropriée est de lancer une exception, qui sera &amp;quot;catchée&amp;quot; par la méthode ou la fonction appelante
    mysql_query(&amp;quot;ROLLBACK&amp;quot;);
    throw new RuntimeException(&amp;quot;Unable to add commande to database: &amp;quot;.mysql_error());
}
...
$result = mysql_query(&amp;quot;UPDATE comptes SET montant = montant + 699 WHERE email = 'olivier@email.fr')&amp;quot;);
if (false === $result) {
    // une erreur d'exécution de la requête SQL a été détecté, il est nécessaire de ne pas continuer le processus et de traiter l'erreur
    // une façon apropriée est de lancer une exception, qui sera &amp;quot;catchée&amp;quot; par la méthode ou la fonction appelante
    mysql_query(&amp;quot;ROLLBACK&amp;quot;);
    throw new RuntimeException(&amp;quot;Unable to update comptes in database: &amp;quot;.mysql_error());
}
...

$result = mysql_query(&amp;quot;COMMIT&amp;quot;);
if (false === $result) {
    // impossible de commiter la transaction. aucune donnée ne sera inserée/modifiée en base
    // il est nécessaire d'afficher une page d'erreur à l'utilisateur pour lui indiquer que sa commande n'a pas été prise en compte
    throw new RuntimeException(&amp;quot;Unable to commit transaction to database: &amp;quot;.mysql_error());
}

&lt;/pre&gt;



&lt;p&gt;Ce type de technique est intéressant car il sécurise et rend votre application plus robuste. Un point cependant a noter est que l'application deviendra alors moins silencieuse au problème et le moindre problème de connexion ou de charge sera tout de suite mis en exergue et déclenchera des exceptions. A vous donc de vérifier systématiquement dans vos méthodes/fonctions appelantes qu'il n'y a pas eu une exception.&lt;/p&gt;


&lt;p&gt;Les fonctions mysql_query/mysql_error ont été utilisée pour l'exemple, mais cette technique s'applique sans soucis à des frameworks/lirairies d'abstraction de connexion au base de données tels que PDO, Zend_Db (Zend Framework), ... &lt;strong&gt;Il faut toujours vérifier dans la documentation la valeur ou le type de retour d'une fonction/méthode d'appel à la base base de données en cas d'erreur&lt;/strong&gt; (les fonctions standards PHP retourne souvent FALSE au lieu d'une ressource si un problème est survenu en plus de lancer un warning).&lt;/p&gt;


&lt;p&gt;Et vous quelles sont vos techniques pour gérer ce genre de problème&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/02/04/De-la-necessite-de-verifier-l-etat-des-connexions/requetes-a-la-base#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/02/04/De-la-necessite-de-verifier-l-etat-des-connexions/requetes-a-la-base#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/25</wfw:commentRss>
      </item>
    
  <item>
    <title>Points de contrôle automatisés avant un développement ou comment maîtriser un déploiement</title>
    <link>http://blog.phppro.fr/?post/2009/01/23/Points-de-controle-automatises-avant-un-developpement-ou-comment-maitriser-un-deploiement</link>
    <guid isPermaLink="false">urn:md5:c4362d5ba30bd70a96e18c053f919bb7</guid>
    <pubDate>Fri, 23 Jan 2009 08:51:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Outillage</category>
        <category>environnement</category><category>integration</category><category>tests</category>    
    <description>&lt;p&gt;Vous avez une mise en production hyper importante aujourd'hui. Votre client (interne) vous a mis la pression et vous avez dû raccourcir les délais pour être à l'heure.
Vous packagez votre release d'application, vous la passez sur l'environnement de tests fonctionnels, tout se passe bien.
Vous la passez sur l'environnement d'intégration et tout se passe bien, mais vous noter un ou deux comportement étrange, sans gravité, bizarre.
Vous décidez de la passer en pré-production, et là ... Tout plante&amp;nbsp;! L'application ne se lance même plus et c'est la page blanche...&lt;/p&gt;    &lt;p&gt;Vous avez une mise en production hyper importante aujourd'hui. Votre client (interne) vous a mis la pression et vous avez dû raccourcir les délais pour être à l'heure.
Vous packagez votre release d'application, vous la passez sur l'environnement de tests fonctionnels, tout se passe bien.
Vous la passez sur l'environnement d'intégration et tout se passe bien, mais vous noter un ou deux comportement étrange, sans gravité, bizarre.
Vous décidez de la passer en pré-production, et là ... Tout plante&amp;nbsp;! L'application ne se lance même plus et c'est la page blanche...&lt;/p&gt;


&lt;p&gt;Pourquoi&amp;nbsp;?&lt;/p&gt;


&lt;p&gt;Vous investiguez et vous découvrez que l'environnement de pré-production n'est absolument pas à jour (choisissez une ou plusieurs lignes dans la liste)&amp;nbsp;:&lt;/p&gt;


&lt;p&gt;- mauvaise version de PHP&lt;/p&gt;


&lt;p&gt;- mauvaise version de votre framework MVC habituel&lt;/p&gt;


&lt;p&gt;- mauvaise version de votre librairie de composants interne partagée&lt;/p&gt;


&lt;p&gt;- extension SOAP manquante&lt;/p&gt;


&lt;p&gt;- répertoire de log inexistant sur la machine (votre application ne le crée pas si il n'existe pas...)&lt;/p&gt;


&lt;p&gt;- connexion à la base de données non fonctionnelle car les identifiants ont changé...&lt;/p&gt;


&lt;p&gt;- ...&lt;/p&gt;



&lt;p&gt;La catastrophe.&lt;/p&gt;


&lt;p&gt;Vous décidez donc de regarder sur la machine de production, et bien sûr vous faites les mêmes découvertes...&lt;/p&gt;


&lt;p&gt;Résultat: votre mise en production est décalée de 1/2 journée, vous mettez la pression aux équipes plateformes pour qu'elles mettent tous les environnements à jour, et vous prenez un savon par votre client (interne) qui vous prend pour un amateur.&lt;/p&gt;


&lt;p&gt;Comment éviter cela&amp;nbsp;?&lt;/p&gt;


&lt;p&gt;Vous pouvez acheter le dernier outils à la mode qui compte XXXX k€ et qui vérifie tout, mais c'est long à mettre en place et surtout ca coûte très cher dans votre budget (ou celui des équipes plateformes), en plus les tests réalisés seront soit très générique soit il faudra peut être un peu tordre l'outils pour faire certaines vérifications un peu spécifique à votre application (test de la connexion mysql, vérification de la version de votre framework préféré...)&lt;/p&gt;


&lt;p&gt;Une autre solution consiste à réaliser un petit script, PHP, qui va faire tous les tests spécifiques qui vous intéresse et vous faire un rapport directe. Appellons le &quot;PHP Sanity Check(er)&quot;.&lt;/p&gt;


&lt;p&gt;Une contrainte cependant&amp;nbsp;: tout doit tenir dans un script (1 fichier) et aucune installation ou dépendance autre que le dépôt du fichier dans l'arborescence du serveur web ne doit être nécessaire. En effet, il serait dommage que votre script ne fonctionne qu'avec une dépendance à la librairie TUTU et que cette librairie ne soit pas installée sur la plateforme (qui le vérifierait ?)&lt;/p&gt;


&lt;p&gt;Le script peut être très simple et faire une série de version_compare(), de stream_socket_client(), de file_exists(), extension_loaded(), de vérification de variable $_SERVER.... c'est du PHP, vous pouvez donc tout faire ;), par contre, avant d'utiliser mysql(i)_connect, vérifiez (via un test dans le script) que l'extension mysql(i) est bien chargée&amp;nbsp;!&lt;/p&gt;


&lt;p&gt;Oui mais j'ai plusieurs applications croisées avec plusieurs environnements, ce qui m'oblige à faire des suites de tests différentes, comment faire&amp;nbsp;? je duplique le script pour chacun des cas et je maintiens les différentes versions&amp;nbsp;? une usine à gaz&amp;nbsp;!&lt;/p&gt;


&lt;p&gt;Non.&lt;/p&gt;


&lt;p&gt;Vous êtes malin.&lt;/p&gt;


&lt;p&gt;Vous développez donc une seule fois, vous faites des fichiers de configuration spécifiques à chacun des applications/environnements et &quot;vous générez un script spécifique pour chacun des cas&quot;.&lt;/p&gt;


&lt;p&gt;Comment&amp;nbsp;?&lt;/p&gt;


&lt;p&gt;Grâce, par exemple à Phing et Phar.&lt;/p&gt;


&lt;p&gt;Vous créez votre script générique qui prend une liste de tests écrit dans un fichier de configuration (par exemple xml). Voici un exemple de fichier&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?xml version='1.0' encoding='iso-8859-1' ?&amp;gt;

&amp;lt;psc&amp;gt;
	&amp;lt;test info=&amp;quot;PHP Version&amp;quot; type=&amp;quot;php:minimum-version&amp;quot; arg1=&amp;quot;5.0.0&amp;quot;/&amp;gt;
	&amp;lt;test info=&amp;quot;Windows Platform&amp;quot; type=&amp;quot;php:os&amp;quot; arg1=&amp;quot;winnt&amp;quot;/&amp;gt;
	&amp;lt;test info=&amp;quot;Google is Reachable&amp;quot; type=&amp;quot;network:tcp-port-reachable&amp;quot; arg1=&amp;quot;www.google.com&amp;quot; arg2=&amp;quot;80&amp;quot;/&amp;gt;
&amp;lt;/psc&amp;gt;
&lt;/pre&gt;


&lt;p&gt;Votre script lit le contenu du fichier xml, en sort une liste de tests correspondant à des appels de méthodes de classes&amp;nbsp;:&lt;/p&gt;


&lt;p&gt;- Php::checkMinimumVersion('5.0.0')&lt;/p&gt;


&lt;p&gt;- Php::checkOs('winnt')&lt;/p&gt;


&lt;p&gt;- Network::checkTcpPortReachable('www.google.com','80')&lt;/p&gt;



&lt;p&gt;Vous écrivez ensuite les classes Php (dans Php.php ;)) et Network (dans Network.php) qui contiennent chacunes les méthodes listées, vous pouvez bien sûr en rajouter d'autres, si elles ne sont pas listées dans le fichier de configuration elle ne seront pas utilisées.&lt;/p&gt;


&lt;p&gt;Vous faites en sorte que chacune des méthodes lève une exception si le test ne passe pas avec un message d'erreur compréhensible sur le problème.&lt;/p&gt;


&lt;p&gt;A l'exécution, vous récupérez le résultat d'exécution de chacune des méthodes (en faisant un try/catch par dessus) et vous affichez cela dans un tableau HTML qui va bien avec des couleurs verte et rouge.&lt;/p&gt;


&lt;p&gt;Mais à quoi sert Phar&amp;nbsp;?&lt;/p&gt;


&lt;p&gt;Venons-en à Phar. Phar ou encore &quot;PHP Archive&quot; permet de condenser plusieurs scripts PHP dans un seul fichier (.phar), il peut être compressé ou non. Le moteur PHP saura exécuter le fichier .phar comme si il était un script .php
La seule différence est que le .phar contenant plusieurs fichiers php, il faut lui indiquer précisément quel est le premier script a exécuter (celui-ci pourra faire des includes des autres éventuellement).&lt;/p&gt;


&lt;p&gt;Vous pouvez donc faire plein de fichiers/scripts/classes dans votre outils et les packager dans un seul et même fichier .phar en y embarquant même le fichier de configuration nécessaire. A l'exécution, vos scripts se comporteront comme si ils étaient dans un répertoire sur le disque et vous pourrez utiliser les includes.&lt;/p&gt;


&lt;p&gt;Mais à quoi sert Phing&amp;nbsp;?&lt;/p&gt;


&lt;p&gt;Phing, est un outils de scripting cross-platform (xml) écrit en PHP (port de Ant venant de Java). Il permet de décrir des batchs de commandes (systèmes souvent) à réaliser sous forme xml (ca marche donc sous windows, mac, linux...), voici ce que nous pourrions faire pour packager notre PHP sanity Checker pour générer un .phar à installer sur les plateformes pour notre application TOTO&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
    &amp;lt;target name=&amp;quot;package&amp;quot; depends=&amp;quot;init&amp;quot; description=&amp;quot;Packages the phar script to deploy on platform (use -Dsuite=&amp;lt;your-suite&amp;gt;)&amp;quot;&amp;gt;
    	&amp;lt;delete dir=&amp;quot;tmp/package&amp;quot;/&amp;gt;
    	&amp;lt;mkdir dir=&amp;quot;tmp/package&amp;quot;/&amp;gt;
    	&amp;lt;mkdir dir=&amp;quot;tmp/package/config/suites&amp;quot;/&amp;gt;
    	&amp;lt;copy file=&amp;quot;config/suites/${suite}.xml&amp;quot; tofile=&amp;quot;tmp/package/config/suites/default.xml&amp;quot;/&amp;gt;
    	&amp;lt;copy todir=&amp;quot;tmp/package&amp;quot;&amp;gt;
    		&amp;lt;fileset dir=&amp;quot;.&amp;quot;&amp;gt;
    			&amp;lt;include name=&amp;quot;**/*.php&amp;quot;/&amp;gt;
    		&amp;lt;/fileset&amp;gt;
    	&amp;lt;/copy&amp;gt;
    	&amp;lt;phar to=&amp;quot;tmp/check.phar&amp;quot;&amp;gt;
    		&amp;lt;fileset dir=&amp;quot;tmp/package&amp;quot;&amp;gt;
    			&amp;lt;include name=&amp;quot;**/*&amp;quot;/&amp;gt;
    		&amp;lt;/fileset&amp;gt;
    	&amp;lt;/phar&amp;gt;
    &amp;lt;/target&amp;gt;
&lt;/pre&gt;


&lt;p&gt;Pour exécuter cette liste de commandes, vous faites&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$ phing package -Dsuite=mon-application
&lt;/pre&gt;


&lt;p&gt;ce qui aura pour but de packager le fichier mon-application.xml avec vos scripts php (ceux de votre outils de tests) dans un fichier tmp/check.phar.&lt;/p&gt;


&lt;p&gt;Dernière étape, vous déployez (vous copiez) le fichier check.phar sur votre plateforme cible dans l'arborescence du serveur web, et vous accédez à ce fichier comme si il s'agissait d'un fichier .php (configurer apache pour qu'il traite les fichiers .phar comme les fichiers .php).&lt;/p&gt;


&lt;p&gt;Des commentaires&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2009/01/23/Points-de-controle-automatises-avant-un-developpement-ou-comment-maitriser-un-deploiement#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2009/01/23/Points-de-controle-automatises-avant-un-developpement-ou-comment-maitriser-un-deploiement#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/24</wfw:commentRss>
      </item>
    
  <item>
    <title>Charger un fichier de configuration à la demande sans changer l'url</title>
    <link>http://blog.phppro.fr/?post/2008/11/24/Charger-un-fichier-de-configuration-a-la-demande-sans-changer-l-url</link>
    <guid isPermaLink="false">urn:md5:5defe85864d6de918d3e2203c2918c52</guid>
    <pubDate>Mon, 24 Nov 2008 16:03:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Trucs et astuces</category>
        <category>application</category><category>base de données</category><category>configuration</category><category>environnement</category>    
    <description>&lt;p&gt;Votre application est exécutée sur plusieurs environnements différents (dev, integration, preprod, prod, ...).&lt;br /&gt;
Vous avez bien fait les choses, les différences entre les environnements sont externalisées dans un fichier de configuration de sorte que le code (php) soit identique sur l'ensemble des environnements.&lt;br /&gt; ...&lt;/p&gt;    &lt;p&gt;Votre application est exécutée sur plusieurs environnements différents (dev, integration, preprod, prod, ...).&lt;br /&gt;
Vous avez bien fait les choses, les différences entre les environnements sont externalisées dans un fichier de configuration de sorte que le code (php) soit identique sur l'ensemble des environnements.&lt;br /&gt;
Dans votre code, vous détectez l'environnement sur lequel est exécuté votre code grâce à l'utilisation d'une variable d'environnement &quot;système&quot; (ex: PHP_ENV) qui contient le nom de l'environnement. La valeur de cette variable est donc différente sur chacun des serveurs. Vous utilisez soit une variable d'environnement système et la directive PassEnv d'Apache, soit la directive SetEnv d'Apache pour positionner la valeur dans vos fichiers de configuration Apache des différents serveurs, par exemple dans le fichier httpd.conf du serveur de pré-production :&lt;br /&gt;&lt;/p&gt;

&lt;pre&gt;
SetEnv PHP_ENV preprod
&lt;/pre&gt;


&lt;p&gt;Si vous êtes dans ce cas là, Apache vous permet de surcharger facilement la valeur de la variable PHP_ENV lors d'une requête HTTP, rajoutez la ligne suivante dans votre fichier httpd.conf&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
SetEnvIf X-PHP-Env &amp;quot;(.+)&amp;quot; PHP_ENV=$1
&lt;/pre&gt;


&lt;p&gt;Maintenant si vous faites une requête HTTP en passant le header HTTP&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
X-PHP-Env: integration
&lt;/pre&gt;


&lt;p&gt;Même si votre application est exécutée sur l'environnement de pré-production, elle chargera le fichier (ou la section de configuration) liée à l'intégration.&lt;/p&gt;


&lt;p&gt;Cette technique peut vous aider dans différents cas&amp;nbsp;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;vous n'avez qu'un seul serveur, mais vous souhaitez gérer plusieurs environnements sur la même version du code (l'url reste la même, c'est juste le header X-PHP-Env qui change)&lt;/li&gt;
&lt;li&gt;vous souhaitez charger une base de tests facilement, vous créez alors un environnement prod_test qui à les mêmes caractéristiques que prod mais qui pointent sur une base de tests au lieu de pointer sur la base de production. (l'url reste la même c'est juste le header X-PHP-Env qui change)&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vous voyez d'autres utilisations&amp;nbsp;?&lt;/p&gt;


&lt;p&gt;PS: dans mon cas, cela permet d'effectuez des tests de non regressions sur des WebServices, sans changer l'url, mais en chargeant une base de données distantes qui contient un jeux de données de tests&lt;/p&gt;


&lt;p&gt;PS2: pour tester manuellement (i.e. avec un navigateur) cette solution, vous devrez installez un plugin à votre navigateur qui vous permettent de modifier les headers http de la requête (LiveHTTPHeaders de mémoire sous Firefox fait cela très bien)&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2008/11/24/Charger-un-fichier-de-configuration-a-la-demande-sans-changer-l-url#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2008/11/24/Charger-un-fichier-de-configuration-a-la-demande-sans-changer-l-url#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/23</wfw:commentRss>
      </item>
    
  <item>
    <title>Comment utiliser le même fichier build.xml (Phing) pour plusieurs projets</title>
    <link>http://blog.phppro.fr/?post/2008/11/20/Comment-utiliser-le-meme-fichier-buildxml-Phing-pour-plusieurs-projets</link>
    <guid isPermaLink="false">urn:md5:eff3bf451ae7586fea093d7a9de868fe</guid>
    <pubDate>Thu, 20 Nov 2008 16:39:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Méthodologie</category>
        <category>application</category><category>build</category><category>phing</category><category>projet</category>    
    <description>&lt;p&gt;Voici un problème que l'on rencontre souvent quand le nombre de projets grossit, grossit...&lt;/p&gt;


&lt;p&gt;Au début, nous avons un projet.&lt;/p&gt;


&lt;p&gt;On créé un fichier build.xml, que l'on versionne avec le code. ...&lt;/p&gt;    &lt;p&gt;Voici un problème que l'on rencontre souvent quand le nombre de projets grossit, grossit...&lt;/p&gt;


&lt;p&gt;Au début, nous avons un projet.&lt;/p&gt;


&lt;p&gt;On créé un fichier build.xml, que l'on versionne avec le code.&lt;/p&gt;


&lt;p&gt;Ensuite, on crée un deuxième projet, en utilisant les mêmes pratiques que le premier (ce sont les mêmes développeurs ou les mêmes outils / pratiques de développpement).&lt;/p&gt;


&lt;p&gt;Solution de facilité, on copie le fichier build.xml du premier projet dans le second et on le versionne (duplicata) dans le deuxième projet.&lt;/p&gt;


&lt;p&gt;Ensuite on créé un 3ième, 4ième, ..., Nième projet (toujours en dupliquant le fichier build.xml).&lt;/p&gt;


&lt;p&gt;Viens le jour ou nous mettons à jour nos pratiques / outillage de développement. On se rend alors compte qu'il faut une &quot;cible&quot; PHING de plus pour gérer le déploiement sur notre nouveau serveur de préprod. Et la&amp;nbsp;: Damned&amp;nbsp;! Il faut modifier tous les fichiers build.xml de chacun de projets (qui au passage ont certainement évolué chacun dans leur coin...)&lt;/p&gt;


&lt;h3&gt;Comment faire pour mutualiser ce fameux fichier (build.xml)&amp;nbsp;?&lt;/h3&gt;


&lt;p&gt;Build.xml, c'est du XML non&amp;nbsp;? On aurait pu se dire, nous n'avons qu'à utiliser un mécanisme d'inclusion ou d'héritage (par exemple un extends=&quot;c:/common/build.xml&quot;), oui, mais ce n'est pas possible (aujourd'hui) a priori.&lt;/p&gt;


&lt;p&gt;Comment faire&amp;nbsp;?&lt;/p&gt;


&lt;p&gt;Phing propose le paramètre en ligne de commande &quot;-f&quot; ou &quot;-buildfile&quot; permettant de spécifier un fichier build.xml alternatif. En effet, par défaut c'est celui du répertoire courant qui est utilisé si il existe (l'exécution de phing plante si il n'existe aucun fichier build.xml dans le répertoire courant)&lt;/p&gt;


&lt;p&gt;L'idée est alors la suivante&amp;nbsp;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;créer un fichier build.xml &quot;générique&quot; que vous positionnez dans un répertoire précis de votre système (ex: c:\dev\phpcommon\phing\build.xml, ou /etc/phpcommon/phing/build.xml)&lt;/li&gt;
&lt;li&gt;créer un script myphing.bat ou myphing.sh qui contient le code suivant&amp;nbsp;:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;
phing -f $PHPCOMMON_HOME/bin/build.xml $*
&lt;/pre&gt;

&lt;ol&gt;
&lt;li&gt;ajouter la variable d'environnement PHPCOMMON_HOME avec la valeur c:/dev/phpcommon ou /etc/phpcommon dans la liste des variables d'environnement système&lt;/li&gt;
&lt;li&gt;ajouter le répertoire %PHPCOMMON_HOME%/bin dans le path de votre système (variable d'environnement)&lt;/li&gt;
&lt;li&gt;redémarrer votre système (ou la ligne de commande)&lt;/li&gt;
&lt;li&gt;créer le fichier c:/dev/phpcommon/phing/build.xml avec le contenu suivant&amp;nbsp;:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;

&amp;lt;project name=&amp;quot;php&amp;quot; basedir=&amp;quot;.&amp;quot; default=&amp;quot;build&amp;quot;&amp;gt;

    &amp;lt;property name=&amp;quot;common.dir&amp;quot;      value=&amp;quot;${project.basedir}/../&amp;quot;  override=&amp;quot;true&amp;quot;/&amp;gt;
    &amp;lt;property name=&amp;quot;project.basedir&amp;quot; value=&amp;quot;${application.startdir}&amp;quot; override=&amp;quot;true&amp;quot;/&amp;gt;

     ...

&amp;lt;/project&amp;gt;
&lt;/pre&gt;


&lt;p&gt;Vous pouvez maintenant utiliser la ligne de commande&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$ myphing &amp;lt;target&amp;gt;
&lt;/pre&gt;


&lt;p&gt;pour exécuter la cible &amp;lt;target&amp;gt; situé dans le fichier c:/dev/phpcommon/phing/build.xml, à partir de la racine de votre projet.&lt;/p&gt;


&lt;p&gt;Vous n'avez plus forcément besoin d'un fichier build.xml local à votre projet, sauf si vous avez des cibles (targets) spécifiques à votre projet (ce que je vous déconseille si vous avez plusieurs projets, je pense qu'il vaut mieux rationnaliser vos cibles). Si vous décidez toutefois de garder un fichier build.xml local à votre projet, vous pourrez l'utiliser en appelant la ligne de commande habituelle&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
$ phing &amp;lt;target&amp;gt;
&lt;/pre&gt;


&lt;p&gt;Maintenant, il ne vous reste plus qu'à créer l'ensemble de vos cibles communes / génériques à tous vos projets. Je vous propose d'ailleurs les miennes dans &lt;a href=&quot;http://blog.phppro.fr/?post/2008/11/07/Phing-:-mes-targets-preferes&quot;&gt;ce billet&lt;/a&gt;.&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2008/11/20/Comment-utiliser-le-meme-fichier-buildxml-Phing-pour-plusieurs-projets#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2008/11/20/Comment-utiliser-le-meme-fichier-buildxml-Phing-pour-plusieurs-projets#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/21</wfw:commentRss>
      </item>
    
  <item>
    <title>Entreprises du CAC 40 : Quelles plateformes pour leur site internet ?</title>
    <link>http://blog.phppro.fr/?post/2008/11/14/Entreprises-du-CAC-40-%3A-Quelles-plateformes-pour-leur-site-internet</link>
    <guid isPermaLink="false">urn:md5:4881b963222221770882f6a152ad9376</guid>
    <pubDate>Fri, 14 Nov 2008 08:00:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Analyse</category>
        <category>entreprise</category><category>plateforme</category>    
    <description>&lt;p&gt;Voici un graphique de répartition des plateformes utilisées par les entreprises du CAC40 pour leur site internet&lt;/p&gt;    &lt;p&gt;&lt;a href=&quot;http://blog.phppro.fr/public/analyse-site-cac40.jpg&quot;&gt;&lt;img src=&quot;http://blog.phppro.fr/public/analyse-site-cac40.jpg&quot; alt=&quot;Plateformes des sites des entreprises du CAC40&quot; title=&quot;Plateformes des sites des entreprises du CAC40, nov 2008&quot; /&gt;&lt;/a&gt;&lt;/p&gt;


&lt;p&gt;(Analyse réalisée en Juin 2008)&lt;/p&gt;


&lt;p&gt;Vous pouvez &lt;a href=&quot;http://blog.phppro.fr/public/20080614_-_FR_-_Olivier_Hoareau_-_Etude_Sites_Php.xls&quot;&gt;télécharger la feuille excel&lt;/a&gt; qui a permi de construire cette synthèse&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2008/11/14/Entreprises-du-CAC-40-%3A-Quelles-plateformes-pour-leur-site-internet#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2008/11/14/Entreprises-du-CAC-40-%3A-Quelles-plateformes-pour-leur-site-internet#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/20</wfw:commentRss>
      </item>
    
  <item>
    <title>D'une application mono-marque à une application multi-marque : comment faire simple ?</title>
    <link>http://blog.phppro.fr/?post/2008/11/03/D-une-application-mono-marque-a-une-application-multi-marque-%3A-comment-faire-simple</link>
    <guid isPermaLink="false">urn:md5:1ee3152d357d3396a2082ee8195b1459</guid>
    <pubDate>Wed, 12 Nov 2008 08:00:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Trucs et astuces</category>
        <category>application</category><category>configuration</category><category>environnement</category>    
    <description>&lt;p&gt;Vous avez développé une application PHP complexe (ou pas).&lt;/p&gt;


&lt;p&gt;Le succès de votre application auprès de votre client (interne ou externe) vous amène à devoir décliner votre application dans plusieurs versions&amp;nbsp;: ...&lt;/p&gt;    &lt;p&gt;Vous avez développé une application PHP complexe (ou pas).&lt;/p&gt;


&lt;p&gt;Le succès de votre application auprès de votre client (interne ou externe) vous amène à devoir décliner votre application dans plusieurs versions&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;une pour votre client historique&lt;/li&gt;
&lt;li&gt;une pour un nouveau client (concurrent de l'autre ?!)&lt;/li&gt;
&lt;li&gt;une &quot;marque blanche&quot; pour démontrer les fonctionnalités de votre application en mode démo&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Votre application n'a pas été prévu pour cela, vous avez donc plusieurs options&amp;nbsp;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;dupliquer le code, purement et simplement&lt;/li&gt;
&lt;li&gt;mutualiser le code, et l'altérer pour qu'il gère les différences entre les versions (marques)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;La première solution, bien que potentiellement plus rapide à mettre en oeuvre, se révèle couteuse dans le temps car il faut maintenir deux versions de l'application en parallèle (et répercuter les fix de bugs sur les différentes versions,...).&lt;/p&gt;


&lt;p&gt;Pour implémenter la deuxième solution, il vous faut principalement&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;externaliser les paramètres de configuration de l'application susceptible de changer en fonction de la version (connexion base de données, nom du client...)&lt;/li&gt;
&lt;li&gt;mutualiser le code du coeur de l'application (i.e. la logique)&lt;/li&gt;
&lt;li&gt;spécialiser la charte graphique (pour tel ou tel client ou version)&lt;/li&gt;
&lt;li&gt;ajouter des &quot;plugins&quot; spécifiques à votre application qui seront spécifiques à telles ou telles versions (pour tel ou tel client)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Concrètement, il s'agit pour vous à minima (si il s'agit de la même application fonctionnellement) pour chacune des versions de&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;donner un nom de code à votre version, ex: client1&lt;/li&gt;
&lt;li&gt;créer un virtual host Apache spécifique, ex: client1.mondomaine.com&lt;/li&gt;
&lt;li&gt;réaliser un fichier CSS spécifique, ex: styles/client1.css&lt;/li&gt;
&lt;li&gt;réaliser un fichier JS spécifique, ex: scripts/client1.js&lt;/li&gt;
&lt;li&gt;réaliser un fichier de configuration spécifique, ex: configs/client1.php&lt;/li&gt;
&lt;li&gt;réaliser un fichier d'initialisation PHP spécifique, ex: inits/client1.php&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vous faites ensuite pointer tous les Virtual Hosts sur le même DocumentRoot (i.e. il n'y a qu'une seule application, un seul répertoire racine) définissez ensuite à l'intérieur du virtual host de chacune des versions une variable d'environnement dont la valeur prend le nom de code de la version, par exemple&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
SetEnv APP_NAME client1
&lt;/pre&gt;


&lt;p&gt;Ensuite au début de (chacun de vos|votre) script&lt;a href=&quot;http://blog.phppro.fr/?post/2008/11/03/s&quot; title=&quot;s&quot;&gt;s&lt;/a&gt;?, vous pouvez récupérer la valeur de cette variable d'environnement et la positionner dans une constante&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php

define('APP_NAME',$_SERVER['APP_NAME']);
...
&lt;/pre&gt;


&lt;p&gt;Vous disposez maintenant d'une constante disponible dans tous vos fichiers inclus qui contient le nom de l'application, elle peut servir pour charger les bons fichiers&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php

require_once dirname(__FILE__).'/../configs/'.APP_NAME.'.php';
require_once dirname(__FILE__).'/../inits/'.APP_NAME.'.php';

...
// la logique de votre application ici
...
?&amp;gt;
&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        ...
        &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; href=&amp;quot;/styles/@common.css&amp;quot;/&amp;gt;
        &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; href=&amp;quot;/styles/&amp;lt;?php echo APP_NAME ?&amp;gt;.css&amp;quot;/&amp;gt;
        &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;/scripts/@common.js&amp;quot;&amp;gt;&amp;lt;!-- --&amp;gt;&amp;lt;/script&amp;gt;
        &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;/scripts/&amp;lt;?php echo APP_NAME ?&amp;gt;.js&amp;quot;&amp;gt;&amp;lt;!-- --&amp;gt;&amp;lt;/script&amp;gt;
        ...
&lt;/pre&gt;


&lt;p&gt;Si la logique de votre application diffère quelques peu en fonction de la version, vous avez 2 solutions&amp;nbsp;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;faire des switch(APP_NAME) dans la logique et coder les spécificités dans le même fichier&lt;/li&gt;
&lt;li&gt;externaliser les spécificités dans des fichiers distincts (de préférence des classes ;)), faire un include('features/'.APP_NAME.'.php'); au début de votre script et coder différentes versions de la fonction ou de la classe dans chacun des fichiers features/*.php&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Si vous travaillez en objet, vous pouvez réaliser des interfaces que devront implémenter vos implémentations spécifiques de vos &quot;features&quot; et charger la bonne classe en faisant par exemple&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php

...
$featureClass = ucfirst(APP_NAME).'_Feature1Service';
require_once dirname(__FILE__).'/../classes/'.str_replace('_','/',$featureClass).'.php');
$feature = new $featureClass;

$result = $feature-&amp;gt;laMethodeQuiMInteresse($leParam1,$leParam2);

...
&lt;/pre&gt;


&lt;p&gt;Vous n'avez plus qu'à coder le fichier classes/Client1/Feature1Service.php&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php

class Client1_Feature1Service implements Feature1Interface {
    public function laMethodeQuiMInteresse($leParam1,$leParam2) {
        // votre code spécifique ici
        ...
    }
}
&lt;/pre&gt;


&lt;p&gt;Si un nouveau client vous demande une version de votre application adapté à ses besoins, vous créez simplement ou nouveau virtual host Apache, un nouveau nom de code pour la version (ex: client2), un nouveau fichier de configuration et d'initialisation (configs/client2.php, inits/client2.php), des nouveaux fichiers CSS et JS (styles/client2.css, scripts/client2.js) et une nouvelle version de vos features spécifiques (classes/Client2/Feature1Service.php)&lt;/p&gt;



&lt;p&gt;Ce type de méthode peut être mise en conjonction avec l'utilisation de frameworks comme Zend Framework, pensez à la variable d'environnement APP_NAME dans Apache&amp;nbsp;!&lt;/p&gt;


&lt;p&gt;Et vous quelles sont vos pratiques&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2008/11/03/D-une-application-mono-marque-a-une-application-multi-marque-%3A-comment-faire-simple#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2008/11/03/D-une-application-mono-marque-a-une-application-multi-marque-%3A-comment-faire-simple#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/18</wfw:commentRss>
      </item>
    
  <item>
    <title>Les 10 commandements du développeur PHP en entreprise : ma proposition</title>
    <link>http://blog.phppro.fr/?post/2008/11/10/Les-10-commandements-du-developpeur-PHP-en-entreprise-%3A-ma-proposition</link>
    <guid isPermaLink="false">urn:md5:198b6f3e6d40d695b8c0cb16cb31e447</guid>
    <pubDate>Mon, 10 Nov 2008 19:22:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Méthodologie</category>
        <category>agile</category><category>equipe</category><category>tests</category>    
    <description>&lt;p&gt;Si PHP développeur tu es, ces 10 commandements tu respecteras&amp;nbsp;: ...&lt;/p&gt;    &lt;p&gt;Si PHP développeur tu es, ces 10 commandements tu respecteras&amp;nbsp;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Faire simple ET propre&lt;/li&gt;
&lt;li&gt;Développer en TDD&amp;nbsp;: le test d’abord, le code ensuite&lt;/li&gt;
&lt;li&gt;Committer sur barre verte (tests en succès)&lt;/li&gt;
&lt;li&gt;Respecter des conventions de codage connus en dehors de l'entreprise&lt;/li&gt;
&lt;li&gt;Développer 1 fois, Réutiliser plusieurs fois&lt;/li&gt;
&lt;li&gt;Tester unitairement toutes les fonctionnalités du coeur de l'application&lt;/li&gt;
&lt;li&gt;Utiliser les comparaisons de valeurs ET de types (===, !==)&lt;/li&gt;
&lt;li&gt;Réaliser systématiquement des implémentation mocks (bouchons)&lt;/li&gt;
&lt;li&gt;Ne pas dépasser plus de 80 lignes aérées et commentées pour une fonction/méthode&lt;/li&gt;
&lt;li&gt;Always Have Fun&amp;nbsp;!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Qu'en pensez-vous ?&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;(TDD = Test Driven Development)&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2008/11/10/Les-10-commandements-du-developpeur-PHP-en-entreprise-%3A-ma-proposition#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2008/11/10/Les-10-commandements-du-developpeur-PHP-en-entreprise-%3A-ma-proposition#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/19</wfw:commentRss>
      </item>
    
  <item>
    <title>Phing : mes targets préférés</title>
    <link>http://blog.phppro.fr/?post/2008/11/07/Phing-%3A-mes-targets-preferes</link>
    <guid isPermaLink="false">urn:md5:3a0d18b578b4548d7d0a5edb1b820269</guid>
    <pubDate>Fri, 07 Nov 2008 08:00:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Méthodologie</category>
        <category>build</category><category>phing</category>    
    <description>&lt;pre&gt;
$ phing -l
&lt;/pre&gt;


&lt;p&gt;et voici mes targets phing préférées&amp;nbsp;: ...&lt;/p&gt;    &lt;pre&gt;
$ phing -l
&lt;/pre&gt;


&lt;p&gt;et voici mes targets phing préférées&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;check-code&lt;/strong&gt;&amp;nbsp;: Checks the quality of the code towards PEAR standard and display errors&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;check-code-xml&lt;/strong&gt;&amp;nbsp;: Checks the quality of the code towards PEAR standard and export to xml file&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;check-source&lt;/strong&gt;&amp;nbsp;: Checks the quality of the source files and print HTML report with metrics&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;check-syntax&lt;/strong&gt;&amp;nbsp;: Checks the syntax of the code (Lint)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;checks&lt;/strong&gt;&amp;nbsp;: Checks the quality of the code (check-syntax, check-code-xml, check-source)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;doc-api&lt;/strong&gt;&amp;nbsp;: Generates the PHPDocumentor API Documentation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;docs&lt;/strong&gt;&amp;nbsp;: Generates the documentation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;init&lt;/strong&gt;&amp;nbsp;: Preparation/initialization of the build&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;test-functional-fitnesse&lt;/strong&gt;&amp;nbsp;: Executes functional tests (Fitnesse)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;test-functional-greenpepper&lt;/strong&gt;&amp;nbsp;: Executes functional tests (GreenPepper)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;test-gui-selenium&lt;/strong&gt;&amp;nbsp;: Executes GUI tests (Selenium)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;test-unit&lt;/strong&gt;&amp;nbsp;: Executes unit tests (PHPUnit) and code coverage (XDebug)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;tests&lt;/strong&gt;&amp;nbsp;: Executes all tests (unit, functional, gui)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;build&lt;/strong&gt;&amp;nbsp;: Automatic Build executed by Hudson (checks, tests)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;post-build&lt;/strong&gt;&amp;nbsp;: Automatic Post-build executed by Hudson (docs)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;generate-package-xml&lt;/strong&gt;&amp;nbsp;: Prepare PEAR package2.xml&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;package-tar&lt;/strong&gt;&amp;nbsp;: Create a Tar package of the application&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;packages&lt;/strong&gt;&amp;nbsp;: Launches all the packages&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;prepare-package&lt;/strong&gt;&amp;nbsp;: Prepare packages&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Quelles sont les vôtres&amp;nbsp;?&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2008/11/07/Phing-%3A-mes-targets-preferes#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2008/11/07/Phing-%3A-mes-targets-preferes#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/17</wfw:commentRss>
      </item>
    
  <item>
    <title>Pattern Factory : Ou comment faire tout en 1 ligne</title>
    <link>http://blog.phppro.fr/?post/2008/11/05/Pattern-Factory-%3A-Ou-comment-faire-tout-en-1-ligne</link>
    <guid isPermaLink="false">urn:md5:cee688fd43160626a43e6de22f125922</guid>
    <pubDate>Wed, 05 Nov 2008 08:00:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Méthodologie</category>
        <category>factory</category><category>pattern</category><category>service</category><category>syntaxe</category>    
    <description>&lt;p&gt;Vous révez de ça&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php

$service = new MonService()
    -&amp;gt; setConfig(...)
    -&amp;gt; setAdapter(...)
    -&amp;gt; setModel(...)
    -&amp;gt; process();
&lt;/pre&gt;


&lt;p&gt;...&lt;/p&gt;    &lt;p&gt;Vous révez de ça&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php

$service = new MonService()
    -&amp;gt; setConfig(...)
    -&amp;gt; setAdapter(...)
    -&amp;gt; setModel(...)
    -&amp;gt; process();
&lt;/pre&gt;


&lt;p&gt;Mais ce n'est pas possible en PHP et vous avez l'erreur suivante (ligne 4 = ligne du setConfig() )&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
PHP Parse error:  syntax error, unexpected T_OBJECT_OPERATOR in &amp;lt;votre-fichier-ici&amp;gt; on line 4
&lt;/pre&gt;


&lt;p&gt;Du coup, désespérement vous devez faire&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$service = new MonService();
$service
    -&amp;gt; setConfig(...)
    -&amp;gt; setAdapter(...)
    -&amp;gt; setModel(...)
    -&amp;gt; process();
&lt;/pre&gt;


&lt;p&gt;Imaginez d'autre part,  qu'à plusieurs endroits dans votre application vous utilisez systématiquement toujours la même classe, avec des instances différentes, mais qu'à chaque fois vous deviez reconfigurer l'objet de la même manière en faisant par exemple&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$service1 = new MailService();
$service1-&amp;gt;setOutgoingAdapter(new SendmailAdapter());
$service1-&amp;gt;setIncomingAdapter(new PostfixAdapter());
...
$service1-&amp;gt;send(...);
...
...
$service2 = new MailService();
$service2-&amp;gt;setOutgoingAdapter(new SendmailAdapter());
$service2-&amp;gt;setIncomingAdapter(new PostfixAdapter());
...
$service2-&amp;gt;send(...);
&lt;/pre&gt;


&lt;p&gt;A chaque fois que vous désirez instancier votre MailService, vous avez le droit d'écrire 3 lignes de code...&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;Le pattern Factory&lt;/strong&gt; peut alors vous aidez. Vous pourriez définir un &quot;paramétrage&quot; par défaut de votre service qui soit déjà tout préconfiguré&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
class MailService {
   ...
   public final static function factory($type='default') {
       $service = new MailService();
       switch($type) {
       case 'default':
           $service-&amp;gt;setOutgoingAdapter(new SendmailAdapter());
           $service-&amp;gt;setIncomingAdapter(new PostfixAdapter());
           break;
      case 'mock':
          $service-&amp;gt;setOutgoingAdapter(new MockOutgoingAdapter());
          $service-&amp;gt;setIncomingAdapter(new MockIncomingAdapter());
          break;
      default:
          throw new RuntimeException(&amp;quot;Unknown factory type '$type'&amp;quot;);
      }
      return $service;
   }
}
&lt;/pre&gt;


&lt;p&gt;Vous pourrez maintenant appeler votre service en une seule ligne de code&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$result = MailService::factory('default')-&amp;gt;send(...);
&lt;/pre&gt;


&lt;p&gt;Pensez à factoriser votre code&amp;nbsp;!&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2008/11/05/Pattern-Factory-%3A-Ou-comment-faire-tout-en-1-ligne#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2008/11/05/Pattern-Factory-%3A-Ou-comment-faire-tout-en-1-ligne#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/16</wfw:commentRss>
      </item>
    
  <item>
    <title>Web Services : Avoir le réflexe mise en cache pour optimiser la charge serveur</title>
    <link>http://blog.phppro.fr/?post/2008/11/03/Web-Services-%3A-Avoir-le-reflexe-mise-en-cache-pour-optimiser-la-charge-serveur</link>
    <guid isPermaLink="false">urn:md5:33c9c25effe05dfde2dea3e129bb9952</guid>
    <pubDate>Mon, 03 Nov 2008 08:00:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Architecture</category>
        <category>cache</category><category>optimisation</category><category>webservices</category><category>xml</category>    
    <description>&lt;p&gt;Vous avez développé votre tout dernier web service destinés à être utilisé par 2 ou 3 autres équipes en interne (voire en externe par des partenaires).&lt;/p&gt;


&lt;p&gt;Il (le web service) fonctionne bien, tellement bien, que vous commencez à avoir plusieurs dizaines de requêtes par jour. ...&lt;/p&gt;    &lt;p&gt;Vous avez développé votre tout dernier web service destinés à être utilisé par 2 ou 3 autres équipes en interne (voire en externe par des partenaires).&lt;/p&gt;


&lt;p&gt;Il (le web service) fonctionne bien, tellement bien, que vous commencez à avoir plusieurs dizaines de requêtes par jour.&lt;/p&gt;


&lt;p&gt;Vous n'aviez pas forcément optimisé les performances du web service, les temps de réponses ne sont pas au beau fixe car vous chargez 150 classes PHP qui sont nécessaires au tout dernier framework à la mode pour accéder à une table de base de données et faire une requête select sur un champ avec une clé primaire pour retourner la fiche descriptive d'un utilisateur, d'un partenaire ou d'un produit. Ou bien, pire encore, pour renvoyez votre catalogue de produits qui fait 150Ko une fois transformé au format XML.&lt;/p&gt;


&lt;p&gt;Vous êtes vous posé la question suivante&amp;nbsp;:&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;A quelle fréquence mes données changent-elles ?&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;En effet, à quoi cela vous sert-il de générer par une logique complexe un export (xml, json, text, soap...) de vos données si elles ne changent pas à la même fréquence que les requêtes qui vous parviennent&amp;nbsp;?&lt;/p&gt;


&lt;p&gt;De façon plus optimale, vous pourriez générer une première fois cet export, la première fois qu'il vous est demandé par exemple, et le mettre en cache dans un fichier. Ce fichier pourrait alors être servi pour toutes les requêtes successives, et ce jusqu'à temps que vos données ne changent (en base de données par exemple), vous auriez alors à supprimer le fichier de cache et faire en sorte que votre application le détecte et le régénère à la demande.&lt;/p&gt;


&lt;p&gt;Comment faire&amp;nbsp;?&lt;/p&gt;


&lt;p&gt;Si vous utilisez Apache, vous pouvez mettre en oeuvre l'url rewriting en créant un fichier .htaccess à la racine publique de votre application en y mettant, par exemple&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
...
RewriteEngine On

RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_URI} !-f
RewriteRule ^/mon-webservice/(.*).xml monwebservice.php?id=$1
...
&lt;/pre&gt;


&lt;p&gt;Ainsi, Si un utilisateur requête /mon-webservice/voitures/peugeots.xml, Apache commencera par vérifier si le fichier 'mon-webservice/voitures/peugeots.xml' existe sur le disque dur (dans votre DocumentRoot) et si c'est le cas, il le renverra directement sans passer par aucun script PHP. Si le fichier n'existe pas, alors votre script 'monwebservice.php' sera appelé et sera chargé de renvoyer le contenu.&lt;/p&gt;


&lt;p&gt;Vous pourriez alors avoir dans monwebservice.php un code similaire à celui-ci&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;

ob_start();

// votre logique ici
// ...

$xml = ob_get_contents();
ob_end_clean();

file_put_contents(dirname(__FILE__).&amp;quot;/mon-webservice/$id.xml&amp;quot;,$xml);

header('Content-Type: text/xml');
echo $xml;
&lt;/pre&gt;


&lt;p&gt;Ainsi, à chaque fois qu'il sera appelé, votre script générera le xml, l'écrira dans un fichier sur le disque (le fichier de cache visible dans votre DocumentRoot) et renverra le contenu XML au navigateur.&lt;/p&gt;


&lt;p&gt;Dernière étape, il vous reste maintenant à supprimer le fichier de cache lorsque celui-ci devient obsolète, par exemple quand les données en base de données changent ou bien que certaines données temporelles ou calculées doivent être mises à jour. Dans le premier cas, la mise à jour de la base de données se fait en règle général par l'intermédiaire d'un autre de vos script (par exemple /admin/index.php), il suffit d'include dans ce script la ligne suivante&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
...
unlink(dirname(__FILE__).'/../mon-webservice/voitures/peugeot.xml');
...
&lt;/pre&gt;


&lt;p&gt;Si vous ne maîtrisez pas la mise à jour de la base de données ou que le cache devient obsolète à cause de données temporelles, songez à proposer un script déclenchable à distance (par un cron ou bien pas l'application mettant à jour la base de données) qui supprimerait les fichiers de cache incriminés.&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2008/11/03/Web-Services-%3A-Avoir-le-reflexe-mise-en-cache-pour-optimiser-la-charge-serveur#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2008/11/03/Web-Services-%3A-Avoir-le-reflexe-mise-en-cache-pour-optimiser-la-charge-serveur#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/15</wfw:commentRss>
      </item>
    
  <item>
    <title>SessionSwitcher : Ou comment accélerer vos tests IHMs manuels en 60 lignes de code</title>
    <link>http://blog.phppro.fr/?post/2008/10/31/SessionSwitcher-%3A-Ou-comment-accelerer-vos-tests-IHMs-manuels-en-60-lignes-de-code</link>
    <guid isPermaLink="false">urn:md5:eb347a907f15d8ce52e061c19ab0205d</guid>
    <pubDate>Fri, 31 Oct 2008 23:42:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Trucs et astuces</category>
        <category>ihm</category><category>session</category><category>tests</category>    
    <description>&lt;p&gt;Vous avez développé une belle appli toute sympathique avec plein de jolis formulaires qui sont tellement bien faits, qu'ils vous rendent la vie moins sympathique lorsqu'il s'agit de tester &quot;manuellement&quot; votre application. ...&lt;/p&gt;    &lt;p&gt;Vous avez développé une belle appli toute sympathique avec plein de jolis formulaires qui sont tellement bien faits, qu'ils vous rendent la vie moins sympathique lorsqu'il s'agit de tester &quot;manuellement&quot; votre application.&lt;/p&gt;


&lt;p&gt;En effet, vous devez saisir, re-saisir et re-re-saisir très régulièrement toutes les informations du formulaire en 56 étapes pour accéder à la 57ème étape qui est, justement, celle que vous voulez tester manuellement. Vous avez donc perdu plusieurs (précieuses) secondes (voire minutes) à la fin de la journée, presque inutilement.&lt;/p&gt;


&lt;h3&gt;Comment faire pour ne plus perdre autant de temps&amp;nbsp;?&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Nous pourrions très bien automatiser les tests IHM, mais c'est compliqué (ou pas ?) et c'est long à maintenir (pas toujours, ca dépend des cas)...&lt;/li&gt;
&lt;li&gt;Nous pourrions accéder directement à la &quot;page&quot; qui nous intéressent, mais nous n'aurions pas les données de session... Voila la solution&amp;nbsp;!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Les données de session justement, cela nous aiderait énormément si nous pouvions charger à la demande les données de sessions pour un scénario donné (par exemple, le scénario ou j'ai rempli les 2 premières pages de formulaires mais pas la 3ième, un autre ou j'ai tout rempli mes 7 onglets/pages de formulaire de dossier en ligne et je suis prêt à appuyer sur le bouton envoyer...)&lt;/p&gt;


&lt;p&gt;Imaginons la démarche suivante&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;je me loggue sur mon appli (je créé alors une session dans mon appli, et je récupère un cookie de session, dans la plupart des cas)&lt;/li&gt;
&lt;li&gt;j'évolue dans mon application jusqu'à un point précis (3ème onglet rempli sur 5 par exemple)&lt;/li&gt;
&lt;li&gt;je prend une &quot;photo&quot; de la session à cet instant, je la mets de côté de manière à la recharger à tout moment quand j'en ai besoin&lt;/li&gt;
&lt;li&gt;je continue dans mon appli, j'évolue dans mes développements&lt;/li&gt;
&lt;li&gt;je recharge un peu plus tard, quand j'en ai besoin, ma session préalablement sauvegardée, et hop&amp;nbsp;!&lt;/li&gt;
&lt;li&gt;j'accède directement (à la main) à l'url de la page qui m'intéresse avec cette session rechargée et je peux refaire mon test&lt;/li&gt;
&lt;li&gt;je réutilise à volonté ma session sauvegardée et j'en sauvegarde d'autres sur différents écrans de mon application&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;L'idée vous intéresse&amp;nbsp;?&lt;/h3&gt;


&lt;p&gt;Les 60 lignes de code PHP suivantes, à mettre dans sessionswitcher.php, probablement aussi alors&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php

if (!array_key_exists('sessionswitcher',$_GET)) return;

list ($mode,$value) = explode(':',$_GET['sessionswitcher']);

$sessionpath = session_save_path();
if (strpos ($sessionpath, &amp;quot;;&amp;quot;) !== FALSE)
  $sessionpath = substr ($sessionpath, strpos ($sessionpath, &amp;quot;;&amp;quot;)+1);

switch ($mode) {
case 'show': 
	session_start();
	header('Content-Type: text/plain');
	print_r($_SESSION);
	exit;
case 'reset': 
	session_start();
	$_SESSION = array();
	if (isset($_COOKIE[session_name()]))
		setcookie(session_name(), '', time()-42000, '/');
	session_destroy();
	header('X-Session-Switcher-Result: destroyed');
	break;
case 'save': 
	session_start();
	$file = &amp;quot;$sessionpath/sessionswitcher-$value.session&amp;quot;;
	$_SESSION['sessionswitcher']['name'] = $value;
	file_put_contents($file,session_encode());
	header('X-Session-Switcher-Result: saved');
	header(&amp;quot;X-Session-Switcher-Session: &amp;quot;.$value);
	break;
case 'del': 
	$file = &amp;quot;$sessionpath/sessionswitcher-&amp;quot;.urlencode($value).&amp;quot;.session&amp;quot;;
	if (!is_file($file)) break;
	unlink($file);
	header('X-Session-Switcher-Result: deleted');
	break;
case 'list': 
	if (!is_dir($sessionpath)) break;
	$dh = opendir($sessionpath);
	echo &amp;quot;&amp;lt;p&amp;gt;Choose a session in the list to switch on it:&amp;lt;/p&amp;gt;
&amp;quot;;
	echo &amp;quot;&amp;lt;table border=1&amp;gt;
&amp;quot;;
	while (($file = readdir($dh))!==false) {
		if(!preg_match('/^sessionswitcher\-(.+)\.session$/',$file,$matches))
			continue;
		echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;a href='?sessionswitcher=use:{$matches[1]}'&amp;gt;{$matches[1]}&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;
&amp;quot;;
	}
	echo &amp;quot;&amp;lt;/table&amp;gt;
&amp;quot;;
	header('X-Session-Switcher-Result: listing');
	exit;
case 'use':
	$file = &amp;quot;$sessionpath/sessionswitcher-$value.session&amp;quot;;
	if (!is_file($file)) break;
	session_decode(file_get_contents($file));
	header('X-Session-Switcher-Result: switched');
	header(&amp;quot;X-Session-Switcher-Name: {$_SESSION['sessionswitch']['name']}&amp;quot;);
	break;
default:
	header('X-Session-Switcher-Result: unknown');
}
&lt;/pre&gt;


&lt;h3&gt;Comment utiliser le SessionSwitcher&amp;nbsp;?&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ajoutez la ligne suivante dans votre fichier php.ini (en local sur votre poste de développement, ou bien sur le serveur de recette interne, pas en production !)&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
auto_prepend_file = &amp;quot;&amp;lt;chemin-vers-le-fichier-contenant-le-code-ci-dessus&amp;gt;/sessionswitcher.php&amp;quot;
&lt;/pre&gt;


&lt;p&gt;(vous pouvez aussi faire un 'require &quot;sessionswitcher.php&quot;;' au tout début de vos fichiers/scripts php si la solution de l'auto_prepend ne vous convient pas et que vous avez ajouté le fichier sessionswitcher dans l'include_path PHP ou que vous indiquez le chemin absolu)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rédémarrez votre serveur web (local ou de recette)&lt;/li&gt;
&lt;li&gt;Accédez comme d'habitude à votre application via votre navigateur, connectez vous éventuellement si vous avez un formulaire de connexion/authentification&lt;/li&gt;
&lt;li&gt;Naviguez dans votre application, notamment sur les pages/formulaires qui modifient votre session (i.e. qui stockent des données dans votre session)&lt;/li&gt;
&lt;li&gt;Dès que vous atteignez une étape que vous souhaitez sauvegarder, ajoutez les paramètres suivant dans l'url&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
?sessionswitcher=save:mon-etape
&lt;/pre&gt;


&lt;p&gt;(vous pouvez mettre ce que vous voulez comme nom de sauvegarde à la place de 'mon-etape')&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Notez l'url sur laquelle vous étiez (sans le ?sessionswitcher...)&lt;/li&gt;
&lt;li&gt;A partir de maintenant, vous pourrez recharger à tout moment votre session telle qu'elle était, en ajoutant le paramètre suivant dans l'url&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
?sessionswitcher=use:mon-etape
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;A tout moment vous pouvez voir le contenu de votre session en ajoutant le paramètre suivant dans l'url&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
?sessionswitcher=show
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;A tout moment vous pouvez lister les différentes sauvegardes que vous avez faites en ajoutant le paramètre suivant dans l'url&amp;nbsp;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
?sessionswitcher=list
&lt;/pre&gt;


&lt;p&gt;Si vous utilisez certains plugins de navigateurs qui permettent de voir le contenu des entêtes HTTP renvoyées par le serveur, vous pourrez voir les entêtes suivantes&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
X-SessionSwitcher-Result: xxxxx
...
X-SessionSwitcher-Name: yyyyy
&lt;/pre&gt;



&lt;p&gt;Ce code est expérimental et assez condensé (le but étant de ne pas impacter trop les performances de votre application...), mais vos retours sur son utilisation sont les bienvenus&amp;nbsp;!&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2008/10/31/SessionSwitcher-%3A-Ou-comment-accelerer-vos-tests-IHMs-manuels-en-60-lignes-de-code#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2008/10/31/SessionSwitcher-%3A-Ou-comment-accelerer-vos-tests-IHMs-manuels-en-60-lignes-de-code#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/14</wfw:commentRss>
      </item>
    
  <item>
    <title>1 application = plusieurs environnements</title>
    <link>http://blog.phppro.fr/?post/2008/10/29/1-application-Plusieurs-environnements</link>
    <guid isPermaLink="false">urn:md5:0f202bbf92f9f8634d3fc96af00e4347</guid>
    <pubDate>Wed, 29 Oct 2008 08:00:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Méthodologie</category>
        <category>config</category><category>environnement</category><category>zend framework</category>    
    <description>&lt;p&gt;Lorsqu'on nous développons nos applications PHP, nous sommes très souvent confrontés à plusieurs environnements d'exécution&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;local (développement)&lt;/li&gt;
&lt;li&gt;tests (build continue, ...)&lt;/li&gt;
&lt;li&gt;intégration&lt;/li&gt;
&lt;li&gt;recette&lt;/li&gt;
&lt;li&gt;pré-production&lt;/li&gt;
&lt;li&gt;production&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;    &lt;p&gt;Lorsqu'on nous développons nos applications PHP, nous sommes très souvent confrontés à plusieurs environnements d'exécution&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;local (développement)&lt;/li&gt;
&lt;li&gt;tests (build continue, ...)&lt;/li&gt;
&lt;li&gt;intégration&lt;/li&gt;
&lt;li&gt;recette&lt;/li&gt;
&lt;li&gt;pré-production&lt;/li&gt;
&lt;li&gt;production&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bien souvent les paramétrages sont légèrement différents suivant les environnements notamment sur les points suivants&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;type de base de données (ex: sqlite en développement, oracle en production)&lt;/li&gt;
&lt;li&gt;serveur ou identifiants de connexion base de données (ex: root en développement, user restreint en production)&lt;/li&gt;
&lt;li&gt;niveau de log et d'erreurs (ex: debug en développement, info en production)&lt;/li&gt;
&lt;li&gt;système d'exploitation (ex: windows en développement, linux en production)&lt;/li&gt;
&lt;li&gt;emplacements de fichiers et de l'application&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Une chose est sûre, la logique de votre application doit, elle, être identique sur tous ces environnements (sauf cas particulier...).&lt;/p&gt;


&lt;h3&gt;Comment faire&amp;nbsp;?&lt;/h3&gt;


&lt;p&gt;Voici une méthode simple qui permet de gérer le multi-environnements&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;externaliser tous les paramétrages dépendants de l'environnement dans un fichier de configuration (ex: run.ini)&lt;/li&gt;
&lt;li&gt;lister tous les environnements dans ce fichier (ex: dev, tests, preprod, prod), le fichier contient donc toutes les &quot;versions&quot; de paramétrages sous forme de section&lt;/li&gt;
&lt;li&gt;réaliser un code d'initialisation de l'application qui se base sur une variable d'environnement (ex: PHP_ENV) pour détecter l'environnement sur lequel tourne l'application&lt;/li&gt;
&lt;li&gt;lire le fichier de configuration à l'initialisation et récupérer les paramétrages spécifiques à la section d'environnement courante (ex: prod)&lt;/li&gt;
&lt;li&gt;injecter ces paramétrages dans l'application.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Proposition d'implémentation avec Zend Framework&lt;/h3&gt;


&lt;p&gt;Voici comment implémenter cela avec le Zend Framework&amp;nbsp;:&lt;/p&gt;


&lt;p&gt;Fichier config/run.ini&lt;/p&gt;
&lt;pre&gt;
[prod]

database.adapter=pdo_mysql
database.params.host=dbserver
database.params.dbname=dbprod
database.params.username=dbprod
database.params.password=XXxxxxX

logger.level=info

[test]

database.adapter=pdo_sqlite
database.params.host=
database.params.dbname=:memory:
database.params.username=
database.params.password=

logger.level = debug

[dev]

database.adapter=pdo_mysql
database.params.host=localhost
database.params.dbname=dbdev
database.params.username=root
database.params.password=

logger.level = debug

&lt;/pre&gt;


&lt;p&gt;Fichier html/index.php (ou bien votre bootstrap, ou bien votre fichier commun à tous vos contextes)&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
// ...
env = $_SERVER['PHP_ENV'];
Zend_Registry::set('config', new Zend_Config_Ini(dirname(__FILE__).'/../config/run.ini', $env));
// ...
&lt;/pre&gt;


&lt;p&gt;Votre application peut alors utiliser la variable de registre Zend_Registry('config') pour récupérer toutes les informations concernant l'environnement dans lequel elle évolue (dev, test ou prod)
Il vous suffit maintenant de définir la variable système PHP_ENV au niveau des serveurs de test, de production et en local. Typiquement vous pouvez soit positionner une variable d'environnement au niveau système, soit créer une variable d'environnement au niveau serveur web, sous Apache il suffit de faire&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
PassEnv PHP_ENV
&lt;/pre&gt;


&lt;p&gt;ou bien pour définir la variable uniquement à l'intérieur du serveur web&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
SetEnv PHP_ENV prod
&lt;/pre&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2008/10/29/1-application-Plusieurs-environnements#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2008/10/29/1-application-Plusieurs-environnements#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/13</wfw:commentRss>
      </item>
    
  <item>
    <title>Tests unitaires et Exception : attention aux try/catch !</title>
    <link>http://blog.phppro.fr/?post/2008/10/24/Tests-unitaires-et-Exception-%3A-attention-aux-try/catch</link>
    <guid isPermaLink="false">urn:md5:cfa26b2d885c342317cee666d073cd68</guid>
    <pubDate>Mon, 27 Oct 2008 09:00:00 +0000</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Trucs et astuces</category>
        <category>exception</category><category>phpunit</category><category>tests</category>    
    <description>&lt;p&gt;Les tests unitaires, pour ceux qui les utilisent, sont bien pratiques pour tester notre code. Malheureusement ils peuvent introduire, si ils sont rédigés de façon maladroite des problèmes qui peuvent être compliqués à comprendre a posteriori.
Imaginons le code suivant (volontairement) mal codé&amp;nbsp;: ...&lt;/p&gt;    &lt;p&gt;Les tests unitaires, pour ceux qui les utilisent, sont bien pratiques pour tester notre code. Malheureusement ils peuvent introduire, si ils sont rédigés de façon maladroite des problèmes qui peuvent être compliqués à comprendre a posteriori.
Imaginons le code suivant (volontairement) mal codé&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
class UserService {
    /**
      * Checks user access rights
      *
      * @param User $user the user
      *
      * @return null
      * @throws Exception if access is denied for the specified user
      */
    public function checkAccess($user) {
        // ... (100 lignes)
        if($user-&amp;gt;role==='standard') {
            throw new Exception(&amp;quot;Access denied&amp;quot;);
        }
        if($user-&amp;gt;role==='basic-admin') {
            throw new Exception(&amp;quot;Access denied&amp;quot;);
        }
        if($user-&amp;gt;role==='editor') {
            throw new Exception(&amp;quot;Access denied&amp;quot;);
        }
        // ... (100 lignes)
    }
}
&lt;/pre&gt;


&lt;p&gt;Imaginons que nous n'avions pas écrit ce code nous même, et qu'il soit assez volumineux et complexe (plus de 300 lignes).
Nous désirons rajouter des tests unitaires sur cette méthode, nous réalisons le premier test suivant&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
class UserServiceTest extends PHPUnit_Framework_TestCase {
    public function testGetUserForIdNullThrowException() {
        $svc = new UserService();
        try {
            $svc-&amp;gt;checkUser(null);
            $this-&amp;gt;fail(&amp;quot;No exception thrown&amp;quot;);
        } catch(Exception $e) {
        }
    }
}
&lt;/pre&gt;


&lt;p&gt;Nous exécutons le test pour voir le résultat et ... le test est vert (succès). Confiant dans notre code, nous pensons que ce cas ($user null) est traité.
Grosse erreur (je vous le dis, cela n'est pas traité dans le code, mais le test est quand même vert)&amp;nbsp;!&lt;/p&gt;


&lt;p&gt;Nous sommes en effet devant un énorme effet de bord du au fonctionnement du framework PHPUnit...&lt;/p&gt;


&lt;p&gt;En effet, lorsque nous appelons checkUser(null), le code de checkUser ne vérifie pas la nullité du paramètre $user, il fait les différents if($user-&amp;gt;role === ...), (avec $user == null), mais comme vous le savez PHP est permissif et ne va donc pas planter, les if seronts juste faux et aucune exception ne sera levée par notre code applicatif. la ligne en gras suivante:&lt;/p&gt;

&lt;pre&gt;
        try {
__            $svc-&amp;gt;checkUser(null);__
            $this-&amp;gt;fail(&amp;quot;No exception thrown&amp;quot;);
        } catch(Exception $e) {
        }
&lt;/pre&gt;


&lt;p&gt;ne va donc pas lever d'exceptions (comme attendu), et la ligne suivante dans le tests unitaires va être exécutée&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
        try {
            $svc-&amp;gt;checkUser(null);
__            $this-&amp;gt;fail(&amp;quot;No exception thrown&amp;quot;);__
        } catch(Exception $e) {
        }
&lt;/pre&gt;


&lt;p&gt;Cette ligne (le fail), va en réalité lever une exception de type Exception (c'est le fonctionnement standard de PHPUnit). Or, comme nous avons fait un try/catch par dessus, cette exception va être attrapé et le bloc catch va être exécuté, comme il est vide, aucune assertion fausse ne sera déclenchée et la fin du test sera atteinte sans aucune erreur, le test sera donc&amp;nbsp;: vert (succès)&amp;nbsp;!&lt;/p&gt;


&lt;p&gt;Le test sera donc en succès alors que notre code ne lève pas d'exception quand le $user est null&amp;nbsp;!&lt;/p&gt;


&lt;p&gt;Je vous l'accorde, il est recommandé de mettre des assertions dans toutes les branches d'un test, nous aurions pu alors écrire&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
        try {
            $svc-&amp;gt;checkUser(null);
            $this-&amp;gt;fail(&amp;quot;No exception thrown&amp;quot;);
        } catch(Exception $e) {
            $this-&amp;gt;assertTrue(true);
        }
&lt;/pre&gt;


&lt;p&gt;Le problème aurait été le même et nous serions passé dans le catch, l'assertions triviale aurait été vraie et le test sera toujours vert&amp;nbsp;!&lt;/p&gt;


&lt;p&gt;Nous aurions pu voir apparaître le problème si nous avions fait une assertions légèrement différente dans le catch&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
        try {
            $svc-&amp;gt;checkUser(null);
            $this-&amp;gt;fail(&amp;quot;No exception thrown&amp;quot;);
        } catch(Exception $e) {
            $this-&amp;gt;assertEquals(&amp;quot;Access denied&amp;quot;,$e-&amp;gt;getMessage());
        }
&lt;/pre&gt;


&lt;p&gt;Le test aurait été rouge (cool !) mais le message d'échec aurait été le suivant&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
Failed asserting that &amp;lt;string:No exception thrown&amp;gt; is equal to &amp;lt;string:Access denied&amp;gt;
&lt;/pre&gt;


&lt;h3&gt;Quézako&amp;nbsp;?&lt;/h3&gt;


&lt;p&gt;En fait, dans la branche catch(), $e contient l'exception levée par le fail() (car notre code ne lève pas d'exception dans ce cas puisqu'il est buggé) et PHPUnit tente donc de faire une assertion entre la chaîne &quot;Access denied&quot; écrite en dur dans le test, et le message de l'exception qui est en fait &quot;No exception thrown&quot;&amp;nbsp;!&lt;/p&gt;


&lt;h3&gt;Une méthode plus sûre&lt;/h3&gt;


&lt;p&gt;Mais alors qu'elle est la bonne méthode pour écrire un test qui aurait mis en exergue le bug de notre méthode checkAccess()&amp;nbsp;?&lt;/p&gt;


&lt;p&gt;Le problème réel vient du fait que nous réalisons un catch sur le même type d'exception que celui levé par le framework PHPUnit (Exception), il suffit juste de réaliser le catch sur un type d'exception plus spécialisé, par exemple NullPointerException (classe à créer) comme ceci&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
        try {
            $svc-&amp;gt;checkUser(null);
            $this-&amp;gt;fail(&amp;quot;No exception thrown&amp;quot;);
        } catch(NullPointerException $e) {
            $this-&amp;gt;assertEquals(&amp;quot;Access denied&amp;quot;,$e-&amp;gt;getMessage());
        }
&lt;/pre&gt;


&lt;p&gt;Lorsque nous exécuterions le test, il échouerait, et le message d'erreur serait&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
No exception thrown
&lt;/pre&gt;


&lt;p&gt;Gagné&amp;nbsp;! Nous aurions vu que notre code applicatif était buggé et qu'il ne traite pas le cas ou le user est nul.
Cela impose cependant que l'exception levée par notre méthode checkAccess() dans le cas d'un user null soit par exemple du type NullPointerException (et non pas Exception).&lt;/p&gt;


&lt;p&gt;Récemment, PHPUnit a ajouté le support des &quot;annotations&quot; dans le commentaire de la méthode de test pour indiquer que le test doit lever une exception, nous aurions pu utiliser le test suivant&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
class UserServiceTest extends PHPUnit_Framework_TestCase {
    /**
      * @expectedException NullPointerException
      */
    public function testGetUserForIdNullThrowException() {
        $svc = new UserService();
        $svc-&amp;gt;checkUser(null);
    }
}
&lt;/pre&gt;


&lt;p&gt;il est bien plus concis&amp;nbsp;!&lt;/p&gt;


&lt;h3&gt;Conclusion&amp;nbsp;:&lt;/h3&gt;


&lt;p&gt;Spécialisez toujours vos exceptions dans votre code, n'utilisez jamais des exceptions de type Exception si vous voulez réaliser des tests unitaires.&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2008/10/24/Tests-unitaires-et-Exception-%3A-attention-aux-try/catch#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2008/10/24/Tests-unitaires-et-Exception-%3A-attention-aux-try/catch#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/12</wfw:commentRss>
      </item>
    
  <item>
    <title>1 application = de multiples contextes d'exécution</title>
    <link>http://blog.phppro.fr/?post/2008/10/23/1-application-de-multiples-contextes-d-execution</link>
    <guid isPermaLink="false">urn:md5:250fa5b7f095da30928c36c130d4a293</guid>
    <pubDate>Thu, 23 Oct 2008 21:28:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Architecture</category>
        <category>zend framework</category>    
    <description>&lt;p&gt;Votre application PHP (script, pages, sites, application complexe) est avant tout développée pour être exécutée dans un contexte unique&amp;nbsp;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;soit par serveur web&lt;/li&gt;
&lt;li&gt;soit en ligne de commande&lt;/li&gt;
&lt;li&gt;soit sous forme d'application graphique&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;(dans la majeure partie des cas)
...&lt;/p&gt;    &lt;p&gt;Votre application PHP (script, pages, sites, application complexe) est avant tout développée pour être exécutée dans un contexte unique&amp;nbsp;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;soit par serveur web&lt;/li&gt;
&lt;li&gt;soit en ligne de commande&lt;/li&gt;
&lt;li&gt;soit sous forme d'application graphique&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;(dans la majeure partie des cas)&lt;/p&gt;


&lt;p&gt;Votre application est souvent &quot;optimisée&quot; pour son contexte d'exécution principal et même très souvent ne peut fonctionner que dans ce contexte (est-il possible de faire tourner votre site internet en ligne de commande&amp;nbsp;? d'accéder à des fonctionnalités de votre application graphique depuis un serveur web&amp;nbsp;? ...)&lt;/p&gt;


&lt;p&gt;C'est normal.&lt;/p&gt;


&lt;p&gt;Imaginons que vous souhaitiez réaliser des tests sur votre application (par exemple unitaire avec PHPUnit), pourrez vous tester unitairement une fonctionnalité qui a besoin d'une connexion à une base de données ou de lire dans un fichier de config, depuis la ligne de commande (exécutable PHPUnit ou bien même batch Phing)&amp;nbsp;?&lt;/p&gt;


&lt;p&gt;Ou encore, si vous développez une fonctionnalité &quot;non graphiques&quot;, comme par exemple la gestion des utilisateurs, l'appel d'un webservices distant, le stockage d'information en base de données, l'envoi de sms ou de mail, ... il peut être intéressant de pouvoir réutiliser vos développements (ou en tout cas une partie) dans un script de migration ou de mise à jour en ligne de commande, même si initiallement le développement a été réalisé pour être exposé sur le web.&lt;/p&gt;


&lt;p&gt;Pour répondre à ce besoin &quot;multi-contextes&quot;, voici ma méthode.&lt;/p&gt;


&lt;p&gt;&lt;a href=&quot;http://blog.phppro.fr/public/contexts.jpg&quot;&gt;&lt;img src=&quot;http://blog.phppro.fr/public/contexts.jpg&quot; alt=&quot;Différents contextes&quot; title=&quot;Différents contextes, oct 2008&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Créer un répertoire &quot;contexts/&quot; à la racine de votre projet&lt;/li&gt;
&lt;li&gt;Ajouter les différents contextes nécessaires à votre application&amp;nbsp;:
&lt;ul&gt;
&lt;li&gt;web.php =&amp;gt; le fichier inclu par votre index.php ou votre page.php&lt;/li&gt;
&lt;li&gt;unit-tests.php =&amp;gt; le fichier inclu systématiquement dans vos classes de tests unitaires&lt;/li&gt;
&lt;li&gt;functional-tests.php =&amp;gt; le fichier inclu systémtatiquement dans vos fixtures greenpeper-php ou fitnesse-php&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;li&gt;@common.php =&amp;gt; le fichier central inclu par tous les autres, il est censé contenir les functions communes et les inclusions de vos développements&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Concrètement ces fichiers (à part @common.php) contiennent en règle générale quelques lignes seulement. Voici quelques exemples&amp;nbsp;:&lt;/p&gt;


&lt;p&gt;Exemple: web.php&lt;/p&gt;

&lt;pre&gt;
$env = $_SERVER['PHP_ENV'];
/**
 * Includes the test application functions.
 */
require dirname(__FILE__) . '/@common.php';
/**
 * Setup controller
 */
$controller = Zend_Controller_Front::getInstance();
$controller-&amp;gt;addModuleDirectory('../application/default');
$controller-&amp;gt;addControllerDirectory('../application/default/controllers');
$controller-&amp;gt;setControllerDirectory('../application/admin/controllers');
$controller-&amp;gt;throwExceptions(false); // should be turned on in development time
/**
 * Bootstrap Layouts
 */
Zend_Layout::startMvc(array(
    'layoutPath' =&amp;gt; '../application/default/layouts',
    'layout' =&amp;gt; 'main'
    ));
/**
 * Runs
 */
$controller-&amp;gt;dispatch();
&lt;/pre&gt;


&lt;p&gt;Exemple: unit-tests.php&lt;/p&gt;

&lt;pre&gt;
$env = 'tests';

require dirname(__FILE__) . '/@common.php';

$svc = new DatabaseService();

$dbFile = dirname(dirname(__FILE__)).'/tmp/db/test.sqlite';
if(is_file($dbFile)) {
    @unlink($dbFile);
}
$dsn = 'sqlite://'.$dbFile;
$svc-&amp;gt;create($dsn,dirname(__FILE__) .&amp;quot;/../config/db/{$env}/schema.xml&amp;quot;);
$svc-&amp;gt;update($dsn,dirname(__FILE__) .&amp;quot;/../config/db/{$env}/all.xml&amp;quot;);
&lt;/pre&gt;


&lt;p&gt;Exemple: @common.php&lt;/p&gt;

&lt;pre&gt;
require 'Zend/Loader.php';
require dirname(__FILE__) . '/../application/functions.php';

/**
 * Registers autoload
 */
Zend_Loader::registerAutoload();

/**
 * Sets the root directory of the application
 */
$root = dirname(dirname(__FILE__));

/**
 * Sets the global include path of the application
 */
set_include_path(// the list of include paths
    $root                                   . PATH_SEPARATOR .
    $root.&amp;quot;/application/default/controllers&amp;quot;. PATH_SEPARATOR .
    $root.&amp;quot;/application/default/models&amp;quot;     . PATH_SEPARATOR .
    ...
    get_include_path());

/**
 * Loads the main configuration from the specific environment section of the
 * configuration file.
 */
if(!$env) {
     die('You must set $env variable.');
}
$cnf = new Zend_Config_Ini(dirname(__FILE__).'/../config/run.ini', $env);
Zend_Registry::set('_cnf', $cnf);

// ...

/**
 * Sets the database connection configuration
 */
if (@$cnf-&amp;gt;database!==null) {
    Zend_Db_Table_Abstract::setDefaultAdapter(Zend_Db::factory($cnf-&amp;gt;database));
}
&lt;/pre&gt;


&lt;p&gt;Comment utiliser ces &quot;contextes&quot;&amp;nbsp;?&lt;/p&gt;


&lt;p&gt;Exemple d'utilisation du contexte web dans un fichier html/index.php&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php
require_once dirname(__FILE__).'/../contexts/web.php';
&lt;/pre&gt;


&lt;p&gt;Exemple d'utilisation du contexte unit-tests dans une classe de tests PHPUnit dans tests/MyServiceTest.php&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php
require_once dirname(__FILE__).'/../contexts/unit-tests.php';

// no requires anymore since we use autoloading

class CityServiceTest extends PHPUnit_Framework_TestCase {

    /**
      * @expectedException NullPointerException
      */
    public function testGetCityNameFromZipCodeForNullZipCodeThrowException() {
         $svc = new CityService();
         $svc-&amp;gt;getCityNameFromZipCode(null);
    }

}
&lt;/pre&gt;


&lt;p&gt;Exemple d'utilisation du contexte cli dans un script en ligne de commande scripts/migrate-data.php&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php

require_once dirname(__FILE__).'/../contexts/cli.php';

// no requires anymore since we use autoloading

$svc = new MigrationService();

try {
    $result = $svc-&amp;gt;migrateFromTo($from,$to);
} catch(Exception $e) {
    die(&amp;quot;Error occured when data migration : &amp;quot;.$e-&amp;gt;getMessage());
}

echo &amp;quot;Migration completed&amp;quot;;
&lt;/pre&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2008/10/23/1-application-de-multiples-contextes-d-execution#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2008/10/23/1-application-de-multiples-contextes-d-execution#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/11</wfw:commentRss>
      </item>
    
  <item>
    <title>Le pattern Adapter : Quezako par l'exemple</title>
    <link>http://blog.phppro.fr/?post/2008/10/20/Le-pattern-Adapter-Quezako-par-l-exemple</link>
    <guid isPermaLink="false">urn:md5:6a3a2e6722e15f9ab4c9c3b309cc199d</guid>
    <pubDate>Mon, 20 Oct 2008 21:15:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Architecture</category>
        <category>adapter</category><category>pattern</category>    
    <description>&lt;p&gt;Imaginons une classe qui est censé fournir une liste de villes et de départements qui seront utilisés dans une balise HTML select pour permettre à un internaute de sélectionner sa ville de naissance à partir d'une liste.&lt;/p&gt;


&lt;p&gt;Nous commençons simplement par récupérer la liste des villes disponibles dans un fichier texte CSV ayant la structure suivante&amp;nbsp;: ...&lt;/p&gt;    &lt;p&gt;Imaginons une classe qui est censé fournir une liste de villes et de départements qui seront utilisés dans une balise HTML select pour permettre à un internaute de sélectionner sa ville de naissance à partir d'une liste.&lt;/p&gt;


&lt;p&gt;Nous commençons simplement par récupérer la liste des villes disponibles dans un fichier texte CSV ayant la structure suivante&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;

code_ville;nom_ville
PAR;Paris
NYK;New York
SVL;Seville
BXL;Bruxelles
...
&lt;/pre&gt;


&lt;p&gt;Puis nous récupérons la liste des villes comme suit en PHP&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
class VilleService {
    public function getVilles() {
        $handle = fopen(&amp;quot;villes.csv&amp;quot;, &amp;quot;r&amp;quot;);
        while (($data = fgetcsv($handle, 1000, &amp;quot;,&amp;quot;)) !== FALSE) {
           $villes[$data[0]] = $data[1];
        }
        fclose($handle);
        return $villes;
    }
}

$service = new VilleService;
$villes = $service-&amp;gt;getVilles();

// $villes contient la liste des villes comme suit :
// $villes = array('PAR'=&amp;gt;'Paris', 'NYK'=&amp;gt;'New York','SVL'=&amp;gt;'Seville','BXL'=&amp;gt;'Bruxelles');

&lt;/pre&gt;


&lt;p&gt;Notre script est alors fonctionnel et nous pouvons utiliser la variable $villes pour construire notre balise HTML select (liste déroulante).
Notre application évolue et nous voulons utiliser une base de données pour stocker la liste des villes, ce qui nous donneras plus de souplesse pour mettre à jour cette liste, par exemple depuis un formulaire. Nous devons alors recoder la récupération de la liste de villes depuis zéro depuis la base de données, notre script est impacté.&lt;/p&gt;


&lt;p&gt;Imaginons que nous avions eu le code suivant&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$service = new VilleService;
$adapter = new CsvAdapter;
$adapter-&amp;gt;setFile('villes.csv');
$service-&amp;gt;setAdapter($adapter);
$villes = $service-&amp;gt;getVilles();
&lt;/pre&gt;


&lt;p&gt;La classe CsvAdapter correspond à une implémentation de la récupération des villes depuis un fichier CSV. Voici le contenu des classes&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
class VilleService {
    protected $adapter;
    public function setAdapter(AdapterInterface $adapter) {
        $this-&amp;gt;adapter = $adapter;
    }
    public function getVilles() {
        return $this-&amp;gt;adapter-&amp;gt;listRows();
    }
}

interface AdapterInterface {
    public function listRows();
}

class CsvAdapter implements AdapterInterface {
    public function listRows() {
        $handle = fopen(&amp;quot;villes.csv&amp;quot;, &amp;quot;r&amp;quot;);
        while (($data = fgetcsv($handle, 1000, &amp;quot;,&amp;quot;)) !== FALSE) {
            $rows[$data[0]] = $data[1];
        }
        fclose($handle);
        return $rows;
    }
}
&lt;/pre&gt;


&lt;p&gt;A quoi cela nous aurait-il servit&amp;nbsp;? Revenons à notre besoin d'évolution&amp;nbsp;: nous voulions faire évoluer notre script pour récupérer les données en base de données et non plus à partir d'un fichier. En utilisant les concepts de service et d'adapter, nous pourrions créer un nouvel adapter, DatabaseAdapter, qui implémenterait l'interface AdapterInterface et fournirait donc la méthode listRows() comme suit&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
class DatabaseAdapter implements AdapterInterface {
    protected $dsn;
    protected $user;
    protected $password;
    public function setConnectionParameters($dsn,$user,$password) {
        $this-&amp;gt;dsn = $dsn;
        $this-&amp;gt;user = $user;
        $this-&amp;gt;password = $password;
    }
    public function listRows() {
        try {
            $dbh = new PDO($this-&amp;gt;dsn, $this-&amp;gt;user, $this-&amp;gt;password);
            foreach($dbh-&amp;gt;query('SELECT * from villes') as $row) {
                $rows[$row[0]] = $row[1];
            }
            $dbh = null;
        } catch (PDOException $e) {
            $rows = array();
        }
        return $rows;
    }
}
&lt;/pre&gt;


&lt;p&gt;Nous pourrions ainsi utiliser notre nouvel adapter dans notre script comme suit&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$service = new VilleService;
$adapter = new DatabaseAdapter;
$adapter-&amp;gt;setConnectionParameters('mysql:host=localhost;dbname=mydb','myuser','mypassword');
$service-&amp;gt;setAdapter($adapter);
$villes = $service-&amp;gt;getVilles();
&lt;/pre&gt;


&lt;p&gt;Soit 2 lignes de changées dans notre script (c'est peu !).&lt;/p&gt;


&lt;p&gt;Nous pourrions faire encore évoluer par la suite notre script pour récupérer la liste des villes depuis un webservice SOAP, simplement en ajoutant un adapter, par exemple SoapAdapter&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
$service = new VilleService;
$adapter = new SoapAdapter;
$service-&amp;gt;setAdapter($adapter);
$villes = $service-&amp;gt;getVilles();
&lt;/pre&gt;


&lt;p&gt;Notre classe SoapAdapter devrait implémenter AdapterInterface et notamment la méthode listRows()&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
class SoapAdapter implements AdapterInterface {
    ...
    public function listRows() {

        $soapClient = new SoapClient(...);
        ...
        $villesList = $soapClient-&amp;gt;getVilles();
        ...
        return $viles;
    }
}
&lt;/pre&gt;


&lt;p&gt;Le pattern Adapter vous permet d'externaliser des opérations (récupération d'une liste de villes par exemple) dans une classe d'implémentation spécifique (récupération depuis un fichier csv, récupération depuis une base de données...) vous permettant ainsi de changer l'implémentation (passer d'un fichier à une base) sans impacter votre script (vous créez un nouvel adapter, le code d'appel reste le même).&lt;/p&gt;


&lt;p&gt;Nous verrons plus tard (autre article) que le concept d'Adapter est souvent utilisé dans les tests unitaires en conjonction avec le pattern Mock (fausse implémentation) pour réaliser des tests sans déclencher certains opérations couteuses (par exemple tester la logique de construction d'un email sans envoyer réellement l'email).&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2008/10/20/Le-pattern-Adapter-Quezako-par-l-exemple#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2008/10/20/Le-pattern-Adapter-Quezako-par-l-exemple#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/9</wfw:commentRss>
      </item>
    
  <item>
    <title>Refactoring : ma démarche</title>
    <link>http://blog.phppro.fr/?post/2008/10/16/Refactoring-%3A-ma-demarche</link>
    <guid isPermaLink="false">urn:md5:e632b376503033d62ffafe0b2fe31e8d</guid>
    <pubDate>Thu, 16 Oct 2008 00:20:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Méthodologie</category>
        <category>refactoring</category>    
    <description>&lt;p&gt;Lorsque je dois étendre une fonctionnalité de mon application (existante au travers d'une méthode), voici ma démarche&amp;nbsp;: ...&lt;/p&gt;    &lt;p&gt;Lorsque je dois étendre une fonctionnalité de mon application (existante au travers d'une méthode), voici ma démarche&amp;nbsp;:
&lt;a href=&quot;http://blog.phppro.fr/public/process-refactoring.jpg&quot;&gt;&lt;img src=&quot;http://blog.phppro.fr/public/./.process-refactoring_m.jpg&quot; alt=&quot;Process de refactoring&quot; title=&quot;Process de refactoring, oct 2008&quot; /&gt;&lt;/a&gt;&lt;/p&gt;


&lt;p&gt;Je commence par identifier dans la méthode existante les macro-blocs, c'est à dire les bloc de code &quot;atomique&quot; et autonome, en règle générale il y en a entre 3 et 6.
Je commence par mettre une ligne de commentaire au dessus de chaque ligne de début de macro-bloc.
Puis j'externalise chaque macro-bloc dans une méthode à part (par exemple une méthode protected, voire public si je fais des tests unitaires sur cette méthode uniquement)
Je me retrouve donc avec un nouveau corps de méthode initiale qui fait quelques lignes (3 à 6).
Je créé alors une nouvelle méthode censée répondre à mon évolution à côté de la méthode existante, en essayant de réutiliser au maximum les méthodes sous-jacentes que j'ai fait apparaître (les macro-blocs).
Ma nouvelle fonctionnalité impose certainement la création d'un nouveau macro bloc que j'externalise dans une méthode à part.&lt;/p&gt;


&lt;p&gt;Je me retrouve donc à avoir le schéma suivant&amp;nbsp;:&lt;/p&gt;


&lt;p&gt;&lt;a href=&quot;http://blog.phppro.fr/public/methode-refactoring.jpg&quot;&gt;&lt;img src=&quot;http://blog.phppro.fr/public/./.methode-refactoring_m.jpg&quot; alt=&quot;Methode refactoring&quot; title=&quot;Methode refactoring, oct 2008&quot; /&gt;&lt;/a&gt;&lt;/p&gt;


&lt;p&gt;Je constitue ainsi une architecture de code &quot;zoomable&quot; en partant des méthodes &quot;haut niveau&quot; qui ne contiennent que des appels vers les méthodes sous-jacentes bas niveau. Cela me permet aussi de réaliser des tests unitaires sur les méthodes bas niveau et de tester mon code plus facilement. Accessoirement mes méthodes font rarement plus de 80 lignes car elles sont découpées.&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2008/10/16/Refactoring-%3A-ma-demarche#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2008/10/16/Refactoring-%3A-ma-demarche#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/10</wfw:commentRss>
      </item>
    
  <item>
    <title>Une arborescence projet typique avec Zend Framework</title>
    <link>http://blog.phppro.fr/?post/2008/10/11/Une-arborescence-projet-typique-avec-Zend-Framework</link>
    <guid isPermaLink="false">urn:md5:6cf0580632d9a669496cd828e368c07c</guid>
    <pubDate>Sat, 11 Oct 2008 13:54:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Méthodologie</category>
        <category>zend framework</category>    
    <description>&lt;p&gt;Voici une proposition d'arborescence de projet zend framework typique...&lt;/p&gt;    &lt;p&gt;&lt;img src=&quot;http://blog.phppro.fr/public/my-zf-project.jpg&quot; alt=&quot;Exemple d&amp;#039;arborescence de projet Zend Framework&quot; style=&quot;display:block; margin:0 auto;&quot; title=&quot;Exemple d&amp;#039;arborescence de projet Zend Framework, oct 2008&quot; /&gt;&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2008/10/11/Une-arborescence-projet-typique-avec-Zend-Framework#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2008/10/11/Une-arborescence-projet-typique-avec-Zend-Framework#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/8</wfw:commentRss>
      </item>
    
  <item>
    <title>Code HTML/JS simplifié (sans framework) pour faire de l'ajax</title>
    <link>http://blog.phppro.fr/?post/2008/10/10/Code-HTML/JS-simplifie-sans-framework-pour-faire-de-l-ajax</link>
    <guid isPermaLink="false">urn:md5:c1e08d9ec7cd89d746a872ad727f27a3</guid>
    <pubDate>Fri, 10 Oct 2008 15:42:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Outillage</category>
        <category>ajax</category><category>javascript</category>    
    <description>&lt;p&gt;Pour des besoins internes, j'ai spiké un code html/js de quelques lignes pour faire des requêtes Ajax (POST/PUT/DELETE/GET/HEAD) en voyant par exemple du contenu xml (REST ;) )&lt;/p&gt;


&lt;p&gt;Je l'ai mis en ligne, vous pouvez récupérer à tout moment le code source (code source html) à l'adresse&amp;nbsp;: ...&lt;/p&gt;    &lt;p&gt;Pour des besoins internes, j'ai spiké un code html/js de quelques lignes pour faire des requêtes Ajax (POST/PUT/DELETE/GET/HEAD) en voyant par exemple du contenu xml (REST ;) )&lt;/p&gt;


&lt;p&gt;Je l'ai mis en ligne, vous pouvez récupérer à tout moment le code source (code source html) à l'adresse&amp;nbsp;:&lt;/p&gt;


&lt;p&gt;&lt;a href=&quot;http://octo.warkodev.net/ajax/&quot;&gt;http://octo.warkodev.net/ajax/&lt;/a&gt;&lt;/p&gt;


&lt;p&gt;C'est bâteau, mais ca arrive qu'on ait besoin d'un morceaux de code de ce type rapidement sous la main.&lt;/p&gt;


&lt;p&gt;Hope this helps (même si ca remplace pas prototype, extjs et autres jquery ;) )&lt;/p&gt;


&lt;p&gt;PS: testé et fonctionnel sur FF3, IE7, GChrome&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2008/10/10/Code-HTML/JS-simplifie-sans-framework-pour-faire-de-l-ajax#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2008/10/10/Code-HTML/JS-simplifie-sans-framework-pour-faire-de-l-ajax#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/3</wfw:commentRss>
      </item>
    
  <item>
    <title>Lille : Rencontre AFUP sur PHP 5.3 le 20/10</title>
    <link>http://blog.phppro.fr/?post/2008/10/10/Lille-%3A-Rencontre-AFUP-sur-PHP-53-le-20/10</link>
    <guid isPermaLink="false">urn:md5:aebb224f03a5ffb96c70396c5b473b0a</guid>
    <pubDate>Thu, 09 Oct 2008 15:54:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Evènements</category>
        <category>afup</category>    
    <description>&lt;p&gt;L'AFUP organise une rencontre pour présenter les nouveautés de PHP 5.3 le 20 octobre prochain à Lille (19h30 - 21h30)&lt;/p&gt;


&lt;p&gt;Detail ci dessous&amp;nbsp;: ...&lt;/p&gt;    &lt;p&gt;L'AFUP organise une rencontre pour présenter les nouveautés de PHP 5.3 le 20 octobre prochain à Lille (19h30 - 21h30)&lt;/p&gt;


&lt;p&gt;Detail ci dessous&amp;nbsp;:
http://www.afup.org/article.php3?id_article=366&lt;/p&gt;


&lt;p&gt;Je ne pourrais malheureusement pas y être...&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2008/10/10/Lille-%3A-Rencontre-AFUP-sur-PHP-53-le-20/10#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2008/10/10/Lille-%3A-Rencontre-AFUP-sur-PHP-53-le-20/10#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/6</wfw:commentRss>
      </item>
    
  <item>
    <title>Scrinch : Outil de gestion de projet agile</title>
    <link>http://blog.phppro.fr/?post/2008/10/10/Scrinch-%3A-Outil-de-gestion-de-projet-agile</link>
    <guid isPermaLink="false">urn:md5:c89964e084a481ad4c335b2851e275cb</guid>
    <pubDate>Wed, 08 Oct 2008 15:46:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Outillage</category>
        <category>agile</category><category>equipe</category><category>projet</category>    
    <description>&lt;p&gt;Bonjour à tous amis Agilistes,&lt;/p&gt;


&lt;p&gt;Que pensez-vous de cette outils pour gérer les projets Agile&amp;nbsp;: ...&lt;/p&gt;    &lt;p&gt;Bonjour à tous amis Agilistes,&lt;/p&gt;


&lt;p&gt;Que pensez-vous de cet outil pour gérer les projets Agile&amp;nbsp;:&lt;/p&gt;


&lt;p&gt;&lt;a href=&quot;http://social.hortis.ch/2008/09/25/16-octobre-08-mardi-gras-sur-scrinch-outil-de-gestion-de-projet-agile/&quot;&gt;http://social.hortis.ch/2008/09/25/16-octobre-08-mardi-gras-sur-scrinch-outil-de-gestion-de-projet-agile/&lt;/a&gt;&lt;/p&gt;


&lt;p&gt;Ca ne remplace pas nos bons vieux post-it sur les murs mais cela peut-il avoir une pertinence à vos yeux pour le Product Owner et pour l'équipe.&lt;/p&gt;


&lt;p&gt;A vos plumes&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2008/10/10/Scrinch-%3A-Outil-de-gestion-de-projet-agile#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2008/10/10/Scrinch-%3A-Outil-de-gestion-de-projet-agile#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/4</wfw:commentRss>
      </item>
    
  <item>
    <title>PHP, Oracle et  Zend Framework</title>
    <link>http://blog.phppro.fr/?post/2008/10/10/PHP-Oracle-et-Zend-Framework</link>
    <guid isPermaLink="false">urn:md5:ba524c51c04c4684e16c32e895131159</guid>
    <pubDate>Fri, 03 Oct 2008 15:49:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Trucs et astuces</category>
        <category>base de données</category><category>zend framework</category>    
    <description>&lt;p&gt;Après avoir potassé un peu la doc sur Oracle et les character set, voici un script et surtout la documentation d'explication de certaines choses à avoir à l'esprit quand on travaille avec Oracle, PHP et Zend Framework...&lt;/p&gt;    &lt;p&gt;Après avoir potassé un peu la doc sur Oracle et les character set, voici un script et surtout la documentation d'explication de certaines choses à avoir à l'esprit quand on travaille avec Oracle, PHP et Zend Framework.&lt;/p&gt;
&lt;pre&gt;
&amp;lt;?php

/**
 * Connecting to Oracle database
 *
 * @author Olivier Hoareau &amp;lt;olivier@phppro.fr&amp;gt;
 * @license New BSD License
 *
 * The NLS_LANG variable is used to specify the character set of the
 * client or server.
 *
 * The NLS_LANG must reflect the charset of the operating system.
 *
 * [Server]
 *
 * To check Oracle Database Server NLS values:
 *
 * NLS_LANGUAGE      =&amp;gt; SELECT NLS_LANGUAGE      FROM NLS_DATABASE_PARAMETERS;
 * NLS_TERRITORY     =&amp;gt; SELECT NLS_TERRITORY     FROM NLS_DATABASE_PARAMETERS;
 * NLS_CHARACTERSETS =&amp;gt; SELECT NLS_CHARACTERSETS FROM NLS_DATABASE_PARAMETERS;
 *
 * The NLS_LANG value is &amp;lt;NLS_LANGUAGE&amp;gt;_&amp;lt;NLS_TERRITORY&amp;gt;.&amp;lt;NLS_CHARACTERSETS&amp;gt;
 *
 * Example: NLS_LANG = AMERICAN_AMERICA.WE8ISO8859P15
 *
 *
 * [Session or Client]
 *
 * To check Oracle Database Client NLS values:
 *
 * NLS_LANGUAGE      =&amp;gt; SELECT NLS_LANGUAGE      FROM NLS_SESSION_PARAMETERS;
 * NLS_TERRITORY     =&amp;gt; SELECT NLS_TERRITORY     FROM NLS_SESSION_PARAMETERS;
 * NLS_CHARACTERSETS =&amp;gt; SELECT NLS_CHARACTERSETS FROM NLS_SESSION_PARAMETERS;
 *
 * The NLS_LANG value is &amp;lt;NLS_LANGUAGE&amp;gt;_&amp;lt;NLS_TERRITORY&amp;gt;.&amp;lt;NLS_CHARACTERSETS&amp;gt;
 *
 * You can set the NLS_LANG variable on windows using the registry key:
 *  =&amp;gt; for Oracle XE : HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE\KEY_XE\NLS_LANG
 *
 * You can set the NLS_LANG variable on windows command line using:
 *  =&amp;gt; set NLS_LANG=&amp;lt;value&amp;gt;
 *
 * You can set the NLS_LANG variable on *nix command line using:
 *  =&amp;gt; export NLS_LANG=&amp;lt;value&amp;gt;
 *  =&amp;gt; export LANG=en_US (or fr_FR, ...)
 *
 * Tips #1: Do not forget to use Apache's PassEnv directives on environment vars.
 * Tips #2: Check your php.ini default_charset directive value
 *
 * Example: NLS_LANG = FRENCH_FRANCE.WE8MSWIN1252
 *
 * You can modify the NLS_LANG variable for the current session using:
 *  =&amp;gt; ALTER SESSION SET NLS_LANGUAGE='&amp;lt;value&amp;gt;';
 *  =&amp;gt; ALTER SESSION SET NLS_TERRITORY='&amp;lt;value&amp;gt;';
 *
 * @see http://fadace.developpez.com/oracle/nls/
 * @see http://www.oracle.com/technology/tech/globalization/htdocs/nls_lang%20faq.htm
 * @see http://download.oracle.com/docs/cd/B25329_01/doc/install.102/b25143/toc.htm#BABHJAID
 */

require 'Zend/Loader.php';
Zend_Loader::registerAutoload();

$params = array(
    'dbname'   =&amp;gt; 'XE',
    'username' =&amp;gt; 'TESTUSER',
    'password' =&amp;gt; 'TESTUSER',
    'host'     =&amp;gt; 'localhost',
    'port'     =&amp;gt; '1521',
    'options'  =&amp;gt; array(
        Zend_Db::AUTO_QUOTE_IDENTIFIERS =&amp;gt; false,
#       Zend_Db::CASE_FOLDING =&amp;gt; 2,
#       autoQuoteIdentifiers =&amp;gt; 0,
    ),
);


$sql = &amp;quot;SELECT * FROM MYTABLE&amp;quot;;

$db = Zend_Db::factory('Pdo_Oci',$params);

$r = $db-&amp;gt;query($sql);

print_r($r);

&lt;/pre&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2008/10/10/PHP-Oracle-et-Zend-Framework#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2008/10/10/PHP-Oracle-et-Zend-Framework#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/5</wfw:commentRss>
      </item>
    
  <item>
    <title>Forum PHP 2008 Paris</title>
    <link>http://blog.phppro.fr/?post/2008/10/10/Forum-PHP-2008-Paris</link>
    <guid isPermaLink="false">urn:md5:43f2df4a11767d8e30600c2576800bb3</guid>
    <pubDate>Wed, 24 Sep 2008 15:38:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Evènements</category>
        <category>afup</category>    
    <description>&lt;p&gt;Les 8 et 9 décembre prochains se tiendra à Paris le Forum PHP 2008 de l'&lt;a href=&quot;http://www.afup.org&quot;&gt;AFUP&lt;/a&gt; (Association Française des Utilisateurs de PHP).&lt;/p&gt;


&lt;p&gt;Les sujets intéressants à retenir&amp;nbsp;: ...&lt;/p&gt;    &lt;p&gt;Les 8 et 9 décembre prochains se tiendra à Paris le Forum PHP 2008 de l'&lt;a href=&quot;http://www.afup.org&quot;&gt;AFUP&lt;/a&gt; (Association Française des Utilisateurs de PHP).&lt;/p&gt;


&lt;p&gt;Vous trouverez la liste des sessions à cette adresse&amp;nbsp;:
&lt;a href=&quot;http://afup.org/pages/forumphp2008/sessions.php&quot;&gt;http://afup.org/pages/forumphp2008/sessions.php&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;Les sujets intéressants à retenir&amp;nbsp;:&lt;/p&gt;


&lt;h3&gt;Retours d'expériences&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;retour d'expérience 20minutes.fr (volumétrie, déploiement)&lt;/li&gt;
&lt;li&gt;retour d'expérience rue89.com (Drupal&amp;nbsp;: gestion de contenus)&lt;/li&gt;
&lt;li&gt;retour d'expérience BNP Paribas + Zend Technologies&lt;/li&gt;
&lt;li&gt;retour d'expérience Mozilla (sites des add-ons réalisées avec CakePHP)&lt;/li&gt;
&lt;li&gt;Retour d'expérience placedestendances.com (RIA Ajax)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Outillage et Bonnes Pratiques&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Javascript Best Pratices (jQuery)&lt;/li&gt;
&lt;li&gt;Best Pratices Performance Web (Yahoo)&lt;/li&gt;
&lt;li&gt;Rapports de Qualité et Outillage (Phing, rats, PHP_CodeSniffer, PHP Depend, PHP Beautifier, spikePHPSecAudit, spikephpcoverage...)&lt;/li&gt;
&lt;li&gt;IDE&amp;nbsp;: Le cas d'Eclipse + PDT&lt;/li&gt;
&lt;li&gt;Analyse statique et dynamique de code (~XDepend ?)&lt;/li&gt;
&lt;li&gt;Design Patterns et PHP 5 (Injection de dépendances et Observateur)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Autres Tech&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Oracle 11g DRCP (20000 SQL connections simultanées)&lt;/li&gt;
&lt;li&gt;Solutions de Haute disponibilité avec MySQL (Cluster, Replication, Shared Disk Clustering)&lt;/li&gt;
&lt;li&gt;séance Audit sécurité (boite noire et boite blanche)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Manager&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Histoire et évolution de la Plateforme PHP en Entreprise&lt;/li&gt;
&lt;li&gt;PHP, a mystery success story&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Les sponsors&lt;/h2&gt;


&lt;p&gt;Zend Technologies
Oracle
Anaska / AlterWay
Eyrolles
Nexen Services
Open States&lt;/p&gt;


&lt;h2&gt;Le prix&lt;/h2&gt;


&lt;p&gt;180€ / 2 jours / pers.&lt;/p&gt;


&lt;h2&gt;Le lieux&lt;/h2&gt;


&lt;p&gt;ASIEM
6, rue Albert de Lapparent
75007 Paris
Tél.&amp;nbsp;: 01 42 73 13 36
Metro&amp;nbsp;: Ségur (ligne 10)&lt;/p&gt;




&lt;p&gt;Malheureusement, je ne pourrais pas y assister, mais si certains sont intéressés et y vont, merci de me ternir au courant ;)&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2008/10/10/Forum-PHP-2008-Paris#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2008/10/10/Forum-PHP-2008-Paris#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/2</wfw:commentRss>
      </item>
    
  <item>
    <title>Zend PHP5 Certified Engineer</title>
    <link>http://blog.phppro.fr/?post/2008/08/29/Zend-PHP5-Certified-Engineer</link>
    <guid isPermaLink="false">urn:md5:9057db873fb5f7cebdd098a0d32f794a</guid>
    <pubDate>Fri, 29 Aug 2008 17:18:00 +0100</pubDate>
    <dc:creator>Olivier Hoareau</dc:creator>
        <category>Evènements</category>
        <category>certification</category>    
    <description>    &lt;p&gt;Ca y est, c'est fait&amp;nbsp;!&lt;/p&gt;


&lt;p&gt;&lt;img src=&quot;http://blog.phppro.fr/public/php5-zce-logo-new.gif&quot; alt=&quot;Zend PHP5 Certified Engineer Logo&quot; style=&quot;float:left; margin: 0 1em 1em 0;&quot; title=&quot;Zend PHP5 Certified Engineer Logo, oct 2008&quot; /&gt;
PHPPro (en ma personne ;) ) est Zend PHP5 Certified Engineer depuis le Vendredi 29 aout 2008 à 12h00.&lt;/p&gt;


&lt;p&gt;Prochaine étape, Zend Framework certified Engineer&amp;nbsp;? ;)&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.phppro.fr/?post/2008/08/29/Zend-PHP5-Certified-Engineer#comment-form</comments>
      <wfw:comment>http://blog.phppro.fr/?post/2008/08/29/Zend-PHP5-Certified-Engineer#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.phppro.fr/?feed/atom/comments/7</wfw:commentRss>
      </item>
    
</channel>
</rss>