PHP hors web

Tout le monde connait php, ce langage si pratique pour écrire des pages web dynamiques pouvant accéder à des bases de données, générer des images, des pdf et des animations flash à la volée. Ce que beaucoup ignorent c'est que php est un langage qui peut être utilisé pour autre chose que des pages web.
php est un langage interprété dont vous pouvez vous servir comme perl, python, ruby et les autres. Un autre aspect souvent ignoré de php (et de perl mais c'est une autre histoire) est son orientation objet.
Le troisième côté méconnu de php que je traiterai est sa capacité à construire des interfaces, grâce à php-gtk.

Introduction

Nous aurons besoin d'installer php en version cgi ( pour disposer de l'exécutable php ) et le package php-gtk. Vous trouverez ces packages en téléchargement à partir de la page principale de php, http://www.php.net/. Les versions utilisées pour la rédaction de cet article sont la 4.2.1 pour php et la 0.5.1 pour php-gtk.

Compilation et installation

Il est nécessaire d'avoir sur son système gtk+ 1.2.6 ou supérieur et libglade

Je n'entrerai pas ici dans le détail dans les options de configuration de php, pour celà vous pourrez vous reporter au manuel. On installe php sans options supplémentaires et avec le support MySQL natif. Nous utiliserons le chemin par défaut, celà vous simplifiera la vie pour installer php-gtk. Pour changer le répertoire d'installation, utilisez l'argument --prefix=path du script configure.

