php in java -quercus- によるレガシーマイグレーション実例 #jjug_ccc #ccc_r12

Post on 13-Jun-2015

3.462 Views

Category:

Documents

6 Downloads

Preview:

Click to see full reader

TRANSCRIPT

PHP in Java -Quercus- による レガシーマイグレーション

山下 竜司 株式会社アットウェア/Facebook4J

#jjug_ccc #ccc_r12

自己紹介•山下 竜司

•株式会社アットウェア

•@roundrop

•Facebook4J - http://facebook4j.org

PHP: Hypertext Preprocessor

http://venturebeat.com/2013/05/17/google-app-engine-finally-supports-php-the-language-that-runs-75-of-the-web/

世界一 広まっている言語

世界一 dis られている言語

Quercus

https://www.flickr.com/photos/justinwkern/6140775849/

Quercus•けっこう昔からあった

> svn log svn://svn.caucho.com/home/svn/svnroot/resin/trunk/modules/quercus

Quercus•PHP (PHP5 相当) を Java 上で動かすことを可能にする Java で実装されたエンジン

Quercus•PHP (PHP5 相当) を Java 上で動かすことを可能にする Java で実装されたエンジン

•オープンソース (ライセンスは GPL)

Quercus•PHP (PHP5 相当) を Java 上で動かすことを可能にする Java で実装されたエンジン

•オープンソース (ライセンスは GPL)

•Caucho Technology 社の商用アプリケーションサーバー Resin の一部

PHP アプリといえば WordPress

WordPress on Quercus デモ

WordPress on Quercus デモ

!

https://gist.github.com/roundrop/4977262

Links•Home

‣ http://quercus.caucho.com/

•Change Log

‣ http://caucho.com/resin-4.0/changes/changes.xtp

•ソースコード

‣ svn://svn.caucho.com/home/svn/svnroot/resin/trunk/modules/quercus

•バグトラッカー

‣ http://bugs.caucho.com/view_all_bug_page.php

見た目に反して 開発はわりとアクティブ

Quercus の構造•実体は Servlet ※Servlet 以外の起動方法もあるが割愛

[web.xml] <servlet> <servlet-name>Quercus Servlet</servlet-name> <servlet-class> com.caucho.quercus.servlet.QuercusServlet </servlet-class> <init-param> : </servlet> <servlet-mapping> <servlet-name>Quercus Servlet</servlet-name> <url-pattern>*.php</url-pattern> </servlet-mapping>

Quercus の構造

QuercusServlet

Tomcat など

xxxx.php

1. phpファイル読込 2. 解析 3. 実行

PHP コードの再現力•Quercus の PHP 再現力はかなりのもの

• (モノによるが) だいたい 90% 以上のコードは動く

•逆に言うとある程度は改修・実装が必要

‣ Quercus で未実装の PHP 関数の自前実装

‣対応していないシンタックスを調整 など

PHP コードの実行速度•速い

•素の PHP より速くなる事例もある

•キャッシュ、コネクションプールなど Java 資産が使える

•プロダクションに投入できるレベル

PHP から Java のコードを呼び出せる

•Java 上での PHP コードの実行だけでなく、PHP コードから Java のコードを呼び出せる

PHP から Java のコードを呼び出せる

•Java 上での PHP コードの実行だけでなく、PHP コードから Java のコードを呼び出せる

<?php import java.lang.System; import java.util.Date; $date = new Date(); System::out->println($date);

PHP から Java のコードを呼び出せる

QuercusServlet

Tomcat など

xxxx.php

Java コード 呼び出し

Java Class

つまり、•PHP アプリを Tomcat 等の上で動かしつつ

つまり、•PHP アプリを Tomcat 等の上で動かしつつ

•これからつくる新機能は Java で開発したり

つまり、•PHP アプリを Tomcat 等の上で動かしつつ

•これからつくる新機能は Java で開発したり

•PHP の処理の一部だけ Java に置き換えたり

つまり、•PHP アプリを Tomcat 等の上で動かしつつ

