k4200’s notes and thoughts

Programmer side of k4200

既存のPHPプロジェクトをZend Frameworkのプロジェクトに

やりたい事

タイトル通りだけど、既存のPHPプロジェクト(フレームワークとかは特に使っていない)をZend Frameworkのプロジェクトに移行する。

参考ページ

基本はこのページのとおり。

具体的な処理の流れは以下の通り。

  • mod_rewrite で、ディレクトリに対するアクセスなどはZend Frameworkに飛ばす
  • それ以外の、実際に存在する PHP ファイル、画像ファイルなどは今までどおり処理。
環境

/var/www/html 直下に既存のプロジェクトがあるものとする。

手順

Zend Frameworkのプロジェクト作成

適当なディレクトリでZFのプロジェクトを作成(projectxという名前)。

$ zf create project projectx

色々ファイルができるので、それを /var/www/html 配下に移動。

$ mv projectx/* /var/www/html

既存のファイル、ディレクトリと名前が被る場合は、既存のプロジェクトの方を適当に修正する。

mod_rewrite の設定

.htaccess とかに以下のように記載。参考にしたページの内容とは若干変えている。

RewriteCond     %{REQUEST_FILENAME} !-f    [OR]
RewriteCond     %{REQUEST_FILENAME} \.php$ [OR]
RewriteCond     %{REQUEST_FILENAME} =/
RewriteRule     ^(.*)$ /public/index.php/$1 [L,NS]
  • リクエストが以下の「どれか」を満たす時、/public/index.php が呼ばれる。
    • ファイルでない(ディレクトリと、それ以外に何かあったっけ?)
    • ファイル名が .php で終わる
    • /
  • それ以外の画像ファイル・CSSJavaScript とかは、この RewriteRule が適用されずにそのまま呼ばれる。

基本的な仕組みとしては、php ファイルに対するアクセスは全て public/index.php が処理をする

mod_rewrite に関しては、詳しくはApacheのドキュメントを参照。

public/index.php

ここでやりたい事は、以下の通り。

  • 存在するPHPファイル(既存プロジェクトのファイル)へのアクセスは、そのファイルが処理する
  • 存在しないPHPファイルへのアクセスはZFのFront Controllerが処理をする

ファイルの中身は以下の通り。

<?php

// Define path to application directorydefined('APPLICATION_PATH')
    || define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application'));

// Define application environment
defined('APPLICATION_ENV')
    || define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV') : 'production'));

// Ensure library/ is on include_path
set_include_path(implode(PATH_SEPARATOR, array(
    realpath(APPLICATION_PATH . '/../library'),
    get_include_path(),
)));


//ここがキモ
$request = new Zend_Controller_Request_Http();
$docroot = $request->get('DOCUMENT_ROOT');
$uri     = $request->getPathInfo(); // 1. これだとダメだった
$path    = $_SERVER['PATH_INFO'];

$filePath = $docroot . $path;
if (is_file($filePath)) {  // 2. file_exists だとディレクトリでも true になる

    chdir(dirname($filePath)); // 3. これがないと require_once でエラーになるケースも

    ob_start();
    include $filePath;

    $response = new Zend_Controller_Response_Http();
    $response->setBody(ob_get_clean());
    $response->sendResponse();

    exit;
}

/** Zend_Application */
require_once 'Zend/Application.php';

// Create application, bootstrap, and run                                                                                                                     
$application = new Zend_Application(
    APPLICATION_ENV,
    APPLICATION_PATH . '/configs/application.ini'
);
$application->bootstrap()
            ->run();


コードの中でコメントを書いている所が、参考にしたサイトと違う箇所。3箇所あるので順に説明。

1. アクセスされたURI→実際のファイル名への変換

元のコードだと、mod_rewrite で URLが書き換わっている場合にうまくいかなかった。

2. file_exists だとディレクトリでも true になる

file_exists だとディレクトリでもtrueが返るので、その後のinclude でエラーになる。is_file にすればOK。

3. chdir でディレクトリを変更

/var/www/html/page.php から /var/www/html/lib/someclass.inc.php を読み込んでいる場合を考える。

page.php の中身は以下のような感じ。

<?php
// page.php 
require_once('lib/someclass.inc.php'); // エラー
?>

http://example.com/page.php にアクセスすると、エラーになる。理由は以下の通り。

  • mod_rewrite により public/index.php が呼び出され
  • そこから /var/www/html/page.php が includeされる
  • この場合、カレントディレクトリは /var/www/html/public なので、page.php の require_once が失敗する
<?php
// page.php 
require_once(__DIR__ . '/lib/someclass.inc.php'); // こうなってればOKだけど・・・
?>

応用編

mod_rewrite を使っている場合もこれでOK

SEOの為に mod_rewrite を使ってURLを書き換えている場合でも、上の設定でOK。

参考ページではわざわざZFの custom route を作っていたけど、実際には必要ない。

phpMyAdmin 等、複雑なものの場合

さっきの設定で基本的には大体大丈夫なんだけど、複雑なシステムの場合はダメかもしれない。自分のところの例で言うと、phpMyAdmin が上手くいかなかったので、.htaccess に以下のルールを追加して例外扱いにしている。

RewriteCond %{REQUEST_URI} ^/phpMyAdmin/(.*)$
RewriteRule ^.*$ - [L]

まとめ

レガシーコードは一気に置き換えるのではなくて、徐々に置き換えていった方がいいと思う。

Zend Framework は今回初めて使ったんだけど、単なるライブラリとしても使えるので、

ってやるのもいいかな、と。