Procédure pour une installation par défaut :
Devenez root puis placez vous par exemple dans /usr/local/src et décompressez y les archives php-4.2.1.tar.bz2 et php-gtk-0.5.1.tar.gz (nota: si tar xvfy ne fonctionne pas chez vous, essayez tar xvfj). Suivez la procédure ci-dessous (n'oubliez pas le ./buildconf - inhabituel) :

xavier@zazou:~$ su
Password:
root@zazou:/home/xavier# cd /usr/local/src
root@zazou:/usr/local/src# tar xvfy /cdrom/php-4.2.1.tar.bz2
...
root@zazou:/usr/local/src# tar xvfz /cdrom/php-gtk-0.5.1.tar.gz
...
root@zazou:/usr/local/src# cd php-4.2.1/ && ./configure && make && make install && cd ..
... (allez boire un café)
root@zazou:/usr/local/src# cd php-gtk-0.5.1/ && ./buildconf && ./configure && make && make install && cd ..
... (allez boire un petit deuxième café)

Si quelque chose s'est mal passé, soit il vous manque une librairie (libglade par exemple), soit vous avez oublié de taper ./buildconf

Premiers essais

Nous allons commencer par le grand classique des exemples : Hello World.

Tapez dans un fichier test1.php ces quelques lignes :

<?php
	echo "Hello World\n";
?>

Faites lire ce programme par php de la façon suivante :

xavier@zazou:~/DEVELPhp$ php test1.php
X-Powered-By: PHP/4.2.1
Content-type: text/html

Hello World
xavier@zazou:~/DEVELPhp$

Si les trois lignes d'introduction vous dérangent, vous pouvez utiliser l'option -q de php :

xavier@zazou:~/DEVELPhp$ php -q test1.php
Hello World
xavier@zazou:~/DEVELPhp$

Et pour améliorer le tout, on rend le script exécutable. Celà peut être fait comme montré ci-dessous dans le fichier test2.php. Notez au passage que l'on peut utiliser <? en lieu et place de <?php :

#!/usr/local/bin/php -q
Ligne non traitée par php.
<?
	echo "Hello World\n";
?>

Affectez le mode 755 au fichier test2.php et testez votre oeuvre. Notez que la ligne 2 n'est pas traitée par php mais ne l'importune pas (comme du code html dans une page .php sur votre site).

xavier@zazou:~/DEVELPhp$ chmod 755 test2.php
xavier@zazou:~/DEVELPhp$ ./test2.php
Ligne non traitée par php.
Hello World
xavier@zazou:~/DEVELPhp$

Les classes dans php

On peut, en php, définir des classes, avec des propriétés et des méthodes. L'héritage simple est supporté mais pas l'héritage multiple. Par ailleurs, il n'y a pas de gestion de l'encapsulation, toutes les méthodes et propriétés d'une classe sont publiques. On peut aisément critiquer le modèle objet dans PHP mais n'étant pas un grand amateurs de trolls, je me contenterai ici de vous dire comment utiliser ce qu'on met à votre disposition.

On va définir deux classes, rectangle et pave. Bien sûr pave hérite de rectangle.

Le fichier rectangle.php
<?php
class rectangle {
	var $largeur, $longueur;
	
	function rectangle ($larg, $long) {
		$this->largeur = $larg;
		$this->longueur = $long;
	}
	
	function aire () {
		return $this->largeur*$this->longueur;
	}
}
?>
On voit que la classe rectangle possède deux propriétés largeur et longueur et deux méthodes rectangle() et aire(). On retrouve également un pointeur this, que les habitués de java et de c++ connaissent déjà et qui est une référence sur l'instance de classe (ou objet) dont la méthode est appélée.

La méthode rectangle est le constructeur de la classe rectangle, car elle porte le même nom (tout bêtement).

Le fichier pave.php

<?php
require ("rectangle.php");
class pave extends rectangle {
	var $hauteur;
	
	function pave ($larg, $long, $haut) {
		rectangle::rectangle($larg, $long);
		$this->hauteur = $haut;
	}
	
	function volume () {
		return parent::aire()*$this->hauteur;
	}
}
?>
On voit ici l'utilisation du mot clé extends, qui sert à préciser que la classe pave hérite les propriétés et méthodes de la classe rectangle, dont on importe la définition grâce à la l'instruction require ("rectangle.php");.

La méthode pave est le constructeur de la classe pave, vous vous en êtes doutés. Une chose que l'on peut ici préciser est que, depuis la version 4 de php, si une fonction de même nom que la classe est absente de la classe, alors php parcours l'ensemble des classes parentes à la recherche d'une fonction de même nom que la classe en cours d'instanciation. Pour être plus clair : si la classe pave, qui hérite de rectangle, ne contient pas de méthode nommée pave mais que la classe rectangle en contient une, c'est alors la méthode pave() de la classe rectangle qui est automatiquement exécutée lors de la création d'un objet de la classe pave.

On remarque par ailleurs que le constructeur de la classe pave appelle explicitement le constructeur de sa classe parente, rectangle. En effet, celà n'est pas automatique en php.

Dans la méthode volume, on découvre le mot clé parent, qui fait référence à la classe parente (rectangle) de pave.
J'ai volontairement utilisé cette notation parent:: mais elle est ici équivalente à rectangle::, comme on le voit utilisé dans le constructeur.

Test

Avant d'aller plus avant, on peut vérifier que notre code ne contient pas d'erreurs en vérifiant les fichiers :

xavier@zazou:~/DEVELPhp$ php -q pave.php
xavier@zazou:~/DEVELPhp$ php -q rectangle.php
xavier@zazou:~/DEVELPhp$

Utilisation : le fichier main.php

Il est grand temps de créer un fichier utilisant nos classes, le fichier main.php

#!/usr/local/bin/php -q
<?php
require ("pave.php");

$r1 = new rectangle (5,4);
echo "Aire de r1 : ".$r1->aire()."\n";
echo "\n";
$p1 = new pave (5, 4, 8);
echo "Aire de la base de p1 : ".$p1->aire()."\n";
echo "Volume de p1 : ".$p1->volume()."\n";
?>

On voit ici les appels aux constructeurs de rectangle et de pave, ainsi que les appels des méthodes aire et volume. On constate notamment que p1->aire() appelle la méthode aire de la classe rectangle comme si elle était une méthode de la classe pave.

Après avoir rendu ce script exécutable, on obtient :

xavier@zazou:~/DEVELPhp$ ./main.php
Aire de r1 : 20

Aire de la base de p1 : 20
Volume de p1 : 160
xavier@zazou:~/DEVELPhp$
Vous avez là les bases qui vous permettront de fouillez plus avant les fonctionnalités objet de php.

Et gtk dans tout ça ?

C'est maintenant que l'on y vient. On va reprendre l'exemple ci-dessus mais on va "piloter" les calculs grâce à une interface simple en gtk+ écrite en php, à l'aide de php-gtk.
Pour la création des interfaces, je laisse généralement glade générer le squelette des fichiers, il reste ensuite à en enlever les choses "inutiles" et à traduire le code généré en php. Je suppose que glade ne tardera pas à proposer l'export php. Je ne dis pas que c'est la meilleure méthode, je donne juste mes recettes de cuisine pour que vous les amélioriez ;-).
On va garder les fichiers de définition de classes et ajouter dans le répertoire les fichiers callbacks.php qui contiendra les fonctions de rappels que l'on connectera aux signaux gtk+ et interface.php qui contiendra la création de l'interface du programme.
Pour créer cette interface, on ne va pas utiliser glade (une fenêtre avec trois boutons, faut pas pousser, non plus) et on va donc taper ces fichiers à la main, ainsi que gtkmain.php, qui contiendra l'initialisation et le lancement de la boucle principale du programme.