•これからつくる新機能は Java で開発したり

•PHP の処理の一部だけ Java に置き換えたり

レガシーマイグレーションに使える

Java Application Server (Tomcat など)

PHP

Java Application Server (Tomcat など)

PHPJava

(Spring等)既存機能はPHPのまま 新機能はJava

Java Application Server (Tomcat など)

PHPJava

(Spring等)既存機能はPHPのまま 新機能はJava

性能上のボトルネックだけ Java 化

Java Application Server (Tomcat など)

PHPJava

(Spring等)既存機能はPHPのまま 新機能はJava

あまりにひどい部分だけ Java 化性能上のボトルネックだけ Java 化

Java Application Server (Tomcat など)

PHPJava

(Spring等)既存機能はPHPのまま 新機能はJava

あまりにひどい部分だけ Java 化性能上のボトルネックだけ Java 化

スモールスタートしてから徐々に Java の範囲を 広げていき、最終的には PHP をなくす

実践

https://www.flickr.com/photos/pshab/1366448271/

まずは PHP をまともに動かす

PHP をまともに動かすまでの障壁•文字化け

•Quercus が解釈できないシンタックス

•Quercus で未対応な PHP 関数

PHP をまともに動かすまでの障壁•文字化け

•Quercus が解釈できないシンタックス

•Quercus で未対応な PHP 関数

文字化け対策•既存 PHP アプリを Quercus 上で動かしてみたところ絶望的に文字化け

文字化け対策•既存 PHP アプリを Quercus 上で動かしてみたところ絶望的に文字化け

•Java でつくる部分は UTF-8 にしたいので、全体的に UTF-8 で統一したい

文字化け対策•文字コードを UTF-8 に揃える

‣ソースコード

‣画面の charset

‣データベース

‣リソースファイル

‣:

文字化け対策•Quercus は ISO-8859-1 前提で動作する

•unicode.semantics=on

‣ on にすることで UTF-8 前提で動作するようになる

‣ php.ini に書いて Quercus に読み込ませる

文字化け対策•QuercusServlet に ini-file パラメータを指定

[WEB-INF/php.ini] unicode.semantics=on ![web.xml] <servlet> <servlet-name>Quercus Servlet</servlet-name> <servlet-class> com.caucho.quercus.servlet.QuercusServlet </servlet-class> <init-param> <param-name>ini-file</param-name> <param-value>WEB-INF/php.ini</param-value> </init-param> :

文字化け対策

QuercusServlet

Tomcat など

xxxx.php

php.iniunicode.semantics=on で動作モードを ISO-8859-1 → UTF-8 に変更

文字化け対策•文字化けの発生する可能性のある箇所

‣画面に記述した日本語の表示

‣GET/POST した日本語パラメータの表示

‣DB に入っている日本語の表示・登録

‣セッションに入れた日本語の表示

‣ファイル入力・出力

‣ログ  などなど

文字化け対策•アップロードファイル名

‣最新版でも文字化け

‣ファイル名が重要な場合は commons-fileupload の実装に差し替えるなどの対応が必要

‣ com.caucho.quercus.env.Post#fillPost()

文字化け対策•UTF-8 以外でのレスポンス出力

‣ Excel 前提だから CSV ファイルのダウンロードは SJIS で、といった要件のとき

‣ “SJIS-win” → “Windows-31J”

‣ print じゃなくて echo を使うprintf("%s\r\n", mb_convert_encoding($data, "SJIS-win", "UTF-8")); ↓ echo mb_convert_encoding($data, "Windows-31J", "UTF-8")."\r\n";

PHP をまともに動かすまでの障壁•文字化け

•Quercus が解釈できないシンタックス

•Quercus で未対応な PHP 関数

解釈できないシンタックス•動的に変数名やクラス名を決定するシンタックスは Quercus が解釈できない

!

!

!

!

‣そうしないように修正するしかない

$code = "red"; $color_{$code} = "..."; //$color_red = ..

$name = "Login"; $action = new {$name}Action(); //LoginAction

