Mais… on peut faire « ça » en PHP ? Mais c’est horrible !

26 mar

{lang: 'fr'}

Pour changer, je ne vais pas parler de ce qui est super avec PHP, mais plutôt de ce qui pue dans PHP. Et oui…

Attention, qu’on ne me fasse pas dire ce que je n’ai pas dit : j’adore PHP ! C’est la techno que j’utilise tous les jours, c’est un beau langage, riche et puissant, mais aussi très souple, et il est facile de faire n’importe quoi avec. Les codes suivants sont des exemples de ce qu’on peut faire grâce (ou à cause) de cette souplesse.

Ces techniques sont vraiment crades, mais peuvent parfois, en dernier recours, être utiles. Après, si vous voyez un code qui les utilise, posez-vous la question : est-ce PHP qui est crade, ou bien n’est-ce pas plutôt le développeur derrière qui ne s’est pas posé assez de questions ?

Redéfinir la portée d’un attribut

Commençons doucement. Un attribut privé est un attribut qui n’est accessible que par la classe qui le possède (par définition)… Ah bon ? Et si on essaye de changer sa portée ?

class Foo
{

    private $bar = 5;

    public function bar()
    {
        return $this->bar;
    }

}

$foo = new Foo;


$attribute = new ReflectionProperty($foo, 'bar');
$attribute->setAccessible(true);
$attribute->setValue($foo, 'nouvelle valeur');

var_dump($foo->bar());
// string(15) "nouvelle valeur"
        

Nom de zeus ! On vient de modifier la valeur d’un attribut privé !

Lire des attributs privés

On a pu changer la portée d’une variable, mais l’honneur est sauf : il n’est pas possible de lire un attribut privé, tant qu’il reste privé.

Bon, il doit bien y avoir une solution, non ? Essayons avec un petit coup de serialize().

class Foo {
    private $bar = 5;
}

$foo = new Foo;
$serialized = serialize($foo);

preg_match('!bar";(.):(.*);!', $serialized, $matches);
list(,$type,$value) = $matches;

var_dump("bar vaut $value");
// bar vaut 5
        

Facile ! Bien sûr, l’exemple est trivial, il existe des solutions plus complètes.

Hériter… directement de ses grand-parents

L’héritage c’est pratique ; un objet hérite de son parent, qui lui-même hérite d’un autre objet, le tout dans une harmonie parfaite.

Mais attendez, saviez-vous qu’il est possible d’hériter de sa classe grand-mère, sans passer par sa classe Mère ? (par pitié, n’essayez pas de vous représenter ce que ça ferait dans la vraie vie)

class GrandMother {
    public $bar;
    public function foo() {
        return $this->bar. ' in GrandMother';
    }
}

class Mother extends GrandMother {
    public function foo() {
        return $this->bar. ' in Mother';
    }
}

class Child extends Mother {
    public function foo() {
        return GrandMother::foo();
    }
}

$child = new Child;
$child->bar = 'abc';
var_dump($child->foo());
// string(18) "abc in GrandMother"
        

Il suffit donc de ne pas utiliser le mot-clef « parent », mais directement le nom de la classe Grand-mère. Le contexte ($this) est bel et bien préservé. C’est un reliquat de PHP 4…

Ne pas donner de nom à une variable

Toute variable possède un nom : $i, $foo, $bar… Que se passe t-il si vous tenez de déclarer une variable sans lui donner de nom ?

$ = 5;
// PHP Parse error:  syntax error, unexpected '=', expecting variable
        

Arf… Mais attendez, ne peut-on pas utiliser l’évaluation dynamique des noms de variables pour créer une variable dont le nom serait une chaîne vide ? Essayons :

$foo = '';
${$foo} = 5;

print_r(get_defined_vars());
//    ...
//    [foo] => 
//    [] => 5
//)
        

Et bien si, on peut. Constatez au passage que la variable est bien visible dans le get_defined_vars().

Du coup, on peut s’amuser à faire plein de trucs pas très utiles, comme nommer sa varaible $0 par exemple :

echo $0; 
// syntax error, unexpected '0' ...

echo ${0}; 
// ok
        

Transtyper un objet

Un objet est d’une classe donnée. Chaque classe possède un contexte propre (des attributs) et son propre comportement.

Du coup il ne devrait pas être possible de réutiliser un objet A pour en faire un objet B. Et pourtant …

class Foo
{

    public $a = 5;
    public $b = 6;

}

class Bar
{

    public $a = 'abc';
    public $b = 'def';

}

$foo = new Foo;
$bar = new Bar;


$object = convert($foo, 'Bar');
print_r($object);
//Bar Object
//(
//    [a] => 5
//    [b] => 6
//)
        

Comment a t-on fait ? C’est assez simple, il suffit une fois de plus de sérializer notre objet, de modifier une valeur dans la chaîne obtenue, puis de créer un nouvel objet.

function convert($object, $class)
{
    $serialized = serialize($object);

    $className = get_class($object);
    $len = strlen($className);
    $start = $len + strlen($len) + 6;


    $serializedInfos = 'O:' . strlen($class) . ':"' . $class . '":';
    $serializedInfos .= substr($serialized, $start);

    return unserialize($serializedInfos);
}
        

Vous trouverez de nombreux exemples sur le net.

« Ecouter » les changements d’une variable

Vous connaissez sûrement la fonction JavaScript watch(), très pratique, qui permet d’écouter les changements qui peuvent survenir sur une variable.

Vous me voyez venir : il est possible (à grands frais : lenteurs, charge mémoire, etc !) de faire la même chose en PHP. Je vous aurai prévenu : ne faites pas ça en prod !

L’idée consiste à utiliser une fonction qui va être exécutée à chaque tick, pour observer si la variable que l’on souhaite « écouter » à été modifiée ou non. Si c’est le cas, un simple appel à debug_baktrace() nous permettra de savoir comment cette variable a été modifiée.

$var = 'abc';

function tick()
{
    global $var, $expectedVar;
    if (isset($var)) {
        if (isset($expectedVar) && $var !== $expectedVar) {
            
            //
            // La variable a été modifiée
            $context = debug_backtrace();
            $where = (isset($context[1]['class']) ? $context[1]['class'] . '::' : '')
                    . $context[1]['function'] . '()';

            printf('la variable $var a été modifiée par %s (fichier %s, ligne %d), et vaut désormais "%s"'
                    , $where, $context[1]['file'], $context[1]['line'], $var);
        }
        $expectedVar = $var;
    }
}

