1. Introduction▲
Le but de cet article est de vous montrer comment faire tourner un script PHP5 en tant que service Windows. Ce besoin s'avère parfois nécessaire lorsque vous disposez de scripts "démons" qui doivent s'exécuter inconditionnellement pour effectuer leurs opérations. Sous linux ou unix, il est assez simple de mettre en oeuvre de tels démons et de les faire démarrer au boot de la machine. Sous windows par contre, c'est une autre paire de manches. Wez Furlong a créé l'extension win32service.dll qui permet de faire cela. Nous allons donc voir comment la mettre en oeuvre à l'aide d'un petit exemple.
2. Téléchargement de l'extension▲
Avant de télécharger l'extension, assurez-vous que vous disposez d'une version de PHP5 suffisament récente pour être compatible avec celle-ci. Pour l'exemple qui vous sera expliqué en section 3, j'ai utilisé la version 5.0.3.3 disponible ici et l'extension proprement dite est disponible ici
Si vous ne savez pas comment installer PHP5, référez-vous à d'autres tutoriels de ce site décrivant les étapes à suivre. Pour installer l'extension, vous devez simplement télécharger le fichier et le sauvegarder dans le répertoire "ext" de php, ce répertoire étant le répertoire par défaut pour les extensions. Si vous avez spécifié un autre répertoire dans php.ini via la directive extension_dir, placez-y votre extension.
Il est inutile de faire charger cette extension par Apache car les scripts de type démon seront exécutés via PHP-CLI. Vous ne devez donc pas modifier votre php.ini. Nous verrons comment charger l'extension dynamiquement directement dans le script.
3. Petit exemple de démon▲
Pour vous fournir un petit exemple, j'ai décidé de développer un petit démon qui effectue le backup d'une DB SQLite toutes les heures. Tous les commentaires utiles sont dans le script.
<?php
define(TARGETDB,
'c:/dvpactivedb.db'
);
define(BACKUPDB,
'd:/dvpbackupdb.db'
);
define(LOGFILE,
'c:/backupDaemon.log'
);
define(BACKUPINTERVAL,
3600
);
define(ERR_CD1,
'Impossible d
\'
ouvrir le fichier Log'
);
define(ERR_CD2,
'La base de données à sauvegarder n
\'
a pu être trouvée'
);
define(ERR_CD3,
'Impossible de copier la base de donnée'
);
class
BackupDaemon
{
private
$logFileRes
=
null
;
function
__construct
()
{
$this
->
startDaemon();
}
private
function
startDaemon()
{
$x
=
win32_start_service_ctrl_dispatcher('PHP_BackupDB'
);
$sleepTime
=
1
;
$this
->
backupDB();
/* On commence par effectuer un backup directement
La condition de cette boucle sert à vérifier qu'on a pas tenté de stopper
le service dans le SCM. Il ne faut donc pas trop dormir trop longtemps(sleep) sous peine que l'utilisateur
ne puisse stopper le service.
*/
while
(WIN32_SERVICE_CONTROL_STOP !=
win32_get_last_control_message()) {
if
($sleepTime
==
BACKUPINTERVAL)
{
$sleepTime
=
1
;
$this
->
backupDB();
}
sleep(1
);
$sleepTime
++;
}
}
private
function
backupDB()
{
if
(!
file_exists(TARGETDB))
{
$this
->
handleError(ERR_CD2);
}
if
(!
(copy(TARGETDB,
BACKUPDB)))
{
$this
->
handleError(ERR_CD3);
}
$this
->
logIt('Dernier backup effectué correctement:'
.
date('y/m/d h:i:s'
));
}
private
function
logIt($msg
)
{
if
(!
is_resource($this
->
logFileRes))
{
$this
->
logFileRes=
fopen(LOGFILE,
'a+'
) or
$this
->
handleError();
}
fwrite($this
->
logFileRes,
$msg
.
"
\r\n
"
);
}
private
function
handleError($err
)
{
if
($err
!=
ERR_CD1)
{
$this
->
logIt($err
);
}
$this
->
__destruct
();
}
function
__destruct
()
{
/* Si le fichier log a été correctement ouvert, on le ferme */
if
(is_resource($this
->
logFileRes))
{
fclose($this
->
logFileRes);
}
die();
}
}
/* On vérifie que l'extension est chargée, si elle ne l'est pas, on la charge*/
if
(!
extension_loaded('php_win32service'
))
{
dl('php_win32service.dll'
) or
die('Impossible de charger la librairie php_win32service.dll'
);
}
/* On vérifie les paramètres passés au script */
if
(isset($argv
[
1
]
))
{
$validParams
=
array
('install'
,
'uninstall'
);
if
(!
in_array($argv
[
1
],
$validParams
))
{
die('Veuillez specifier install || uninstall comme premier parametre'
);
}
if
($argv
[
1
]
==
'install'
) {
/*
Création du service dans le SCM (service control manager)
La fonction win32_create_service crée le service dans le SCM. Le nom du service
est donné en tant que premier paramètre, le label qu'il aura dans le SCM dans le second
et le fichier qu'il devra lancer ainsi que l'argument run dans le 3ème paramètre.
*/
$createService
=
win32_create_service(array
(
'service'
=>
'PHP_BackupDB'
,
'display'
=>
'Exemple de démon DVP'
,
'params'
=>
__FILE__
,
));
if
($createService
!==
true
)
{
die('Impossible de créer le service!'
);
}
else
{
die('Service cree avec succes'
);
}
}
else
if
($argv
[
1
]
==
'uninstall'
)
{
/* win32_delete_service marque le service comme "disabled/désactivé" dans le SCM. */
$removeService
=
win32_delete_service('PHP_BackupDB2'
);
switch
($removeService
)
{
case
1060
:
die('Ce service n
\'
existe pas'
);
break
;
case
1072
:
die('Ce service n
\'
a pu être supprimé, il est probablement actif ou corrompu!'
);
break
;
case
0
:
die('Service supprime avec succes'
);
break
;
default
:
die();
break
;
}
}
}
else
{
$daemon
=
new
BackupDaemon();
}
?>
Lorsque vous aurez téléchargé ou recopié ce script, vous devez l'exécuter en ligne de commande comme suit pour installer le service:
Lorsque le service a été créé, vous pourrez dès lors le démarrer soit en exécutant
net start lenomdevotreservice
soit, en ouvrant l'interface graphique permettant de gérer les services, comme ceci:
et en cliquant sur démarrer/start
4. Fonctionnement▲
Grâce à cette nouvelle extension, il est désormais assez facile d'exécuter un script PHP5 en tant que service windows. A l'heure actuelle, il n'y a quasiment aucune documentation sur cette extension. Elle est assez basique et comprend peu de fonctions. Ces fonctions ont été utilisées dans le script donné en exemple.
L'utilisateur par défaut qui est "owner" du service est l'utilisateur system. La fonction win32_create_service permet de spécifier un autre utilisateur en lui passant 'user' => le_user comme paramètre. Ne perdez pas de vue lorsque vous créerez vos services qu'il faut toujours vérifier si le SCM envoie un signal d'interruption du service. Si vous faites un démon qui dort pendant 1h (sleep 3600), l'utilisateur ne pourra jamais stopper le service car le signal ne sera jamais intercepté par le script. La plateforme que j'ai utilisé pour tester cette extension est Windows XP pro service pack1.
5. Téléchargement▲
Vous pouvez télécharger ce script ici