Deblan blog

Tag #php

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.


PDNS-Console is released!

Je viens de finaliser la première version de pdns-console, un outil en ligne de commande qui permet de gérer un serveur PowerDNS : un puissant serveur DNS qui repose sur des bases de données comme MySQL.

La particularité de pdns-console est qu'il permet de versionner ses zones.

  • On peut donc écrire différentes zones
  • On peut associer une zone à plusieurs domaines
  • Une zone est composée d'une ou plusieurs version dont l'une d'elle pourra être activée
  • La version activée d'une zone sera appliquée aux noms de domaine associés

pdns-console ajoute également des contraintes dans la base de données pour rendre les enregistrements cohérents.

Sous licence BSD, les sources et la procédure d'installation sont dispos sur Github. Je vous invite à tester, forker et bien sur contribuer !

Voici un exemple qui provient du README (en anglais) :

Specifications

  • We want to mange the domain example.tld
  • We need 3 records:
    • example.tld -> 1.2.3.4
    • www.example.tld -> same as example.tld
    • The MX of example.tld is mail.foo.net

Add the domain

$ ./app/console domain:add
Name: example.tld
MASTER [null]:
Type [NATIVE]:
Domain added
$ ./app/console domain:list
DOMAIN: example.tld
ID    : 5             # This ID is important
TYPE  : NATIVE
MASTER:

Create a zone

Interactive (or not)
$ ./app/console zone:add
Name: Example zone
Description: My example zone
Zone added.
The list is updated
$ ./app/console zone:list
Example zone
------------
My example zone
ID: 4                 # This ID is important

No version found
New version for "Example zone" (ID=4)
$ ./app/console zone:version:add 4 # My zone ID
Zone version added.
$ ./app/console zone:list
Example zone
------------
My example zone
ID: 4

Version: 1 - Active: No

No record found.

Add the records

"SOA" record (no interactive)
$ ./app/console zone:record:add 4 1 --name @ --type SOA --content "localhost. postmaster@localhost 0 10800 3600 604800 3600" --ttl 3600 --prio null
"A" record (interactive)
$ ./app/console zone:record:add 4 1
Name: @
Content: 1.2.3.4

Available types: A AAAA CNAME MX NS TXT SPF WKS SRV LOC SOA
Type: A
TTL: 3600
Prio [null]:
Zone record added.
"CNAME" record. "--ttl" is missing
$ ./app/console zone:record:add 4 1 --name www --type CNAME --content example.tld. --prio null
TTL: 3600
Zone record added.
"MX" record. The validation of "--prio" failed
$ ./app/console zone:record:add 4 1 --name @ --type MX --content mail.foo.net. --ttl 3600 --prio badValue
Prio [null]: badValueAgain
Prio [null]: 10
Zone record added.
My version is now ready
$ ./app/console zone:list
Example zone
------------
My example zone
ID: 4

Version: 1 - Active: No

   ID | NAME                  | TYPE      | TTL    | PRIO    | CONTENT
----------------------------------------------------------------------
   13 | @                     | SOA       | 3600   |         | localhost postmaster@localhost 0 10800 3600 604800 3600
   14 | @                     | A         | 3600   |         | 1.2.3.4
   15 | www                   | CNAME     | 3600   |         | example.tld.
   16 | @                     | MX        | 3600   | 10      | mail.foo.net.

Active and assign the new zone

$ ./app/console zone:version:active 4 1
Zone version activated.
$ ./app/console zone:assign 4 5
Domain zone updated.
./app/console domain:list --zone
DOMAIN: example.tld
ID    : 5
TYPE  : NATIVE
MASTER: 


      > Example zone
      > ------------
      > My example zone
      > ID: 4
      > 
      > Version: 1 - Active: Yes
      > 
      >    ID | NAME                  | TYPE      | TTL    | PRIO    | CONTENT
      > ----------------------------------------------------------------------
      >    13 | @                     | SOA       | 3600   |         | localhost postmaster@localhost 0 10800 3600 604800 3600
      >    14 | @                     | A         | 3600   |         | 1.2.3.4
      >    15 | www                   | CNAME     | 3600   |         | example.tld.
      >    16 | @                     | MX        | 3600   | 10      | mail.foo.net.
      > 

Push modifications

./app/console zone:push

Test :)

$ dig +short -t A @localhost example.tld
1.2.3.4
$ dig +short -t CNAME @localhost www.example.tld
example.tld.
$ dig +short -t MX @localhost example.tld
10 mail.foo.net.

