4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

CakePHPのプラグインやテーマで静的ファイルを使用する際の注意点

Posted at

CakePHPには、プラグイン機能やViewを切り替えられるテーマ機能があり、静的なアセットファイルをwebrootとして一緒に含んで配布することが可能。便利なんだけど、当然、外から見られるような場所にあるわけじゃないので、CakePHPが用意したディスパッチャーを通して出力される。

前提

  • CakePHP 2.4.x
  • Apache 2.2.x

注意する点

何に注意しなくちゃいけないのかというと2点。

  • PHPを通して静的ファイルを読み込み、出力するのでパフォーマンスが悪い(マニュアルにも記載されている)
  • JSとCSSファイルは readfile() ではなく include で読み込まれるためPHPとして解釈される

1点目のパフォーマンス問題に関しては、マニュアルにあるように通常のwebrootからシンボリックリンクを貼れば解消される。

問題は2点目のJSとCSSをPHPとして解釈して出力する点。例えば以下の様なJSファイルが書ける。

/app/View/Themed/hoge/webroot/piyo.js
var url = '<?php echo 'http://example.com'; ?>';
実際に出力されるpiyo.js
var url = 'http://example.com';

一見便利そうに見える。えせsassっぽいこともできそう。が、これを表示させると出力が途中で切れる可能性がある。なぜ…と思って、CakePHPのディスパッチャーの該当ソースを見てみた。AssetDispatcher.phpに書かれている。

/lib/Cake/Routing/Filter/AssetDispatcher.php
<?php
protected function _deliverAsset(CakeResponse $response, $assetFile, $ext) {
	ob_start();
	$compressionEnabled = Configure::read('Asset.compress') && $response->compress();
	if ($response->type($ext) === $ext) {
		$contentType = 'application/octet-stream';
		$agent = env('HTTP_USER_AGENT');
		if (preg_match('%Opera(/| )([0-9].[0-9]{1,2})%', $agent) || preg_match('/MSIE ([0-9].[0-9]{1,2})/', $agent)) {
			$contentType = 'application/octetstream';
		}
		$response->type($contentType);
	}
	if (!$compressionEnabled) {
		$response->header('Content-Length', filesize($assetFile));
	}
	$response->cache(filemtime($assetFile));
	$response->send();
	ob_clean();
	if ($ext === 'css' || $ext === 'js') {
		include $assetFile;
	} else {
		readfile($assetFile);
	}

	if ($compressionEnabled) {
		ob_end_flush();
	}
}

最後の方にある、拡張子がcssとjsだった場合は、readfile() ではなく include を使っていることが分かる。これによってPHPのコードが書ける恩恵がある。が、その上のコードを見るとContent-Lengthを出力している。

$response->header('Content-Length', filesize($assetFile));

これ、何がまずいかというと、PHPのコードを含んだ状態でのファイルサイズになるため、PHPが解釈されて実際に出力されるファイルサイズとは違うことになる。そうなると、10Kbあるはずのものが、12Kb出力しようとして、途中で切れてしまったりする。

解決方法

正直ここの根本的な解消が思いつかなかったが、gzip(mod_deflate)を有効にして、ここのContent-Lengthを無視させることで解消できる。

deflate.conf
<IfModule mod_deflate.c>
  AddOutputFilterByType DEFLATE text/html
  AddOutputFilterByType DEFLATE text/css
  AddOutputFilterByType DEFLATE text/plain
  AddOutputFilterByType DEFLATE text/xml
  AddOutputFilterByType DEFLATE application/xhtml+xml
  AddOutputFilterByType DEFLATE application/xml
  AddOutputFilterByType DEFLATE image/svg+xml
  AddOutputFilterByType DEFLATE application/rss+xml
  AddOutputFilterByType DEFLATE application/atom_xml
  AddOutputFilterByType DEFLATE application/javascript
  AddOutputFilterByType DEFLATE application/x-javascript
  AddOutputFilterByType DEFLATE application/x-httpd-php
  AddOutputFilterByType DEFLATE application/x-httpd-fastphp
  AddOutputFilterByType DEFLATE application/x-httpd-eruby
</IfModule>

設定は超適当。

だが、なんかそもそもこのJSやCSSにPHPのコードを書いて、PHP経由で出力させること自体キモい感じするので、素直にシンボリックリンクを貼って、静的ファイルとして配信することが一番かもしれない。

参考URL

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?