Deblan blog

Tag #Symfony2

Validation de fichier CSV en PHP

Je publie aujourd'hui une première version fonctionnelle et presque totalement testée d'un validateur de fichier CSV : deblan/csv-validator. J'ai décidé de m'appuyer sur les contraintes de validation proposées dans Symfony.

Pour installer ce composant, il faut passer par composer :

composer require deblan/csv-validator dev-master

Voici un exemple simple et concret :

C'est sous lience BSD donc éclatez-vous avec !


Projet symfony2, convention PSR2 et validation pre-receive GIT

Au mettre titre que SVN, je vais déployer progressivement des hooks pour traiter le code source "pushé" vers mon Gitlab (et les projets que je gère).

En m’appuyant sur un script récupéré sur Github, j'ai réalisé un hook GIT pre-receive qui permet de vérifier que le code soumis est valide PSR2. Il fonctionne pour le moment très bien mais je suis ouvert aux corrections si vous en avez à me proposer. Il faudra prendre une ou deux minutes pour configurer les premières variables.


Projet symfony2, convention PSR2 et validation pré-commit SVN

Les gens qui travaillent avec moi savent que je suis un "Syntax nazi". Je ne supporte pas quand le code ne suit pas les conventions. Ainsi, pour éviter de rager quand j'update mes sources, j'ai décidé de créer un hook SVN pour valider les commits des fichiers PHP.

Vous l'aurez donc compris, il s'agit ici de valider les uniquement les fichiers PHP suivant la norme PSR2. Par ailleurs, je ne m'attarde que sur le code de l'équipe de développement. Il s'agit donc de tout ce qui se trouve dans le répertoire "src" des applications symfony2 que nous développons.

Un hook SVN est un script qui sera exécuté à un instant donné et qui, suivant le code d'erreur, va valider ou non l'action en cours. Dans le cas de commit SVN, le hook est "pre-commit". Il se place dans le répertoire "hooks" du dépôt SVN et se nomme "pre-commit". Vous y trouvez d'ailleurs des fichiers .tmpl donnant quelques exemples.

Mon script s'appuie sur PHP_CodeSniffer (phpcs) que j'ai installé via composer. En effet, il existe un paquet Debian pour phpcs mais le standard PSR2 n'est pas pris en charge dans celui de la Wheezy (version de Debian du serveur SVN).

Voici le contenu du script :

#!/bin/sh

SVNLOOK=/usr/bin/svnlook
PHPCS=/usr/local/src/phpcs/vendor/bin/phpcs
STANDARD=PSR2
SEVERITY=1
FINAL_REPORT=

for FILE in `$SVNLOOK changed -t "$2" "$1" | egrep -v '^D' | cut -b 5- | egrep '^src.*\.php$'`; do
    REPORT=`$SVNLOOK cat -t "$2" "$1" "$FILE" | $PHPCS --standard=$STANDARD -n --report=full --error-severity=$SEVERITY | sed 's,STDIN,'$FILE','`

    if [ -n "$REPORT" ]; then
        FINAL_REPORT="$FINAL_REPORT$REPORT\n\n"
    fi
done

if [ -n "$FINAL_REPORT" ]; then
    echo "$FINAL_REPORT" >&2
    exit 1
fi

exit 0

Voici un exemple de sortie lorsque la validation échoue :

Envoi          src/FooBar/Bundle/PacteBundle/Controller/CollaboratorController.php
Envoi          src/FooBar/Bundle/PacteBundle/Controller/PacteController.php
Envoi          src/FooBar/Bundle/PacteBundle/Controller/TeamController.php
Transmission des données ..svn: E165001: Échec de la propagation (commit), détails : 
svn: E165001: Commit blocked by pre-commit hook (exit code 1) with output:

FILE: src/FooBar/Bundle/PacteBundle/Controller/CollaboratorController.php
--------------------------------------------------------------------------------
FOUND 5 ERROR(S) AFFECTING 3 LINE(S)
--------------------------------------------------------------------------------
 18 | ERROR | Spaces must be used to indent lines; tabs are not allowed
 18 | ERROR | Line indented incorrectly; expected at least 4 spaces, found 1
 18 | ERROR | Line indented incorrectly; expected 4 spaces, found 1
 19 | ERROR | Opening brace indented incorrectly; expected 1 spaces, found 4
 32 | ERROR | Closing brace indented incorrectly; expected 1 spaces, found 4
--------------------------------------------------------------------------------


FILE: src/FooBar/Bundle/PacteBundle/Controller/PacteController.php
--------------------------------------------------------------------------------
FOUND 5 ERROR(S) AFFECTING 3 LINE(S)
--------------------------------------------------------------------------------
 15 | ERROR | Spaces must be used to indent lines; tabs are not allowed
 15 | ERROR | Line indented incorrectly; expected at least 4 spaces, found 1
 15 | ERROR | Line indented incorrectly; expected 4 spaces, found 1
 16 | ERROR | Opening brace indented incorrectly; expected 1 spaces, found 4
 18 | ERROR | Closing brace indented incorrectly; expected 1 spaces, found 4
--------------------------------------------------------------------------------

Édit : mise à jour du code qui plantait lors de la suppression d'un fichier.


Générer des PDF dans Symfony2

