php-java/README-ja.md at 5894f799c586fd0f549d4d649ad820574e73ef16 · php-java/php-java
PHPJava - JVM Emulator by PHP
What is PHPJava?
PHPJava は PHP で JVM (Java Virtual Machine) をエミュレーションさせたり、JVM 上で実行できる中間コードのコンパイラを提供している実験的なライブラリです 🐘 PHPJava は予めコンパイルされた Java ファイル(一般的には class ファイル)を読み込み逐次処理をしていきます ☕ そして、 PHPJava は Java を ブリッジや通信をするためのライブラリではありません。 PHPJava は 100% PHP のみ で動きます このプロジェクトは Java Virtual Machine Specification を参考に創られています。
私達は心よりあなたのコントリビューションをお待ちしています 💪
コントリビューションガイド:
ドキュメント
Java Virtual Machine
中間コードコンパイラ
PHP のシンタックスによる JVM 言語
デモ
必須環境
- PHP >= 7.2
- Composer
- ext-zip
PHPJava を実行ファイルとして実行する
PHPJava は実行ファイルとして処理させることが可能です。
クラスファイルを動かす場合
./vendor/bin/PHPJava HelloWorld
または、
./vendor/bin/PHPJava HelloWorld.class
Jar ファイルを動かす場合
./vendor/bin/PHPJava -m jar HelloWorld.jar
ヘルプを表示したい場合
クイックスタート
- PHPJava をインストールします。
$ composer require php-java/php-java:dev-master
- Java を書きます。
class HelloWorld { public static void main(String[] args) { System.out.println(args[0]); } }
- Java をコンパイルします。
$ javac -encoding UTF8 /path/to/HelloWorld.java
- main メソッドを呼びます。
<?php use PHPJava\Core\JavaClass; use PHPJava\Core\Stream\Reader\FileReader; JavaClass::load('HelloWorld') ->getInvoker() ->getStatic() ->getMethods() ->call( 'main', ["Hello World!"] ); // または、以下のようにファイルパスを指定することも可能です。 (new JavaClass(new JavaCompiledClass(new FileReader('/path/to/HelloWorld.class')))) ->getInvoker() ->getStatic() ->getMethods() ->call( 'main', ["Hello World!"] );
- 結果を取得します。
$ php /path/to/HelloWorld.php
Hello World!
Java Archive (*.jar ファイルの実行)
- Jar ファイルを作成します。
$ javac -encoding UTF8 -d build src/*
$ cd build && jar -cvfe ../Test.jar Test *
- エントリーポイントを指定するか、またはすでに指定されたエントリーポイントをもとに Jar ファイルを動かします.
- Execute the jar on PHPJava with either an enrtypoint or your target method.
<?php use PHPJava\Core\JavaArchive; // execute メソッドは初期パラメータを定義していないため、 // エントリーポイントを呼び出すための execute メソッドは必ず、 パラメータを指定する必要があります。 (new JavaArchive('Test.jar'))->execute([]); // または、 (new JavaArchive('Test.jar')) ->getClassByName('Test') ->getInvoker() ->getStatic() ->getMethods() ->call( 'main', [] );
静的フィールドの取得または代入
- 下記のように取得または代入を行うことが可能です
<?php use PHPJava\Core\JavaClass; use PHPJava\Core\Stream\Reader\FileReader; $staticFieldAccessor = JavaClass::load('HelloWorld') ->getInvoker() ->getStatic() ->getFields(); // 代入 $staticFieldAccessor->set('fieldName', 'value'); // 取得 echo $staticFieldAccessor->get('fieldName');
静的メソッドの呼び出し
- 下記のように静的メソッドを呼び出します。
<?php use PHPJava\Core\JavaClass; use PHPJava\Core\Stream\Reader\FileReader; JavaClass::load('HelloWorld') ->getInvoker() ->getStatic() ->getMethods() ->call( 'methodName', $firstArgument, $secondArgument, $thirdArgument, ... ); // または、メソッドが返り値をもつ場合は、下記のようにして、返り値を変数に代入することが可能です。 $result = JavaClass::load('HelloWorld') ->getInvoker() ->getStatic() ->getMethods() ->call( 'methodWithSomethingReturn', $firstArgument, $secondArgument, $thirdArgument, ... ); // 返り値を出力します。 echo $result;
動的フィールドの取得または代入
- 動的フィールドを取得または代入したい場合は、
constructメソッド呼ぶ必要があります。
<?php use PHPJava\Core\JavaClass; use PHPJava\Core\Stream\Reader\FileReader; $javaClass = JavaClass::load('HelloWorld'); $javaClass->getInvoker()->construct(); $dynamicFieldAccessor = $javaClass ->getInvoker() ->getDynamic() ->getFields(); // 代入 $dynamicFieldAccessor->set('fieldName', 'value'); // 取得 echo $dynamicFieldAccessor->get('fieldName');
動的メソッドの呼び出し
- 動的メソッドを呼びたい場合動的フィールドのように、
constructメソッド呼ぶ必要があります。
<?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, ... ); // または、メソッドが返り値をもつ場合は、下記のようにして、返り値を変数に代入することが可能です。 $dynamicMethodAccessor ->call( 'methodWithSomethingReturn', $firstArgument, $secondArgument, $thirdArgument, ... ); // 返り値を出力します。 echo $result;
Java のビルトインパッケージ内のメソッドの呼び出し
Ver. 0.0.8.5 より通常の JavaClass::load と同様の呼び出し方法でビルトインパッケージの呼び出しが可能になりました。
なお、これは PHP の ReflectionClass を用いてエミュレートされており、静的なメソッドやフィールドも実際には動的に生成されます。
下記は java.lang.Math の呼び出し例です。
<?php use PHPJava\Core\JavaClass; use PHPJava\Core\Stream\Reader\FileReader; echo JavaClass::load('java.lang.Math') ->getInvoker() ->getStatic() ->getMethods() ->call( 'pow', 2, 4 );
上記の結果は 16 となります。
あいまいなメソッドを PHPJava から呼び出す場合
- PHP は Java と比べると型がだいぶ曖昧です。そのため、 PHPJava では正確にメソッドを呼び出すための手段をいくつか用意しています。
- 以下は、
longパラメータを受け取るメソッドを呼び出す場合の例です。
[推奨] パラメータを \PHPJava\Kernel\Types\Long_ にする。
Java
class Test { public static void includingLongTypeParameter(long n) { System.out.println(n); } }
PHP
<?php $javaClass->getInvoker()->getStatic()->getMethods()->call( 'includingLongTypeParameter', new \PHPJava\Kernel\Types\Long_ (1234) );
この例は 1234 を返却します。
strict オプションを 無効 にする
PHP
<?php use PHPJava\Core\JavaClass; use PHPJava\Core\Stream\Reader\FileReader; $javaClass = JavaClass::load( 'HelloWorld', [ 'strict' => false, ] );
ラインタイムオプション
JavaClassまたは、JavaArchiveで使用可能なランタイムオプションは下記のとおりです。
| オプション名 | 型 | デフォルト値 | 概要 | 対象 |
|---|---|---|---|---|
| entrypoint | string または、 null | null | Jar のエントリーポイントを指定します | JavaArchive |
| max_stack_exceeded | integer | 9999 | オペレーションを最大何回実行できるかを指定します。 | JavaClass |
| max_execution_time | integer | 30 | 最大実行時間を指定します。 | JavaClass |
| strict | boolean | true | このオプションが true の場合、 PHPJava はメソッド、変数などを厳格に評価し実行します。 false の場合は、曖昧に評価して実行します。. |
Both |
| validation.method.arguments_count_only | boolean | false | このオプションが true の場合、 クラス解決をして、メソッドを呼び出す際に、引数の数のみを比較します。 |
JavaClass |
| operations.enable_trace | boolean | false | このオプションが true の場合、 PHPJava はオペレーションの実行ログを記録します。 |
JavaClass |
| operations.temporary_code_stream | string | php://memory | 実行用のバイトコードの一時保存先を指定します。 | JavaClass |
| operations.injector.before | callable | null | オペレーション実行前に処理をするトリガーを設定します。 | JavaClass |
| operations.injector.after | callable | null | オペレーション実行後に処理をするトリガーを設定します。 | JavaClass |
| log.level | int | Logger::EMERGENCY | Monolog によるログの出力レベルを設定します |
Both |
| log.path | string または resource | php://stdout | Monolog の出力先を指定します。. |
Both |
| dry_run (未実装) | boolean | false | このオプションが true の場合、 JavaClass または JavaArchive の構造のチェックのみを行います。 |
Both |
| env (未実装) | enum | Environment::EXPERIMENTAL | あなたの実行時環境を設定します。 | Both |
- 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, ], ], ] );
GlobalOptionsを使用して設定する場合は下記のとおりです。
<?php use PHPJava\Core\JVM\Parameters\GlobalOptions; use Monolog\Logger; GlobalOptions::set([ 'log' => [ 'level' => Logger::DEBUG, ], 'validation' => [ 'method' => [ 'arguments_count_only' => true, ], ], ]);
PHPJava の実行結果を出力する
- 実行中のオペレーションの処理を確認したい場合は下記のとおりにします。
<?php use PHPJava\Core\JavaClass; use PHPJava\Core\Stream\Reader\FileReader; $javaClass = JavaClass::load('HelloWorld'); $javaClass ->getInvoker() ->getStatic() ->getMethods() ->call( 'main', ["Hello", 'World'] ); // デバッグトレースを表示します。 $javaClass->debug();
- デバッグトレースを出力します。
[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] は呼ばれたメソッドを表示します。
- [code] は JVM 上の実際のコードを表示します。
- [executed] は実行されたオペレーションコードの一覧を表示します。
- PC はプログラムカウンタを表示します。
- OPCODE はオペレーションコードを表示します。
- MNEMONIC はニーモニックを表示します。.
- OPERANDS はオペランドスタック上のアイテムを表示します。
- LOCAL STORAGE はローカルストレージに格納されているアイテムの数を表示します。
大きな数字の計算について
- PHP は通常、 Java における long 型や double 型といった大きな値の計算を行うことができません。 PHPJava ではそれらをカバーするために数値計算用ライブラリを使用します。 それらは、 下記の Java の型でラップされて使用されます。 そのため、数値をPHP側で取り扱う場合は、 string 型にキャストすることを推奨します。 また、通常の 64bit 版 PHP で計算できる範囲については、PHP の四則演算を使用して計算を行います。
- 下記はJava と PHPJava の型の比較表です。
| Java | PHPJava |
|---|---|
| null | null |
| boolean | \PHPJava\Kernel\Types\Boolean_ (__toString を含む) |
| char | \PHPJava\Kernel\Types\Char_ (__toString を含む) |
| byte | \PHPJava\Kernel\Types\Byte_ (__toString を含む) |
| short | \PHPJava\Kernel\Types\Short_ (__toString を含む) |
| int | \PHPJava\Kernel\Types\Int_ (__toString を含む) |
| long | \PHPJava\Kernel\Types\Long_ (__toString を含む) |
| float | \PHPJava\Kernel\Types\Float_ (__toString を含む) |
| double | \PHPJava\Kernel\Types\Double_ (__toString を含む) |
Run Kotlin on the PHPJava
Kotlin を PHPJava で動かす。
Kotlin を PHPJava で動かしたいですか? 可能ではありますが、現状は試験的な実装となっています。
クイックスタート
- Kotlin を書きます。
fun main(args: Array<String>) { println("Hello World!") }
- ランタイム付きでコンパイルします。
$ kotlinc HelloWorld.kt -include-runtime -d HelloWorld.jar
- Jar として実行します。
<?php use PHPJava\Core\JavaArchive; $jar = new JavaArchive(__DIR__ . '/HelloWorld.jar'); $jar->execute([]);
Hello World! と出力されます。
ユニットテスト
- PHPUnit でテストを動かします。
$ ./vendor/bin/phpunit tests/Cases
- コーディングルールをチェックします。
$ ./vendor/bin/phpcs --standard=phpcs.xml src
- すべてのテストを実行します。
参照
ライセンス
MIT

