CakePHP1系でのお話。
本来ならpluginで拡張できるコアな部分ってControllerとModelだけなんだけど、エラーのところもpluginで拡張したかったので、強引に弄ってみた。
(ホントに強引なんであんまり参考にはならないと思います。)
前提としてこんな構成を想定しています
app
└──plugins
└──hoge
├──hoge_app_controller.php
├──hoge_app_error.php
├──hoge_app_model.php
├──controllers
├──models
└──views
└──layouts
└──default.ctp
1. 拡張したControllerにcakeError関数をオーバーライドしてみる
通常だとcakeError関数はObjectクラス内で設定されていてこんな感じになっている。
function cakeError($method, $messages = array()) { if (!class_exists('ErrorHandler' )) { App:: import('Core', 'Error'); if (file_exists(APP . 'error.php' )) { include_once (APP . 'error.php' ); } elseif (file_exists(APP . 'app_error.php' )) { include_once (APP . 'app_error.php' ); } } if (class_exists('AppError' )) { $error = new AppError($method, $messages); } else { $error = new ErrorHandler($method, $messages); } return $error; }
で、これだとそもそもpluginに記述した拡張エラークラスは拾わないので、プラグイン内に設置した拡張エラーハンドリンククラスも有効にするように、こんな感じに修正。
class HogeAppController extends AppController { public function cakeError($method, $messages = array()) { if (!class_exists('ErrorHandler' )) { App:: import('Core', 'Error'); if (file_exists(APP . 'plugins' . DS . $this-> plugin . DS . $this->plugin . '_app_error.php' )) { include_once (APP . 'plugins' . DS . $this-> plugin . DS . $this->plugin . '_app_error.php' ); } elseif (file_exists(APP . 'error.php' )) { include_once (APP . 'error.php' ); } elseif (file_exists(APP . 'app_error.php' )) { include_once (APP . 'app_error.php' ); } } if (!is_null($this-> plugin) && class_exists(ucfirst($this-> plugin) . 'AppError')) { $errorClass = ucfirst($this-> plugin) . 'AppError'; $error = new $errorClass($method, $messages, $this->plugin); } elseif (class_exists('AppError' )) { $error = new AppError($method, $messages); } else { $error = new ErrorHandler($method, $messages); } return $error; } }
これでplugin内に設置した拡張エラークラスが有効になるようになった。
2. 拡張エラークラス用のファイルを設置
/app/plugins/hoge配下に、拡張エラークラス(hoge_app_error.php)を作成
3. エラー処理時に利用するコントローラー(CakeErrorController)をプラグインように別で用意
通常のCakeErrorControllerを利用すると、継承されているコントローラーはAppControllerになる。
今回はプラグインで用意したHogeAppControllerを継承したいので、通常のCakeErrorControllerとは別で用意する。
(ここでは仮にAppendCakeErrorControllerとする)
class AppendCakeErrorController extends HogeAppController { var $name = 'AppendCakeError'; var $uses = array(); function __construct() { parent::__construct(); $this->_set(Router::getPaths()); $this-> params = Router:: getParams(); $this->constructClasses(); $this-> Component->initialize($this); $this->_set( array('cacheAction' => false, 'viewPath' => 'errors')); } }
4. 拡張エラークラスを用意
プラグイン内で利用するエラークラスを用意し、ErrorHandlerクラスの__construct関数を持ってきて、プラグイン内のもの利用するように修正。
(通常のErrorHandlerを拡張したものと違い、コンストラクタの第3引数にプラグインの名前を入力するのは、プラグイン内のlayoutを利用するようにするため)
class HogeAppError extends ErrorHandler { function __construct($method, $messages, $pluginName = null) { App:: import('Core', 'Sanitize'); static $__previousError = null; if ($__previousError != array($method, $messages)) { $__previousError = array($method, $messages); $this-> controller =& new AppendCakeErrorController(); } else { $this-> controller =& new Controller(); $this-> controller->viewPath = 'errors' ; } if (!is_null($pluginName)) { $this-> controller->plugin = $pluginName; } $options = array('escape' => false); $messages = Sanitize:: clean($messages, $options); if (!isset ($messages[0])) { $messages = array($messages); } if (method_exists($this->controller , 'apperror' )) { return $this->controller ->appError($method, $messages); } if (!in_array(strtolower($method), array_map('strtolower', get_class_methods($this)))) { $method = 'error'; } if ($method !== 'error' ) { if (Configure::read( 'debug') == 0) { $parentClass = get_parent_class($this); if (strtolower($parentClass) != 'errorhandler' ) { $method = 'error404'; } $parentMethods = array_map( 'strtolower', get_class_methods($parentClass)); if (in_array(strtolower($method), $parentMethods)) { $method = 'error404'; } if (isset ($messages[0]['code']) && $messages[0]['code'] == 500) { $method = 'error500'; } } } $this->dispatchMethod($method, $messages); $this->_stop(); } }
これでとりあえずプラグインに設置した拡張エラークラスを見に行くようになった。