Le PHP/Java Bridge de sourceforge.net permet d’intégrer Java et PHP dans les deux sens : il permet d’appeler des classes Java depuis PHP, mais aussi d’appeler des scripts PHP depuis Java. Pour ce sens, le bridge fournit même deux techniques : appel en CGI, ou via la nouvelle technique d’appel de script de Java 6, référencée sous la JSR-223. On notera que le pont peut aussi fonctionner entre PHP et .NET ou Mono.
Ces notes ont servi de base pour la rédaction d’un article paru le numéro 6/2008 de PHP Solutions de novembre 2008.
Installation
L’environnement que je décris ci-dessous n’est qu’un exemple d’environnement compatible avec la version 5.2 du pont. Il a été testé sous Linux Ubuntu 8.04 et sous Window XP SP2 et SP3.
Environnement PHP :
-
Apache Web Server 2.2
-
PHP 5.2
-
allow_url_include = On dans php.ini
Environnement Java :
-
Java JDK 6
-
Apache Tomcat 6.0
Ensuite, il faut déployer JavaBridge.war dans Tomcat. Tomcat étant par défaut en mode de déploiement automatique, le déploiement se fait par copie du fichier dans son répertoire webapps/.
Intégration par CGI
On ne peut pas réellement parler d’intégration. Cette technique permet surtout de déployer des pages PHP dans Tomcat, sans avoir recours à Apache ou IIS.
Java Scripting API
La Java Scripting API, parfois connue sous le nom de sa spécification, JSR-223, définit une API permettant d’utiliser des langages de script dans une application Java. Le langage JavaScript est nativement supporté par la machine virtuelle qui intègre l’interpréteur Rhino. Le pont peut servir d’interpreteur PHP pour cette API.
Il fournit cinq modes d’intégration :
-
php simple, qui exécute simplement un script,
manager.getEngineByName("php")
-
php interactif, qui exécute une série de lignes de code et qui permet de récupérer un résultat dans Java,
manager.getEngineByName("php-interactive")
-
php invocable, qui permet d’appeler une fonction PHP et de récupérer son retour,
manager.getEngineByName("php-invocable")
-
un mode Web, qui permet de partager un contexte et des informations de session,
-
un mode Web invocable, qui fait la synthèse de deux derniers (c’est mon préféré).
// Affichage du titre
out.println ("Essai de JSR-223 - mode Web invocable
");
// Récupération de l'interpréteur de PHP via la méthode de fabrique
ScriptEngine engine = EngineFactory.getInvocablePhpScriptEngine (this, application, request, response);
// Connexion des sorties Java et PHP
engine.getContext ().setWriter (out);
// Déclaration de la fonction hello
engine.eval ("<?php function hello ($who) {return 'Hello '.$who;}; ?>");
// Appel de la fonction hello et affichage du résultat
out.println (((Invocable) engine).invokeFunction ("hello", new Object[] {"world"}));
// Réinitialisation de l'interpréteur
engine.eval ((java.io.Reader)null);
Pont PHP vers Java
Appeler une classe standard
<?php
// Fichier des fonctions et classes PHP du pont
require_once ("link:http://localhost:8080/JavaBridge/java/Java.inc[http://localhost:8080/JavaBridge/java/Java.inc]");
// Instanciation de la classe Java StringBuilder via la classe PHP Java
$buf=new Java ("java.lang.StringBuilder");
// Appel de la méthode append sur l'objet Java StringBuilder
$buf->append ("Hello");
$buf->append (" ");
$buf->append ("World");
// Appel de la méthode toString sur l'objet Java StringBuilder et affichage du résultat
echo ($buf->toString ());
?>
A chaque appel de la page, une requête est envoyée vers Tomcat, à la servlet PhpJavaServlet de l’application JavaBridge, puis les échanges se font ensuite sur un port spécifique (entre 9267 et 9367)
Appeler une classe personnalisée
Pour que PHP puisse utiliser des classes métier, il faut en plus que l’application puisse avoir accès aux archives jar qui contiennent leurs définitions. L’accès peut être donné côté PHP, par la fonction java_autoload, ou côté Java. Si une seule archive est utilisée, la première solution est plus pratique, et si plusieurs archives sont nécessaires, ce qui est le cas le plus fréquent, il est plus pratique de gérer leur mise à disposition du côté de Java.
<?php
require_once ("link:http://localhost:8080/JavaBridge/java/Java.inc[http://localhost:8080/JavaBridge/java/Java.inc]");
// Chargement de l'archive java copiée localement
java_autoload ("java/university.jar") ;
// Instanciation d'un objet Course
$course=new Java ("fr.sewatech.university.model.Course", "mm-uml", "Analyse et conception avec UML");
// Affichage du résultat
echo ($course->toString ());
?>
Appeler un bean Spring
En effet, depuis plusieurs années, de nombreuses applications Java ont été développées sur ce type de framework. Que ce soit Spring ou ses concurrents, ils ont pour point commun de gérer des composants, en les instanciant et en les mettant à disposition via des noms logiques plutôt que par des noms de classes.
Pour appeler des services métier intégrés à Spring, notre application PHP doit d’abord accéder au contexte Spring avant d’accéder aux beans.
<?php
require_once("link:http://localhost:8080/UniBridge/java/Java.inc[http://localhost:8080/UniBridge/java/Java.inc]");
// Instanciation du contexte applicatif de Spring paramétré dans le fichier application-context.xml
$ctx = new Java(
"org.springframework.context.support.ClassPathXmlApplicationContext",
"application-context.xml");
// Demande d'une référence au bean courseService inscrit dans Spring
$service = $ctx->getBean("courseService");
// Utilisation du bean
$course=$service->findById(1);
echo ($course->toString());
?>
Il faut toutefois faire attention à la gestion des librairies dépendantes. En effet, lorsque nous utilisons des composants métier intégrés à Spring, nous devons accéder aux archives métier et à des archives techniques (spring.jar, commons-beanutils.jar, commons-collections.jar,…). Nous avons trois possibilités pour rendre celles-ci accessibles :
-
les ajouter à la fonction java_autoload, ce qui peut être fastidieux,
-
les ajouter au répertoire lib/ de Tomcat, ce qui les rend accessibles à toutes les applications déployées, mais peut provoquer des conflits avec des versions utilisées dans d’autres applications,
-
les ajouter au répertoire WEB-INF/lib de l’application JavaBridge, ce qui constitue la meilleure option si une seule application Java est utilisée depuis PHP.
Pour éviter les risques de conflit lorsque plusieurs applications Java sont utilisées, on peut mettre en place une quatrième technique, en intégrant le contenu de JavaBridge à chaque application Java. Chacune embarque donc son propre pont ; de plus, toutes les classes Java nécessaires à PHP sont déjà chargées par le pont, ce qui nous dispense d’appeler la fonction java_autoload. Seul le require_once doit être adapté pour charger celui de l’application Java accédée. Cette technique a été utilisée dans l’exemple précédent, ce qui explique l’absence de la fonction java_autoload.
Pour ajouter le pont à une application, il faut :
-
ajouter les fichiers JavaBridge.jar et php-servlet.jar, dans le répertoire WEB_INF/lib de l’application,
-
ajouter le répertoire java et son contenu à la racine de l’application,
-
prendre, dans le fichier WEB-INF/web.xml, les deux portions qui concernent la servlet PhpJavaServlet (éléments <servlet> et <servlet-mapping>) et les ajouter dans le fichier équivalent de notre application.