Le fichier callbacks.php

<?php
function on_ferme () {
	print ("\nOn ferme ...\n\n");
	gtk::main_quit();
}

function rect_cb() {
	$la = rand(1, 100);
	$lo = rand(1, 100);
	echo " *  Création d'un rectangle de ".$lo."x".$la."\n";
	$rect = new rectangle ($la,$lo);
	echo "    --> aire : ".$rect->aire()."\n";
}

function pave_cb($button, $param) {
	$la = rand(1, 100);
	$lo = rand(1, 100);
	$ha = rand(1, 100);
	echo " *  Création d'un pave de ".$lo."x".$la."x".$ha."\n";
	$pave = new pave ($la,$lo,$ha);
	echo "    --> aire : ".$pave->aire()."\n";
	echo "    --> aire : ".$pave->volume()."\n";
	echo "    --> paramètre : ".$param."\n";
	echo "        --> Nom : ".$param[0]."\n";
	echo "        --> Heure de création : ".$param[1]."\n";
}
?>

callbacks.php contient trois fonctions de rappel. on_ferme, la première servira à fermer l'application avec le bouton quitter, elle affiche une courte phrase et appelle la méthode statique main_quit de la classe gtk.
Les deux fonctions suivantes créent respectivement un rectangle et un pave de dimensions pseudo-aléatoires, elles affichent ensuite l'aire dans le cas du rectangle ou l'aire et le volume dans le cas du pavé. Rien de fantastique ici mais on remarque que ces deux fonctions n'ont pas le même prototype. La raison en est que chaque fonction de rappel en php-gtk se voit passer un ou plusieurs paramètre(s).
Le premier paramètre est habituellement le widget qui émet le signal, mais ce n'est pas toujours le cas. On verra à ce propos l'utilisation de la fonction connect_object dans le fichier interface.php.
On peut trouver un autre paramètre facultatif, le paramètre personnalisé, c'est le cas du paramètre $param de la méthode pave_cb.
Pour utiliser le premier paramètre dans la fonction de rappel, il faut tout simplement le déclarer dans le prototype de cette dernière.
On aurait par exemple pu écrire function rect_cb($bouton) { si on avait voulu faire quelque chose au bouton "rectangle" dans le callback comme le rendre insensible aux clics, l'instruction $bouton->set_sensitive(false); dans le corps de cette fonction aurait pu remplir ce rôle.
Le second paramètre est le paramètre personnalisé. Pour l'utiliser, il faut déclarer les deux paramètres dans le prototype de la fonction de rappel. Une fois cela fait, tout ce qu'il y a à savoir est que le second paramètre est le paramètre personnalisé. Nous verrons comment le passer à la fonction de rappel dans le fichier interface.php.

Le fichier interface.php

<?php
function create_window () {
  $window = &new GtkWindow ();

  $vbox = &new GtkVBox(FALSE, 5);
  $window->add($vbox);

  $hbbox_hi = &new GtkHButtonBox();
  $vbox->add($hbbox_hi);
  $hbbox_lo = &new GtkHButtonBox();
  $vbox->add($hbbox_lo);

  $rc_btn = &new GtkButton ("Rectangle");
  $hbbox_hi->add ($rc_btn);
  $pv_btn = &new GtkButton ("Pavé");
  $hbbox_hi->add ($pv_btn);

  $qt_btn = &new GtkButton ("Quitter");
  $hbbox_lo->add ($qt_btn);

  $param = array("parametre", date("H:i:s"));

  $rc_btn->connect ("clicked", "rect_cb");
  $pv_btn->connect ("clicked", "pave_cb", $param);
  $qt_btn->connect ("clicked", "on_ferme");
  $window->connect_object ("destroy", array("gtk", "main_quit"));

  return $window;
}

?>

Une seule fonction dans ce fichier, create_window, qui, comme son nom l'indique, a pour rôle la création de la fenêtre pricipale de notre application de test.
C'est ici que sont regroupés l'essentiel des appels à gtk.

Pour créer un widget en php-gtk, on crée une instance de la classe du widget. Le constructeur prend un certain nombre de paramètres et renvoie un objet de la classe widget. Dans la pratique, on utilise toujours la forme suivante : objet = &new widget(paramètres); de façon à récupérer une référence sur l'objet et non l'objet lui même.

Comme vous pouvez le voir sur les illustrations, notre interface très simple est composée de trois boutons. Nous aurons donc besoin d'une fenêtre :
$window = &new GtkWindow ();
Cette ligne crée une fenêtre principale et nous renvoie une référence sur cette dernière. Le constructeur de GtkWindow prend en réalité un paramètre spécifiant le type de fenêtre gtk que l'on souhaite créer. Ce paramètre ayant pour valeur par défaut GTK_WINDOW_TOPLEVEL, on ne le passe pas. $window = &new GtkWindow (); et $window = &new GtkWindow (GTK_WINDOW_TOPLEVEL); sont équivalentes.

La suite du code ajoute une VBox aux lignes non homogènes et avec un espacement de 5. Le constructeur d'une VBox est : GtkVBox ([bool homogeneous = false , [int spacing = 0 ]]); Notez qu'on est obligés de spécifier la valeur false du paramètre homogeneous alors que c'est la valeur par défaut pour pouvoir fixer le spacing à 5.

On ajoute ensuite des boutons GtkButton que l'on range dans des GtkHButtonBox. Le constructeur d'un GtkButton prend en paramètre le texte à placer sur le bouton.

Les callbacks : Rien de très difficile ici non plus. Notez juste la construction du paramètre $param = array ... qui nous sert dans le callback pave_cb que nous avons vu au-dessus...
Chaque widget émettant des signaux met à disposition les méthodes connect, connect_after, connect_object et connect_object_after.

La méthode connect sert à assigner un callback à un signal envoyé par le widget. Les paramètres sont le signal, la fonction à appelé lors de l'émission de ce dernier et un paramètre personnalisé facultatif.

$rc_btn->connect ("clicked", "rect_cb");
$pv_btn->connect ("clicked", "pave_cb", $param);
Ici on appelle la fonction rect_cb sans paramètre lors d'un clic sur le bouton rc_btn et la fonction pave_cb avec $param en paramètre lors d'un clic sur le bouton pv_btn.

La méthode connect_object(string signal, string function, GtkObject param) assigne une fonction de rappel function à un signal signalmais passe comme premier paramètre à la fonction de rappel le GtkObject param en lieu et place de l'objet émetteur du signal.
Il existe une autre version, peu documentée, que l'on utilise ici, démontrée dans l'exemple 3.4 du manuel de php-gtk permettant d'associer à un signal une fonction statique d'une classe, dont le prototype est connect_object(string signal, array arr)arr est un tableau contenant le nom de la classe et le nom de la méthode à appeler.

$window->connect_object ("destroy", array("gtk", "main_quit"));
Cette ligne permet d'appeler la méthode gtk::main_quit() lors de la fermeture ("destruction") de la fenêtre window.

Le fichier gtkmain.php

#!/usr/local/bin/php -q
<?php
	if (!class_exists('gtk')) {
		if (strtoupper(substr(PHP_OS, 0,3) == 'WIN'))
			dl('php_gtk.dll');
		else
			dl('php_gtk.so');
	}

	require ("pave.php");
	require ("interface.php");
	require ("callbacks.php");

	srand (time());
	
	$ma_fen = create_window();
	$ma_fen->show_all();
	
	gtk::main();
?>

Les cinq premières lignes servent à charger la librairie php-gtk et ce, quel que soit le système d'exploitation utilisé.

Les trois lignes suivantes chargent les fichiers dont on a besoin.

srand (time()); initialise le générateur de nombres pseudo-aléatoires.

$ma_fen = create_window(); crée la fenêtre principale de l'application grâce à la fonction définie dans interface.php. On affiche la fenêtre grâce à la méthode show_all() à la ligne suivante.

Enfin, la méthode statique gtk::main() lance la classique boucle gtk.

Après avoir rendu le script gtkmain.php exécutable, vous devriez pouvoir taper sans obtenir d'erreur ./gtkmain.php

Portabilité et doc ...

php-gtk existe aussi sous windows et on a vu que charger la librairie windows (.dll) ou la librairie linux (.so) en fonction du système d'exploitation sous lequel le script est exécuté ne présente pas de problème. Cela sous entend que vos scripts écrits sous linux tourneront sous windows et vice versa (si vous n'utilisez pas des options extravagantes comme le support de COM sous windows).