La génération de PDF fait souvent partie des cahiers des charges de projets de moyenne et grande envergure. Il existe quelques outils comme HTML2PDF qui repose sur FPDF ou TCPDF. Cependant, bien qu'ils soient puissants et fonctionnels, leur utilisation peut très vite devenir complexe quand il s'agit de mettre en forme précisément les contenus.

C'est pourquoi nous avons décidé d'utiliser Wkhtmltopdf. Ce petit bijou a la particularité de reposer sur un moteur de rendu Webkit. Le javascript est interprété et des plugins additionnels (comme Flash) peuvent être ajoutés. En plus de ça, il interprète extrêmement bien le CSS et toutes les balises HTML sont prises en compte.

Bon, trêve de bavardage, voici comment l'installer et l'utiliser.

Je travail avec Debian Wheezy, que ce soit sur mon poste de développement ou sur les serveurs en production. Vous devrez donc adapter la suite de l'article à votre système.

Installation de Wkhtmltopdf
Coté système
$ su - 
# aptitude update
# aptitude install wkhtmltopdf

Wkhtmltopdf a besoin d'un serveur X pour fonctionner. Comme les serveurs n'en disposent pas, nous allons installer xvfb qui permet d'en créer un virtuel.

# aptitude install xvfb

Il faut ensuite préparer un wrapper pour lancer wkhtmltopdf via xvfb :

# cat > /usr/bin/wkhtmltopdf.sh << EOF
#!/bin/sh

xvfb-run -a wkhtmltopdf "$@"
EOF
# chmod +x /usr/bin/wkhtmltopdf.sh
Coté Symfony2

KNP Labs a développé un bundle dédié à wkhtmltopdf. Ke vais passer par composer pour l'intégrer au framework :

$ cd /chemin/vers/le/projet
$ ./composer.phar require knplabs/knp-snappy-bundle

Activons à présent le bundle dans AppKernel.php :

$bundles = array(
    /* ... */
    new Knp\Bundle\SnappyBundle\KnpSnappyBundle(),
)

Il est nécessaire de le configurer en indiquant le chemin de wkhtmltopdf. Dans le fichier app/config/config.yml, ajoutez ces lignes :

knp_snappy:
    pdf:
        enabled:    true
        binary:     /usr/bin/wkhtmltopdf.sh
        options:    []

Videz le cache et lisez la doc pour savoir comme il fonctionne. Un truc intéressant à savoir : il utilise des feuilles de styles dédiées à l'impression.

Une astuce pour gérer la pagination

Bien qu'il soit ultra puissant, il n'y a pas d'outil pour gérer la pagination. Wkhtmltopdf va récupérer du code HTML et convertir le tout en PDF. En fonction de la taille de la page (qui est paramétrable) et de son format (portait ou paysage), il va enquiller le contenu et générer une page quand il sera nécessaire de le faire. Si vous souhaitez faire des sauts de page, vous devrez faire un peu de javascript. Voici la démarche que j'ai employée pour un projet.

J'ai découpé le contenu en différentes divisions portants la classe "print-page". Chaque division représente un contenu qui doit nécessairement être affiché en début de page. Dans mon projet, j'ai utilisé un rendu en A4 paysage et la zone représentant le contenu fait 1340px de largeur pour 934px de hauteur. Il suffit à présent de modifier la hauteur des divisions en calculant le nombre de pages nécessaires à leur affichage.

// Dépend de jQuery

var page_height   = 934;
var $print_pages  = $('.print-page');

$print_pages.each(function(i, v) {
	var $content    = $(this);	
	var height      = $content.height();
	var pages_count = 1;
	var newheight   = pages_count * page_height;

	while(height - 20 > newheight) {
		pages_count++;
		newheight = pages_count * page_height;
	}
	
	$content.css({height: newheight});
});

Il est également important d'indiquer des largeurs à vos contenus car j'ai eu quelques problèmes de génération avant de le faire.


Vim, PHP et les espaces de noms dans Symfony

Travailler avec les espaces de noms est utile mais dans vim, il faut manuellement les insérer. Cependant, il existe un outil qui s’appuie sur ctags pour traiter le problème : vim-php-namespace. Après avec généré le fichiers de tags, vous aurez la possibilité d'ajouter le "use" nécessaire à l'utilisation d'une classe grâce à un bind déclenché sur le nom d'une classe. Si plusieurs espaces de noms correspondent à votre classe, il va vous les proposer et à vous de choisir celui qui convient.

Vim

Il ne reste plus qu'à traiter un problème : ajouter le "namespace" d'une classe qu'on est en train d'éditer. Je n'ai pas chercher de plugin pour le faire et je me suis contenté d'écrire un bout de script shell pour m'en sortir.

#!/bin/sh

FILE=$1

if [ -z "$FILE" ]; then
	echo "namespace \\"
	exit
fi

DIR=$(dirname "$FILE")
NAMESPACE=$(echo /$DIR | sed 's,/[a-z0-9][^/]*/,,g;s,/,\\,g')

echo "namespace $NAMESPACE;"

Il suffira de le déclancher avec un bind (map) du type :

map <F7> :r!/path/to/namespace %<CR>

Il va générer le code php à insérer en s'appuyant sur l'arborescence où apparait le fichier :

$ namespace src/Deblan/Bundle/BlogBundle/Controller/DefaultController.php
namespace Deblan\Bundle\BlogBundle\Controller;