Skip to content

Instantly share code, notes, and snippets.

@k-holy
Created December 9, 2011 05:49
Show Gist options
  • Save k-holy/1450371 to your computer and use it in GitHub Desktop.
Save k-holy/1450371 to your computer and use it in GitHub Desktop.
エラーハンドラ・例外ハンドラのサンプル
<?php
// エラー画面出力処理のサンプル(main.php内)
$errorDisplay = function($data) {
$status = '500 Internal Server Error';
if (!headers_sent()) {
header($_SERVER['SERVER_PROTOCOL'] . ' ' . $status);
}
$body = <<< HTML
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<title>%s - Holy\Example</title>
</head>
<body>
<h1>%s</h1>
<h2>システムエラーが発生しました。</h2>
<hr />
<h3>ログ出力内容</h3>
<pre>%s</pre>
<p><a href="javascript:history.back();">戻る</a></p>
</body>
</html>
HTML;
echo sprintf($body,
U::H($status),
U::H($status),
U::H(implode("\n", $data['messages'])));
restore_exception_handler();
restore_error_handler();
exit();
};
<?php
// エラーハンドラのサンプル(main.php内)
$errorHandler = function($errno, $errstr, $errfile, $errline)
use (&$data, $logger, $errorDisplay)
{
$titles = array (
E_ERROR => 'Fatal error',
E_WARNING => 'Warning',
E_NOTICE => 'Notice',
E_STRICT => 'Strict standards',
E_RECOVERABLE_ERROR => 'Catchable fatal error',
E_DEPRECATED => 'Depricated',
E_USER_ERROR => 'Fatal error (User)',
E_USER_WARNING => 'Warning (User)',
E_USER_NOTICE => 'Notice (User)',
E_USER_DEPRECATED => 'Depricated (User)',
);
$message = sprintf('%s: %s in %s on line %d',
(isset($titles[$errno])) ? $titles[$errno] : 'Unknown error',
$errstr,
$errfile,
$errline
);
$trace = debug_backtrace();
$stackTrace = U::formatTrace(array_slice($trace, 2, count($trace)));
if (!empty($stackTrace)) {
$message .= sprintf("\nStack trace:\n%s", implode("\n", $stackTrace));
}
if ($data['log_level'] & $errno) {
$logger($message);
}
if (error_reporting() & $errno) {
if (ini_get('display_errors')) {
echo '<pre>' . U::H($message) . '</pre>';
}
// 復帰できないレベルのエラーはエラー画面を表示して終了
if ((E_ERROR | E_USER_ERROR) & $errno) {
$errorDisplay($data);
}
}
return true;
};
<?php
// 例外ハンドラのサンプル (main.php内)
$exceptionHandler = function($exception) use (&$data, $logger, $errorDisplay) {
$message = sprintf("Fatal Error: Uncaught exception '%s' with message '%s' in %s:%d\nStack trace:\n%s\n thrown in %s on line %d",
get_class($exception),
$exception->getMessage(),
$exception->getFile(),
$exception->getLine(),
implode("\n", U::formatTrace($exception->getTrace())),
$exception->getFile(),
$exception->getLine());
$logger($message);
if (ini_get('display_errors')) {
echo '<pre>' . U::H($message) . '</pre>';
}
$errorDisplay($data);
};
<?php
// ログ処理のサンプル(main.php内)
$logFile = realpath(__DIR__ . '/logs') .
DIRECTORY_SEPARATOR . date('Y-m') . '.log';
$logger = function($message) use (&$data, $logFile) {
$logMessage = sprintf("[%s] %s\n",
date('Y-m-d H:i:s'),
htmlspecialchars_decode(strip_tags($message)));
$data['messages'][] = $logMessage;
// error_log($logMessage , 3, $logFile); //テストなのでログ取らない
};
<?php
namespace Holy\Example;
use Holy\Example\Util as U;
// ユーティリティクラス
class Util {
public static function H($var, $default = '') {
if (isset($var) && strlen($var) >= 1) {
return htmlspecialchars($var, ENT_QUOTES, 'UTF-8');
}
return $default;
}
/**
* エラーレベル定数(E_USER_***)の配列を生成して返します。
* @return array
*/
public static function buildErrorLevels() {
$levels = array_fill_keys(array_filter(array_keys(get_defined_constants()),
function($name) {
return (strncmp('E_USER_', $name, 7) === 0);
}
), array());
array_walk($levels, function(&$item, $key) {
if (defined($key)) {
$item['name' ] = $key;
$item['value'] = constant($key);
}
});
usort($levels, function($item1, $item2) {
return ($item1['value'] > $item2['value']) ? +1 : -1;
});
return $levels;
}
/**
* スタックトレースの配列をエラー表示用に整形して返します。
* @param array
* @return array
*/
public static function formatTrace($trace) {
$stack = array();
foreach ($trace as $i => $t) {
// 引数は型が分かるよう文字列に整形
$args = '';
if (isset($t['args']) && !empty($t['args'])) {
// 配列は一階層目のみ回す
$args = implode(', ', array_map(function($arg) {
if (is_array($arg)) {
$vars = array();
foreach ($arg as $key => $var) {
$vars[] = sprintf('%s=>%s',
U::formatVar($key), U::formatVar($var));
}
return sprintf('Array[%s]', implode(', ', $vars));
}
return U::formatVar($arg);
}, $t['args']));
}
$stack[] = sprintf('#%d %s(%d): %s%s%s(%s)',
$i,
(isset($t['file' ])) ? $t['file' ] : '', // ファイル
(isset($t['line' ])) ? $t['line' ] : '', // 行番号
(isset($t['class' ])) ? $t['class' ] : '', // クラス名
(isset($t['type' ])) ? $t['type' ] : '', // コール方式(->, ::)
(isset($t['function'])) ? $t['function'] : '', // 関数名、メソッド名
$args);
}
return $stack;
}
/**
* 変数の文字列表現を返します。
* @param mixed
* @return string
*/
public static function formatVar($var) {
if (is_null($var)) {
return 'NULL';
}
if (is_int($var)) {
return sprintf('Int(%d)', $var);
}
if (is_float($var)) {
return sprintf('Float(%F)', $var);
}
if (is_string($var)) {
return sprintf('"%s"', $var);
}
if (is_bool($var)) {
return sprintf('Bool(%s)', $var ? 'true' : 'false');
}
if (is_array($var)) {
return 'Array';
}
if (is_object($var)) {
return sprintf('Object(%s)', get_class($var), $var);
}
return sprintf('%s', gettype($var));
}
}
//============================================================================//
$data = array();
// エラーレベル定数(E_USER_***)の配列
$data['error_levels'] = U::buildErrorLevels();
// エラーメッセージ出力の可否、エラー画面の出力レベル、エラーログの出力レベルを設定
$data['display_errors' ] = 0;
$data['rep_level'] = error_reporting();
$data['log_level'] = error_reporting();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['display_errors']) && $_POST['display_errors']) {
$data['display_errors'] = 1;
}
if (isset($_POST['rep_level_all'])) {
$data['rep_level'] = E_ALL;
} elseif (isset($_POST['rep_level_flags']) && is_array($_POST['rep_level_flags'])) {
$data['rep_level'] = array_sum($_POST['rep_level_flags']);
} else {
$data['rep_level'] = 0;
}
if (isset($_POST['log_level_all'])) {
$data['log_level'] = E_ALL;
} elseif (isset($_POST['log_level_flags']) && is_array($_POST['log_level_flags'])) {
$data['log_level'] = array_sum($_POST['log_level_flags']);
} else {
$data['log_level'] = 0;
}
}
// エラーログの内容
$data['messages'] = array();
ini_set('display_errors', $data['display_errors']);
error_reporting($data['rep_level']);
// ログ処理のサンプル
$logFile = realpath(__DIR__ . '/logs') .
DIRECTORY_SEPARATOR . date('Y-m') . '.log';
$logger = function($message) use (&$data, $logFile) {
$logMessage = sprintf("[%s] %s\n",
date('Y-m-d H:i:s'),
htmlspecialchars_decode(strip_tags($message)));
$data['messages'][] = $logMessage;
// error_log($logMessage , 3, $logFile); //テストなのでログ取らない
};
// エラー画面出力のサンプル
$errorDisplay = function($data) {
$status = '500 Internal Server Error';
if (!headers_sent()) {
header($_SERVER['SERVER_PROTOCOL'] . ' ' . $status);
}
$body = <<< HTML
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<title>%s - Holy\Example</title>
</head>
<body>
<h1>%s</h1>
<h2>システムエラーが発生しました。</h2>
<hr />
<h3>ログ出力内容</h3>
<pre>%s</pre>
<p><a href="javascript:history.back();">戻る</a></p>
</body>
</html>
HTML;
echo sprintf($body,
U::H($status),
U::H($status),
U::H(implode("\n", $data['messages'])));
restore_exception_handler();
restore_error_handler();
exit();
};
// 例外ハンドラのサンプル
$exceptionHandler = function($exception) use (&$data, $logger, $errorDisplay) {
$message = sprintf("Fatal Error: Uncaught exception '%s' with message '%s' in %s:%d\nStack trace:\n%s\n thrown in %s on line %d",
get_class($exception),
$exception->getMessage(),
$exception->getFile(),
$exception->getLine(),
implode("\n", U::formatTrace($exception->getTrace())),
$exception->getFile(),
$exception->getLine());
$logger($message);
if (ini_get('display_errors')) {
echo '<pre>' . U::H($message) . '</pre>';
}
$errorDisplay($data);
};
// エラーハンドラのサンプル
$errorHandler = function($errno, $errstr, $errfile, $errline)
use (&$data, $logger, $errorDisplay)
{
$titles = array (
E_ERROR => 'Fatal error',
E_WARNING => 'Warning',
E_NOTICE => 'Notice',
E_STRICT => 'Strict standards',
E_RECOVERABLE_ERROR => 'Catchable fatal error',
E_DEPRECATED => 'Depricated',
E_USER_ERROR => 'Fatal error (User)',
E_USER_WARNING => 'Warning (User)',
E_USER_NOTICE => 'Notice (User)',
E_USER_DEPRECATED => 'Depricated (User)',
);
$message = sprintf('%s: %s in %s on line %d',
(isset($titles[$errno])) ? $titles[$errno] : 'Unknown error',
$errstr,
$errfile,
$errline
);
$trace = debug_backtrace();
$stackTrace = U::formatTrace(array_slice($trace, 2, count($trace)));
if (!empty($stackTrace)) {
$message .= sprintf("\nStack trace:\n%s", implode("\n", $stackTrace));
}
if ($data['log_level'] & $errno) {
$logger($message);
}
if (error_reporting() & $errno) {
if (ini_get('display_errors')) {
echo '<pre>' . U::H($message) . '</pre>';
}
// 復帰できないレベルのエラーはエラー画面を表示して終了
if ((E_ERROR | E_USER_ERROR) & $errno) {
$errorDisplay($data);
}
}
return true;
};
// 例外、エラーハンドラを登録
set_exception_handler($exceptionHandler);
set_error_handler($errorHandler);
// スタックトレースのテスト用クラス
class スマイルセラピー
{
public static function 開始($callback)
{
self::10度($callback, 9, 3.14);
}
public static function 10度($callback, $arg1, $arg2)
{
self::20度($callback, 'text', false);
}
public static function 20度($callback, $arg1, $arg2)
{
self::30度($callback, array(1, 'a', 'foo'=>'bar'), new \stdClass());
}
public static function 30度($callback, $arg1, $arg2)
{
$callback();
}
}
if (isset($_POST['raiseException'])) {
スマイルセラピー::開始(function() {
throw new \Exception('Exceptionのテスト');
});
} elseif (isset($_POST['raiseError'])) {
スマイルセラピー::開始(function() {
if (defined($_POST['raiseError']) &&
strncmp('E_USER_', $_POST['raiseError'], 7) === 0
) {
trigger_error(sprintf('%sのテスト', $_POST['raiseError']),
constant($_POST['raiseError']));
}
});
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<title>エラーハンドラ・例外ハンドラのテスト - Holy\Example</title>
</head>
<body>
<form method="post">
<p>
<label>エラーメッセージを出力<input type="checkbox" name="display_errors" value="1"
<?=($data['display_errors'])?' checked="checked"':''?> /></label>
</p>
<table>
<?php foreach ($data['error_levels'] as $item) : ?>
<tr>
<td><?=U::H($item['name'])?></td>
<td style="text-align:right;">(<?=U::H($item['value'])?>)</td>
<td>
<label>画面出力<input type="checkbox" name="rep_level_flags[]"
value="<?=U::H($item['value'])?>"
<?=($data['rep_level'] & $item['value'])?' checked="checked"':'' ?> /></label>
</td>
<td>
<label>ログ出力<input type="checkbox" name="log_level_flags[]"
value="<?=U::H($item['value'])?>"
<?=($data['log_level'] & $item['value'])?' checked="checked"':'' ?> /></label>
<input type="submit" name="raiseError" value="<?=U::H($item['name'])?>" />
</td>
</tr>
<?php endforeach ?>
<tr>
<td>E_ALL</td>
<td style="text-align:right;"><?=U::H(E_ALL)?></td>
<td>
<label>画面出力<input type="checkbox" name="rep_level_all" value="1"
<?=($data['rep_level'] === E_ALL)?' checked="checked"':''?> /></label>
</td>
<td><label>ログ出力<input type="checkbox" name="log_level_all" value="1"
<?=($data['log_level'] === E_ALL)?' checked="checked"':''?> /></label>
</td>
</tr>
</table>
<p>
<input type="submit" name="raiseException" value="Exception" />
<input type="submit" value="何もしない" />
</p>
</form>
<hr />
ログ出力内容
<pre>
<?=U::H(implode("\n",$data['messages']))?>
</pre>
</body>
</html>
<?php
namespace Holy\Example;
use Holy\Example\Util as U;
$data = array();
// エラーレベル定数(E_USER_***)の配列
$data['error_levels'] = U::buildErrorLevels();
// エラーメッセージ出力の可否、エラー画面の出力レベル、エラーログの出力レベルを設定
$data['display_errors' ] = 0;
$data['rep_level'] = error_reporting();
$data['log_level'] = error_reporting();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['display_errors']) && $_POST['display_errors']) {
$data['display_errors'] = 1;
}
if (isset($_POST['rep_level_all'])) {
$data['rep_level'] = E_ALL;
} elseif (isset($_POST['rep_level_flags']) && is_array($_POST['rep_level_flags'])) {
$data['rep_level'] = array_sum($_POST['rep_level_flags']);
} else {
$data['rep_level'] = 0;
}
if (isset($_POST['log_level_all'])) {
$data['log_level'] = E_ALL;
} elseif (isset($_POST['log_level_flags']) && is_array($_POST['log_level_flags'])) {
$data['log_level'] = array_sum($_POST['log_level_flags']);
} else {
$data['log_level'] = 0;
}
}
// エラーログの内容
$data['messages'] = array();
ini_set('display_errors', $data['display_errors']);
error_reporting($data['rep_level']);
// 例外、エラーハンドラを登録
set_exception_handler($exceptionHandler);
set_error_handler($errorHandler);
// スタックトレースのテスト用クラス
class スマイルセラピー
{
public static function 開始($callback)
{
self::10度($callback, 9, 3.14);
}
public static function 10度($callback, $arg1, $arg2)
{
self::20度($callback, 'text', false);
}
public static function 20度($callback, $arg1, $arg2)
{
self::30度($callback, array(1, 'a', 'foo'=>'bar'), new \stdClass());
}
public static function 30度($callback, $arg1, $arg2)
{
$callback();
}
}
if (isset($_POST['raiseException'])) {
スマイルセラピー::開始(function() {
throw new \Exception('Exceptionのテスト');
});
} elseif (isset($_POST['raiseError'])) {
スマイルセラピー::開始(function() {
if (defined($_POST['raiseError']) &&
strncmp('E_USER_', $_POST['raiseError'], 7) === 0
) {
trigger_error(sprintf('%sのテスト', $_POST['raiseError']),
constant($_POST['raiseError']));
}
});
}
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<title>エラーハンドラ・例外ハンドラのテスト - Holy\Example</title>
</head>
<body>
<form method="post">
<p>
<label>エラーメッセージを出力<input type="checkbox" name="display_errors" value="1"
<?=($data['display_errors'])?' checked="checked"':''?> /></label>
</p>
<table>
<?php foreach ($data['error_levels'] as $item) : ?>
<tr>
<td><?=U::H($item['name'])?></td>
<td style="text-align:right;">(<?=U::H($item['value'])?>)</td>
<td>
<label>画面出力<input type="checkbox" name="rep_level_flags[]"
value="<?=U::H($item['value'])?>"
<?=($data['rep_level'] & $item['value'])?' checked="checked"':'' ?> /></label>
</td>
<td>
<label>ログ出力<input type="checkbox" name="log_level_flags[]"
value="<?=U::H($item['value'])?>"
<?=($data['log_level'] & $item['value'])?' checked="checked"':'' ?> /></label>
<input type="submit" name="raiseError" value="<?=U::H($item['name'])?>" />
</td>
</tr>
<?php endforeach ?>
<tr>
<td>E_ALL</td>
<td style="text-align:right;"><?=U::H(E_ALL)?></td>
<td>
<label>画面出力<input type="checkbox" name="rep_level_all" value="1"
<?=($data['rep_level'] === E_ALL)?' checked="checked"':''?> /></label>
</td>
<td><label>ログ出力<input type="checkbox" name="log_level_all" value="1"
<?=($data['log_level'] === E_ALL)?' checked="checked"':''?> /></label>
</td>
</tr>
</table>
<p>
<input type="submit" name="raiseException" value="Exception" />
<input type="submit" value="何もしない" />
</p>
</form>
<hr />
ログ出力内容
<pre>
<?=U::H(implode("\n",$data['messages']))?>
</pre>
</body>
</html>
<?php
namespace Holy\Example;
use Holy\Example\Util as U;
// ユーティリティクラス
class Util {
public static function H($var, $default = '') {
if (isset($var) && strlen($var) >= 1) {
return htmlspecialchars($var, ENT_QUOTES, 'UTF-8');
}
return $default;
}
/**
* エラーレベル定数(E_USER_***)の配列を生成して返します。
* @return array
*/
public static function buildErrorLevels() {
$levels = array_fill_keys(array_filter(array_keys(get_defined_constants()),
function($name) {
return (strncmp('E_USER_', $name, 7) === 0);
}
), array());
array_walk($levels, function(&$item, $key) {
if (defined($key)) {
$item['name' ] = $key;
$item['value'] = constant($key);
}
});
usort($levels, function($item1, $item2) {
return ($item1['value'] > $item2['value']) ? +1 : -1;
});
return $levels;
}
/**
* スタックトレースの配列をエラー表示用に整形して返します。
* @param array
* @return array
*/
public static function formatTrace($trace) {
$stack = array();
foreach ($trace as $i => $t) {
// 引数は型が分かるよう文字列に整形
$args = '';
if (isset($t['args']) && !empty($t['args'])) {
// 配列は一階層目のみ回す
$args = implode(', ', array_map(function($arg) {
if (is_array($arg)) {
$vars = array();
foreach ($arg as $key => $var) {
$vars[] = sprintf('%s=>%s',
U::formatVar($key), U::formatVar($var));
}
return sprintf('Array[%s]', implode(', ', $vars));
}
return U::formatVar($arg);
}, $t['args']));
}
$stack[] = sprintf('#%d %s(%d): %s%s%s(%s)',
$i,
(isset($t['file' ])) ? $t['file' ] : '', // ファイル
(isset($t['line' ])) ? $t['line' ] : '', // 行番号
(isset($t['class' ])) ? $t['class' ] : '', // クラス名
(isset($t['type' ])) ? $t['type' ] : '', // コール方式(->, ::)
(isset($t['function'])) ? $t['function'] : '', // 関数名、メソッド名
$args);
}
return $stack;
}
/**
* 変数の文字列表現を返します。
* @param mixed
* @return string
*/
public static function formatVar($var) {
if (is_null($var)) {
return 'NULL';
}
if (is_int($var)) {
return sprintf('Int(%d)', $var);
}
if (is_float($var)) {
return sprintf('Float(%F)', $var);
}
if (is_string($var)) {
return sprintf('"%s"', $var);
}
if (is_bool($var)) {
return sprintf('Bool(%s)', $var ? 'true' : 'false');
}
if (is_array($var)) {
return 'Array';
}
if (is_object($var)) {
return sprintf('Object(%s)', get_class($var), $var);
}
return sprintf('%s', gettype($var));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment