mageekblog - Mot-clé - tipsLe blog personnel de Frédéric Hardy. Au menu, PHP, agilité, FreeBSD, cuisine et photographies.2021-12-02T08:20:54+01:00Frédéric Hardyurn:md5:26874ca5b8cd4cac8d08b0e68e64f63aDotclearOn ferme !urn:md5:7dedb704e5071599a72187a990440eb52010-02-22T09:30:00+01:002010-02-22T09:30:00+01:00mageekguyPHP5.3adapterbufferisationclosureechofermeturePHPtests unitairestips<p>Sous ce titre <del>idiot</del> ne se cache pas l'annonce de la fermeture de ce blog, mais bien un cas concret d'utilisation des <a hreflang="fr" href="http://blog.pascal-martin.fr/post/php-5.3-2-closures-et-lambdas">fermetures de PHP 5.3</a>.</p>
<p>Je me suis longtemps demandé ce que je pourrais bien en faire, et il semble que je ne sois pas le <a href="http://blog.pascal-martin.fr/post/php-5.3-2-closures-et-lambdas">seul</a>, au vu des commentaires sur le blog de <a href="http://blog.pascal-martin.fr">Pascal</a> et de la discussion que j'avais eu avec <a href="http://fr.linkedin.com/in/damienseguy">Damien Seguy</a> à ce sujet lors d'un apéro PHP à Lille.</p>
<p>J'avais bien une vague idée du potentiel du concept via mon expérience de développeur javascript, mais je n'en voyais aucun cas d'utilisation intéressant en <a href="http://www.php.net">PHP</a>, jusqu'à maintenant.</p> <p>En effet, aujourd'hui, dans le cadre des tests unitaires d'<a href="https://svn.mageekbox.net/repositories/ogo/trunk/">ogo</a>, j'ai eu besoin de tester non pas la valeur d'une variable, mais ce que PHP était censé afficher à l'aide de la commande <code>echo</code>.</p>
<p>Mon premier réflexe a été d'utiliser un <a href="http://blog.mageekbox.net/?post/2009/06/15/G%C3%A9n%C3%A9ricit%C3%A9-%21"><code>adapter</code></a>, mais<code> echo</code> est une construction du langage et non une fonction, et à ce titre, il n'est pas possible d'utiliser cette technique.</p>
<p>Pourtant, il fallait bien que je puisse tester ce que <code>echo</code> recevait comme argument...</p>
<p>Pour résoudre ce problème, ma première approche, très classique, a été d'utiliser les <a href="http://fr.php.net/manual/fr/ref.outcontrol.php">fonctions de bufferisation</a> de <a href="http://www.php.net">PHP</a>, qui permettent de stocker en mémoire l'intégralité de ce qui est envoyé à la commande <code>echo</code> et de ne l'afficher qu'à la demande ou à la fin du script, voir même de récupérer dans une variable la totalité des données.</p>
<p>Le code était alors le suivant :</p>
<blockquote><pre><code><?php<br />...<br />protected function testMakeReport()<br />{<br /> $score = new \ogo\test\cases\score($this);<br /> $reporter = new \ogo\test\cases\reporter\cli();<br /> $reporter->addScore($score);<br /><br /> ob_start();<br /> $reporter->makeReport(); // méthode utilisant la commande echo<br /> $echo = ob_get_clean();<br /> $this->assert->string($echo)->isEmpty();<br />}<br />...<br />?><br /></code></pre></blockquote>
<p>A première vue, ce n'est pas mal puisque ma problèmatique est résolue, mais en réalité, c'est de la merde !</p>
<p>En effet, à chaque fois que j'aurais besoin de tester le comportement de \<code>ogo\test\cases\reporter\cli::makeReport()</code> ou d'une méthode faisant appel à une commande du type d'<code>echo</code>, je serais obligé de dupliquer la séquence de code suivante :</p>
<blockquote><pre><code><?php<br />...<br />ob_start();<br />// des trucs faisant appel à echo<br />$echo = ob_get_clean();<br />$this->assert->string($echo)->...</code><br /><code>...<br />?><br /></code></pre></blockquote>
<p>Et je sais pas ce qu'il en a été pour vous, mais mes professeurs m'ont appris qu'un développeur qui fait de la duplication de code méritait le bûcher, et personnellement, je suis plutôt allergique aux flammes.</p>
<p>De plus, je <del>suis un fainéant</del> n'aime pas avoir le bout des doigts carrés à force de taper sur mon clavier.</p>
<p>Et c'est à ce moment que j'ai pensé aux fameuses fermetures de <a href="http://blog.pascal-martin.fr/post/php-5.3-2-closures-et-lambdas">PHP 5.3</a>.</p>
<p>En effet, pourquoi ne pas créer une méthode dans ma classe de test qui accepterait une fonction anonyme comme argument, qui démarrerait la bufferisation, éxécuterait la fonction anonyme, arrêterait la bufferisation et renverrait son contenu ?</p>
<p>C'est donc ce que j'ai fais, de la manière suivante.</p>
<p>J'ai ajouté à ma classe de test <code>\ogo\test\cases\base</code> la méthode <code>getOutput()</code>, dont le code est le suivant :</p>
<blockquote><pre><code><?php<br />...<br />protected function getOutput($function)<br />{<br /> ob_start();<br /> $function();<br /> $output = ob_get_clean();<br /><br /> return $output;<br />}<br />...<br />?><br /></code></pre></blockquote>
<p>Une fois cela fait, il ne me restait plus qu'à la mettre en oeuvre dans mon test à l'aide d'une <a href="http://blog.pascal-martin.fr/post/php-5.3-2-closures-et-lambdas">fermeture</a> :</p>
<blockquote><pre><code><?php<br />...<br />protected function testMakeReport()<br />{<br /> $score = new \ogo\test\cases\score($this);<br /> $reporter = new \ogo\test\cases\reporter\cli();<br /> $reporter->addScore($score);<br /><br /> $this->assert->string($this->getOutput(function() use ($reporter) { $reporter->makeReport(); }))->isEmpty();<br />}<br />...<br />?><br /></code></pre></blockquote>
<p>Et en poussant le vice un peu plus loin, il serait même possible de déporter ce traitement dans un <code>asserter</code> spécifique puisque finalement, la méthode <code>getOutput()</code> n'a rien à faire dans la classe de test.</p>
<p>De plus cela permettrait de réduire encore le code :</p>
<blockquote><pre><code><?php<br />...<br />protected function testMakeReport()<br />{<br /> $score = new \ogo\test\cases\score($this);<br /> $reporter = new \ogo\test\cases\reporter\cli();<br /> $reporter->addScore($score);<br /><br /> // nous passons maintenant directement par l'asserter<br /> $this->assert->output(function() use ($reporter) { $reporter->makeReport(); })->isEmpty();<br />}<br />...<br />?><br /></code></pre></blockquote>
<p>Ainsi, la duplication de code est supprimée, je ne brulerais pas sur un bûcher, mes doigts ne deviendront pas carrés, et j'ai un moyen standard pour tester tout ce qui est envoyé à <code>echo</code> et aux autres commandes <a href="http://www.php.net">PHP</a> du même type.</p>
<p>Pour ceux que cela intéresse, le <a href="https://svn.mageekbox.net/repositories/ogo/trunk/tests/cases/test/cases/asserter/output.php">code final</a> est disponible dans <a href="https://svn.mageekbox.net/repositories/ogo/trunk">le dépôt de <code>ogo</code></a>.</p>
<p>Et finalement, les <a hreflang="fr" href="http://blog.pascal-martin.fr/post/php-5.3-2-closures-et-lambdas">fermetures</a>, assez paradoxalement, m'ouvre tout un tas de perspectives intéressantes dans le cadre de futurs développements...</p>http://blog.mageekbox.net/?post/2010/02/19/On-ferme-%21#comment-formhttp://blog.mageekbox.net/?feed/atom/comments/89Makefly !urn:md5:69d4cb1bd2f0082c40811b616c33d87d2009-02-13T14:05:00+01:002010-05-09T14:31:56+02:00mageekguyPHPFreeBSDmakePHPtips<p>J'utilise maintenant depuis quelques année <code>make</code> pour effectuer différentes tâches au niveau de mes projets en <a href="http://www.php.net">PHP</a>, un utilitaire pourtant beaucoup plus connu pour faciliter la compilation de code C/C++ que pour gérer des projets en <a href="http://www.php.net">PHP</a>.</p>
<p>Je peux être amené à l'utiliser pour faire de la vérification syntaxique, créer une <code>tarball</code> d'un projet en passant par son installation, sa mise à jour, la génération des fichiers de ses fichiers de localisation ou de configuration, j'en passe et des meilleurs.</p>
<p>L'intêret, me direz vous ?</p> <p>Il est multiple.</p>
<p>Tout d'abord, <code>make</code> est disponible en standard sur la plupart, pour ne pas dire la totalité, des UNIX.</p>
<p>C'est d'ailleurs l'outil de base du système de <code>ports</code> de <a href="http://www.freebsd.org">FreeBSD</a>.</p>
<p>Un fichier <code>Makefile</code> est donc facilement portable d'un UNIX à un autre.</p>
<p>De plus, il n'est pas nécéssaire d'installer des bibliothèques ou des langages externes au système d'exploitation pour l'utiliser, au contraire d'un <a href="http://www.capify.org/">capistrano</a> ou d'un <a href="http://ant.apache.org">ant</a>.</p>
<p>Ensuite, <code>make</code> est un outil très puissant, puisqu'il est capable de faire appel aussi bien à des commandes <code>shell</code> qu'à des scripts en <a href="http://www.php.net">PHP</a> ou en tout autre langage.</p>
<p>Il est donc virtuellement possible d'effectuer n'importe quelle tâche avec un <code>Makefile</code> bien conçu.</p>
<p>En résumé, <code>make</code> est donc un outil, rapide à mettre en oeuvre, très puissant, portable, et universel sous UNIX.</p>
<p>Seul bémol, il existe différente version de <code>make</code>, avec notament le <code><a href="http://www.gnu.org/software/make/">make GNU</a></code> et <code><a href="http://www.freebsd.org/doc/en/books/pmake/index.html">pmake</a></code>, utilisé par <a href="http://www.freebsd.org">FreeBSD</a>, évidement incompatible entre elle.</p>
<p>Il est donc important de bien choisir la version que l'on veut utiliser, en fonction des plate-formes de destination de l'application.</p>
<p>Par ailleurs, <code><a href="http://www.gnu.org/software/make/">make GNU</a></code> est également disponible sous <a href="http://www.freebsd.org">FreeBSD</a> sous le nom de <code>gmake</code> via <code>/usr/ports/devel/gmake</code>.</p>
<p>Je vais maintenant vous présenter le fonctionnement de base de <code>pmake</code>.</p>
<p>Cependant, le principe de fonctionnement du <code><a href="http://www.gnu.org/software/make/">make GNU</a></code> est en tout point identique.</p>
<p>Par défaut, <code>make</code>, lorsqu'il est appelé sans argument, utilise le fichier <code>Makefile</code> situé dans le répertoire courant.</p>
<p>Evidement, si ce fichier n'existe pas, <code>make</code> s'arrête sur une jolie erreur.</p>
<p>Un fichier <code>Makefile</code> ressemble à cela :</p>
<pre><code>CWD != pwd<br />ECHO != which echo<br />GREP != which grep<br />SED != which sed<br />FIND != which find<br /><br />.SILENT:<br /><br />check-depends:<br /> extensions=`${PHP} -i | ${GREP} extension_dir | ${SED} 's/^.*=>\s*\([^\s=]*\)/\1/'`; \<br /> for extension in `${GREP} -rhoE "dl\([^)]+\)" --include \*.php --exclude .svn ${CWD} | ${SED} -e "s/dl('\([^.]*.so\)')/\1/" | ${SORT} -u`; do \<br /> ${ECHO} -n "Checking PHP extension $$extension: "; \<br /> if [ `${LS} $$extensions | ${GREP} -E ^$$extension` ]; then ${ECHO} "ok"; fi \<br /> done;<br /><br />check-syntax: .SILENT<br /> ${FIND} ${CWD} -type f -name \*.php -exec ${PHP} -l -d display_errors=off {} 1 > /dev/null \;;<br /><br />check: check-syntax check-depends<br /><br />.MAIN: check<br /></code></pre>
<p>Les premières lignes définissent un certain nombre de variable, <code>!=</code> permettant d'indiquer à <code>pmake</code> que leur contenu doit être le résultat de la commande qui suit.</p>
<p>Ainsi, la variable <code>CWD</code> contiendra le chemin vers le répertoire courant, et <code>SED</code> le chemin d'accès à l'utilitaire du même nom.</p>
<p>Il est à noter que la définition de ces variables n'a rien d'obligatoire, mais elle permet d'avoir un <code>Makefile</code> plus portable, plus facilement maintenable, et indépendant de l'environnement d'éxécution de <code>make</code>.</p>
<p><code>.SILENT</code> est ce qu'on appelle une <q>cible</q>, dans le jargon de <code>make</code>, tout comme d'ailleurs <code>check-depends</code>, <code>check-syntax</code>, <code>check</code> et <code>.MAIN</code>.</p>
<p><code>.MAIN</code> permet de définir la cible principale, à savoir dans notre cas <code>check</code>.</p>
<p>Cela signifie qu'un appel à <code>make</code> sans autre argument déclenchera l'éxécution de la cible <code>check</code>.</p>
<p>Une <q>cible</q> est défini par un nom, suivit de <code>:</code>, suivit éventuellement d'aucune ou plusieurs autres cibles, nommées dans ce cas <q>dépendance</q>.</p>
<p>Ainsi, la <q>cible</q> <code>check</code> a pour <q>dépendance</q> <code>check-syntax</code> et <code>check-depends</code>, ce qui veut dire que les cibles <code>check-syntax</code> et <code>check-depends</code> seront éxécutées lors d'un appel à la cible <code>check</code>.</p>
<p>Il peut venir à la suite de la définition de la <q>cible</q> et de ses <q>dépendances</q> du code <code>shell</code>.</p>
<p>Dans le cas de <code>check-syntax</code>, le code suivant doit être éxécuté :</p>
<pre><code>${FIND} ${CWD} -type f -name \*.php -exec ${PHP} -l -d display_errors=off {} 1 > /dev/null \;;</code></pre>
<p>De cette manière, un appel à <code>check-syntax</code> déclenchera l'éxécution de ce code et lancera la vérification syntaxique de tout les fichiers se terminant par <code>.php</code> dans le répertoire courant :</p>
<pre><code># make check-syntax<br />PHP Parse error: syntax error, unexpected T_STRING, expecting T_FUNCTION in /path/to/current/directory/file.php on line 5<br />*** Error code 255<br /><br />Stop in /path/to/current/directory.<br /></code></pre>
<p>De même, un appel à <code>check-depends</code> lance la recherche de tout les appels à la fonction <code><a href="http://www.php.net/dl">dl()</a></code> et permet de vérifier que la version de <a href="http://www.php.net">PHP</a> installée sur la machine sur laquelle est éxécutée <code>make</code> dispose de toutes les extensions PHP nécéssaires au fonctionnement de l'application.</p>
<p>Il y a encore bien d'autres utilisations possibles de <code>make</code> dans le cadre de projets utilisant PHP, et j'espère avec ces quelques exemples vous avoir donné envie d'en découvir un petit peu plus sur cet outil peu populaire mais extrémement puissant.</p>
<p>Si vous désirez un exemple un peu plus complet, je vous invite à regarder le <code>Makefile</code> de <a href="http://svn.mageekbox.net/repositories/wmiirc.php/trunk/Makefile">wmiirc.php</a>, et pour de plus amples informations sur la syntaxe et le fonctionnement de <code>make</code>, je vous invite à consulter la <a href="http://www.freebsd.org/doc/en/books/pmake/index.html">documentation</a>.</p>
<p>[PS] le titre de ce billet est une référence à ma période d'étudiant, ou nous passions notre temps avec mes camarades de promotions à appeler nos <code>Makefile</code> des <em>Makefly</em>, en réference à Marty McFly de <a href="http://fr.wikipedia.org/wiki/Retour_vers_le_futur">Retour vers le futur</a>... ne me demandez pas pourquoi, je n'en sais absolument rien, mais quelqu'en soit la raison, nous trouvions cela hilarant...</p>http://blog.mageekbox.net/?post/2009/02/13/Makefly-%21#comment-formhttp://blog.mageekbox.net/?feed/atom/comments/26De camelCase à camel_case en une commande avec vimurn:md5:079598ed96b8c5139297b48160f2759b2009-02-03T15:07:00+01:002009-02-03T15:44:22+01:00mageekguyVimtipsvim <p>Si vous voulez passer en une opération, et sur la totalité de votre fichier, de ceci :</p>
<pre><code>function maFonctionEnCamelCase()</code></pre>
<p>A cela :</p>
<pre><code>function ma_fonction_en_camel_case()</code></pre>
<p>La commande magique avec <a href="http://www.vim.org">vim</a> est la suivante :</p>
<pre><code>:%s/\(\l\)\(\u\)/\1_\l\2/g<br /></code></pre>
<p>Il n'y a pas à dire, <a href="http://www.vim.org">vim</a>, c'est puissant !</p>http://blog.mageekbox.net/?post/2009/02/03/camelCase-to-camel_case-avec-vim#comment-formhttp://blog.mageekbox.net/?feed/atom/comments/21