PHPJava is an experimental library which emulates JVM (a.k.a. Java Virtual Machine) by PHP 🐘 And PHPJava reads binary from pre-compiled Java file(s) ☕ So, PHPJava is NOT bridge to Java. This library can be run 100% in PHP. This project referred to Java Virtual Machine Specification documentation at the time we made it.
We are welcoming any contributions to this project 💪
Contribution guide is here:
- PHP >= 7.2
- Composer
- ext-zip
You can run PHPJava as same as an executable binary.
./vendor/bin/PHPJava HelloWorldor
./vendor/bin/PHPJava HelloWorld.class./vendor/bin/PHPJava -m jar HelloWorld.jar./vendor/bin/PHPJava -h- Install PHPJava in your project:
$ composer require php-java/php-java
- Write Java:
class HelloWorld
{
public static void main(String[] args)
{
System.out.println(args[0]);
}
}- Compile Java:
$ javac -UTF8 /path/to/HelloWorld.java
- Call the main method as follows:
<?php
use PHPJava\Core\JavaClass;
use PHPJava\Core\Stream\Reader\FileReader;
(new JavaClass(new FileReader('/path/to/HelloWorld.class')))
->getInvoker()
->getStatic()
->getMethods()
->call(
'main',
["Hello World!"]
);- Get the result
$ php /path/to/HelloWorld.php
Hello World!
- Build your Java files into a class:
$ javac -encoding UTF8 -d build src/*
$ cd build && jar -cvfe ../Test.jar Test *
- Execute the jar on PHPJava with either an enrtypoint or your target method.
<?php
use PHPJava\Core\JavaArchive;
// You must pass parameters to entrypoint within the `execute` method.
// The `execute` method does not have any default parameters.
(new JavaArchive('Test.jar'))->execute([]);
// or
(new JavaArchive('Test.jar'))
->getClassByName('Test')
->getInvoker()
->getStatic()
->getMethods()
->call(
'main',
[]
);e.g., Set or Get static fields:
<?php
use PHPJava\Core\JavaClass;
use PHPJava\Core\Stream\Reader\FileReader;
$staticFieldAccessor = (new JavaClass(new FileReader('/path/to/HelloWorld.class')))
->getInvoker()
->getStatic()
->getFields();
// Set
$staticFieldAccessor->set('fieldName', 'value');
// Get
echo $staticFieldAccessor->get('fieldName');e.g., Call a static method:
<?php
use PHPJava\Core\JavaClass;
use PHPJava\Core\Stream\Reader\FileReader;
(new JavaClass(new FileReader('/path/to/HelloWorld.class')))
->getInvoker()
->getStatic()
->getMethods()
->call(
'methodName',
$firstArgument,
$secondArgument,
$thirdArgument,
...
);
// Or, if the called method has a return value, you can store it to a variable.
$result = (new JavaClass(new FileReader('/path/to/HelloWorld.class')))
->getInvoker()
->getStatic()
->getMethods()
->call(
'methodWithSomethingReturn',
$firstArgument,
$secondArgument,
$thirdArgument,
...
);
// Output the $result you want
echo $result;If you want to get/set dynamic fields, you need to call the construct method on Java by PHPJava.
e.g., Call dynamic field:
<?php
use PHPJava\Core\JavaClass;
use PHPJava\Core\Stream\Reader\FileReader;
$javaClass = new JavaClass(new FileReader('/path/to/HelloWorld.class'));
$javaClass->getInvoker()->construct();
$dynamicFieldAccessor = $javaClass
->getInvoker()
->getDynamic()
->getFields();
// Set
$dynamicFieldAccessor->set('fieldName', 'value');
// Get
echo $dynamicFieldAccessor->get('fieldName');If you want to get/set dynamic method (same as a field), you need to call the construct method on Java by PHPJava.
e.g., Call dynamic method:
<?php
use PHPJava\Core\JavaClass;
use PHPJava\Core\Stream\Reader\FileReader;
$dynamicMethodAccessor = (new JavaClass(new FileReader('/path/to/HelloWorld.class')))
->getInvoker()
->construct()
->getDynamic()
->getMethods();
$dynamicMethodAccessor
->call(
'methodName',
$firstArgument,
$secondArgument,
$thirdArgument,
...
);
// Or, if the called method has a return value, you can store it to a variable.
$dynamicMethodAccessor
->call(
'methodWithSomethingReturn',
$firstArgument,
$secondArgument,
$thirdArgument,
...
);
// Output the $result you want
echo $result;- In PHP, types are more ambiguous than Java.
- For example, you may want to call a method that accepts a
longparameter in Java from PHP. In this case, you can call that method as follows:
class Test
{
public static void includingLongTypeParameter(long n)
{
System.out.println(n);
}
}<?php
$javaClass->getInvoker()->getStatic()->getMethods()->call(
'includingLongTypeParameter',
new \PHPJava\Kernel\Types\_Long(1234)
);The example will return 1234.
<?php
use PHPJava\Core\JavaClass;
use PHPJava\Core\Stream\Reader\FileReader;
$javaClass = new JavaClass(
new Stream\Reader\FileReader('Test'),
[
'strict' => false,
]
);- Available options on
JavaClassorJavaArchive:
| Options | Value | Default | Description | Targeted |
|---|---|---|---|---|
| entrypoint | string or null | null | The entrypoint in JAR. | JavaArchive |
| max_stack_exceeded | integer | 9999 | Execute more than the specified number of times be stopped the operation. | JavaClass |
| max_execution_time | integer | 30 | Maximum execution time. | JavaClass |
| strict | boolean | true | When true, PHPJava calls a method, variables, and so on strictly; otherwise, it calls them ambiguously. |
Both |
| preload | boolean | false | When true, PHPJava pre-reads JavaClasses when emulating JAR. This may consume larger size of memory depending on the size of the JAR file; otherwise, JavaArchive defers loading. |
JavaArchive |
| validation.method.arguments_count_only | boolean | false | When true, ClassResolver validates arguments by their number only. |
JavaClass |
| operations.enable_trace | boolean | true | When true, PHPJava stores the operation history. |
JavaClass |
| operations.temporary_code_stream | string | php://memory | Operation code will be output to temporary stream. Change this if your code is heavy so you'll be happy. | JavaClass |
| operations.injector.before | callable | null | Inject an executor before executing an operation. | JavaClass |
| operations.injector.after | callable | null | Inject an executor after executing an operation. | JavaClass |
| log.level | int | Logger::EMERGENCY | The output level of Monolog. |
Both |
| log.path | string or resource | php://stdout | The output destination of Monolog. |
Both |
| dry_run (Not Implemented) | boolean | false | Checking JavaClass/JavaArchive structure only. When true, PHPJava runs in dry-run mode. |
Both |
| env (Not Implemented) | enum | Environment::EXPERIMENTAL | Your environment. | Both |
- Example of JavaClass:
<?php
use PHPJava\Core\JavaClass;
use PHPJava\Core\Stream\Reader\FileReader;
$javaClass = new JavaClass(
new FileReader('Test'),
[
'max_stack_exceeded' => 12345,
'validation' => [
'method' => [
'arguments_count_only' => true,
],
],
]
);- Example of GlobalOptions
<?php
use PHPJava\Core\JVM\Parameters\GlobalOptions;
use Monolog\Logger;
GlobalOptions::set([
'log' => [
'level' => Logger::DEBUG,
],
'validation' => [
'method' => [
'arguments_count_only' => true,
],
],
]);- Output debug trace if you want to see operation log:
<?php
use PHPJava\Core\JavaClass;
use PHPJava\Core\Stream\Reader\FileReader;
$javaClass = new JavaClass(new FileReader('/path/to/HelloWorld.class'));
$javaClass
->getInvoker()
->getStatic()
->getMethods()
->call(
'main',
["Hello", 'World']
);
// Show debug traces.
$javaClass->debug();- Output debug trace:
[method]
public static void main(java.lang.String[])
[code]
<0xb2> <0x00> <0x02> <0x2a> <0x03> <0x32> <0xb6> <0x00> <0x03> <0xb2> <0x00> <0x02> <0x2a> <0x04> <0x32> <0xb6> <0x00> <0x03> <0xb2> <0x00>
<0x02> <0x2a> <0x05> <0x32> <0xb6> <0x00> <0x03> <0xb1>
[executed]
PC | OPCODE | MNEMONIC | OPERANDS | LOCAL STORAGE
---------+--------+----------------------+------------+-----------------
0 | 0xB2 | getstatic | 0 | 1
3 | 0x2A | aload_0 | 1 | 1
4 | 0x03 | iconst_0 | 2 | 1
5 | 0x32 | aaload | 3 | 1
6 | 0xB6 | invokevirtual | 2 | 1
9 | 0xB2 | getstatic | 0 | 1
12 | 0x2A | aload_0 | 1 | 1
13 | 0x04 | iconst_1 | 2 | 1
14 | 0x32 | aaload | 3 | 1
15 | 0xB6 | invokevirtual | 2 | 1
18 | 0xB2 | getstatic | 0 | 1
21 | 0x2A | aload_0 | 1 | 1
22 | 0x05 | iconst_2 | 2 | 1
23 | 0x32 | aaload | 3 | 1
24 | 0xB6 | invokevirtual | 2 | 1
27 | 0xB1 | return | 0 | 1
---------+--------+----------------------+------------+-----------------
- [method] shows the called method.
- [code] shows the JVM's real programs.
- [executed] shows the executed programs.
- PC shows the Program Counter.
- OPCODE shows the Operation Codes.
- MNEMONIC shows the names of the Operation Codes.
- OPERANDS shows the stacked items on memory.
- LOCAL STORAGE shows the stacked items on a method.
-
Problem 1: PHP cannot calculate big numbers because PHP is different from Java. But PHPJava uses
bcmathfunctions andgmpfunctions to a certain extent to cover such calculations. Therefore, PHPJava returns a mixed value and we recommend to cast them tostringon PHPJava. -
Problem 2: PHPJava cannot completely cover Java types because PHP is different from Java. The comparison table of Java and PHPJava is shown below:
| Java | PHPJava |
|---|---|
| null | null |
| boolean | \PHPJava\Kernel\Types\_Boolean (including __toString) |
| char | \PHPJava\Kernel\Types\_Char (including __toString) |
| byte | \PHPJava\Kernel\Types\_Byte (including __toString) |
| short | \PHPJava\Kernel\Types\_Short (including __toString) |
| int | \PHPJava\Kernel\Types\_Int (including __toString) |
| long | \PHPJava\Kernel\Types\_Long (including __toString) |
| float | \PHPJava\Kernel\Types\_Float (including __toString) |
| double | \PHPJava\Kernel\Types\_Double (including __toString) |
- Problem 3: PHPJava cannot calculate big number of
doubleandfloatbecausegmp_powcannot calculate negative exponents. So, PHPJavas use built-in functionpow.
Do you wanna run Kotlin on the PHPJava? Are you serious? Haha, yes, you can, but this feature is currently experimental.
- Write Kotlin:
fun main(args: Array<String>) {
println("Hello World!")
}- Compile Kotlin:
$ kotlinc HelloWorld.kt -include-runtime -d HelloWorld.jar
- Execute JAR:
<?php
use PHPJava\Core\JavaArchive;
$jar = new JavaArchive(__DIR__ . '/HelloWorld.jar');
$jar->execute([]);You'll get the result: Hello World!.
- To run a PHPUnit test:
$ ./vendor/bin/phpunit tests
- To run PHP Coding standards test:
$ ./vendor/bin/phpcs --standard=phpcs.xml src
- To run all tests:
$ composer run tests
MIT

