CakePHP on Java Servlet - quercus 使用
前回 id:fits:20101111 に続き、今度は CakePHP を Java Servlet Engine(jetty)上で実行してみました。
使用した環境は以下の通り。CakePHP 以外は id:fits:20101111 と同じです。
- CakePHP 1.3.5
- quercus 4.0.11
- Maven 3.0(jetty 6.1.26)
サンプルのソースコード、CakePHP の変更ファイルは http://github.com/fits/try_samples/tree/master/blog/20101112/
Maven プロジェクトの作成・設定や quercus のファイルの配置に関しては id:fits:20101111 を参照。
CakePHP を Java Servlet で実行するための作業
Java Servlet(quercus)で CakePHP を実行するには、以下を実施します。
なお、事前準備として src/main/webapp に CakePHP のソースファイルを配置して、core.php に必要最小限の設定を行っておきます。
CakePHPのシングルトンの実装を変更
まず、シングルトンの実装を変更します。
これをやらないとシングルトンが有効に機能せず、ページの表示時に Fatal Error が発生し、CakePHP が正常に動作しません。
cake/libs 内のソースファイルを対象に getInstance の実装を以下のように変更します。
変更後(src/main/webapp/cake/libs/configure.php)
function &getInstance() { if (is_null(self::$instance)) { self::$instance =& new App(); self::$instance->__map = (array)Cache::read('file_map', '_cake_core_'); } return self::$instance; } //$instance を外出し static $instance = null;
ちなみに、元の実装は以下のようになっています。
変更前(src/main/webapp/cake/libs/configure.php)
function &getInstance() { static $instance = array(); if (!$instance) { $instance[0] =& new App(); $instance[0]->__map = (array)Cache::read('file_map', '_cake_core_'); } return $instance[0]; }
なお、変更対象ファイルは以下のようなものです。
シングルトンの実装変更対象ファイル
- src/main/webapp/cake/libs
また、変数未定義の Notice が発生する箇所も必要に応じて修正します。(例えば、debugger.php の compact に渡す helpID の変数定義など)
CakePHP用の Servlet Filter を用意
.htaccess の代わりにアクセス先を変更する Servlet Filter を用意します。
必要最低限の処理はファイルの有無に応じて app/webroot もしくは app/webroot/index.php?url=[URL] にフォワードする事です。今回も自作してみました。
src/main/java/fits/sample/CakeRoutingFilter.java
package fits.sample; ・・・ public class CakeRoutingFilter implements Filter { ・・・ public void init(FilterConfig config) { this.ctx = config.getServletContext(); } //フィルター処理 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { HttpServletRequest req = (HttpServletRequest)request; String contextPath = req.getContextPath(); String requestPath = req.getRequestURI(); String path = requestPath.replace(contextPath + "/", "").trim(); if (!path.startsWith("app/webroot")) { String vPath = "app/webroot/" + path; String rPath = this.ctx.getRealPath(vPath); if (new File(rPath).exists()) { this.dispatchForward("/" + vPath, request, response); return; } else { this.dispatchForward("/app/webroot/index.php?url=" + path, request, response); return; } } chain.doFilter(request, response); } ・・・ private void dispatchForward(String path, ServletRequest request, ServletResponse response) throws ServletException, IOException { request.getRequestDispatcher(path).forward(request, response); } }
キャッシュを無効化
キャッシュが有効な状態だと 2度目以降のアクセスで例外が発生するようになるため、app/config/core.php で Cache.disable を true に設定しキャッシュを無効化します。
src/main/webapp/app/config/core.php
Configure::write('Cache.disable', true);
動作確認
DB に MySQL を使って、簡単なモデル・ビュー・コントローラーを実行してみます。
DB・テーブルを作成(sql/todo_db.sql 参照)し、app/config/database.php を作成します。
なお、src/main/webapp/WEB-INF/lib に MySQL の JDBC ドライバー(mysql-connector-java-5.1.13-bin.jar)を配置しておきます。
src/main/webapp/app/config/database.php
<?php class DATABASE_CONFIG { var $default = array( 'driver' => 'mysql', 'persistent' => false, 'host' => 'localhost', 'login' => 'root', 'password' => '', 'database' => 'todo', 'prefix' => '', 'encoding' => 'utf8' ); }
モデル・コントローラーを作成します。
src/main/webapp/app/models/datasources/tasks.php
<?php class Task extends AppModel { var $name = "Task"; }
src/main/webapp/app/controllers/tasks_controller.php
<?php class TasksController extends AppController { var $name = "Tasks"; function index() { $tasks = $this->Task->find('all'); $this->set('tasks', $tasks); } //追加 function add() { $this->Task->save($this->data); $this->redirect('.'); } }
ビューを作成します。(レイアウトは src/main/webapp/app/views/layouts/default.ctp 参照)
Java Servlet として実行する場合、find の結果がテーブル名 tasks の連想配列の配列になるようなので注意。(通常はモデル名 Task の連想配列の配列になる)
src/main/webapp/app/views/tasks/index.ctp
<div> <?php echo $form->create('Task', array('type' => 'POST', 'action' => 'add')); echo $form->label(null, 'タイトル'); echo $form->text('Task.title'); echo $form->submit('追加'); echo $form->end(); ?> </div> <div> <table> <tr> <th>ID</th> <th>タイトル</th> <th>登録日</th> <th>更新日</th> </tr> <?php foreach ($tasks as $task) { //Java で実行する場合はテーブル名 tasks の連想配列になっている //XAMPP で普通に実行する場合はモデル名 Task の連想配列になる $t = $task['tasks']; ?> <tr> <td><?php echo $t['task_id']; ?></td> <td><?php echo $t['title']; ?></td> <td><?php echo $t['created']; ?></td> <td><?php echo $t['modified']; ?></td> </tr> <?php } ?> </table> </div>
jetty を起動して http://localhost:8080/cakephp-sample/tasks/ にアクセスすれば、一応動作している事が確認できると思います。