PHP をまともに動かすまでの障壁•文字化け

•Quercus が解釈できないシンタックス

•Quercus で未対応な PHP 関数

未対応な PHP 関数•Quercus は未実装関数を検知した場合、

UnimplementedException を投げる

•特にマルチバイト系の関数(mb_****) の未実装 or 不備が多い

‣ mb_convert_kana : 未実装

‣ mb_send_mail : 不備

‣ mb_encode_numericentity : 未実装  などなど

未対応な PHP 関数•PHP 関数については同じ名前のメソッドがあるのでわりとわかりやすい

未対応な PHP 関数

•以下のいずれかで対応

‣自力で Quercus を改変してビルド

‣ AOP でひっかけて実装

‣ PHP の呼び出し箇所を Java 化する

PHP と Java を

結合してみる

PHP - Java 結合のポイント•セッション情報

‣ Quercus - JavaEE 間セッション情報共有

‣セッションタイムアウト設定

•PHP ファイルの配置方法

•ビューレイアウト共有

PHP - Java 結合のポイント•セッション情報

‣ Quercus - JavaEE 間セッション情報共有

‣セッションタイムアウト設定

•PHP ファイルの配置方法

•ビューレイアウト共有

Tomcat など

PHP Java

PHP 5.2 Spring など

$_SESSION

• Quercus を使っても、  PHP のセッションと Java のセッションは別空間  になる !

HttpSession

Tomcat など

PHP Java

PHP 5.2 Spring など

$_SESSION

• Quercus を使っても、  PHP のセッションと Java のセッションは別空間  になる • なんらかの方法で、2つの空間を同期する仕組みが必要

HttpSession

Quercus-JavaEE セッション情報共有

•Quercus から HttpSession は参照できる

•PHP の auto_prepend 及び auto_append のしくみが Quercus でも使えるのでこれで同期

‣ auto_prepend‣ Java(HttpSession) → PHP($_SESSION)

‣ auto_append‣ PHP($_SESSION) → Java(HttpSession)

auto_prepend/auto_append で同期

QuercusServlet

Tomcat など

xxxx.php

php.ini

auto_prepend.php

auto_prepend/auto_append で同期

QuercusServlet

Tomcat など

xxxx.php

php.iniauto_append.php

PHP($_SESSION) → Java(HttpSession)

Java(HttpSession) → PHP($_SESSION)

auto_prepend/auto_append で同期

•php.ini に以下のように記述auto_prepend_file=/path/to/before.php auto_append_file=/path/to/after.php

[before.php: Java -> PHP] <?php session_start(); $request = quercus_servlet_request(); $session = $request->getSession(); $keys = $session->getAttributeNames(); while ($keys->hasMoreElements()) { $key = $keys->nextElement(); $value = $session->getAttribute($key); $_SESSION[$key] = $value; }

[after.php: PHP -> Java] <?php session_start(); $request = quercus_servlet_request(); $session = $request->getSession(); foreach ($_SESSION as $key => $value) { $session->setAttribute($key, $value); }

Tomcat など

PHP Java

Quercus Spring など

$_SESSION

• session-config ‣ HttpSession にのみ適用される

!!!!

HttpSession

Tomcat など

PHP Java

Quercus Spring など

$_SESSION

• session-config ‣ HttpSession にのみ適用される

• Quercus 管理の PHP セッション ‣ デフォルト 30 分で変更する手段なし? ‣ リフレクションで変更して Quercus がもっているタイマーを起動すればなんとかできる

HttpSession

セッションタイムアウト•アプリケーション起動時に 1 回 Quercus がもっているセッション管理のタイマーを起動すれば OK

•コードは長いので Gist に書きました

‣ https://gist.github.com/roundrop/96edc5f4d7135e60a2d0

PHP - Java 結合のポイント•セッション情報

‣ Quercus - JavaEE 間セッション情報共有

‣セッションタイムアウト設定

•PHP ファイルの配置方法

•ビューレイアウト共有

PHP ファイルの配置既存 PHP