Pour installer php-gtk sous windows, récupérez php-gtk pour windows. Décompressez l'archive et conformez vous aux instructions données dans le fichier README. Il y a deux exécutables fournis, php et php_win. Le premier offre une console pour les messages alors que le deuxième ne le fait pas.
Je n'en dis pas plus sur windows ici, ce serait manquer de respect. L'important est juste de retenir que vos scripts php et php-gtk sont portables ...

Pour ce qui est de la documentation, reportez vous à la zone de liens pour la trouver, elle a été traduite en français, entre autres langues, pour les anglophobes.

Et enfin ...

Pour les exemples ci dessus, j'ai utilisé un php "de base" mais rien ne vous empêche, si vous en avez besoin, de lui adjoindre les fonctionnalités qui vous font défaut lors de la configuration, comme le support pgsql, pdf, ming ou encore la prise en charge de gd (comme on se retrouve ;-) ...).

Bon allez, digérez ça pendant l'été et à bientôt pour de nouvelles aventures.

Xavier GARREAU - http://www.xgarreau.org/ - <xavier@xgarreau.org>
Ingénieur de recherche PRIM'TIME TECHNOLOGY - http://www.prim-time.com/
Président du ROCHELUG - http://www.rochelug.org/

Liens :

PHP : http://www.php.net
Manuel PHP : http://www.php.net/docs.php
PHP-GTK : http://gtk.php.net/
Manuel PHP-GTK : http://gtk.php.net/docs.php

a+

Auteur : Xavier GARREAU
Modifié le 26.04.2006