CakePHP + CacheHelper + html-minifier を CacheHelper と併用して使用する
概要
CakePHP + CacheHelper + html-minifier を併用できるようにしました。
エレメントとレイアウトのビューファイルの圧縮済みファイルを相対パスで保存し、かつ、読み込み時には、App::build で View
パスの先頭にキャッシュパスを追加しています。
キャッシュファイルからのViewクラスでは、エレメントのビューファイルを App::pasth('View')
に従い、検索します。
この時、App::build で View
パスの先頭にキャッシュパスを追加しているので、キャッシュファイルがあればキャッシュファイルを、キャッシュファイルがなければ、通常のビューファイルを使ってレンダリングを行います。
環境
機能
- キャッシュファイルがあればキャッシュファイルを優先して使用をする
- プラグイン経由でビューもしっかり考慮
- ビューキャッシュ化を回避するnocacheコメントは残す
- _getViewFileName で検索されるビューファイルは相対パスでキャッシュをしない
- Emails/text にあるビューファイルはHTML圧縮しない
- テキストが想定されているので、改行や空白に意味がある
コード
[composer.json]
{ "require": { "zaininnari/html-minifier": "*" } }
curl -sS https://getcomposer.org/installer | php php composer.phar install # HTML圧縮後のビューファイルの置場への書き込み権限を付加する
[app/Config/boostrap.php]
<?php // パスは適宜変更 require(dirname(ROOT) . DS . 'vendor' . DS . 'autoload.php'); // ビューパスにHTML圧縮済みのキャッシュパスを追加する // App::PREPEND は既存の先頭への追加 App::build(['View' => [CACHE . 'cached' . DS]], App::PREPEND);
<?php App::uses('View', 'View'); App::uses('Folder', 'Utility'); class MyView extends View { protected function _getElementFileName($name) { $path = parent::_getElementFileName($name); $cacheRelativePath = $this->_getCacheRelativePath($name, $path); if ($cacheRelativePath === false) { return $path; } return $this->minifyHtml($path, $cacheRelativePath); } protected function _getViewFileName($name = null) { $path = parent::_getViewFileName($name); // View のテンプレートファイルは、 // 絶対パスや相対パスの指定で任意のファイルが読み込めるので、 // 相対パスの生成は行わない return $this->minifyHtml($path); } protected function _getLayoutFileName($name = null) { $path = parent::_getLayoutFileName($name); $cacheRelativePath = $this->_getCacheRelativePath($name, $path); if ($cacheRelativePath === false) { return $path; } return $this->minifyHtml($path, $cacheRelativePath); } protected function _getCacheRelativePath($name, $path) { // HTML圧縮済みのテンプレートパスからの読み込みの場合は、何もしない if (strpos($path, CACHE . 'cached' . DS) === 0) { return false; } $relative_path = null; list($plugin) = $this->pluginSplit($name); // 定義済みのパスから相対パスを生成 foreach ($this->_paths($plugin) as $_path) { if (strpos($path, $_path) === 0) { $relative_path = substr($path, strlen($_path)); break; } } if ($relative_path === null) { return false; } // プラグインの場合は混在しないよう'Plugin'ディレクトリに保存 if ($plugin !== null) { $relative_path = 'Plugin' . DS . $plugin . DS . $relative_path; } return $relative_path; } protected function isHTMLTemplate($fileName) { $noHtmlTemplatePaths = [ DS . 'Emails' . DS . 'text' . DS, ]; foreach ($noHtmlTemplatePaths as $noHtmlTemplatePath) { if (strpos($fileName, $noHtmlTemplatePath) !== false) { return false; } } return true; } /** * テンプレートファイルのHTML部分を minify して、キャッシュしたファイルパスを返す。 * 失敗した場合、元のファイルパスを返す。 * @param string $fileName テンプレートファイルのフルパス * @param string|null $relative_path 相対パス * @return string テンプレートファイルのフルパス */ protected function minifyHtml($fileName, $relative_path = null) { if (!$this->isHTMLTemplate($fileName)) { return $fileName; } if ($relative_path) { $fileNameCached = CACHE . 'cached' . DS . $relative_path; $fileNamePath = dirname($fileNameCached); $Folder = new Folder(); if (!$Folder->create($fileNamePath, 0777)) { return $fileName; } } else { $fileNameCached = $fileName; if (strpos($fileName, ROOT) === 0) { $fileNameCached = substr($fileName, strlen(ROOT)); } $fileNameCached = CACHE . 'cached' . DS . strtolower(str_replace(DS, '_', $fileNameCached)); } if (file_exists($fileNameCached)) { return $fileNameCached; } if (!is_writable(dirname($fileNameCached))) { return $fileName; } $content = file_get_contents($fileName); $tokens = token_get_all($content); $minify = ''; $option = [ 'excludeComment' => ['/<!--\/?nocache-->/'], ]; foreach ($tokens as $token) { if (is_array($token)) { $token_name = $token[0]; if ($token_name === T_COMMENT || $token_name === T_DOC_COMMENT) { continue; } $token_data = $token[1]; if ($token_name === T_CLOSE_TAG) { $minify .= trim($token_data); } elseif ($token_name === T_WHITESPACE) { $minify .= ' '; } elseif ($token_name === T_INLINE_HTML) { // PHP で属性等を生成する場合、半角空白の区切りを削除してしまうことへの対策 if (preg_match('/^\s+$/', $token_data)) { $minify .= ' '; } else { $minify .= zz\Html\HTMLMinify::minify($token_data, $option); } } else { $minify .= $token_data; } } else { $minify .= $token; } } //@codingStandardsIgnoreStart if (@file_put_contents($fileNameCached, $minify, LOCK_EX) !== false) { return $fileNameCached; } //@codingStandardsIgnoreEnd return $fileName; } }