//
// Enregistrons notre pseudo "écouteur"
register_tick_function("tick");
declare(ticks = 1);
        

Vérifions :

//
// L'heure du test
function foo()
{
    global $var;
    $var = 'def';
}

foo();
//
// Affiche : 
// 
// la variable $var a été modifiée par foo() 
// (fichier /home/data/www/jeff/misc/php-berk/watch-var.php, ligne 39), 
// et vaut désormais "def"        
        

C’est tout ?

J’hésite à aller plus loin : on pourrait parler encore de ce qu’il est possible de faire avec les __PHP_Incomplete_Class, de la confusion possible à utiliser des fonctions comme class_alias() (quoique parfois utile), de ce qu’il est affreusement possible de faire avec runkit, dela possibilité de remplacer $_GET par une valeur de notre choix… Mais je crois que ça suffira là :) .

Bref, vous l’aurez compris, si je suis vraiment convaincu que PHP est un super langage, je vois passer beaucoup de mauvais code, de mauvais développeurs et de mauvaises pratiques, qui me font quotidiennement prendre conscience qu’il est possible de faire vraiment n’importe quoi en développement. Lorsque l’on sort des sentiers battus, on doit avoir une bonne raison et comprendre pourquoi on le fait.

PHP est un beau langage, ce n’est pas parce qu’il est « facile » de débuter en PHP qu’il faut en faire n’importe quoi. Heureusement, on peut aussi faire de très belles choses avec ! Et c’est justement ça le job d’un développeur.

Behat – créer des tests solides et efficaces

25 fév

{lang: 'fr'}

Ca commence à faire déjà quelques temps que j’utilise Behat, et j’ai eu la chance de pouvoir l’utiliser sur différents projets, gros et petits. Je commence donc à avoir un peu plus de recul sur la chose, et à avoir accumulé pas mal de mauvaises pratiques.

Par manque de chance (ou pas ^^), il a fallu que je commence à utiliser Behat sur un projet assez important. C’est donc là que j’ai commencé à faire mes plus grosses erreurs. Je vous propose de voir quelles ont été ces erreurs, et comment éviter de les reproduire.

Utiliser Behat sans faire du Développement piloté par le comportement

C’est évident, mais il faut le dire : Behat n’est qu’un outil, ce qui compte vraiment c’est le Développement piloté par le comportement. Et oui ! Behat n’est ni un outil de test, ni un outil de spécification. Ce n’est « rien de plus » qu’un bonus par rapport à une démarche de travail.

Si on prend Behat, comme il m’est arrivé de le faire, comme simple outil de test, on fonce dans le mur : au bout de plusieurs mois on risque d’avoir substitué Behat à des outils de tests unitaires (PHPUnit, atoum). Behat permet (entre autres) de tester du besoin fonctionnel. Encore faut-il que ce besoin soit exprimé !

Faire du refractoring de code plutôt que du refractoring de phrases

Allez, créons une méthode privée réutilisable dans notre Contexte de définition. Ah, et puis passons la publique le jour où il nous faudra l’utiliser dans un autre Contexte…

Au final on se retrouve avec une application dans l’application. Quand c’est le cas, et ça m’est arrivé, c’est qu’on confond refractoring de code et refractoring de définitions.

Les Contextes de définition servent à traduire des expressions en code source. C’est donc sur ces expressions qu’il faut se focaliser. L’idéal est de réussi à parvenir à découper chaque expression en unités atomiques fondamentales.

Par exemple :

Quand que j'ajoute dans mon panier "Télévision Sony" depuis le catalogue produits

On peut découper cette expression en sous-expressions (étapes)

Etant donné que je suis sur la page du catalogue produit
Quand j'ajoute "Télévision Sony" au panier

Et au final on peut arriver à des expressions atomiques simples :

Etant donné que je suis sur "/catalogue/produits"
Quand je sélectionne "Télévision Sony"
Et je clique "Ajouter au panier"

Vous voyez que là on fait du refractoring de phrases. L’avantage est que c’est hyper simple avec Behat :

<?php
use Behat\Behat\Context\Step;

//

/**
* @Given /^que je consulte le catalogue produit$/
*/
public function queJeConsulteLeCatalogueProduit()
{
	return array(
	    new Step\Given('que je suis sur la page "/catalogue/produits"')
	);
}

etc.

Il suffit d’utiliser des sous-appels d’étapes dans la définitions.

C’est la seule manière que j’ai trouvé pour se retrouver avec des Contextes de définition simples et surtout stables dans le temps. Sinon on passe notre temps à les réécrire dans leur ensemble.

Par contre ça oblige à avoir plein de définitions, d’où le point suivant :

Utiliser Behat sans couche d’isolation

Au départ, quand on utilise Behat, on a tendance à se créer 2 ou 3 contextes de définition, et puis c’est tout. C’est à mon avis une erreur. Aujourd’hui, je démarre chaque projet qui utilise Behat avec au moins ce découpage :

  • Contextes de définition métiers
  • Contexte de définition de vue (web)
  • Couche d’isolation pour la persistance des données si besoin

Pourquoi ? Tout simplement parce que je me suis rendu compte d’une chose : il arrive souvent de devoir repasser sur des définitions ! Et oui. Et c’est long, laborieux et, disons-le, très très démotivant.

Il faut au moins regrouper les définitions qui concernent l’interface graphique dans un contexte spécifique. Par défaut, si vous utilisez Mink, c’est ce qui fait plus ou moins implicitement lorsque vous utilisez des définitions toute-prêtes de Mink.

Mais que se passera t-il quand le client vous annoncera qu’il sort une appli mobile ? Normalement, appli mobile ou web, aucune spécification ne devrait changer. Par contre, si vous avez mal organisés vos Contextes de définition, vous allez galérer.

Au contraire, si vous regroupez tout ce qui concerne l’UI dans un Contexte de vue, mettons par exemple « View\WebContext.php », il vous suffit simplement de gérer vos sous-contextes :

public function __construct(array $parameters)
{
    if ($parameters['view'] == 'mobile') {
        $this->useContext('view', new View\MobileContext($parameters));
    } else {
        $this->useContext('view', new View\WebContext($parameters));
    }
}

