Home
PHPJava - JVM Emulator by PHP

What is the PHPJava?
The PHPJava is an experimental library which emulates JVM (a.k.a. Java Virtual Machine) by PHP ๐ The PHPJava reads binary from pre-compiled Java file(s) โ 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:
Requirements
- PHP >= 7.2
- Composer
- ext-zip
Quick start
-
- Install the PHPJava into your project:
$ composer require memory-agape/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!
Java Archive (Execute to *.jar file)
-
- Build your Java files to class. An example is shown below:
$ javac -encoding UTF8 -d build src/*
$ cd build && jar -cvfe ../Test.jar Test *
-
- Execute jar on PHPJava with either an enrtypoint or your targeted method.
<?php use PHPJava\Core\JavaArchive; // You must pass parameters to entrypoint within the `execute` method. // The `execute` method haven't default parameters. (new JavaArchive('Test.jar'))->execute([]); // or (new JavaArchive('Test.jar')) ->getClassByName('Test') ->getInvoker() ->getStatic() ->getMethods() ->call( 'main', [] );
Get/Set static fields
- ex) Set or Get static fields as follows:
<?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');
Call a static method
- ex) Call a static 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( 'methodName', $firstArgument, $secondArgument, $thirdArgument, ... ); // Or if called method have return value then you can store to variable. $result = (new JavaClass(new FileReader('/path/to/HelloWorld.class'))) ->getInvoker() ->getStatic() ->getMethods() ->call( 'methodWithSomethingReturn', $firstArgument, $secondArgument, $thirdArgument, ... ); // The $result you want is output. echo $result;
Get/Set dynamic fields
If you want to get/set dynamic fields, you need to call the construct method on Java by PHPJava.
- ex) Call dynamic field as follows:
<?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');
Call a dynamic method
If you want to get/set dynamic method (same as a field), you need to call the construct method on Java by PHPJava.
- ex) Call dynamic method as follows:
<?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 called method have return value then you can store to variable. $dynamicMethodAccessor ->call( 'methodWithSomethingReturn', $firstArgument, $secondArgument, $thirdArgument, ... ); // The $result you want is output. echo $result;
Call ambiguous method into Java from PHP
- PHP types are ambiguous than Java.
- You may want to call a methods in PHP that contain long type in Java. In its case, you can call a method as follows:
ex. ) [Recommended] Wrap with \PHPJava\Kernel\Types\\_Long.
In Java
class Test { public static void includingLongTypeParameter(long n) { System.out.println(n); } }
In PHP
<?php $javaClass->getInvoker()->getStatic()->getMethods()->call( 'includingLongTypeParameter', new \PHPJava\Kernel\Types\\_Long(1234) );
The example will return 1234.
ex. ) Set false to strict mode within options.
In PHP
<?php use PHPJava\Core\JavaClass; use PHPJava\Core\Stream\Reader\FileReader; $javaClass = new JavaClass( new Stream\Reader\FileReader('Test'), [ 'strict' => false, ] );
Runtime options
- Available options on
JavaClassorJavaArchiveis below:
| Options | Value | Default | Description | Targeted |
|---|---|---|---|---|
| entrypoint | string or null | null | Specify to run 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 | This option control max execution times. | JavaClass |
| strict | boolean | true | If strict mode is true then execute method, variables and so on with strict. But if strict mode is false then execute ambiguously method, variable and etc in PHPJava. |
Both |
| preload | boolean | false | preload is pre-read JavaClass in emulating JAR. This may be a lot of consuming memories by large JAR file. but JavaArchive use defer loading if this option is false. | JavaArchive |
| validation.method.arguments_count_only | boolean | false | If this mode true then ClassResolver validate arguments size only. |
JavaClass |
| operations.enable_trace | boolean | true | Store operations history into memory if this is enabled. | JavaClass |
| operations.temporary_code_stream | string | php://memory | Operation code will be outputted to temporary stream. Change temporary stream if your code is heavy. You'll get happy. | JavaClass |
| operations.injector.before | callable | null | Inject an executor before from executing an operation. | JavaClass |
| operations.injector.after | callable | null | Inject an executor after to executing an operation. | JavaClass |
| log.level | int | Logger::EMERGENCY | This option set Monolog output level. |
Both |
| log.path | string or resource | php://stdout | This option set Monolog output destination. |
Both |
| dry_run (Not Implemented) | boolean | false | Dry-run denotes to check JavaClass/JavaArchive structures only. If this options is true then to run dry-run mode. |
Both |
| env (Not Implemented) | enum | Environment::EXPERIMENTAL | Set your environment. | Both |
- For example in 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, ], ], ] );
- For example in GlobalOptions
<?php use PHPJava\Core\JVM\Parameters\GlobalOptions; use Monolog\Logger; GlobalOptions::set([ 'log' => [ 'level' => Logger::DEBUG, ], 'validation' => [ 'method' => [ 'arguments_count_only' => true, ], ], ]);
Output PHPJava operations
- Output debug trace as follows if you want to show 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 is shown below:
[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.
PHP problems
-
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.
Run Kotlin on the PHPJava
Are you wanna run Kotlin on the PHPJava? Are you a serious? Haha, yes you can. But this feature is experimental currently.
Quick Start
-
- Write Kotlin:
fun main(args: Array<String>) { println("Hello World!") }
-
- Compile Kotlin:
$ kotlinc HelloWorld.kt -include-runtime -d HelloWorld.jar
-
- Execute JAR as follows:
<?php use PHPJava\Core\JavaArchive; $jar = new JavaArchive(__DIR__ . '/HelloWorld.jar'); $jar->execute([]);
You'll get a result Hello World!.
Run unit tests
- 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:
Reference
License
MIT