T411 en console

Pour les addicts de t411, j'ai développé un outil en ligne de commande permettant d'accéder aux données du site. Ainsi, il est possible de faire des recherches avec critères, afficher les détails d'un torrent ou bien le télécharger. L'API du site a des bugs qui ne sont pas encore corrigés mais il est déjà possible de faire quelques trucs sympas.

Le code source et la doc sont disponibles sur Github. Tout est libre et ne vous génez pas pour forker :)


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.


Installer plusieurs versions de PHP sur un serveur web

Il arrive qu'il soit nécessaire d'installer des versions de PHP spécifiques pour des projets qu'on héberge. Le must du must est de passer par son gestionnaire de paquet mais ce n'est pas toujours possible. Il convient donc de récupérer le ou les versions nécessaires et de les compiler.

Une des problématiques réside dans la sécurisation du serveur web. Il est important de considérer que se passer de son gestionnaire de paquet est dangereux car on ne bénéficie pas des mises à jour de sécurité et qu'il est impératif de gérer les droits pour que les projets impactés s'exécutent avec des utilisateurs qui leur sont dédiés.

La procédure d'installation n'est pas compliquée mais ça demande d'être minutieux.

Pour gérer la récupération du code source et de sa compilation, j'aime beaucoup travailler avec PhpFarm. On va donc récupérer le projet et simplement balancer un peu de config.

Pour cet exemple, je veux d'installer PHP 5.4.28. L'ensemble des commandes est fait en root.

aptitude update
aptitude install git make
cd /usr/local
git clone git://git.code.sf.net/p/phpfarm/code phpfarm
cd phpfarm/src

Je vous propose de modifier les options de compilation afin d'avoir quelques outils souvent très utilisés. Il suffit d'éditer le fichier src/options.sh et d'éditer la liste des paramètres :

configoptions="\
--enable-bcmath \
--enable-calendar \
--enable-exif \
--enable-ftp \
--enable-mbstring \
--enable-pcntl \
--enable-soap \
--enable-sockets \
--enable-sqlite-utf8 \
--enable-wddx \
--enable-zip \
--with-openssl \
--with-zlib \
--with-gettext \
--enable-libxml \
--with-zlib \
--with-xsl \
--with-jpeg-dir \
--with-png-dir \
--with-gd \
--with-ttf \
--with-freetype-dir \
--with-pdo-mysql \
--with-mysql \
$gcov"

À présent, il suffit de demander à PhpFarm de télécharger et compiler notre version de PHP.

./compile.sh 5.4.28

Il manquera sans doute des lib de dev. Par exemple, s'il remonte une erreur concerant jpeg, faite une recherche du type aptitude search jpeg | grep dev et installez ce qui convient (le paquet libjpeg8-dev dans ce cas).

Une fois la compilation achevée, il faut configurer le serveur web (apache2 dans mon cas). Je rappel que je veux que mes projets aient un utilisateur dédié. Je vais faire une configuration similaire à celle-ci.

a2enmod actions suexec
mkdir -p /var/www/service-web/bin/cgi
echo /usr/local/phpfarm/inst/bin /var/www/service-web/bin/cgi none bind >> /etc/fstab
mount /usr/local/phpfarm/inst/bin

Si j'ai un projet "blog", mon utilisateur dédié sera "webblog". J'ai également un groupe "webgroup" pour tout mes projets.

addgroup webgroup
useradd -G webgroup -s /bin/false -M webblog
chown -R webblog:webgroup /chemin/vers/projet/blog
mkdir /var/www/service-web/bin/cgi/webblog
cd /var/www/service-web/bin/cgi/webblog
cat > php-cgi <<EOS
#!/bin/sh
exec /usr/local/phpfarm/inst/bin/php-cgi-5.4.28
EOS
chown -R webblog:webgroup .
chmod u+x php-cgi

Il manque plus que la modification du VirtualHost :

<VirtualHost *:80>
	[...]
	SuexecUserGroup webblog webgroup
	ScriptAlias /phpfarm-bin/ /var/www/service-web/bin/cgi/webblog/
	AddHandler application/x-httpd-php5 php
	Action application/x-httpd-php5 /phpfarm-bin/php-cgi
	[...]
</VirtualHost>

Faites une petite prière et relancez apache :

service apache2 restart

Comme je n'utilise que du FastCGI (ou des CGI), je ne sais pas comment la configuration réagit si PHP (en module apache) est activé.