C’est aussi simple que ça : il vous suffit de regrouper tout ce qui concerne l’affichage dans un Contexte spécifique.

Bien entendu, pour les très grosses applications on peut aller plus loin, et d’ailleurs ça devient très vite intéressant : créer un Contexte par page. Par exemple:

$this->useContext('view.catalogue', new View\Web\CatalogueContext($parameters));
$this->useContext('view.panier', new View\Web\PanierContext($parameters));

Ca peut paraître extrémiste, mais dans le cas où votre application est conséquente, c’est très utile. Il faut bien se dire que tout ce travail consiste à solidifier votre architecture de test. De la même manière qu’il faut découper une application en blocs de code (modules), il faut découper les Contextes de définition selon leur… contexte.

Après, il faut prendre la juste mesure des choses, et trouver un découpage adapté :

  • aux besoins
  • à la taille du projet
  • au temps disponible
  • à la compétence de l’équipe

Exactement comme on le ferait pour un applicatif.

Voilà pour ces quelques retours que je souhaitais partager avec vous. Après, ils sont basés sur mon expérience personnelle, et je serai très curieux de savoir ce que vous en pensez, et surtout comment vous vous avez fait pour gérer la croissance de vos projets utilisant Behat / Cucumber / etc.. N’hésitez pas à partager votre expérience…

dependency.me, un service pour savoir si vos dépendances composer sont à jour

30 jan

{lang: 'fr'}

Ça faisait pas mal de temps que l’idée me trottait en tête : un service pour savoir si l’on utilise des dépendances composer à jour ou non.

Tel que je le voyais, je trouvais optimal de pouvoir intégrer une image à notre readme github, exactement comme pour Travis-Ci. Bien entendu, j’ai cherché sur le net ce que je pouvais trouver, et j’ai été assez surpris de trouver pas mal d’outils pour Ruby, Python, NodeJs… mais rien pour les dépendances de PHP avec composer !

Prenant mon courage à deux mains, je me suis donc décidé à coder (très rapidement) http://dependency.me. Il vous suffit de vous connecter avec votre compte Github et de sélectionner les repositories pour lesquels vous souhaitez activer le service, vous aurez en temps (quasi) réel une idée de l’état de vos dépendances, branche par branche :

Vue de l'état des dépendances Composer par propriétaire

Bien entendu, il est tout à fait possible d’avoir le détail pour chaque branche :

Vue de l'état des dépendances Composer par branche

Le site vous donne pour chaque dépôt le Markdown à copier-coller dans votre readme.md (par exemple) pour afficher une image mise automatiquement à jour selon l’état de vos dépendances :

  • build-status-latest
  • build-status-outofdate
  • build-status-recent
  • build-status-unknown

Bien entendu, le site est Open Source, et est hébergé sur Github.

Toutes les bonnes âmes souhaitant m’aider sont les bienvenues : qu’il s’agisse de signaler les bugs, me faire retours, et pourquoi pas directement à faire évoluer le code concerné. Je pense qu’on peut faire mieux : actuellement le service ne gère pas les dépôts privés par exemple… Et un coup de pouce pour l’anglais serait le bienvenue !

Je suis également preneur de toute remarque, positive ou négative, sur ce service.

Merci à vous !

Doctrine n’est pas un ORM ?

4 jan

{lang: 'fr'}

Pour bien commencer l’année, je vous propose de troller un peu sur les ORM. Ce qui suit est mon point de vue et n’engage que moi :)

Selon moi, Doctrine 2 représente un tournant majeur dans le monde de PHP, sans doute plus encore que Symfony2 ou Zend Framework 2. Mais j’ai le sentiment que Doctrine n’est pas utilisé à sa juste valeur…

Les ORM classiques

Prenons un peu de recul ; qu’est-ce qu’un ORM ? Traditionnellement, un ORM est une technique pour représenter des enregistrements (et leurs relations) sous forme d’objets. Généralement, on implémente pour cela principalement ces patterns :

Table Data Gateway:
Un objet est une passerelle vers une table
Active Record:
Un objet représente un enregistrement et dispose du comportement nécessaire pour implémenter la logique métier de cet enregistrement
Data Access Object:
Un objet est utilisé pour encapsuler tous les accès vers une source de donnée

C’est ce qui se passe depuis des années, par exemple avec Zend Framework 1 :

<?php
// Table Data Gateway
class TableUsers extends Zend_Db_Table_Abstract {
    // nom de la table et de la base
    protected $_name = 'tbl_users';
    protected $_schema = 'database_name';
}

$table = new TableUsers;
// Rowset : jeu de résultat Traversable
$users = $table->find(1);
?>

 

<?php
class User extends Zend_Db_Table_Row_Abstract {
    
    public function resetPassword() {
        $this->pwd = md5(uniqid());
    }
    
}

$user = $table->fetchRow(array('id' => 1));
$user->name = 'Jean-François';
$user->resetPassword();
$user->save();
?>

Et les principes SOLID ?

Bon, vous allez me dire que des milliers de projets ont été faits de cette manière, et que ça fonctionne.

Oui, ça fonctionne, mais ça tombe en marche : les coûts de maintenance sont nécessairement élevés, vu que le code ne respecte en rien les principes SOLID. Et oui, notre objet User a plusieurs responsabilités ! Il a la responsabilité de représenter une information, de gérer ses règles métiers, de s’enregistrer… Bref, c’est une God Class (un objet dieu), ce n’est pas de l’orienté objet… Et on fonce dans le mur.

En réalité, un ORM ne peut pas partir du postulat qu’on va représenter des enregistrements par des objets. Non, il doit faire l’inverse !

Je m’explique : le principe de la programmation orientée objet est de manipuler des objets. Il existe différents types d’objet : des agrégats, des objets valeurs… Pour conserver un code propre et maintenable, il ne faut se focaliser que sur le comportement des objets que l’on souhaite utiliser). La manière dont ils sont stockés n’a aucune importance. Vraiment aucune ! Si si; j’insiste : aucune ! Après tout, qu’ils soient stockés sous forme de fichiers, d’une base relationnelle, d’un document… tout cela n’impacte pas le comportement de votre application. Non, ça n’aura un impact que sur la couche basse d’infrastructure de votre application, exclusivement.

L’apport du Domain Driven Design

