Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

PHP in Java -Quercus- によるレガシーマイグレーション実例 #jjug_ccc #ccc_r12

5,253 views

Published on

  • Be the first to comment

PHP in Java -Quercus- によるレガシーマイグレーション実例 #jjug_ccc #ccc_r12

  1. 1. PHP in Java -Quercus- による レガシーマイグレーション 山下 竜司 株式会社アットウェア/Facebook4J #jjug_ccc #ccc_r12
  2. 2. 自己紹介 •山下 竜司 •株式会社アットウェア •@roundrop •Facebook4J - http://facebook4j.org
  3. 3. PHP: Hypertext Preprocessor
  4. 4. http://venturebeat.com/2013/05/17/google-app-engine-finally-supports-php-the-language-that-runs-75-of-the-web/ 世界一 広まっている言語
  5. 5. 世界一 dis られている言語
  6. 6. Quercus https://www.flickr.com/photos/justinwkern/6140775849/
  7. 7. Quercus •けっこう昔からあった > svn log svn://svn.caucho.com/home/svn/svnroot/resin/trunk/modules/quercus
  8. 8. Quercus •PHP (PHP5 相当) を Java 上で動かすことを 可能にする Java で実装されたエンジン
  9. 9. Quercus •PHP (PHP5 相当) を Java 上で動かすことを 可能にする Java で実装されたエンジン •オープンソース (ライセンスは GPL)
  10. 10. Quercus •PHP (PHP5 相当) を Java 上で動かすことを 可能にする Java で実装されたエンジン •オープンソース (ライセンスは GPL) •Caucho Technology 社の商用アプリケーショ ンサーバー Resin の一部
  11. 11. PHP アプリといえば WordPress
  12. 12. WordPress on Quercus デモ
  13. 13. WordPress on Quercus デモ ! https://gist.github.com/roundrop/4977262
  14. 14. 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 見た目に反して 開発はわりとアクティブ
  15. 15. 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>
  16. 16. Quercus の構造 QuercusServlet Tomcat など xxxx.php 1. phpファイル読込 2. 解析 3. 実行
  17. 17. PHP コードの再現力 •Quercus の PHP 再現力はかなりのもの •(モノによるが) だいたい 90% 以上のコード は動く •逆に言うとある程度は改修・実装が必要 ‣Quercus で未実装の PHP 関数の自前実装 ‣対応していないシンタックスを調整 など
  18. 18. PHP コードの実行速度 •速い •素の PHP より速くなる事例もある •キャッシュ、コネクションプールなど Java 資産が使える •プロダクションに投入できるレベル
  19. 19. PHP から Java のコードを呼び出せる •Java 上での PHP コードの実行だけでなく、 PHP コードから Java のコードを呼び出せる
  20. 20. PHP から Java のコードを呼び出せる •Java 上での PHP コードの実行だけでなく、 PHP コードから Java のコードを呼び出せる <?php import java.lang.System; import java.util.Date; $date = new Date(); System::out->println($date);
  21. 21. PHP から Java のコードを呼び出せる QuercusServlet Tomcat など xxxx.php Java コード 呼び出し Java Class
  22. 22. つまり、 •PHP アプリを Tomcat 等の上で動かしつつ
  23. 23. つまり、 •PHP アプリを Tomcat 等の上で動かしつつ •これからつくる新機能は Java で開発したり
  24. 24. つまり、 •PHP アプリを Tomcat 等の上で動かしつつ •これからつくる新機能は Java で開発したり •PHP の処理の一部だけ Java に置き換えたり
  25. 25. つまり、 •PHP アプリを Tomcat 等の上で動かしつつ •これからつくる新機能は Java で開発したり •PHP の処理の一部だけ Java に置き換えたり レガシーマイグレーションに使える
  26. 26. Java Application Server (Tomcat など) PHP
  27. 27. Java Application Server (Tomcat など) PHP Java (Spring等) 既存機能はPHPのまま 新機能はJava
  28. 28. Java Application Server (Tomcat など) PHP Java (Spring等) 既存機能はPHPのまま 新機能はJava 性能上のボトルネックだけ Java 化
  29. 29. Java Application Server (Tomcat など) PHP Java (Spring等) 既存機能はPHPのまま 新機能はJava あまりにひどい部分だけ Java 化 性能上のボトルネックだけ Java 化
  30. 30. Java Application Server (Tomcat など) PHP Java (Spring等) 既存機能はPHPのまま 新機能はJava あまりにひどい部分だけ Java 化 性能上のボトルネックだけ Java 化 スモールスタートしてから徐々に Java の範囲を 広げていき、最終的には PHP をなくす
  31. 31. 実践 https://www.flickr.com/photos/pshab/1366448271/
  32. 32. まずは PHP をまともに動かす
  33. 33. PHP をまともに動かすまでの障壁 •文字化け •Quercus が解釈できないシンタックス •Quercus で未対応な PHP 関数
  34. 34. PHP をまともに動かすまでの障壁 •文字化け •Quercus が解釈できないシンタックス •Quercus で未対応な PHP 関数
  35. 35. 文字化け対策 •既存 PHP アプリを Quercus 上で動かしてみ たところ絶望的に文字化け
  36. 36. 文字化け対策 •既存 PHP アプリを Quercus 上で動かしてみ たところ絶望的に文字化け •Java でつくる部分は UTF-8 にしたいので、 全体的に UTF-8 で統一したい
  37. 37. 文字化け対策 •文字コードを UTF-8 に える ‣ソースコード ‣画面の charset ‣データベース ‣リソースファイル ‣:
  38. 38. 文字化け対策 •Quercus は ISO-8859-1 前提で動作する •unicode.semantics=on ‣on にすることで UTF-8 前提で動作するよ うになる ‣php.ini に書いて Quercus に読み込ませる
  39. 39. 文字化け対策 •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> :
  40. 40. 文字化け対策 QuercusServlet Tomcat など xxxx.php php.ini unicode.semantics=on で動作モードを ISO-8859-1 → UTF-8 に変更
  41. 41. 文字化け対策 •文字化けの発生する可能性のある箇所 ‣画面に記述した日本語の表示 ‣GET/POST した日本語パラメータの表示 ‣DB に入っている日本語の表示・登録 ‣セッションに入れた日本語の表示 ‣ファイル入力・出力 ‣ログ  などなど
  42. 42. 文字化け対策 •アップロードファイル名 ‣最新版でも文字化け ‣ファイル名が重要な場合は commons- fileupload の実装に差し替えるなどの対応 が必要 ‣com.caucho.quercus.env.Post#fillPost()
  43. 43. 文字化け対策 •UTF-8 以外でのレスポンス出力 ‣Excel 前提だから CSV ファイルのダウン ロードは SJIS で、といった要件のとき ‣“SJIS-win” → “Windows-31J” ‣print じゃなくて echo を使う printf("%srn", mb_convert_encoding($data, "SJIS-win", "UTF-8")); ↓ echo mb_convert_encoding($data, "Windows-31J", "UTF-8")."rn";
  44. 44. PHP をまともに動かすまでの障壁 •文字化け •Quercus が解釈できないシンタックス •Quercus で未対応な PHP 関数
  45. 45. 解釈できないシンタックス •動的に変数名やクラス名を決定するシンタック スは Quercus が解釈できない ! ! ! ! ‣そうしないように修正するしかない $code = "red"; $color_{$code} = "..."; //$color_red = .. $name = "Login"; $action = new {$name}Action(); //LoginAction
  46. 46. PHP をまともに動かすまでの障壁 •文字化け •Quercus が解釈できないシンタックス •Quercus で未対応な PHP 関数
  47. 47. 未対応な PHP 関数 •Quercus は未実装関数を検知した場合、 UnimplementedException を投げる •特にマルチバイト系の関数(mb_****) の未実装 or 不備が 多い ‣mb_convert_kana : 未実装 ‣mb_send_mail : 不備 ‣mb_encode_numericentity : 未実装  などなど
  48. 48. 未対応な PHP 関数 •PHP 関数については同じ名前のメソッドがあ るのでわりとわかりやすい
  49. 49. 未対応な PHP 関数 •以下のいずれかで対応 ‣自力で Quercus を改変してビルド ‣AOP でひっかけて実装 ‣PHP の呼び出し箇所を Java 化する
  50. 50. PHP と Java を 結合してみる
  51. 51. PHP - Java 結合のポイント •セッション情報 ‣Quercus - JavaEE 間セッション情報共有 ‣セッションタイムアウト設定 •PHP ファイルの配置方法 •ビューレイアウト共有
  52. 52. PHP - Java 結合のポイント •セッション情報 ‣Quercus - JavaEE 間セッション情報共有 ‣セッションタイムアウト設定 •PHP ファイルの配置方法 •ビューレイアウト共有
  53. 53. Tomcat など PHP Java PHP 5.2 Spring など $_SESSION • Quercus を使っても、  PHP のセッションと Java のセッションは別空間  になる ! HttpSession
  54. 54. Tomcat など PHP Java PHP 5.2 Spring など $_SESSION • Quercus を使っても、  PHP のセッションと Java のセッションは別空間  になる • なんらかの方法で、2つの空間を同期する仕組みが 必要 HttpSession
  55. 55. Quercus-JavaEE セッション情報共有 •Quercus から HttpSession は参照できる •PHP の auto_prepend 及び auto_append のしく みが Quercus でも使えるのでこれで同期 ‣auto_prepend ‣ Java(HttpSession) → PHP($_SESSION) ‣auto_append ‣ PHP($_SESSION) → Java(HttpSession)
  56. 56. auto_prepend/auto_append で同期 QuercusServlet Tomcat など xxxx.php php.ini
  57. 57. auto_prepend.php auto_prepend/auto_append で同期 QuercusServlet Tomcat など xxxx.php php.ini auto_append.php PHP($_SESSION) → Java(HttpSession) Java(HttpSession) → PHP($_SESSION)
  58. 58. 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); }
  59. 59. Tomcat など PHP Java Quercus Spring など $_SESSION • session-config ‣ HttpSession にのみ適用される ! ! ! ! HttpSession
  60. 60. Tomcat など PHP Java Quercus Spring など $_SESSION • session-config ‣ HttpSession にのみ適用される • Quercus 管理の PHP セッション ‣ デフォルト 30 分で変更する手段なし? ‣ リフレクションで変更して Quercus がもってい るタイマーを起動すればなんとかできる HttpSession
  61. 61. セッションタイムアウト •アプリケーション起動時に 1 回 Quercus が もっているセッション管理のタイマーを起動 すれば OK •コードは長いので Gist に書きました ‣https://gist.github.com/roundrop/ 96edc5f4d7135e60a2d0
  62. 62. PHP - Java 結合のポイント •セッション情報 ‣Quercus - JavaEE 間セッション情報共有 ‣セッションタイムアウト設定 •PHP ファイルの配置方法 •ビューレイアウト共有
  63. 63. PHP ファイルの配置 既存 PHP Apache htdocs (docルート) include 機能A index.php いろいろ汚いの 機能B
  64. 64. PHP ファイルの配置 既存 PHP Apache htdocs (docルート) include 機能A index.php いろいろ汚いの Java アプリ web ルート WEB-INF 機能B
  65. 65. PHP ファイルの配置 既存 PHP Apache htdocs (docルート) include 機能A index.php いろいろ汚いの Java アプリ web ルート WEB-INF 機能B
  66. 66. PHP ファイルの配置 既存 PHP Apache htdocs (docルート) include 機能A index.php いろいろ汚いの Java アプリ web ルート WEB-INF 機能A index.php いろいろ汚いの 機能B 機能B include ドキュメントルートをwebルートとして配置
  67. 67. PHP ファイルの配置 既存 PHP Apache htdocs (docルート) include 機能A index.php いろいろ汚いの Java アプリ web ルート WEB-INF 機能A index.php いろいろ汚いの 機能B 機能B include ドキュメントルートをwebルートとして配置
  68. 68. PHP ファイルの配置 既存 PHP Apache htdocs (docルート) include 機能A index.php いろいろ汚いの Java アプリ web ルート WEB-INF 機能A index.php いろいろ汚いの 機能B 機能B include ドキュメントルートをwebルートとして配置 web ルートがきたなくなる 一部階層関係がかわってしまう (この例だと htdocs と include)
  69. 69. PHP ファイルの配置 •階層関係重要 •こういうのありがち ! ! ! ! •階層関係は変えないのが望ましい htdocs/sub/hoge.php ! <?php define('_ROOT', "../.."); define('_INCLUDE', _ROOT."/include"); :
  70. 70. PHP ファイルの配置 既存 PHP Apache htdocs (docルート) include 機能A index.php いろいろ汚いの Java アプリ web ルート WEB-INF 機能B
  71. 71. PHP ファイルの配置 既存 PHP Apache htdocs (docルート) include 機能A index.php いろいろ汚いの Java アプリ web ルート WEB-INF 機能B php htdocs 機能A index.php いろいろ汚いの 機能B include 1つのディレクトリにそのままの構成で配置 Web サーバーで *.php を /php/htdocs へプロキシーする
  72. 72. 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/";
  73. 73. PHP - Java 結合のポイント •セッション情報 ‣Quercus - JavaEE 間セッション情報共有 ‣セッションタイムアウト設定 •PHP ファイルの配置方法 •ビューテンプレート共有
  74. 74. ビューテンプレート共有 •PHP と Java の両方が動いているとはいえ、 サイトとしては 1 つで、デザインも同じ ‣サイトのヘッダーやフッターなどの部品は PHP と Java で共有したい ‣Java のテンプレートエンジンでデザインし て、PHP でもそれを使うのが理想
  75. 75. ビューテンプレート共有 <?php : include_once 'header.php'; : [コンテンツ] : <header> デザインした内容 : : : <header th:include="header... : [コンテンツ] : <header> デザインした内容 : PHP Java (Thymeleaf) header.php header.html
  76. 76. ビューテンプレート共有 <?php : include_once 'header.php'; : [コンテンツ] : <header> デザインした内容 : : : <header th:include="header... : [コンテンツ] : <header> デザインした内容 : PHP Java (Thymeleaf) header.php header.html デザインテンプレートが二重管理 DRY じゃない
  77. 77. ビューテンプレート共有 <?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
  78. 78. : : <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 変数にキャッシュするなど
  79. 79. その他
  80. 80. その他 •PHP の一部を Java に置き換える •ロギング •コネクションプーリング
  81. 81. その他 •PHP の一部を Java に置き換える •ロギング •コネクションプーリング
  82. 82. 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);
  83. 83. : <?php if ($user->is_premium) { : ?> <div><?php echo $user->name; ?></div> <div>ここに表示項目を追加したい</div> : PHP の一部を Java に置き換える
  84. 84. 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> :
  85. 85. 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() { :
  86. 86. その他 •PHP の一部を Java に置き換える •ロギング •コネクションプーリング
  87. 87. ロギング •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);
  88. 88. その他 •PHP の一部を Java に置き換える •ロギング •コネクションプーリング
  89. 89. コネクションプーリング •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 の場合は先頭に「データベース名:」が必要なので注意
  90. 90. コネクションプーリング •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> :
  91. 91. Maven 形式 / JDK 7 でビルド可能な Quercus を GitHub に置いています https://github.com/roundrop/quercus ! (サンプルコードもそのうち GitHub に置きます)
  92. 92. まとめ •Quercus けっこううまく動く •でも完璧ではないので、プロダクションレベ ルで適用するには多少の労力をかける必要は ある •うまくハマれば「段階的に」PHP→Java へ進 化させられる
  93. 93. ありがとうございました

×