Read Me
What is the PHP/Java Bridge?
----------------------------
The PHP/Java Bridge is a network protocol which can be used to
connect PHP with a java or ECMA 335 VM.
Please read the ABOUT.HTM contained in the download archive or
http://php-java-bridge.sf.net for more information.
Build and execution instructions:
---------------------------------
NOTE: If you run Security Enhanced Linux, you must update the policy
and tag the files with the correct SEL contexts, please see below, or
please install the binary RPM instead.
In the directory php-java-bridge-p.x.y type:
java -version # 1.4 or above (1.5 or 1.6 recommended)
gcc --version # 3.2.3 or above (4.0.x recommended)
apachectl -version # Apache 1.3 or above (2.x recommended)
php-config --version # PHP 4.3.4 or above (5.x or 6.x recommended)
make null --version # GNU make 3.79 or above
phpize &&
./configure --with-java=/opt/IBMJava2-141 \
--disable-servlet \
--disable-script \
--disable-faces &&
make &&
su -c "make install"
If your administrator allows you to dynamically load extensions, you
can now test the extension by invoking the test.php with the
command: php ./test.php.
Please see the output of ./configure --help=recursive for further
configure options.
------------------------------------
Permanently activate the module
-------------------------------
The bridge consists of two parts, the "frontent", usually Apache/PHP
and our "java.so", and a "backend". The following describes how to
start the backend (if necessary) and how to configure the frontend to
use it.
To permanently activate the frontend for all users please add the
following lines to the php.ini or add a file java.ini to the
directory that contains the php module descriptions (usually
/etc/php.d/) with the following content:
extension = java.so
[java]
java.log_level="3"
java.log_file="/var/log/php-java-bridge.log"
java.socketname="/var/run/.php-java-bridge_socket"
OPTION 1: "Local backend using a service script."
You can use local sockets to connect to a local backend. Copy the
executable php-java-bridge to /usr/sbin, make it executable with the
command "chmod +x /usr/sbin/php-java-bridge".
This option can be used if "Security Enhanced Linux" is switched on.
It cannot be used in a multi-user environment where each user wants
to run his/her own instance of the backend.
OPTION 2: "The J2EE backend."
You can deploy the PHP/Java Bridge into a standard servlet engine or
an application server. Deploy JavaBridge.war using the application
server's tools. Disable the java.socketname and java.log_file option
and set the java.hosts and java.servlet option, for example:
extension = java.so
[java]
java.log_level="3"
java.servlet=User
java.hosts="127.0.0.1:8080"
This option can be used if "Security Enhanced Linux" is switched
on. It can be used in a multi-user environment, users can install
their own .war files and customize them as necessary. It can also
be used if Apache or IIS are not available, the .war files can be
configured to connect to a system FCGI php server or to run a php
CGI binary for each php request.
OPTION 3: "Local backend as an apache sub-process".
You can start the bridge as a sub-process of apache. Disable
java.socketname, java.servlet and java.hosts. For example:
extension = java.so
[java]
java.log_level="3"
java.log_file="/var/log/php-java-bridge.log"
This option cannot be used if "Security Enhanced Linux" is switched
on.
Option 4: "JSR 223 Scripting API."
You can use the java script API to allocate and to invoke php
instances from a HTTP pool. Disable java.socketname and set
java.servlet. For example:
extension = java.so
[java]
java.servlet=User
After that you can call out to your web server's php scripts, for
example:
<?php
function hello() {return "Hello java world!";}
// call() checks for HTTP request header X_JAVABRIDGE_CONTEXT
java_context()->call(java_closure()) ||die("Call test.php from java!");
?>
PhpScriptEngine engine = new PhpScriptEngine();
// Use a URLReader "adapter" to pass the apache URL to the engine.
// engine.eval will create a php continuation on the web server ...
engine.eval(new URLReader(new URL("http://127.0.0.1/test.php")));
//...and call procedures within the allocated continuation...
String s=((Invocable)engine).invoke("hello", new Object[]{});
System.out.println("test.php::hello() returned: "+s);
//.. release the allocated continuation:
engine.release();
This option can be used if "Security Enhanced Linux" is switched
on. It requires php-script.jar, please see the --enable-script
configure option above. Furthermore this option is required if you
want to run php from a framework such as "Java Server Faces". You can
build the required php-faces.jar using the --enable-faces configure
option. For other frameworks (e.g. "Struts") you must write your own
PhpStrutsScriptContextFactory.java, PhpStrutsScriptContext.java and
PhpStrutsScriptEngine.java. You can use the php faces implementation
as a template.
After you have created the correct .ini entries, start the backend:
Example for option #1: /usr/sbin/php-java-bridge
Example for option #2: /etc/init.d/tomcat5 restart
Example for option #3: apachectl restart
Example for option #4: <not needed, backend is the calling VM itself>
Note that the above options are exclusive; if you switch off the
java.socketname and try to start the local backend using the service
script, the script will complain that the socketname is not set.
Check the status:
echo "<?php phpinfo()?>" | php | fgrep "java status"
Other configuration options which should have been set up by the
configure script but which can be changed later are:
java.libpath = <system dependent path to natcJavaBridge.so>
java.classpath = <system dependent path to JavaBridge.jar>
java.java_home = <system dependent path to the java install dir>
java.java = <system dependent path to the java binary>
java.socketname= <local ("unix domain") communication channel>
java.hosts = <add. backends e.g.: server1:9267;server2:9268>
java.servlet = <On/Off/User>, see NEWS for version 3.0.2
Please first look at the output of phpinfo() to see the original
values.
---------------------------------------------
Starting the PHP/Java Bridge automatically
------------------------------------------
When the java.socketname and java.hosts options are not set, the web
server will start or re-start the bridge automatically as a sub
component when the http service is started or re-started.
However, when running the bridge in a production environment, it is
recommended to start the PHP/Java Bridge as a system service,
independent of the web server. On SysV based init systems the
"php-java-bridge.service" can be used to automatically start and stop
the bridge as a system service. Please see the RedHat RPM download
for an example.
------------------------------------
Sample settings for Apache/Tomcat
---------------------------------
Assuming that tomcat is installed (e.g. in /opt/tomcat5),
JavaBridge.war is deployed (e.g. in /opt/tomcat5/webapps), java.so
(or php_java.dll) is installed in the php modules directory (e.g. in
/usr/lib/php/modules/), mod_jk and php5 are installed in the apache modules
directory (e.g. in /usr/lib/httpd/modules/), we recommend following
settings:
The following settings in httpd.conf direct all .jsp requests to the
tomcat servlet engine and all .php requests to the php module:
httpd.conf:
----------
LoadModule php5_module modules/libphp5.so
LoadModule jk_module modules/mod_jk.so
AddType application/x-httpd-php .php
JkAutoAlias /opt/tomcat5/webapps
JkMount /JavaBridge/*.jsp ajp13
JkMount /JavaBridge/ ajp13
The following settings in php.ini direct all php "Java(...)" calls to
the tomcat servlet engine:
php.ini:
--------
extension = java.so
;; on windows: extension = php_java.dll
[java]
java.hosts = "127.0.0.1:8080"
java.servlet = User
To check the above settings, please visit http://localhost/JavaBridge/
and run the sessionSharing.jsp and sessionSharing.php examples.
Please don't misunderstand the role of the mod_jk adapter.
It is only used to "mount" the tomcat webapps directory into apache and
to forward requests for .jsp files. The mod_jk adapter does this by
"copying" the non jsp files from the tomcat webapps directory into
the apache document root directory at run-time.
A more reasonable approach would be to remove mod_jk and to create a
shared web directory for tomcat and apache. This is the default since
RedHat Fedora 4.
Most modern frameworks, Java Server Faces for example, require that
you manually forward to the "backend", anyway. The code would look
like:
<?php
...
function jsfValidateEntry($ctx, $arg, $value) { ...}
// check if we're called from the framework, forward if call failed.
java_context()->call(java_closure()) || header("Location: index.jsf");
?>
So you probably won't miss mod_jk's automatic forward.
------------------------------------
64 Bit issues
-------------
It is possible to compile the bridge into 64 bit code:
phpize && ./configure --with-java=$JAVA_HOME
make CFLAGS="-m64"
The scripts expect that the default JVM found in
$JAVA_HOME/bin/java is a 64 bit VM. Unfortunately this is not true
for the SUN JDK (Linux and Solaris) installation. The SUN JDK
installs the 64 bit VM in some sub-directory of $JAVA_HOME/bin. On
Solaris9 this is $JAVA_HOME/bin/sparcv9. The location on Linux may
depend on the architecture.
Since there is no standard installation directory and we cannot
blindly search all sub-directories, it is your job to direct the
bridge to the 64 bit JVM. The relevant php.ini entry is java.java,
see install instructions above.
---------------------------------------------
AS/Servlet with PHP CGI
-----------------------
Read the following instructions only if you don't want to use
Apache or IIS.
It is possible to run PHP from java. Unlike the JSR223 sample
implementation, which uses the JNI interface to load/call the native
php5 shared library, we use the Fast CGI interface to call the PHP
binary and use a local channel to connect the two components. This is
more reliable; in case a PHP instance crashes, it will not take down
the whole servlet engine or application server.
Please follow the AS or Tomcat installation instructions above, then
visit http://localhost:8080/JavaBridge and run the supplied JSP and PHP
examples.
If the parameter name "use_fast_cgi" is set to "Autostart" in the
web.xml and a fcgi server does not listen on port 9667 and a fcgi
binary can be found as either /usr/bin/php-cgi or /usr/bin/php or
c:/php5/php-cgi.exe, then the backend automatically starts the fast
cgi server on this computer, with the command:
cd $HOME
export X_JAVABRIDGE_OVERRIDE_HOSTS="/"
export PHP_FCGI_CHILDREN="20"
export PHP_FCGI_MAX_REQUESTS="500"
/usr/bin/php-cgi -b 127.0.0.1:9667
It starts when the VM starts and stops when the VM terminates.
If that failed, the bridge searches for a cgi binary called:
php-cgi-<architecture>-<os>.exe or
php-cgi-<architecture>-<os>.sh or
php-cgi-<architecture>-<os>
in the directory WEB-INF/cgi/. On unix the binary must be executable, it
is therefore recommended to always use a wrapper .sh script, for example:
#!/bin/sh
# This wrapper script reconstructs the executable permissions
# which some zip or .war implementations do not preserve
chmod +x ./php-cgi-i386-linux
exec ./php-cgi-i386-linux
Please see the README located in the directory WEB-INF/cgi/ for
details.
The <architecture> and <os> values are calculated as follows:
System.getProperty("os.arch").toLowerCase();
System.getProperty("os.name").toLowerCase();
Please see the output of test.php for details.
It is also possible to adjust the php_exec setting (see
WEB-INF/web.xml), for example:
<param-name>php_exec</param-name>
<param-value>/usr/local/bin/php-cgi</param-value>
or
<param-name>php_exec</param-name>
<param-value>c:/PHP/php-cgi.exe</param-value>
In case your application server denies calling the cgi binary,
either start apache or IIS or start a fast cgi server on port 9667
as a separate process, for example from a service script.
On Unix the bridge uses named pipes. On Windows, where standard
named pipes are not available, the bridge uses TCP sockets. If your
application server denies socket accept/resolve, please either run the
AS on a Unix operating system or add the following lines to your AS
policy file (for example ...\domains\domain1\config\server.policy):
grant {
permission java.net.SocketPermission "*", "accept,resolve";
};
------------------------------------
Loading on-demand with dl()
---------------------------
It is possible to load the bridge for each new request, for example
with:
<?php
if (!extension_loaded('java')) {
if (!dl("java.so")) {
exit(1);
}
}
phpinfo();
?>
However, this feature is meant for testing, only. For a production
system it is recommended to compile PHP in safe mode (which switches
off the dl() function) and to activate all modules in the global
php ini file.
------------------------------------
Recognized CFLAGS
-----------------
During compilation you can use the following CFLAGS.
* -DJAVA_COMPILE_DEBUG: Enables the assert() statement and other
debug code.
* -DJAVA_COMPILE_DEBUG -O0 -g3: Include full debug information into
the binary.
* -m64: Build 64 bit code. Required if you run a 64 bit JVM.
* -m32: Build 32 bit code. Required if you run a 32 bit JVM on a 64
bit system.
* -DCFG_JAVA_SOCKET_INET: Disables local ("unix domain") sockets on
systems which support them. This is needed on FreeBSD if the java VM
is a linux executable. It is also needed on those Mac OSX platforms
which use special JNI libraries (Apple has made some incompatible
and undocumented changes to the JNI header structure so that we cannot
use the standard Sun or GNU Java JNI to create the natcJavaBridge.so).
Example: make CFLAGS="-m64"
------------------------------------
Log levels
----------
You can set the java.log_level to 6 values:
0: Log nothing, not even fatal errors.
1: Log fatal system errors such as "out of memory error".
2: Log java exceptions.
3: Log verbose, e.g.: "loading jar xyz.jar from http://xy.com"
4: Log debug messages, including the c/s communication protocol.
5: Log method invocations.
The default log level is 2. If java.log_level is missing, the
backend uses the "default" log level supplied when the backend was
started (the second argument after java -jar JavaBridge.jar ...).
---------------------------------------------
GCJ/GNU Java issues
-------------------
Running the PHP/Java Bridge under GCJ/GNU Java is supported on Linux
and Solaris only. If you run FreeBSD 5.3, please use Sun, Blackdown
or IBM java instead.
------------------------------------
Security Enhanced Linux
-----------------------
SELinux is an implementation of a flexible and fine-grained
mandatory access control architecture implemented in the Linux kernel.
It is used in recent Linux distributions such as Debian or RedHat
Fedora Core 3.
A system component running on a SELinux kernel must declare
exactly a) which resources of the operating system it needs in order
to function properly and b) what it provides to other components.
The PHP/Java Bridge distribution contains two policy files,
"php-java-bridge.te" and "php-java-bridge.fc". The
"php-java-bridge.te" declares the javabridge_t domain and the
resources it requires. httpd and user domains are granted connect,
read and write to the PHP/Java Bridge server socket, which is
"@var/run/.php-java-bridge_socket" in the Linux abstract namespace,
and file create/read/write in the tmp_t. Everything else (connections
to other servers, file access, ...) is currently denied.
The "php-java-bridge.fc" contains the file contexts for the PHP/Java
Bridge and the log.
Installation:
-------------
The following discussion assumes that you have a RedHat Linux system
and you are running SELinux with the targeted policy.
1. Install selinux-policy-targeted-sources-*.rpm, for example with
the command:
rpm -i selinux-policy-targeted-sources-1.17.30-2.19.noarch.rpm
2. Update the policy files with the PHP/Java Bridge policy:
su -c "sh update_policy.sh /etc/selinux/targeted/src/policy"
If the default policy is too restrictive and e.g. you want to use the
PHP/Java Bridge to connect to your J2EE server(s), you can temporarily
set the policy to "permissive", for example with the command "setenforce
Permissive". Connect to the server, then extract the permissions
from the audit log, for example with the command "audit2allow -d", then
append them at the end of the "php-java-bridge.te" file and load the
updated policy into the kernel. Don't forget to switch back, for
example with "setenforce Enforcing".
------------------------------------
Loading user classes and libraries
----------------------------------
Although it is possible to manipulate the java.classpath to direct
java to individual classes, this "feature" should not be used.
Custom libraries should be stored in one of the system directories,
either /usr/share/java (or one of its sub-directories) or
java.libpath/lib (or one of its sub-directories) and they shall have
the following format: <name>-<version>.jar. For example:
/usr/share/java/batStore/batStore-1.0.jar.
The global repository /usr/share/java shall be used if the library
is for general interest, otherwise the java.libpath/lib repository may
be used. Please look up the java.libpath from the output
of the test.php or from phpinfo().
When Security Enhanced Linux is configured to allow the PHP/Java
Bridge to make HTTP URL connections to different servers or if
Security Enhanced Linux is switched off in the linux kernel, java
libraries may also be loaded from HTTP URLs.
Java libraries can be created from .class files with the following
command:
jar cvf myLibrary-0.1.jar *.class
The libraries can be linked into the php files at run-time with the
command:
java_require("<library1>;<libraryN>");
For example:
<?php
// process order
java_require("j2ee.jar;batStore/batStore-1.0.jar");
order(...);
?>
Since java does not yet support library versions, the PHP/Java
Bridge must be reset after a new library version has been deployed.
This can be done at runtime by calling the php function
"java_reset()".
(The ECMA 335 CLR supports multiple class versions very well so in
theory the problem could also be solved by compiling the .jar file
into a ECMA 335 dll, e.g. with the command: ikvmc batStore-1.0.jar,
and to use assembly loading instead, please see the
tests.mono+net/load_assembly.php for details).
------------------------------------
Sun java platform issues
------------------------
The sun java platform does not support java "modules". This causes
certain problems when running java programs. When you compile a class
foo which references a class bar and ship the class foo without
providing bar, the sun java platform will not complain unless the user
accidently calls a method which references the non-existing class. If
this happens, a "NoClassDefFound" error is thrown. This error may
not(!) indicate which class is missing and it certainly does not
indicate which external library is missing. The tests.php4 folder
contains two tests, noClassDefFound.php and noClassDefFound2.php which
demonstrate this.
To avoid this problem please document *exactly* (including the
version number) which external libraries (.jar files) your software
needs. If you have written software where certain methods require an
optional library, please document this in the method header.
------------------------------------
PHP 5 issues
------------
All PHP 5 versions < 5.0.4 crash when the dl() function is
used. They first unload the module and then try to invoke its
shutdown method.
If you use one of these versions, please add an entry to the
php.ini, see install instructions above.
------------------------------------
Security issues
---------------
The bridge uses abstract local sockets, named pipes (located in
/dev/shm/ or /tmp/) or local TCP sockets as communication channels.
It is recommended to use the local backend on a Unix machine which
supports abstract local ("unix domain") sockets or named pipes. On
these systems the communication channel is not visible and cannot be
attacked. If you are running a Security Enhanced Linux kernel, which
is standard since RHEL4 or FC3, the backend is also protected by the
SEL policy. The servlet backend uses a HTTP tunnel to execute one
statement and then switches to named pipes for the rest of the
communication.
On other systems, such as Windows and Mac OSX, the bridge opens a
local TCP port on 9167 (JavaBridge.jar) or 9267 (MonoBridge.exe), 9567
(JavaBridge.war) or 9667 (FastCGI). Please make sure that the ports in
the range [9167, ..., 9667] cannot be accessed from the internet.
------------------------------------
UTF-8
-----
Since PHP does not support unicode, the PHP/Java Bridge uses UTF-8 to
convert characters into the host representation. All strings are
created with new String(..., "UTF-8") and all internal String->byte[]
conversions use getBytes("UTF-8").
If you have old PHP files which are not UTF-8 encoded, you can
change the default encoding with java_set_file_encoding(). For
example:
java_set_file_encoding("ISO-8859-1");
For a list of available encodings please see the documentation of
the JVM's file.encoding system property.
The java_set_file_encoding() primitive only affects java.lang.String
creation and internal conversions, it does not alter the JVM's
file.encoding system property nor does it change the behaviour of
methods which use the file.encoding property, getBytes() for
example. If you use:
$str=new Java ("java.lang.String", "Cześć! -- שלום -- Gr");
echo $str->getBytes();
the output conversion depends on the file.encoding system property
which in turn depends on the process' LANG environment variable. You
can check the file.encoding with the test.php script, see above.
To be portable please do not use conversions which depend on the
JVM's file.encoding. They are easy to avoid, the above example
should have been written as:
$str=new Java ("java.lang.String", "Cześć! -- שלום -- Gr");
echo (string)$str; // in PHP5 or higher
echo $str->toString(); // in PHP4
------------------------------------
Mailing List
------------
Please report bugs/problems to the mailing list:
php-java-bridge-users@lists.sourceforge.net