Apache

htdocs (docルート)

include

機能A

index.phpいろいろ汚いの

機能B

PHP ファイルの配置既存 PHP

Apache

htdocs (docルート)

include

機能A

index.phpいろいろ汚いの

Java アプリ

web ルート

WEB-INF

機能B

PHP ファイルの配置既存 PHP

Apache

htdocs (docルート)

include

機能A

index.phpいろいろ汚いの

Java アプリ

web ルート

WEB-INF

機能B

PHP ファイルの配置既存 PHP

Apache

htdocs (docルート)

include

機能A

index.phpいろいろ汚いの

Java アプリ

web ルート

WEB-INF

機能A

index.phpいろいろ汚いの

機能B 機能B

include

ドキュメントルートをwebルートとして配置

PHP ファイルの配置既存 PHP

Apache

htdocs (docルート)

include

機能A

index.phpいろいろ汚いの

Java アプリ

web ルート

WEB-INF

機能A

index.phpいろいろ汚いの

機能B 機能B

include

ドキュメントルートをwebルートとして配置

PHP ファイルの配置既存 PHP

Apache

htdocs (docルート)

include

機能A

index.phpいろいろ汚いの

Java アプリ

web ルート

WEB-INF

機能A

index.phpいろいろ汚いの

機能B 機能B

include

ドキュメントルートをwebルートとして配置web ルートがきたなくなる 一部階層関係がかわってしまう (この例だと htdocs と include)

PHP ファイルの配置•階層関係重要

•こういうのありがち

!

!

!

!

•階層関係は変えないのが望ましい

htdocs/sub/hoge.php !<?php define('_ROOT', "../.."); define('_INCLUDE', _ROOT."/include"); :

PHP ファイルの配置既存 PHP

Apache

htdocs (docルート)

include

機能A

index.phpいろいろ汚いの

Java アプリ

web ルート

WEB-INF

機能B

PHP ファイルの配置既存 PHP

Apache

htdocs (docルート)

include

機能A

index.phpいろいろ汚いの

Java アプリ

web ルート

WEB-INF

機能B

phphtdocs

機能A

index.phpいろいろ汚いの

機能B

include

1つのディレクトリにそのままの構成で配置

Web サーバーで *.php を /php/htdocs へプロキシーする

PHP ファイルの配置•auto_prepend でサーバー変数を調整$_SERVER['SCRIPT_NAME'] = str_replace("/php/htdocs", "", $_SERVER['SCRIPT_NAME']); $_SERVER['SCRIPT_URL'] = str_replace("/php/htdocs", "", $_SERVER['SCRIPT_URL']); $_SERVER['REQUEST_URI'] = str_replace("/php/htdocs", "", $_SERVER['REQUEST_URI']); $_SERVER['PHP_SELF'] = str_replace("/php/htdocs", "", $_SERVER['PHP_SELF']); $_SERVER['DOCUMENT_ROOT'] = $_SERVER['DOCUMENT_ROOT']."php/htdocs/";

PHP - Java 結合のポイント•セッション情報

‣ Quercus - JavaEE 間セッション情報共有

‣セッションタイムアウト設定

•PHP ファイルの配置方法

•ビューテンプレート共有

ビューテンプレート共有•PHP と Java の両方が動いているとはいえ、サイトとしては 1 つで、デザインも同じ

‣サイトのヘッダーやフッターなどの部品は PHP と Java で共有したい

‣ Java のテンプレートエンジンでデザインして、PHP でもそれを使うのが理想

ビューテンプレート共有<?php : include_once 'header.php'; : [コンテンツ] :

<header> デザインした内容 :

: : <header th:include="header... : [コンテンツ] :

<header> デザインした内容 :

PHP Java (Thymeleaf)

header.php header.html

ビューテンプレート共有<?php : include_once 'header.php'; : [コンテンツ] :

<header> デザインした内容 :

: : <header th:include="header... : [コンテンツ] :

<header> デザインした内容 :

PHP Java (Thymeleaf)

header.php header.html

