Laravelコア解読--異常処理
異常処理はプログラミングにおいて非常に重要であるが、最も無視されやすい言語特性であり、開発者にプログラム実行時のエラーを処理するメカニズムを提供し、プログラム設計にとって正しい異常処理はプログラム自身の詳細をユーザーに漏らすことを防止し、開発者に完全なエラー遡及スタックを提供し、同時にプログラムの丈夫性を高めることができる.
この記事では、Laravelで提供されている例外処理能力を簡単に整理し、開発で例外処理を使用する実践、カスタム例外を使用する方法、Laravelの例外処理能力を拡張する方法について説明します.
異常Handlerの登録
ここではまた、Kernelが要求を処理する前のbootstrapフェーズに戻り、bootstrapフェーズの
プロセッサでは主に
ちなみに
一般的なLaravel例外の例
ここで私はいくつかの開発でよく異常に遭遇することを列挙し、彼らがどのような状況で投げ出されたのかを説明し、普段の符号化ではプログラムの中でこれらの異常をキャプチャして異常処理をしてこそ、プログラムをより丈夫にすることができることに注意しなければならない. が投げ出される. . を投げ出す.
Laravelを拡張する例外プロセッサ
Laravelは
また、デフォルトのレンダリング方法では、フォーム検証時に応答を生成するJSONフォーマットは、私たちのプロジェクトで統一された
カスタマイズ後、要求が
カスタム例外の使用
この部分は、
次はカスタム例外クラスです
例外クラスを定義したら、コードロジックに例外インスタンスを投げ出すことができます.
上記の
上記で
異常処理は
この記事では、Laravelで提供されている例外処理能力を簡単に整理し、開発で例外処理を使用する実践、カスタム例外を使用する方法、Laravelの例外処理能力を拡張する方法について説明します.
異常Handlerの登録
ここではまた、Kernelが要求を処理する前のbootstrapフェーズに戻り、bootstrapフェーズの
Illuminate\Foundation\Bootstrap\HandleExceptions
セクションにLaravelがシステム異常処理動作を設定し、グローバルな異常プロセッサを登録します.class HandleExceptions
{
public function bootstrap(Application $app)
{
$this->app = $app;
error_reporting(-1);
set_error_handler([$this, 'handleError']);
set_exception_handler([$this, 'handleException']);
register_shutdown_function([$this, 'handleShutdown']);
if (! $app->environment('testing')) {
ini_set('display_errors', 'Off');
}
}
public function handleError($level, $message, $file = '', $line = 0, $context = [])
{
if (error_reporting() & $level) {
throw new ErrorException($message, 0, $level, $file, $line);
}
}
}
set_exception_handler([$this, 'handleException'])
HandleExceptions
のhandleException
メソッドをプログラムのグローバルプロセッサメソッドとして登録します.public function handleException($e)
{
if (! $e instanceof Exception) {
$e = new FatalThrowableError($e);
}
$this->getExceptionHandler()->report($e);
if ($this->app->runningInConsole()) {
$this->renderForConsole($e);
} else {
$this->renderHttpResponse($e);
}
}
protected function getExceptionHandler()
{
return $this->app->make(ExceptionHandler::class);
}
// CLI
protected function renderForConsole(Exception $e)
{
$this->getExceptionHandler()->renderForConsole(new ConsoleOutput, $e);
}
// HTTP
protected function renderHttpResponse(Exception $e)
{
$this->getExceptionHandler()->render($this->app['request'], $e)->send();
}
プロセッサでは主に
ExceptionHandler
のreport
方法で異常を報告し、ここではstorage/laravel.log
ファイルに異常を記録し、要求タイプレンダリング異常の応答に基づいてクライアントに出力する.ここでExceptionHandlerは、プロジェクトが最初にサービスコンテナに登録された\App\Exceptions\Handler
クラスの例です.// bootstrap/app.php
/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
*/
$app = new Illuminate\Foundation\Application(
realpath(__DIR__.'/../')
);
/*
|--------------------------------------------------------------------------
| Bind Important Interfaces
|--------------------------------------------------------------------------
*/
......
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);
ちなみに
set_error_handler
関数は、エラープロセッサ関数を登録する役割を果たしています.いくつかの古いコードやクラスライブラリではPHPの関数trigger_error
関数を用いてエラーを投げ出すことが多いため、異常プロセッサはExceptionを処理するだけでErrorを処理できません.したがって、従来のクラスライブラリと互換性を持たせるために、set_error_handler
を使用してグローバルなエラープロセッサメソッドを登録し、メソッドでエラーをキャプチャした後、エラーを異常に変換して再放出することで、プロジェクト内のすべてのコードが正しく実行されていない場合に例外インスタンスを放出することができます./**
* Convert PHP errors to ErrorException instances.
*
* @param int $level
* @param string $message
* @param string $file
* @param int $line
* @param array $context
* @return void
*
* @throws \ErrorException
*/
public function handleError($level, $message, $file = '', $line = 0, $context = [])
{
if (error_reporting() & $level) {
throw new ErrorException($message, 0, $level, $file, $line);
}
}
一般的なLaravel例外の例
Laravel
では、一般的なプログラム異常に対して対応する異常例が投げ出され、開発者はこれらの実行時異常をキャプチャし、必要に応じて後続の処理を行うことができる(例えば、catchで別の救済方法を呼び出し、異常をログファイルに記録し、アラームメールを送信し、メールを送信する)ここで私はいくつかの開発でよく異常に遭遇することを列挙し、彼らがどのような状況で投げ出されたのかを説明し、普段の符号化ではプログラムの中でこれらの異常をキャプチャして異常処理をしてこそ、プログラムをより丈夫にすることができることに注意しなければならない.
Illuminate\Database\QueryException
LaravelでSQL文を実行中にエラーが発生した場合に放出されるこの異常は、使用率が最も高い異常であり、SQL実行エラーをキャプチャするために使用されます.例えば、Update文を実行する場合、SQL実行後に変更された行数を判断してUPDATEが成功したかどうかを判断するのが好きな人が多いですが、実行されたUPDATE文には記録値が変更されていない場合もあります.この場合、関数を修正することでUPDATEが成功したかどうかを判断することはできません.また、トランザクション実行中にQueryExceptionがキャプチャされた場合、catchコードブロックでトランザクションをロールバックできます.Illuminate\Database\Eloquent\ModelNotFoundException
モデルのfindOrFail
およびfirstOrFail
メソッドによって単一レコードが取得された場合、この例外が見つからない場合は放出されます(find
およびfirst
データが見つからない場合はNULLに戻ります).Illuminate\Validation\ValidationException
は、LaravelのFormValidator検証に合格していないことを要求すると、この例外を放出します.Illuminate\Auth\Access\AuthorizationException
ユーザがLaravelのポリシー(Policy)検証を通過しないことを要求すると、この例外Symfony\Component\Routing\Exception\MethodNotAllowedException
ルーティング要求時にHTTP Methodが正しくないIlluminate\Http\Exceptions\HttpResponseException
Laravelの処理HTTP要求が成功しない場合にこの異常Laravelを拡張する例外プロセッサ
Laravelは
\App\Exceptions\Handler
をグローバルな例外プロセッサに登録しましたが、コードにはcatch
からの例外はなく、最後に\App\Exceptions\Handler
にキャプチャされ、プロセッサはまず例外をログファイルに記録してから異常応答をレンダリングしてクライアントに応答します.しかし、独自の例外プロセッサの方法は使いにくく、メールやエラーログシステムに異常を報告したい場合が多い.次の例は、異常をSentryシステムに報告し、Sentryはエラー収集サービスで非常に使いやすい.public function report(Exception $exception)
{
if (app()->bound('sentry') && $this->shouldReport($exception)) {
app('sentry')->captureException($exception);
}
parent::report($exception);
}
また、デフォルトのレンダリング方法では、フォーム検証時に応答を生成するJSONフォーマットは、私たちのプロジェクトで統一された
JOSN
フォーマットとは異なり、レンダリング方法の動作をカスタマイズする必要があります.public function render($request, Exception $exception)
{
// JSON , API Validator ValidationException
// .
if ($exception instanceof ValidationException && $request->expectsJson()) {
return $this->error(422, $exception->errors());
}
if ($exception instanceof ModelNotFoundException && $request->expectsJson()) {
// NotFoundHttpException
return $this->error(424, 'resource not found.');
}
if ($exception instanceof AuthorizationException) {
// AuthorizationException
return $this->error(403, "Permission does not exist.");
}
return parent::render($request, $exception);
}
カスタマイズ後、要求が
FormValidator
の検証を通過しない場合、ValidationException
が放出され、その後、異常プロセッサが異常をキャプチャすると、エラープロンプトがプロジェクト統一のJSON応答フォーマットにフォーマットされ、クライアントに出力されます.これにより,我々のコントローラでは,フォーム検証がクライアントに再出力エラーで応答しない論理であるか否かを判断することを完全に省略し,この論理を統一した異常プロセッサに渡して実行することでコントローラ手法を大幅に痩せることができる.カスタム例外の使用
この部分は、
Laravel
フレームワークのカスタム例外ではなく、ここで説明したカスタム例外をどのプロジェクトにも適用できます.Repository
やService
のような方法では、異なるエラーに基づいて異なる配列を返す人が多いのを見たことがあります.応答のエラーコードやエラー情報が含まれています.これはもちろん開発ニーズを満たすことができますが、異常が発生した場合のアプリケーションの実行時コンテキストを記録することはできません.エラーが発生した場合、コンテキスト情報を記録できないと、開発者の問題の位置づけに非常に不利です.次はカスタム例外クラスです
namespace App\Exceptions\;
use RuntimeException;
use Throwable;
class UserManageException extends RuntimeException
{
/**
* The primitive arguments that triggered this exception
*
* @var array
*/
public $primitives;
/**
* QueueManageException constructor.
* @param array $primitives
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct(array $primitives, $message = "", $code = 0, Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
$this->primitives = $primitives;
}
/**
* get the primitive arguments that triggered this exception
*/
public function getPrimitives()
{
return $this->primitives;
}
}
例外クラスを定義したら、コードロジックに例外インスタンスを投げ出すことができます.
class UserRepository
{
public function updateUserFavorites(User $user, $favoriteData)
{
......
if (!$executionOne) {
throw new UserManageException(func_get_args(), 'Update user favorites error', '501');
}
......
if (!$executionTwo) {
throw new UserManageException(func_get_args(), 'Another Error', '502');
}
return true;
}
}
class UserController extends ...
{
public function updateFavorites(User $user, Request $request)
{
.......
$favoriteData = $request->input('favorites');
try {
$this->userRepo->updateUserFavorites($user, $favoritesData);
} catch (UserManageException $ex) {
.......
}
}
}
上記の
Repository
に加えて、上記の汎用例外をキャプチャした後、catch
コードブロックにビジネスに関連するより細分化された例外インスタンスを投げ出して開発者の位置決めを容易にする問題が多いので、上記のupdateUserFavorites
をこのようなポリシーに従って修正します.public function updateUserFavorites(User $user, $favoriteData)
{
try {
// database execution
// database execution
} catch (QueryException $queryException) {
throw new UserManageException(func_get_args(), 'Error Message', '501' , $queryException);
}
return true;
}
上記で
UserMangeException
クラスを定義したときの4番目のパラメータ$previous
は、Throwable
インタフェースクラスを実装したインスタンスであり、このシナリオでは、QueryException
の異常インスタンスをキャプチャしたためにUserManagerException
のインスタンスを投げ出し、このパラメータによってQueryException
インスタンスをPHP
の異常スタックに渡し、これは、現在投げ出されている例外インスタンスだけのコンテキスト情報ではなく、例外全体を遡る能力を提供し、エラー収集システムでは、次のようなコードを使用してすべての例外の情報を取得することができます.while($e instanceof \Exception) {
echo $e->getMessage();
$e = $e->getPrevious();
}
異常処理は
PHP
の非常に重要であるが、開発者に無視されやすい機能であり、この文章はLaravel
内部の異常処理のメカニズムとLaravel
異常処理を拡張する方法を簡単に説明した.もっと多くの紙面はいくつかの異常処理のプログラミング実践を重点的に分かち合って、これらはまさに私がすべての読者がすべて見て実践することができることを望んでいるいくつかのプログラミング習慣で、前に分かち合ったInterface
の応用も同様です.