Entries for tag test

Sun Dec 27 2009

PHP, Programming, Tuto

extension, php, test

3 comments

Créer une extension PHP - Part 1: Installation du matos

Intro:

Qui n'a jamais rêvé de ces mondes souterrains, de ces mers lointaines peuplées de légendes ou d'une richesse soudaine qui se conquérerait au détour d'un chemin de la Cordillière des Andes ?

Okee. En fait je voulais juste dire qu'aujourd'hui on va débuter une série d'articles (probablement entrecoupés de conneries à 2 balles) dont la futile existence consiste à vous apporter les bases nécessaires pour que -un jour peut être- vous soyez en mesure d'écrire votre propre extension php en C ou C++.

Inutile de tergiverser, nous allons nous focaliser sur un environnement UNIX. Bien que de gros efforts ont été faits, développer sous Windows pour PHP est encore légèrement chiant, notamment lorsque l'on fait intervenir des librairies tierces. Néanmoins, je ne dis pas que -par chance- il se peut que un jour -par hasard- je complète la série avec un article focalisé sur Windows.

Si tout s'est bien passé, le titre parle d'installation du matos

Il nous faut:

En fait je pense que c'est à peu près tout. De toute façon, si ce n'est pas suffisant, on le saura: ça va merder. Plus tard, on pourra ajouter re2c lorsqu'on abordera (qui sait?) la customisation de la grammaire de php, par exemple autoriser la syntaxe courte pour les array [1,2].

Votre distrib linux contient probablement déjà tout le nécessaire. Si ce n'est pas le cas, vous pouvez surement installer ces tools via le système de packages ou directement via les sources. Ca fonctionne généralement très bien. Sous OSX, il faudra que vous installiez les developer tools c.a.d. Xcode et sa suite, mais rassurez vous, nous n'aurons pas besoins de le lancer.

Voici ce que nous allons faire: On va compiler et installer une version de php qui sera dédiée pour nos expérimentations. Notre extension php sera compilée avec les sources de cette version fraichement installée. Dans un premier temps, nous utiliserons exclusivement la version CLI de php: plus simple à mettre en oeuvre, notamment pour les tests.

Organisation des fichiers

On va bosser chez nous. Nul besoins de polluer /usr, NUL BESOINS D'ETRE ROOT.

$HOME/                <-- /home/machin ou /Users/machin etc
     vrac/            <-- tout sera ici
         php-5.3.1/   <-- les sources de php
         php/         <-- là où sera installé php
         phpext/      <-- le répertoire de nos extensions
               part1/ <-- la première extension

Choper et compiler les sources de php

Ca se passe ici. Je prends PHP 5.3.1 Complete Source Code en tar.gz.

$ cd ~/vrac
$ wget http://fr.php.net/get/php-5.3.1.tar.gz/from/this/mirror

Une fois les sources téléchargées, une petite vérif qui va bien:

$ md5sum php-5.3.1.tar.gz
41fbb368d86acb13fc3519657d277681  php-5.3.1.tar.gz

Sous OSX, si vous n'avez pas installé GNU Coreutils, ceci devrait fonctionner:

$ md5 php-5.3.1.tar.gz 
MD5 (php-5.3.1.tar.gz) = 41fbb368d86acb13fc3519657d277681

OK. C'est bon. On extrait le tarball compressé:

$ tar zxf php-5.3.1.tar.gz
$ cd php-5.3.1

Avant de compiler php, passons rapidos en revue ce que contiennent les sources:

configure <-- configure et génère les Makefile
ext/      <-- les extensions (core et optionnelles)
main/     <-- les sources "basiques" de php (IO, networks...)
sapi/     <-- Server Application Programming Interface (cli, cgi, apache..)
tests/    <-- les fichiers de tests (make test)
TSRM/     <-- Thread Safe Resource Manager
win32/    <-- trucs spécifiques à Windows
Zend/     <-- le Zend Engine (gestion mémoire, opcodes etc)

On va compiler et installer une version ultra light de php cli. Pour voir la liste des options de compilations:

$ ./configure --help

Ce qui nous intéresse est ceci:

$ ./configure --prefix=$HOME/vrac/php \
  --with-config-file-path=$HOME/vrac/php \
  --disable-all --enable-cli

Ensuite, si tout s'est bien passé, les dernières lignes doivent ressembler à ça:

Generating files
updating cache ./config.cache
creating ./config.status
creating php5.spec
creating main/build-defs.h
creating scripts/phpize
creating scripts/man1/phpize.1
creating scripts/php-config
creating scripts/man1/php-config.1
creating sapi/cli/php.1
creating main/php_config.h
creating main/internal_functions.c
creating main/internal_functions_cli.c
+--------------------------------------------------------------------+
| License:                                                           |
| This software is subject to the PHP License, available in this     |
| distribution in the file LICENSE.  By continuing this installation |
| process, you are bound by the terms of this license agreement.     |
| If you do not agree with the terms of this license, you must abort |
| the installation process at this point.                            |
+--------------------------------------------------------------------+

Thank you for using PHP.

On enchaine avec un grand classique:

$ make

La compilation est rapide puisque notre configuration est très light. Encore une fois, si tout est OK, on doit voir un truc du genre:

Build complete.
Don't forget to run 'make test'.

Avant d'installer les fichiers, on obtempère en lançant les tests. Ca ne coute rien. A si, ça coute pas mal de temps. C'est bien plus long que la compilation...

$ make test

Là sur ma machine, j'obtiens ceci:

=====================================================================
TEST RESULT SUMMARY
---------------------------------------------------------------------
Exts skipped    :   71
Exts tested     :    7
---------------------------------------------------------------------

Number of tests : 10899              5974
Tests skipped   : 4925 ( 45.2%) --------
Tests warned    :    0 (  0.0%) (  0.0%)
Tests failed    :   28 (  0.3%) (  0.5%)
Expected fail   :    6 (  0.1%) (  0.1%)
Tests passed    : 5940 ( 54.5%) ( 99.4%)
---------------------------------------------------------------------
Time taken      :  306 seconds
=====================================================================

La plupart des tests non skippés sont passés (99.4%). C'est bon. S'il y avait eu un sérieux problème, comme ça m'est déjà arrivé, c'est l'ensemble des tests qui auraient échoué.

On continue en installant les fichiers. Rappellez-vous, il seront installés dans $HOME/vrac/php. Encore une fois, pas la peine d'être root puisque nous restons dans notre home.

$ make install

OK, les fichiers ont normalement été installés dans $HOME/vrac/php. Sous OSX, php cli est nommé php.dSYM. On va le renommer en php. Sans changer de répertoire (toujours sous php-5.3.1), on fait:

$ mv ../php/bin/php.dSYM ../php/bin/php

Pour être sur que toute cette daube fonctionne, quoi de mieux que de tester notre php fraichement installé?

$ $HOME/vrac/php/bin/php --version
PHP 5.3.1 (cli) (built: Dec 26 2009 22:32:35) 
Copyright (c) 1997-2009 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2009 Zend Technologies

Si vous n'obtenez pas quelque chose de fortement similaire, alors il y a un prob! Je conseillerai dans ces cas là de prendre un café et une clope, puis de reprendre à zéro. Pour rappel, quand on est dans php-5.3.1 ceci permet de cleaner le chantier:

$ make clean

Ne pas hésiter à checker par 3 fois les paramètres passés à configure.

RAS pour php. (N)ext

Ce premier billet ne s'occupe que de l'installation des trucs nécessaires. Nous allons tout de même compiler une extension absolument bidon et la tester via le système de tests fourni par php. Ca nous sera utile pour la suite. Les sources sont sur github. A vous de vous démerder pour que part1 soit placé dans $HOME/vrac/phpext. Ceci fonctionne:

$ cd ~/vrac
$ git clone git://github.com/metagoto/phpext.git

Comme vous pouvez le voir, les sources de cette extension nommée part1 sont en dehors du source tree de php. On va donc utiliser le tool phpize que nous venons juste de compiler: ce dernier se charge de créer un fichier configure pour notre extension.

$ cd ~/vrac/phpext/part1
$ $HOME/vrac/php/bin/phpize 
$ ./configure --with-php-config=$HOME/vrac/php/bin/php-config

Ensuite, on build, mais on n'installe pas car ça ne sert à rien pour ce qu'on veut faire.

$ make
...
Build complete.
Don't forget to run 'make test'.

Et on ne lance pas les tests! Tout du moins, on ne va pas faire ce qui nous est proposé. Pour tester notre extension, la commande utilisée est plus subtile.

Tester une extension

Le lecteur consciencieux aura remarqué la présence d'un répertoire part1/tests. Dedans y figure un misérable fichier nommé 001.phpt. Ce dernier est au format compatible avec le lanceur de tests de php qui par chance a été créé lors de notre précédent appel à ./configure. Ce fichier est nommé run-tests.php.

Aussi, si tout s'est bien déroulé, notre extension compilée a été placée dans le répertoire part1/modules sous le nom part1.so. Il va donc falloir que php charge cette extension puis lance l'unique test 001.phpt. Nous sommes toujours dans le répertoire part1. La commande est la suivante:

$ TEST_PHP_EXECUTABLE=$HOME/vrac/php/bin/php \
  $HOME/vrac/php/bin/php run-tests.php \
  -d "extension=$HOME/vrac/phpext/part1/modules/part1.so"

Là le test est immédiat. Si ça passe, et ça DOIT:

=====================================================================
PASS part1 test [tests/001.phpt] 
=====================================================================

Maintenant, que fait la commande bizarre que nous venons d'exécuter ? En gros, on exécute le fichier run-tests.php avec notre php. Habituellement, ce fichier est lancé via un make test. Sauf que là nous ne sommes pas dans un source tree de php, nous sommes dans le source tree de notre extension. run-tests.php a besoins de savoir explicitement quel est l'exécutable utilisé pour les tests, c'est pourquoi on définit la variable TEST_PHP_EXECUTABLE. Aussi, nous voulons que php charge notre extension. C'est chose faite en passant l'argument -d qui permet de définir un couple clé/valeur au format ini.

Mais qu'y a-t-il dans part1/tests/001.phpt? Ceci:

--TEST--
part1 test
--SKIPIF--
<?php if (!extension_loaded('part1')) die('part1 not loaded'); ?>
--FILE--
<?php 
part1_test();
?>
--EXPECT--
test from part1

Ca me parait assez explicite. Le test est skippé si l'extension part1 est absente. Oui c'est notre extension! Le test à proprement parler consiste à exécuter la fonction part1_test(). Il s'agit de la seule fonction exportée de notre extension. Cette fonction se contente juste de faire l'équivalent d'un print 'test from part1'.

Dacodac. Nous avons maintenant en main les bases pour compiler, charger et tester des extensions php.

Pour moi c'est RAS.