Le Domain Driven Design est une approche qui tente de focaliser la conduite d’un projet informatique sur le besoin métier exclusivement. Très grossièrement, l’idée est de maximiser la communication entre les équipes techniques et les équipes fonctionnelles pour faciliter l’expression du besoin, puis de calquer le code source sur cette expression de besoin. De cette manière, le code source est en permanence le reflet exact du besoin tel qu’il a été exprimé par les fonctionnels.

Identité et entité

Crise d'identité - Domain Driven Design

Que nous dit le DDD (Domain Driven Design) ? Que chaque projet manipule des concepts, des noms communs, qui semblent plus importants que les autres. Ce pourra être par exemple les mots « acheteur », « panier » et « produit » dans le cas d’une boutique en ligne.

Quelle est la spécificité de ces mots ? C’est qu’ils représente des choses différentiables les unes des autres de manière certaine : chaque produit est unique, chaque acheteur se distingue des autres acheteurs ; bref, chacune des ces choses est unique. On dira dans ce cas que chaque acheteur, chaque produit, ont une identité propre.

Cette notion d’identité est fondamentale : une identité est unique, et est marquée par des informations d’état de l’objet à laquelle elle s’applique. Par exemple, mon identité pourrait être marquée par mon numéro de sécurité sociale.

En informatique, on a longtemps oublié cette notion d’identité, en utilisant un « identifiant unique » (id) à la place d’une vraie information. Souvent, par exemple, on stocke des informations sur des comptes bancaires dans une table qui a une colonne « id » et une autre « account_number ».

C’est une erreur conceptuelle : la colonne « id » n’a aucune valeur fonctionnelle. Ce qui fait l’identité du compte, c’est son numéro de compte. C’est donc la colonne « account_number » qui doit être utilisée pour distinguer un compte d’un autre ! Je conçois qu’il est pratique d’ajouter une colonne « id » pour des raisons d’index et de performance dans une table, mais cet « id » ne doit pas être utilisé dans le code source, puisqu’il ne représente rien en terme de métier (fonctionnellement donc).

<?php
class Account  {
    private $accountNumber = 'xxxxxxxxxx';
}
?>

Ces choses, qui ont une identité, qui sont absolument uniques, sont désignées par le terme d’entités, ou « entity » en anglais dans le monde du Domain Driven Design.

Ah, comme dans Doctrine ? Oui, comme dans Doctrine… Les entités sont des objets qui ont une identité. Simplement, et uniquement.

Dans le DDD, il existe des collections d’entités, que l’on appelle des Agrégats (Aggregate). Oui, toujours comme dans Doctrine :)

Les services

On a vu qu’il existait des concepts importants dans tout projet informatique, concepts représentés par des noms communs.

Il existe d’autres concepts très importants, qui sont eux représentés par des verbes : « acheter », « vendre », « ajouter dans le panier »…

Comment représenter techniquement ces concepts ? En créant, nous dit le DDD, des objets « Services » :

<?php
class ServicePanier {
    
    public function ajouterProduit(ProduitInterface $produit, PanierInterface $panier) {
        // ...
    }
}
?>

Ces objets services sont justement les objets que votre application doit manipuler. Ils constituent le comportement global de votre application. C’est donc eux qui doivent être utilisés dans les Contrôleurs, et eux seuls.

Les dépôts / Repositories

Un projet informatique nécessite souvent de retrouver des informations. Ou plutôt, de retrouver ce qui est désigné fonctionnellement par des noms communs (« acheteur », « panier », « produit »…) et qui possède une identité.

C’est le rôle des dépôts (Repository). Un Repository ne sert qu’à retrouver des informations. Et uniquement selon des critères fonctionnels ; on ne doit pas appeler un repository en passant des tableaux de paramètres… Non, il faut continuer de penser SOLID :

<?php
class RepositoryProduit {
    
    public function listerProduitsDuPanier(PanierInterface $panier) {
        // ...
    }
    
    public function recupererProduit($identite) {
        // ...
    }
}
?>

Attendez, on a aussi le concept de Repository dans le DDD, comme dans Doctrine ?! Et oui ! Vous voyez pourquoi je fais tout ce détour par le DDD pour parler de Doctrine :-)

Et bien plus …

Il y a encore plein de choses dans le DDD, et j’ai schématisé très grossièrement, mais vous voyez où je veux en venir : Doctrine n’est pas un ORM classique (et ne doit pas être utilisé comme tel), Doctrine est un super outil pour pratiquer l’approche du Domain Driven Design.

Surtout, la démarche de Doctrine n’est pas de mapper une base de données sur des objets. Non, c’est exactement l’inverse : on part des objets (et donc du besoin fonctionnel) pour ensuite les faire persister.

De nombreux concepts du DDD sont présents dans Doctrine. Du coup, pourquoi ne pas penser Domain Driven Design dans vos projets Doctrine ? Qu’il s’agisse de projets Symfony 2, Zend Framework 2 (avec le module Doctrine), ou autre… ?

Et dans ce cas, si les entités sont des concepts fonctionnels importants (un panier, un produit, un acheteur…) qui ont une identité… par pitié ne créez pas d’entités pour vos relations dans Doctrine. Une table intermédiaire n’est pas une entité. D’ailleurs ça n’existe même pas : il n’existe que des relations entre les objets, tout le reste n’a aucun sens en terme de Programmation Orientée Objet.

Et si vous voulez me répondre que créer des entités-relations dans Doctrine reste pratique vu que ça permet de gérer et contrôler plus facilement certaines relations, je vous répondrai que ce n’est pas à PHP de vérifier la cohérence d’une donnée, mais à un SGBD relationnel, et que le SGBD sera bien meilleur vu qu’il est conçu pour ça.

Je crois que ça devrait être la résolution pour ce début d’année 2013 de tous les développeurs PHP : arrêtez de voir les ORM comme des solutions pour manipuler des bases de données.

Voyez plutôt des objets, qui ont des responsabilités, des comportements, parfois même une identité ; et considérez qu’il s’agit plutôt d’un accident quand on doit les persister.

Après tout, si on faisait pas du web, le besoin fonctionnel resterait le même, mais on n’aurait pas besoin de persister nos « acheteurs », « produits » et « paniers ». Il suffirait de « livrer », « commander »… Il n’y a que ça qui compte. Pas vrai ? :-)

Automatisation des tâches avec Phing

16 nov

{lang: 'fr'}

