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 HelloWorld
or
./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 -encoding UTF8 /path/to/HelloWorld.java
- Call the main method as follows:
<?php
use PHPJava\Core\JavaClass;
use PHPJava\Core\Stream\Reader\FileReader;
JavaClass::load('HelloWorld')
->getInvoker()
->getStatic()
->getMethods()
->call(
'main',
["Hello World!"]
);
// Or, you can specify file path as follows.
(new JavaClass(new JavaCompiledClass(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 = JavaClass::load('HelloWorld')
->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;
JavaClass::load('HelloWorld')
->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 = JavaClass::load('HelloWorld')
->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 = JavaClass::load('HelloWorld');
$javaClass->getInvoker()->construct();
$dynamicFieldAccessor = $javaClass
->getInvoker()
->getDynamic()
->getFields();
// Set
$dynamicFieldAccessor->set('fieldName', 'value');
// Get
echo $dynamicFieldAccessor->get('fieldName');
If you want to call a 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 = JavaClass::load('HelloWorld')
->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;
PHPJava can call a built-in package in the same way as JavaClass::load
after the version 0.0.8.5.
This feature is emulated by ReflectionClass
on PHP
and any static methods/fields will dynamically be generated in fact.
e.g.) To Call java.lang.Math
is below.
<?php
use PHPJava\Core\JavaClass;
use PHPJava\Core\Stream\Reader\FileReader;
echo JavaClass::load('java.lang.Math')
->getInvoker()
->getStatic()
->getMethods()
->call(
'pow',
2,
4
);
The example will return 16
.
- In PHP, types are more ambiguous than Java.
- For example, you may want to call a method that accepts a
long
parameter 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 = JavaClass::load(
'HelloWorld',
[
'strict' => false,
]
);
- Available options on
JavaClass
orJavaArchive
:
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 |
validation.method.arguments_count_only | boolean | false | When true , ClassResolver validates arguments by their number only. |
JavaClass |
operations.enable_trace | boolean | false | 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 = JavaClass::load(
'HelloWorld',
[
'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 = JavaClass::load('HelloWorld');
$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.
- In normally, PHP cannot calculate big numbers as such as
long
anddouble
types. But, PHPJava uses externalMath
library for covering above problems. And, PHPJava to use Java's type as below comparison table. Therefore, we recommend to cast them tostring
on PHPJava. And, if it can be calculated with 64-bitPHP, PHPJava uses PHP's arithmetic operations.
- 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 ) |
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