<?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, 07 Sep 2010 05:36:06 +0200</pubDate>
  <copyright>Copyright PHPPRO 2009</copyright>
  <docs>http://blogs.law.harvard.edu/tech/rss</docs>
  <generator>Dotclear</generator>
  
    
  <item>
    <title>&quot;\r&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é.\n&amp;quot;;
    } catch(Exception $e) {
        echo &amp;quot;Erreur de traitement du fichier '*file' : &amp;quot;.$e-&amp;gt;getMessage().&amp;quot;\n&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;\n&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;\r                                                                           \r&amp;quot;;
    echo 'Fichiers analysés: '.($counters['success']+$counters['error'])
    echo ', traités: '.$counters['success'].', rejetés: '.$counters['error'].&amp;quot;\n&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 (\r) 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/rss2/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;\n&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/rss2/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/rss2/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/rss2/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/rss2/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/rss2/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/rss2/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/rss2/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/rss2/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;\n&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;\n&amp;quot; . $this-&amp;gt;codePostal . &amp;quot; &amp;quot; . strtoupper($this-&amp;gt;ville) . &amp;quot;\n&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;\n&amp;quot; . $this-&amp;gt;codePostalLogement . &amp;quot; &amp;quot; . strtoupper($this-&amp;gt;villeLogement) . &amp;quot;\n&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/rss2/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/rss2/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/rss2/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/rss2/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/rss2/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/rss2/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;\n&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/rss2/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/rss2/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/rss2/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/rss2/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/rss2/comments/64</wfw:commentRss>
      </item>
    
</channel>
</rss>