Je ne vais pas vous convaincre qu’un bon développeur est un développeur faignant, c’est à dire qui sait employer suffisamment d’énergie à un moment donner pour en gagner plein par la suite… Non, par contre, pour ceux qui ne l’utilisent pas encore, je vais vous montrer qu’on n’a pas besoin de se compliquer la vie lorsqu’on veut automatiser. On peut même s’organiser !

Phing, c’est quoi ? Les mauvaises langues diront que c’est une version instable de Ant. C’est pas totalement faux, mais c’est loin d’être vrai. Phing, c’est un outil d’automatisation de tâches, mais spécialisé et conçu pour PHP, ce qui change vraiment tout, comme vous allez le voir.

Ce qu’il faut noter également avant de commencer c’est que la documentation est très riche, même si elle n’est pas toujours 100% pratique.

Premiers pas avec Phing

L’installation de phing est simple :

pear channel-discover pear.phing.info
pear install phing/phingdocs

Il suffit ensuite de créer un fichier build.xml, qui va décrire un certain nombre de tâches que l’on souhaite exécuter :

<?xml version="1.0" ?>
<project name="demo" basedir="." default="example">
    <target name="example">
        <echo message="yep, I will create a directory" />
        <mkdir dir="/my/path/" />
    </target>
</project>

Rien de bien compliqué : un projet est composé de n tâches (noeud target), identifiées par un nom (attribut name). Si j’ai besoin de créer le dossier « /my/path », il ne me reste plus qu’à exécuter :

phing

Pas besoin de paramètre : vous voyez l’attribut « default » du noeud « project » : c’est la tâche à lancer par défaut si rien n’est précisé, ce qui est le cas ici.

Un peu plus loin

Bon, ok, c’est rigolo… Mais on peut faire plein de choses : il n’y a aucune limite. Vous avez vu le noeud « mkdir ». Il en existe des tas : « echo », mais aussi « phpunit », « ftpdeploy », « gitpull », « phpdepend »… Vous commencez à voir pourquoi utiliser Phing est intéressant : il gravite autour des utils PHP. Il existe enfin le noeud « exec », qui permet de lancer une commande classique.

Un truc bien pratique aussi c’est l’utilisation de variable. Un variable c’est :

  • un noeud « property » dans votre fichier xml
  • une déclaration dans un fichier de propriété
  • un paramètre que vous fournissez lorsque vous lancez phing, avec l’option -D

Chaque variable est réutilisable, il suffit de l’écrire sous la forme ${maVariable}. Soit :

<property name="out" value="/my/path/" />

<target name="prepare">
    <mkdir dir="${out}" />
</target>

Je vais pouvoir écraser la valeur de ${out} en lançant phing :

phing -Dout=/another/path

Je pourrai également importer un fichier de variables :

<!-- file phing.xml -->
<property file="./my.properties"/>

<!-- file my.properties -->
out=/another/path
var2=xxxx
var3=xxxx

Vous trouverez un exemple d’utilisation basique de phing pour générer un rapport minimal de vos sources : CodeSniffer pour vos conventions, PHPUnit pour vos tests, phpMessDetector pour le « smell code », rats pour la sécurité, CopyPasteDetector pour les copier-collers, CodeBrowser pour intégrer tout ça dans un navigateur…

Vous voyez qu’en quelques lignes de xml on se retrouve à ne plus jamais avoir besoin de lancer ces outils à la main. En un mot : on automatise !

Notez qu’il est possible de gérer la dépendance entre différentes tâches :

<target name="t1" depends="t2,t3">
    <!-- (...) -->
</target>

Toute exécution de la tâche t1 lancera les tâches t2 et t3.

Gérer le plan de tâches

Ce qui est chouette avec Phing, c’est qu’on peut faire plein de choses. Et même gérer le déroulement de nos tâches… Comment ? Et bien par exemple avec des si et des alors. C’est possible !

<if>
   <equals arg1="123456" arg2="${valueToCompare}" />
   <then>
       <echo message="${valueToCompare}" est égal à 123456" />
   </then>
   <else>
       <echo message="${valueToCompare}" est différent de 123456" />
   </else>
</if>

Pas mal non ? Bien sûr, on peut faire tout ce qui est classique : égal, différent, plus grand que, elseif…

Un autre moyen de gérer notre déroulement est de demander à phing d’interrompre le processus à moins que… à moins qu’une variable donnée soit vraie. Par exemple :

<fail unless="myVariable" message="On stoppe tout!" />

Structurer, découper et organiser les tâches Phing

Le risque de phing est de se retrouver à devoir copier-coller plein de tâches pour gérer les différents cas . Il existe un moyen simple d’éviter cela : chaque tâche est réutilisable par une autre (un peu à l’image d’une fonction), grâce au noeud « phingCall »

<target name="myTask1">
    <!-- (...) -->
</target>

<target name="myTask2">
    <!-- we call myTask1 -->
    <phingCall target="myTask1" />
</target>

Vous me direz : « Et les variables dans tout ça ? On peut les passer en paramètre ? ». Bien sûr :

<target name="myTask1">
    <echo message="ma variable vaut ${myVar}." />
</target>

<target name="myTask2">
    <!-- we call myTask1 -->
    <phingCall target="myTask1">
        <property name="myVar" value="abcd" />
    </phingCall>
</target>

Pas mal non ?

Pour organiser un peu mieux vos tâches, il est également possible de découper votre fichier xml en plusieurs sous fichiers, et de les importer (à la manière d’un require en PHP) :

<import file="./another-file1.xml"/>
<import file="./another-file2.xml"/>

Les tâches ne sont pas exécutées, mais sont disponibles à l’utilisation lorsque l’on fait un phingCall. Comme un « require » donc…

Evaluer du PHP

Ce qui me plaît le plus avec Phing, c’est la possibilité d’évaluer du code PHP directement, sans se compliquer. Oui c’est possible, je vous ai dit que Phing était justement fait pour PHP.

En plus c’est simple : l’évaluation de l’expression est directement mise dans la variable déclarée dans l’attribut « returnProperty » :

<php expression="in_array('curl', get_loaded_extensions())" returnProperty="curlEnabled"/>
<fail unless="curlEnabled" message="You need cUrl to use it" />

Pour finir

