CakePHP 2系 エラーをRedmine登録

やりたいこと

エラーや例外発生時に、自動的にRedmine上でチケットが作成されるようにする。

やりたいことは簡単なはず・・・だったのだけど、CakePHP2の仕様が少しおかしいので備忘録。

簡単な経緯

  • データに適切にユニークキーが張られてなく、データの不整合が発生
  • データの整理をしてユニークキーを設定
  • ユニークキーを作成したものの、どこのロジックで不整合が発生したかが不明
  • Exceptionをキャッチするようにしたい

実装

実装の流れはおおよそ下記の通り

  • ハンドラーの実装
  • bootstrap.phpでハンドラーの読み込み
  • core.phpにハンドラーの登録

ハンドラーの実装

ハンドラーにはController用・Console用の2種類がある。

デフォルトのcore.phpにはConroller用しか登録されておらず、Consoleのエラーを拾う設定が見当たらず、少し迷ったけど公式ドキュメントにはしっかり記述されている。
参考:(公式)エラーハンドリング

Controllerのエラーハンドラー

ErrorHandlerというクラスがあるので継承して作成。
別に継承しなくてもよいが、継承したほうが既存の動作を保証しやすい。

実装はこんな感じ。
Consoleでも利用しやすいようにTraitを実装してuseしている。
(Traitの実装は省略)

Error用とException用とでそれぞれ別のメソッドを実装。

<?php
App::uses('ErrorHandleRedmineTrait', 'Traits');

/**
 * Class AppErrorHandler
 */
class AppErrorHandler extends ErrorHandler {
	use ErrorHandleRedmineTrait;

	/**
	 * @param \Exception $exception
	 */
	public static function handleException(Exception $exception) {
		self::__makeExceptionIssue($exception);
		parent::handleException($exception);
	}

	/**
	 * @param int    $code
	 * @param string $description
	 * @param string $file
	 * @param int    $line
	 * @param array  $context
	 */
	public static function handleError($code, $description, $file = null, $line = null, $context = null) {
		self::__makeErrorIssue($code, $description, $file, $line, $context);
		parent::handleError($code, $description, $file, $line, $context);
	}

}

Consoleのエラーハンドラー

じつは、こいつが少し厄介だったりする。
いや、わかってしまえば度ってことないのだけど。

エラーハンドラーはcore.phpに登録する時点ではインスタンスが確定していないため、staticで呼び出せる必要があるようなのだけど、CakePHP2で用意されているConsoleErrorHandlerのメソッド軍はすべて非staticで実装されている。

これはあえてなのかバグなのか・・・

そのため、Console側では継承ではなく実装内で親クラスのインスタンスを生成してメソッドを呼び出すようにした。

実装はこんな感じ

Error用とException用とでそれぞれ別のメソッドを実装。

<?php
App::uses('ErrorHandleRedmineTrait', 'Traits');

/**
 * Class AppConsoleErrorHandler
 */
class AppConsoleErrorHandler{
	use ErrorHandleRedmineTrait;

	/**
	 * @param \Exception $exception
	 */
	public static function staticHandleException(Exception $exception) {
		self::__makeExceptionIssue($exception);
		$ConsoleErrorHandler = self::__makeConsoleErrorHandler();
		$ConsoleErrorHandler->handleException($exception);
	}

	public static function staticHandleError($code, $description, $file = null, $line = null, $context = null) {
		self::__makeErrorIssue($code, $description, $file, $line, $context);
		/** @var ConsoleErrorHandler $ConsoleErrorHandler */
		$ConsoleErrorHandler = self::__makeConsoleErrorHandler();
		$ConsoleErrorHandler->handleError($code, $description, $file, $line, $context);
	}

	/**
	 * @return \ConsoleErrorHandler
	 */
	private static function __makeConsoleErrorHandler() {
		$ConsoleErrorHandler = ClassRegistry::init('ConsoleErrorHandler');
		return $ConsoleErrorHandler;
	}
}

bootstrap.phpでハンドラーの読み込み

最後の行にでも下記を追記。

App::uses('AppConsoleErrorHandler', 'Lib/Error');
App::uses('AppErrorHandler', 'Lib/Error');

core.phpにハンドラーの登録

最後の行にでも下記を追記。
error と Exceptionとで別で登録する必要があるので注意。

	// エラーハンドラーの登録
	Configure::write('Error',
					 [ 'handler' => 'AppErrorHandler::handleError',
					   'consoleHandler' => 'AppConsoleErrorHandler::staticHandleError' ]
	);
	Configure::write('Exception',
					 [ 'handler' => 'AppErrorHandler::handleException',
					   'consoleHandler' => 'AppConsoleErrorHandler::staticHandleException' ]
	);

シェアする

  • このエントリーをはてなブックマークに追加

フォローする