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
Trucs et astuces
"\r" : où comment afficher un compteur de lignes traités dans un batch, plutôt qu'un log volumineux
Externaliser les messages d'erreurs des exceptions, pour les internationaliser, par exemple
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.
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.
Cloner une arborescence projet modèle
Vous créez souvent de nouveaux projets pour vous ou pour les autres ?
Vous réalisez souvent des tâches répétitives de "clonage" d'arborescence en changeant certains noms de fichiers ou contenu pour les contextualiser ?
Ce script certainement peut vous aider !
Utilisez PEAR pour gérer vos dépendances
PEAR (http://pear.php.net) est très mal connu dans les entreprises (en tout cas celle que je visite...).
Je ne vais pas vous décrire ici tous les fondements et usages de cet outil, cependant, au delà d'une "librairie" 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.
Incompatibilité Phing 2.3.3 et PHPUnit 3.4.x+
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 :
[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]
Voici une piste pour y remédier :
subversion + fichier .ini + include_path = gestion dynamiques des dépendances (librairies,...)
Votre application utilise certainement le tout dernier framework dernier cri, ou encore LA librairie ultime pour faire du PDF ou de l'ODT en PHP.. Ou encore, votre application dépend d'une autre application que vous avez réalisé ou interne à votre entreprise, elle a besoin de certaines classes par exemple.
Voici une méthode (il en existe certainement plein d'autres !) vous permettant de gérer vos dépendances pour être sûr de travailler toujours sur la bonne version d'une librairie, et pas sur une version modifiée en local sur votre poste et qui serait différente de celle sur votre serveur de production !
Les fichiers de contextes : où comment utiliser la même appli sur le web, les tests unitaires, fonctionnels, la ligne de commande, ...
Votre application peut être exécutée via :
* un navigateur (web) * une ligne de commande (batch) * PHPUnit (tests unitaires ou tests d'intégration) * GreenPepper / Fitnesse (tests fonctionnels) * ...
include_path + conventions + autoload = chargement automatique des classes PHP
Vous avez des dizaines de require/include en début de chacun de vos script ?
Pire dans vos scripts PHP, un peu partout ?
Et si l'autoload vous changeait la vie !
DNS + Proxy + Header, où comment gérer le déplacement d'un site internet en douceur
Cela vous est probablement déjà arrivé de devoir déplacement un site internet sur un autre serveur. L'adresse IP du serveur n'étant plus la même, il faut alors modifier l'entrée DNS (souvent A, mais peut être CNAME) pour que le nom de domaine pointe maintenant sur l'adresse IP du nouveau serveur. Mais cela peut prendre du temps pour se propager...
Installer HUDSON sur Ubuntu 8.10 sur le port 80
Voici la procédure que j'ai déroulée pour installer l'outil d'intégration continue Hudson (v1.3.09) sur un serveur sous Ubuntu 8.10 (64 bits) complètement vierge.
Des valeurs par défaut ... qui cachent des bugs !
function mafunction($param1,$param2='yes') { ... }
Que se passe-t-il si vous faites cet appel :
mafunction("monparam1",null);
ou bien :
mafunction("monparam1",$value2);
avec $value2 = null ?
Installer son propre channel pear en local (par exemple chez un client...)
Un petit tutorial rapide pour vous faire un retour d'expérience sur l'installation de Chiara PEAR Server (aka un channel PEAR perso)
Générer un document OpenXML à partir d'un modèle (pptx, xlsx,docx) et de variables: où comment générer vos documents à la volée simplement
Vous avez produit pour un client un document powerpoint vraiment bien, avec l'entête de votre entreprise, votre logo... sur un sujet qui revient souvent pour vous.
Le temps passe, et voilà qu'on vous redemande le même sujet ou très similaire mais avec quelques informations différentes ou bien à actualiser.
dirname(__FILE__), où comment éviter d'utiliser define('ROOT',...);
Comment récupérer le répertoire courant du fichier dans lequel on se trouve ...
Le lien symbolique : ou comment maîtriser son déploiement
Vous avez déjà fait des mises en production ou mises en ligne.
Vous savez que les problèmes arrivent, que l'on peut faire des erreurs, par exemple supprimer malencontreusement un répertoire au moment du remplacement par la nouvelle version, etc...
Il existe une technique simple qui permet de simplifier les mises en production, de faciliter le retour en arrière et de gérer l'archivage : le lien symbolique.
Typer vos exceptions: les types d'exception génériques mais utiles
Si vous utilisez le mécanisme d'exception de PHP, vous devez savoir qu'il est possible de créer vos propres exceptions en héritant des classes Exception ou RuntimeException. (SPL), par exemple comme ceçi :
class MyException extends Exception
{
}
...
Charger un fichier de configuration à la demande sans changer l'url
Votre application est exécutée sur plusieurs environnements différents (dev, integration, preprod, prod, ...).
Vous avez bien fait les choses, les différences entre les environnements sont externalisées dans un fichier de configuration de sorte que le code (php) soit identique sur l'ensemble des environnements.
...
D'une application mono-marque à une application multi-marque : comment faire simple ?
Vous avez développé une application PHP complexe (ou pas).
Le succès de votre application auprès de votre client (interne ou externe) vous amène à devoir décliner votre application dans plusieurs versions : ...
SessionSwitcher : Ou comment accélerer vos tests IHMs manuels en 60 lignes de code
Vous avez développé une belle appli toute sympathique avec plein de jolis formulaires qui sont tellement bien faits, qu'ils vous rendent la vie moins sympathique lorsqu'il s'agit de tester "manuellement" votre application. ...
Tests unitaires et Exception : attention aux try/catch !
Les tests unitaires, pour ceux qui les utilisent, sont bien pratiques pour tester notre code. Malheureusement ils peuvent introduire, si ils sont rédigés de façon maladroite des problèmes qui peuvent être compliqués à comprendre a posteriori. Imaginons le code suivant (volontairement) mal codé : ...
« billets précédents - page 1 de 2
Commentaires récents
Ah oui je confirme, ne jamais utiliser Exception car PHPUnit lui même les attrape.
De manière générale utiliser Exception n'est pas correct de toute manière ;-)
$this->setExpectedException('MonException'); est aussi possible depuis un test.
Il suffisait d'y penser , merci pour cette info :-)
Pour les tests d'IHM: selenium
Pour les session, perso je fais ça avec un petit curl en ligne de commande. Basic, mais aide parfois
On peut aussi utiliser un système de patch: tu fais une lignée de code 'souche', et 2 stocks de patch pour la spécialiser.
Ca évite les switch interne avec des constantes.
Je me demande aussi si cette technique ne pourrait pas être jouable avec darcs (qui commit des patch, lui)
Il existe aussi le plugin "modify headers" qui permet de (comme son nom l'indique) modifier les headers.
Pourquoi ne pas utiliser les exceptions définies dans la SPL (http://fr.php.net/manual/fr/spl.exc...) ?
Et à la limite :
class MaSuperException extends InvalidArgumentException { }
Cette approche a l'avantage selon moi de pouvoir définir un traitement différent selon les types d'erreurs rencontrés de façon générique, qu'elles soient levées par le langage ou par l'utilisateur (en tenant des journaux avec des niveaux de sévérité différents selon les types d'exceptions rencontrés).
J'utilise ce mécanisme en complément de la classe ErrorException (buggée en ce moment je crois) pour gérer tous les problèmes pouvant être rencontrés durant l'exécution des scripts.
Tu as tout a fait raison, l'utilisation des exceptions de la SPL est une bonne pratique.
Jusqu'à la version 5.3 (non compris), l'extension, packagée avec PHP par défaut, pouvait être désactivée. Depuis la 5.3 elle ne peut plus être désactivé, il n'y a donc plus d'excuse pour ne pas l'utiliser.
Cela ne vous empêche pas de rajouter certains type d'exceptions non prévus dans la SPL, avec parcimonie !
Pour gérer le problème de la base de données, je vois deux solutions qui s'intègrent dans le script de livraison de l'appli :
* Sauvegarde complète de la base que l'on archive dans /opt/mon-appli-2.1
* Versioning de la base de données (non testé) : une base par version du code.
Mais en cas de rollback rest le problème du différentiel de données (nécessite une anticipation).
PS: A noter que les fichiers générés par l'application doivent être géré hors du "code"
J'utilise Subversion pour gérer le déploiement de mes releases en taggant celles-ci dans mon repository.
Ainsi je n'ai qu'à faire un svn export du tag correspondant à la release que je veux déployer. Idem pour revenir en arrière.
@Jean-Sébastien: concernant le problème de différentiel des données en cas de rollback, il y a peut être une piste du côté du mécanisme de réplication des bases de données. Notamment MySQL Replication Mechanism permet de répliquer toutes les requêtes effectuées à partir d'un instant (numéro de version de la base) sur une autre base de données (i.e. un autre serveur). Il suffirait alors de noter le numéro de version quand on fait une mise en production, et lorsqu'on fait un rollback d'activer le mécanisme de réplication sur un nouveau serveur/base de données et faire répliquer toutes les requêtes le numéro de révision et maintenant. A creuser (déjà testé sur une plateforme mais pas dans ce contexte).
@Ladenise: svn export n'est pas forcément "rapide", en effet si ton projet contient des milliers de fichiers, la durée du svn export peut être non négligeable (plusieurs minutes) et rendre indisponible ton application, voir pire, pendant le svn export, si tu n'as pas désactivé l'accès à l'appli il peut y avoir un comportement imprévu si certains fichiers sont exportés mais pas tous. D'autre part, comme l'indique Jean-Sébastien, il faut forcément coupler avec un autre mécanisme pour gérer le rollback de la base de données car svn export ne peut le faire (peut être avec un script côté serveur svn, car c'est possible de déclencher un script sur une action svn, mais si le serveur svn n'accède pas à la prod...). La solution svn export est une solution simple cependant.
Je ne pense pas que dirname( __FILE__ ) remplace le define( 'ROOT' , <dir> ).
L'un n'a rien avoir avec l'autre.
L'utilisation d'une constante qui pointe sur la racine est très utile : pour former un path sans avoir de ROOT, on doit jongler avec les '/..' etc.
L'inclusion dans les autoloader de class (spl_autoload_register), de déplacement au sein du filesystem du code, etc., sont entièrement facilités par une constante "ROOT". Les chemins relatifs portent trop souvent à confusion.
De plus, le fait de définir le ROOT à partir de dirname( __FILE__ ) fait qu'il est absurde de dire une bêtise comme :
"Plus besoin de tenir un fichier de configuration avec des define qui est a modifié à chaque installation."
Encore une fois, l'un et l'autre sont deux choses différentes.
De plus, il aurait été intéressant de préciser qu'à partir de la version 5.3, la constante __DIR__ offre l'équivalent d'un dirname( __FILE__ );
@avetis.kazarian: tout est histoire de pratique, il n'y pas de bonnes et mauvaises pratiques universelles, cependant certaines pratiques sont plus adaptées que d'autres dans certaines situations.
Un fichier qui utilise le principe de dirname(__FILE__) pour inclure ses dépendances, est potentiellement utilisable directement par une autre application ou script, si il nécessite la présence de constantes (ex: ROOT) alors il faudra au préalable définir ces constantes avant d'utiliser ce fichier dans une autre application.
Effectivement, cela n'a pas de réelle importance lorsque l'on développe une application autonome, mais en prend lorsque l'on décide de rendre potentiellement mutualisable à toutes nos applications/scripts tous les développements que nous réalisons.
Attention aux "dépendances" implicites de notre code à des éléments extérieurs (define, variable globales, fichiers de configuration...) ils rendent difficilement testable ou utilisable dans un autre contexte notre code.
Merci pour la précision sur la constante __DIR__ en PHP 5.3, pour toutes les applications legacy des S.I français (et autres !) qui ne pourront pas bénéficier de la 5.3 avant quelques mois voir années, dirname(__FILE__) reste utilisable en attendant...
Bonjour,
Merci pour le tutotial. Par curiosité.
Hormis les paquets pear, je ne me rappel pas avoir été embeté par l'installation de hudson en suivant la procédure suivante.
http://weblogs.java.net/blog/kohsuk...
et pourquoi ajouter lighthttpd ?
@senjy : il s'agit d'une installation sur un serveur dédié à hudson, pas d'applications web sur ce serveur, j'ai jugé que lighttpd serait suffisant et plus léger que apache, sachant que le serveur n'est pas une bête de course. Mais apache est un bon candidat sinon, bien sûr. Concernant le tutorial, votre lien est cassé, je pense qu'il s'agit de http://weblogs.java.net/blog/kohsuke/, je me suis inspiré de certains articles de Kohsuke mais j'avoue ne pas avoir trop cherché de tutoriaux tout fait sur l'installation, étant donné que j'avais la spécificité PHP / Lighttpd / OpenJDK. Merci tout de même pour le pointeur.
Une autre bonne habitude pour éviter ce genre de problème est d'indiquer le type de l'objet attendu, lorsque l'argument est un objet :
function foo(anObject $objet = null) {
...
}
Cette méthode évite bien des problèmes sans surcharge de code dans le corps de la fonction, vu que la vérification est prise en charge par le moteur de php nativement.
Cepdendant, vu que les types de base de PHP ne sont pas "objet" (sic), pour ces derniers, ta technique est la plus appropriée, à part dans les cas des tableaux ou la mienne fonctionne également (encore une aberration de PHP :)) :
function foo(array $array = null) {
...
}
Ben en fait le channel pear.phppro.fr
ne réponds plus
> Subversion (ou CVS, même si je conseille de l'abandonner au profit de CVS)
Je suppose que tu voulais parler de Subversion et non de CVS la seconde fois. ;)
@Bruno : il s'agit d'une indisponibilité temporaire, cela devrait être rétabli fin aôut (réinstallation de mon serveur PEAR :( ), En attendant tu peux télécharger le tarball compatible PEAR sur Google Code à l'adresse http://code.google.com/p/phpcoderatio/downloads/list, puis faire un :
$ pear install phpcr-0.2.0.tgz
petite question sur un détail constaté :
"Exemple dans le fichier contexts/@common.php (inclus par tous les fichiers de mon application) :"
à quoi sert ce @ dans le nom de fichier ?
Classement ? comportement magique ?
Merci, c'est un article interessant pour un sujet qui est d'actualité pour moi. Comment inserer des images dans un documents docx ?
@spm: C'est un peu plus compliqué mais possible, j'utilise ce type de routine (qui contient certainement des bugs, mais qui fonctionne pour mes générations de rapports avec OpenXML) :
// parses for pics if (preg_match_all('/(.*)<\/p\:pic>/U',$content,$matches)>0) { for($i=0;$i
/U',$matches[1][$i],$matches2)>0){ $matches3 = null; if(preg_match_all('/\$\{([^\}]+)\}/',$matches2[3][0],$matches3)>0) { $matches4 = null; if(preg_match_all('/0){ $relFile = dirname($file).'/_rels/'.basename($file).'.rels'; if (true === file_exists($relFile)) { $x = simplexml_load_file($relFile); foreach($x->Relationship as $relationship){ if ((string)$relationship['Id'] === $matches4[1][0]){ $picFile = (string)$relationship['Target']; if(!$picFile) continue; $picFile = realpath(dirname($file).'/'.$picFile); if(!$picFile) continue; unlink($picFile); try { $tplPicFile = $this->parseVar($matches3[0][0],$matches3[1][0],$vars); } catch(Exception $e) { $tplPicFile = dirname(__FILE__).'/resources/no-data.png'; } copy($tplPicFile,$picFile); } } } } } } } } $content = $this->parseString($content,$vars);
Globalement, je mets le nom de la variable dans l'attribute "description" texte de l'image dans mon document openxml (par exemple dans powerpoint) puis je parse le xml du document pour remplacer les occurences des variables par les vrais images sachant que les balises xml existent déjà (puisque créé par powerpoint) sauf sont à ajouter les balises xml des resources (le fichier image complémentaire)
Sorti du contexte, ca doit être un peu rebutant par contre, je le conçois...
olivier
J'ai toujours dit que PEAR c'est 4 choses utilisables indépendament.
1° Une librairie à classe unique. Je veux dire que normalement il n'y a pas 2 classes qui font la même chose.Et quand c'est le cas il faut le dire.
2° C'est un outil de packaging et déploiement pour ce package de classe php (pear) ou de modules de php (PECL)
3° C'est une convention de codage
4° C'est une communauté.
Beaucoup se limitent à croire que c'est juste le 1° et que ca en fait un simple concurrent à Zend_Framework, Symphony, Midgard, .... Hors beacoup de librairies (y compris ces dernières) peuvent être installée avec pear.
Merci Olivier pour ce super billet, sa partie technique et surtout tes remarques.
Tu me donnes des idées pour notre plateforme d'intégration continue...
Conseils d'utilisation de PEAR ? Utilisez-le toujours pour packager/déployer vos devs OS !
Sinon en ce moment j'essaye d'améliorer la débianisation des paquets pear pour qu'elle soit justement plus adaptée aux nouveaux usages : https://www.assembla.com/wiki/show/... Si quelqu'un s'y connait et peut jeter un coup d'oeil...
Sous drupal j'utilise la fonction t() pour traduire les chaînes de caractères.
Ex :
throw new Exception(t('Mon message'));
Au moins ça permet de regrouper toutes les traductions au même endroit.
le problème d'avoir juste un simple code numérique : le code source est moins lisible, et on a donc plus de mal à savoir quel est le message d'erreur, sans avoir aller chercher dans d'autres fichiers.
C'est pour ça que dans jelix, on a un compromis : on donne une clé alphanumerique, qui peut être beaucoup plus parlant qu'un simple nombre.
Par exemple,
throw new jException('jelix~errors.controller.class.unknown', $classname);
Et en fait, ça réutilise le système de localisation de jelix, qui se base sur un principe de clé->valeur plutôt que sur un truc du type de gettext.
Bonjour,
Je suis en train de faire ce type de manipulation avec du code un peu différent selon mon besoin.
Je rencontre un problème au niveau de la compression. j'utilise bien ZipArchive et je change l'extension de zip par pptx mais openOffice me demande de choisir un filtre pour l'ouvrir...chose qui n'arrive pas avec un fichier fais par PowerPoint et quand j'en choisi un il me fais une erreur. Dans l'archive il y a bien tous les dossiers et fichiers ! pourriez vous m'aidez s'il vous plait ?