Vous le voyez, Phing mérite d’être utilisé. Si vous voulez voir un projet plus complet (et pourquoi pas y contribuer, si si, j’insiste), je vous invite à regarder PHPStarter, qui est un simple automate d’installation de sites (téléchargement des sources du framework utilisé, création des virtual hosts…), et qui synthétise tout ce que je viens de présenter.

Enfin, une petite astuce que m’a donnée mon collègue @gaspaio, et à laquelle je n’avais pas pensé : il suffit de définir comme tâche par défaut une tâche qui affiche de l’aide, et vous voici avec un outil encore plus structuré et simple d’utilisation. C’est ce qui se passe par exemple ici.

Phing n’est pas parfait bien entendu, surtout que c’est finalement assez peu utilisé par rapport à son utilité, même si pas mal de monde l’utilise. D’ailleurs je suis curieux : qui l’utilise au quotidien ? De temps en temps ? Jamais ? Et parmi la dernière catégorie, qui compte l’utiliser maintenant ?

Industrialiser le Contrat dans un projet PHP – Slides

31 oct

{lang: 'fr'}

Suite à l’invitation de l’antenne nantaise de l’AFUP pour un rendez-vous PHP (très sympa au passage!), j’ai eu envie de parler de la notion de Contrat dans un projet PHP, et surtout des outils pour s’assurer qu’un contrat, quel qu’il soit, soit respecté.

En effet, mon opinion est que TOUT, absolument tout, est Comportement : une application est un comportement vis-à-vis d’une donnée entrante (requête HTTP) pour fournir une information (réponse HTTP) ; de la même façon une fonction est un comportement vis-à-vis d’une donnée entrante (paramètre) pour fournir une information (valeur de retour) ; etc.

Or, qui dit « comportement », dit « contrat » pour s’assurer que le comportement souhaité est bel et bien appliqué. Je vous propose donc dans ces slides de retrouver différents niveaux de contrat (code source, travail en équipe, besoin fonctionnel…), et à chaque fois différents outils pour s’assurer automatiquement qu’il sont respectés.

Je suis curieux de vos retours sur ces notions de Comportement/Contrat, et aussi curieux de savoir quels outils vous avez l’habitude d’utiliser parmi ceux cités. N’hésitez pas à nous dire ça par commentaire :-)

Gérer des règles métiers complexes et/ou changeantes

27 sept

{lang: 'fr'}

Désolé d’avance pour la longueur de ce billet ; comme ça fait longtemps que mon blog n’a pas été mis à jour, j’en profite pour faire un mini-tutoriel sur un sujet qui me tient à coeur : comment gérer les règles métier, autrement dit les Spécifications fonctionnelles, dans un projet php ?

Je pense qu’on a tous (enfin, on devrait :-) ) avoir en tête les principes SOLID. Ces principes sont étroitement liées à la notion de Complexité Cyclomatique, qui elle, est moins connue.

Derrière ce terme barbare, que vous connaissez bien si vous faites du test unitaire ou si vous utilisez des outils tels que PHPDepend, se cache en réalité quelque chose de simple : si chaque bloc conditionnel de votre projet est un noeud, la complexité cyclomatique est la somme de l’ensemble des chemins empruntable dans votre projet.

De cette manière, le code suivant :

if c1
    a()
else
    b()

if c2
    c()
else
    d()

a une complexité cyclomatique plus élevée que

if c1
    a()
else
    b()

Un programme maintenable est en général un programme dont la complexité cyclomatique est la plus faible possible. Et c'est justement l'enjeu de la POO de nous fournir un moyen de concevoir des applications de Complexité cyclomatique faible.

Bon, ça c'est pour comprendre le principe. Après on me dit souvent : "oui, mais moi comment je fais pour gérer mes différents cas possibles si je dois limiter mes if() ?"

Prenez cet exemple :

  • on a un panier de produits
  • on ne peut ajouter que des produits en stock
  • on ne peut ajouter que 20 produits maximum
  • la règle ci-dessus ne s'applique pas en période de fêtes
  • les règles ci-dessus sont susceptibles de changer souvent