デザインテンプレートが二重管理 DRY じゃない

ビューテンプレート共有<?php : include_once 'header.php'; : [コンテンツ] :

: : <header th:include="header... : [コンテンツ] :

PHP Java (Thymeleaf)

<header> デザインした内容 :

<?php import aa.bb.XxxReader; print XxxReader::readHeader();

header.php header.html

: : <header th:include="header... : [コンテンツ] :

ビューテンプレート共有<?php : include_once 'header.php'; : [コンテンツ] :

PHP Java (Thymeleaf)

<header> デザインした内容 :

<?php import aa.bb.XxxReader; print XxxReader::readHeader();

header.php header.html

PHP では ・Java テンプレートエンジンの html ファイルを読み込み ・テンプレートエンジン特有のアトリビュート(th: 等)を削除 ・static 変数にキャッシュするなど

その他

その他•PHP の一部を Java に置き換える

•ロギング

•コネクションプーリング

その他•PHP の一部を Java に置き換える

•ロギング

•コネクションプーリング

PHP の一部を Java に置き換える•PHP から Java を扱う

‣ http://quercus.caucho.com/quercus-3.1/doc/quercus.xtp#JavaPHPintegration

• import して使う方法

!

!

•new Java(“…”) する方法

<?php import java.util.Date; $a = new Date(123);

<?php $a = new Java("java.util.Date", 123);

: <?php if ($user->is_premium) { : ?> <div><?php echo $user->name; ?></div> <div>ここに表示項目を追加したい</div> :

PHP の一部を Java に置き換える

PHP の一部を Java に置き換える: <?php if ($user->is_premium) { : ?> <div><?php echo $user->name; ?></div> <?php import com.example.helper.StatusHelper; ?> <div><?php echo StatusHelper::getLabel($user->status); ?></div> :

PHP の一部を Java に置き換える•Java の型 → PHP の型 の変換ルール‣ http://quercus.caucho.com/quercus-3.1/doc/

quercus.xtp#MarshallingPHPtoJavaconversions

‣ 例えば java.util.List は array になる

<?php import com.example.php.ContactLogic; $logic = new ContactLogic(); $recents = $logic->getRecents(); foreach ($recents as $contact) { $from = $contact["from"];

public final class ContactLogic { : public List<Contact> getRecents() { :

その他•PHP の一部を Java に置き換える

•ロギング

•コネクションプーリング

ロギング•Quercus 内部では java.util.logging が使われている

•error_log 関数については、ログタイプ=0 の場合、error_log ディレクティブに “syslog” を指定すると java.util.logging.Logger で出力される

!

!

• jul-to-slf4j を使って SLF4J に集約するとよい

<?php ini_set("error_log", "syslog"); error_log("メッセージ", 0);

その他•PHP の一部を Java に置き換える

•ロギング

•コネクションプーリング

コネクションプーリング•Quercus ならではの強力機能!

•mysql_connect(), pg_connect() や PDO で JNDI から接続をとってきて使用できる

‣ $con = pg_connect(“java:comp/env/jdbc/mydb”);

‣ $pdo = new PDO(“pgsql:java:comp/env/jdbc/mydb”);※PDO の場合は先頭に「データベース名:」が必要なので注意

コネクションプーリング•Web サーバーのContext に jdbc リソースを追加

•web.xml に以下を記述<servlet> <servlet-name>quercusServlet</servlet-name> <servlet-class> com.caucho.quercus.servlet.QuercusServlet </servlet-class> : <init-param> <param-name>database</param-name> <param-value>jdbc/mydb</param-value> </init-param> :

Maven 形式 / JDK 7 でビルド可能な Quercus を GitHub に置いています

https://github.com/roundrop/quercus!

(サンプルコードもそのうち GitHub に置きます)

まとめ•Quercus けっこううまく動く

•でも完璧ではないので、プロダクションレベルで適用するには多少の労力をかける必要はある

•うまくハマれば「段階的に」PHP→Java へ進化させられる

ありがとうございました

top related