[CakePHP] index.phpがあるディレクトリの名前と、index.phpにアクセスするときのURLのパス名が異なる場合、index.phpのWEBROOT_DIRを設定する必要がある。
結論
例えば、cake/app/webrootを/home/foo/public_htmlにコピーして公開するとします。すなわち、CakePHPにhttp://example.com/~foo/index.phpというURLでアクセスします。このようにindex.phpが存在するディレクトリの名前(例でいうとpublic_html)と、URLのパス名(例でいうと~foo)が異なる場合、index.phpのWEBROOT_DIR定数を設定する必要があります(例でいうと、'~foo'にします)。
詳細
CakePHPは、cake/dispatcher.phpのDispatcher::baseUrlというメソッドで、自分のURLを導出しています。
<?php /** * Returns a base URL. * * @return string Base URL */ function baseUrl() { $htaccess = null; $base = $this->admin; $this->webroot = ''; if (defined('BASE_URL')) { $base = BASE_URL.$this->admin; } $docRoot = env('DOCUMENT_ROOT'); $scriptName = env('PHP_SELF'); $r = null; $appDirName = str_replace('/', '\/', preg_quote(APP_DIR)); $webrootDirName = str_replace('/', '\/', preg_quote(WEBROOT_DIR)); if (preg_match('/'.$appDirName.'\\'.DS.$webrootDirName.'/', $docRoot)) { $this->webroot = '/'; if (preg_match('/^(.*)\/index\.php$/', $scriptName, $r)) { if(!empty($r[1])) { return $base.$r[1]; } } } else { if (defined('BASE_URL')) { $webroot = setUri(); $htaccess = preg_replace('/(?:'.APP_DIR.'(.*)|index\\.php(.*))/i', '', $webroot).APP_DIR.'/'.$webrootDirName.'/'; } if (preg_match('/^(.*)\\/'.$appDirName.'\\/'.$webrootDirName.'\\/index\\.php$/', $scriptName, $regs)) { if(APP_DIR === 'app') { $appDir = null; } else { $appDir = '/'.APP_DIR; } !empty($htaccess)? $this->webroot = $htaccess : $this->webroot = $regs[1].$appDir.'/'; return $base.$regs[1].$appDir; } elseif (preg_match('/^(.*)\\/'.$webrootDirName.'([^\/i]*)|index\\\.php$/', $scriptName, $regs)) { !empty($htaccess)? $this->webroot = $htaccess : $this->webroot = $regs[0].'/'; return $base.$regs[0]; } else { !empty($htaccess)? $this->webroot = $htaccess : $this->webroot = '/'; return $base; } } return $base; } ?>
メソッドの前のコメントには「URLを返す」とありますが、実際にはそれ以外にも自分のwebrootメンバーを設定しています。このwebrootメンバーが、このあとコントローラや、末端のHtmlヘルパーまで伝わります。さて、上のままでは複雑なので、webrootに関するコード以外を削除します。BASE_URLという定数もありますが、.htaccessを使用していればこの定数は設定されないので、これも削除します。BASE_URLが設定されないと、$htaccessという変数も使われないので、これも削除します。すると、以下のようになります。
<?php function baseUrl() { $docRoot = env('DOCUMENT_ROOT'); $scriptName = env('PHP_SELF'); $appDirName = str_replace('/', '\/', preg_quote(APP_DIR)); $webrootDirName = str_replace('/', '\/', preg_quote(WEBROOT_DIR)); if (preg_match('/'.$appDirName.'\\'.DS.$webrootDirName.'/', $docRoot)) { # (1) $this->webroot = '/'; } else { if (preg_match('/^(.*)\\/'.$appDirName.'\\/'.$webrootDirName.'\\/index\\.php$/', $scriptName, $regs)) { # (2) if(APP_DIR === 'app') { $appDir = null; } else { $appDir = '/'.APP_DIR; } $this->webroot = $regs[1].$appDir.'/'; } elseif (preg_match('/^(.*)\\/'.$webrootDirName.'([^\/i]*)|index\\\.php$/', $scriptName, $regs)) { # (3) $this->webroot = $regs[0].'/'; } else { # (4) $this->webroot = '/'; } } } ?>
URLを判定している箇所を、順に見ていきます。
(1)では、cake/app/webrootがドキュメントルートになっていないか調べています。この場合、webrootは"/"になります。
(2)では、アクセスしているスクリプトがapp/webroot/index.phpではないか調べています。CakePHPをいわゆる開発用にインストールし、/foo/app/webroot/index.phpのようにアクセスする場合が、これに該当すると思います。このとき、webrootは"/foo"になります。.htaccessによるURL書き換えがあるので、"/foo/app/webroot"にはしません。
(3)では、アクセスしているスクリプトが/foo/webrootにないか調べています(おそらく。'([^\/i]*)|index\\\.php$/'を追加している理由が分かりません)。app/webrootを切り離して、いわゆる公開用に設定している場合が、これに当たると思います。このとき、webrootは"/foo/webroot"になります。
(4)は、どれでもない場合です。webrootは"/"にしていますが、おそらくここには到達しないと想定されているような気がします。
さて、(3)で比較しているのは、$webrootDirNameと$scriptNameです。これらは各々、
<?php $scriptName = env('PHP_SELF'); $webrootDirName = str_replace('/', '\/', preg_quote(WEBROOT_DIR)); ?>
として設定されています。ここで、env関数はcake/basics.phpで定義されている関数で、指定された環境変数の値を返します。PHP_SELFの場合、/~foo/cake/app/webroot/index.phpのように、URLのパスとなります。一方、WEBROOT_DIR定数は、index.phpで設定される定数で、
<?php if (!defined('WEBROOT_DIR')) { define('WEBROOT_DIR', basename(dirname(__FILE__))); } ?>
のように、__FILE__定数から求められています。ここで、__FILE__は、index.phpが設置されているパスで、/home/foo/cake/app/webroot/index.phpのようになります。すなわち、$scriptNameと$webrootDirNameは異なるものであり、一致しない場合が出てきます。例えば、cake/app/webrootをcake/appから切り離して、/home/foo/public_htmlとする場合です。このとき、$scriptNameは/~foo/index.phpとなり、$webrootDirNameはpublic_htmlとなります。そのため、(3)では該当せず(4)となり、スタイルシートが適用されないなどの問題が発生します。これを解決するためには、WEBROOT_DIRをURLのパス名に変更します。すなわち、
<?php if (!defined('WEBROOT_DIR')) { define('WEBROOT_DIR', '~foo'); } ?>
などとします。
問題点
app/webroot/index.phpには、
<?php /////////////////////////////// //DO NOT EDIT BELOW THIS LINE// /////////////////////////////// if (!defined('WEBROOT_DIR')) { define('WEBROOT_DIR', basename(dirname(__FILE__))); } ?>
とあるのですが、"DO NOT EDIT BELOW THIS LINE"って、「ここから下はいじるな」という意味じゃないんでしょうか...?