Générallement on imbrique des if(), du coup on se retrouve avec un arbre applicatif assez large, c'est-à-dire de nombreux chemins possibles. A terme :

  • on a un risque de changement du code source très important (à chaque fois qu'on ajoute une règle métier)
  • on gère mal l'ajout de nouvelles règles
  • très vite l'algorithme devient imbuvable car trop complexe

C'est là qu'intervient la notion Specification (bon, je sais il est temps, l'intro était longue ;-) )

Ce pattern répond à : "Comment gérer mes règles métier dans mon projet". Il est généralement associé au DDD (Domain Driven Design), mais on peut l'appliquer dans n'importe quel contexte qui s'y prête.

L'idée est la suivante : chaque règle métier va être représentée par un objet (une Spécification), à qui l'on va demander si la règle est respectée :

$anyObject = new StdClass;
$specification = new MySpecification;
$isOk = $specification->isSatisfedBy($anyObject);

Là où ça devient puissant, c'est qu'on va pouvoir créer des Spécifications composites pour créer des règles métiers complexes à partir d'un ensemble de règles simples :

$anyObject = new StdClass;
$specification =
    new MySpecification1()
    ->and(new MySpecification2())
    ->and(
        new MySpecification3()
        ->or(new MySpecification4())
    );
;
$isOk = $specification->isSatisfedBy($anyObject);

Vous voyez les avantages : vous pouvez désormais appliquer n'importe quelle règle métier sans avoir à imbriquer plein de if() ; si vous souhaitez tester unitairement une règle, vous pouvez mocker les autres ; vos règles sont facilement évolutives...

Bon concrètement comment ça se passe ? Il faut commencer par créer notre contrat pour le fonctionnement de nos Spécifications :

interface SpecificationInterface {

    public function isSatisfiedBy($object);

    public function andSpec(SpecificationInterface $specification);

    public function orSpec(SpecificationInterface $specification);

    public function notSpec(SpecificationInterface $specification);
}

Ensuite, pour permettre la création de Spécification composite il faut créer une classe abstraite générique pour nos spécifications.

abstract class Specification implements SpecificationInterface {

    public function andSpec(SpecificationInterface $specification) {
        return new AndSpecification($this, $specification);
    }

    public function orSpec(SpecificationInterface $specification) {
        return new OrSpecification($this, $specification);
    }

    public function notSpec(SpecificationInterface $specification) {
        return new NotSpecification($this);
    }

}

Il ne nous reste plus qu'à déterminer le comportement de chacunes de nos structures de contrôle :

Pour le "et":

class AndSpecification extends Specification implements SpecificationInterface {

    private $specification1;
    private $specification2;

    function __construct(SpecificationInterface $specification1, SpecificationInterface $specification2) {
        $this->specification1 = $specification1;
        $this->specification2 = $specification2;
    }

    public function isSatisfiedBy($object) {
        return $this->specification1->isSatisfiedBy($object)
                && $this->specification2->isSatisfiedBy($object);
    }

}

Pour le "ou" :

class OrSpecification extends Specification implements SpecificationInterface {

    private $specification1;
    private $specification2;

    function __construct(SpecificationInterface $specification1, SpecificationInterface $specification2) {
        $this->specification1 = $specification1;
        $this->specification2 = $specification2;
    }

    public function isSatisfiedBy($object) {
        return $this->specification1->isSatisfiedBy($object)
                ||  $this->specification2->isSatisfiedBy($object);
    }
}

Et enfin pour le "non" :

class NotSpecification extends Specification implements SpecificationInterface {

    private $specification;

    public function __construct($specification) {
        $this->specification = $specification;
    }

    public function isSatisfiedBy($object) {
        return !$this->specification->isSatisfiedBy($object);
    }
}

Ca y est, on vient de se créer le minimum vital pour gérer nos règles métiers. Ca, c'est fait une bonne fois pour toute...

Maintenant dans notre projet il suffit de faire hériter nos règles de la classe Specification. Par exemple :

class SpecLePannierPeutEtreRempli extends Specification {
    public function isSatisfiedBy($customer) {
        return(boolean)  $x; // la condition de notre règle ici
    }
}

class SpecOnEstEnPeriodeDeFetes extends Specification {
    public function isSatisfiedBy($customer) {
        return (boolean) $x; // la condition de notre règle ici
    }
}

class SpecLeProduitEstEnStock extends Specification {
    public function isSatisfiedBy($customer) {
        return (boolean) $x; // la condition de notre règle ici
    }
}

Et après il suffit simplement d'utiliser nos règles. Depuis PHP 5.4 on peut utiliser une interface fluide sur nos constructeurs, nous voici donc avec :

$specification =
    new SpecLeProduitEstEnStock()
    ->and(SpecLePannierPeutEtreRempli())
    ->and(
        new SpecIlResteDeLaPlaceDansLePannier()
        ->or(new SpecOnEstEnPeriodeDeFetes())
    );

if($specification->isSatisfedBy(specification)) {
    $panier->ajouterProduit($produit);
} else {
    throw new ArticleNePeutPasEtreMisDansLePannierException('...');
}

Pour synthétiser :

  • la Spécification permet de gérer des règles métiers
  • elle permet de combiner n règles métiers dynamiquement
  • elle facilite la gestion du changement fonctionnel
  • elle facile la lisibilité des règles
  • elle vous permet de mocker certaines parties des règles métier
  • ce n'est pas un remède miracle, mais elle mérite d'être plus utilisé ^^

N'hésitez pas à laisser vos retours sur ce pattern Specification, je suis curieux de savoir s'il est utilisé massivement ou non ? Ou peut-être utilisez-vous déjà un framework de gestion de règles métier, comme ceux qu'il existe dans le monde du Java ?

Behat – Une interface graphique pour rédiger / lister ses fonctionnalités

31 juil

Capture d'écran de Behat Wizard
{lang: 'fr'}

Encore du Behat, mais du neuf cette fois !

Ca fait maintenant pas mal de temps que je planche sur une interface graphique pour utiliser Behat. Pourquoi ? Tout simplement parce que je trouve qu’un client / product owner n’a pas à plonger dans des fichiers textes imbuvables pour rédiger ses spécifications. C’est à mon avis le grand problème de Behat : seuls les développeurs savent l’utiliser.

Pour rappel, Behat permet d’écrire des spécifications / tests de recette dans une syntaxe précise (Gherkin), puis de lancer une recette automatisée de ces spécifications, afin d’en ressortir la liste des fonctionnalités finies / en cours de développement / en échec.

Comme je le disais, j’ai donc travaillé sur une interface graphique pour simplifier la vie des clients et des Products Owners qui souhaitent fournir des spécifications à leurs développeurs. Ca a été un travail intéressant pour pas mal de raisons.

La première de ces raisons est technique : je suis originaire du monde Zend Framework, cet outil a été pour moi l’occassion d’approfondir un peu mes connaissances de Symfony 2.

Une autre raison, sans doute la plus importante, concerne l’ergonomie de ce logiciel. C’est sans doute ce qui m’a pris le plus de temps : réussir à me plonger dans la tête d’un utilisateur non technique pour offrir une expérience utilisateur agréable à n’importe qui, même non développeur. Pas si simple, et peut-être pas si réussi, mais je suis tout de même assez satisfait, même si je me doute que cette appli va pas mal évoluer au fil du temps (j’attends vos retours là-dessus hein :-) ! ).

Enfin, et c’est pas négligeable, c’est mon premier projet open-source ! Certes j’ai déjà contribué par-ci par là à des projets open source, mais je n’avais jamais comme ça jusqu’à maintenant. Ce qui me pousse (et m’a poussé à consacrer pas mal de temps à ce projet) c’est l’envie de voir plus utilisé cet outil (Behat) que je trouve pertinent et adapté à la vie en entreprise, mais malheureusement trop difficile d’accès pour les décideurs. J’espère que cet outil sera un pas de plus pour l’appropriation du BDD par les entreprises.

Quoi qu’il en soit, je rappelle je vous invite à regarder la démo de ce produit pour me dire ce que vous en pensez, et pourquoi pas à contribuer à son amélioration en forkant ce projet et en y ajoutant votre pierre. L’installation est, j’espère, on ne peut plus simple, vu que c’est un simple Bundle pour Symfony 2.

Ce n’est pas la seule tentative, et comme les autres ce n’est pas encore stable. Mais j’espère qu’on arrivera ensemble à faire quelque chose de bien pour proposer un outil digne de ce nom.

J’attends vos retours et idées d’amélioration avec impatience ! :-p

Slides AFUP Orléans – Exploiter PHP 5

9 juil

{lang: 'fr'}

Comme promis à ceux qui étaient présents, voici mes slides de notre rendez-vous du 5 juillet à Orléans, durant lequel on a parlé « outils » et « SPL ».

Bien sûr, les slides ne sont pas exhaustifs, mais j’espère avoir réussi à convaincre que PHP était un outil riche, très riche même, et qui mérite largement qu’on aille explorer un peu plus loin la documentation pour éviter de réinventer la roue à chaque développement :-)

Par rapport aux questions qu’il y a pu avoir, voici un lien pour obtenir des informations sur la PSR-0.

Merci à tous ceux qui sont venus, j’ai pris plaisir à faire cette petite présentation ;-)

BDD : produit mal pensé, développement raté

5 juil

{lang: 'fr'}

Ce billet, non technique, peut surprendre, mais je pense qu’en tant que développeur il faut comprendre un minimum ce que doit être une spécification, surtout si on veut faire du Développement Piloté par le Comportement. Cela fait quelques temps que j’avais en tête de rédiger ce petit billet, alors allons-y :-)

Rédiger des tests d’acceptation / tests de recette n’est certes pas mon métier, mais à force de l’expliquer, de le décrire, bref, à force de tenter de former des gens, je crois que je commencer à comprendre ce qui est si difficile dans ce travail de clarification.

En un mot : la majorité des gens est déformée par son expérience professionnelle passée ; trop habitués aux SFG, aux SFD et autres specs bien verbeuses. Fournir une spécification claire et précise, alors que cela devrait être naturelle et facile, semble au contraire contre intuitif.

Au risque de dire des choses évidentes, lorsqu’on fournit des spécifications à un développeur, ce n’est pas pour lui dire comment il doit faire son travail, mais pour lui décrire ce qu’il souhaite possible pour l’utilisateur. Une application, sans utilisateur pour la manipuler, est une coquille vide.

Un produit, au contraire, place les utilisateurs (qui ont donc un rôle) au centre. Le produit est donc constitué par ce que l’utilisateur peur faire avec (son comportement).

J’insiste, l’application n’a en aucun cas un comportement ; non, c’est la manière dont l’utilisateur interagit avec l’application qui constitue un comportement. On ne dit pas « cette application peut » mais « cette application permet »…

Bref, tout ça pour dire quoi ? Et bien que si la personne qui rédige les spécifications n’a pas cette vision du produit, l’utilisation de tests automatisés (Behat) risque d’être contre productive.

Un peu de concret

Je vais prendre un exemple que j’ai pu voir récemment. Il s’agit de spécifier un import de données dans une application :

Fonctionnalité: pouvoir importer des événement
  Afin d'ajouter des événements dans l'application depuis un fichier d'import
  En tant que Batch
  Je dois pouvoir importer un événement

  Scénario :
    Etant donné que je reçois un fichier d'import valide
    Et que je reçois un événement valide
    En tant que Batch
    Je dois pouvoir importer un événement

Au premier coup d’oeil, le développeur voit que quelque chose cloche dans cette fonctionnalité. En tant que Batch ?! Hum, bizarre, mais oui c’est vrai, c’est bien un batch qui va s’en charger. Je reçois cette spéc, elle me paraît logique, je vais donc l’implémenter.

Oui, mais NON. Si on implémente cette fonctionnalité on fonce dans le mur ! Que se passera t-il ? Tout d’abord on est tributaire du moyen technique utilisé. On pourrait très bien envoyer l’info par pigeon voyageur pour une raison x ou y, et dans ce cas la fonctionnalité deviendrait invalide.

Autre souci : quel bénéfice l’utilisateur tire t-il ? Aucun. Non non, aucun. On se moque que l’application puisse importer de la donnée, ça c’est le moyen. Non, la vrai fonctionnalité c’est de pouvoir intégrer des nouvelles données dans l’application. Et là j’ai un bénéfice pour l’utilisateur. J’ai donc aussitôt proposé ceci :

Fonctionnalité: pouvoir intégrer mes nombreux événements dans l'application
  Afin d'ajouter rapidement plusieurs événements
  En tant qu'utilisateur qui a le droit d'ajouter par lots des événements
  Je dois pouvoir intégrer de nouveaux événements rapidement

  Scénario :
    Etant donné que je fournis une liste d'événements
    Et que ces événements sont valides
    Quand je demande à intégrer cette liste d'événements
    Alors je dois pouvoir constater que ces événements ont bien été ajoutés

Pas grand chose n’est changé, mais tout change. Certes, ce n’est toujours pas parfait, mais désormais, quelque soit le moyen technique utilisé (transfert par pigeons voyageurs, clef USB, FTP…), la spécification de la fonctionnalité restera valable.

Plus encore, le développeur comprend tout de suite le souhait du client. Après c’est son rôle à lui, développeur (ou un ergonome, un architecte, mais surtout pas au fonctionnel) de déterminer comment satisfaire la demande du client. Et là, utiliser Behat a un sens.

Un autre exemple : le taxi. On peut dire que ça consiste à demander de l’argent à un client afin de le transporter dans une voiture. Mais on peut aussi dire qu’il s’agit de transporter une ou plusieurs personnes d’un point A à un point B. Et là ça marche, car en tant que client, mon souhait c’est bel et bien de me rendre quelque part. Et là surtout, la spécification reste valable qu’on parle de taxi moto, de calèche…

Impact sur les dates de livraison du projet

Un autre avantage fondamental est le contrôle que cela offre au propriétaire sur la date de livraison. En découpant le produit en fonctionnalités fondamentales, claires et simples, il devient facile de mettre de côté certaines fonctionnalités pour gagner un peu de temps, quitte à les ajouter plus tard.

Dans l’exemple du taxi, si je monte ma compagnie, je vais pouvoir dire ceci : « On du retard. Nous allons donc laisser de côté la fonctionnalité de paiement. Certes je vais perdre de l’argent un temps, mais au moins je serai visible. Nous ajouterons la fonctionnalité de paiement, qui n’est pas fondamentale pour l’instant, dans 15 jours. »

A mon avis, les développeurs doivent en être conscients, au risque sinon d’aboutir à un échec du projet. Pour faire de Développement Piloté par le Comportement, il FAUT un comportement, et donc un produit clair dont le coeur est l’utilisateur.

Après, c’est mon ressenti, je serai curieux de connaître le vôtre sur cette question :-)