>  기사  >  PHP 프레임워크  >  Laravel 핵심 분석 예외 처리(코드)

Laravel 핵심 분석 예외 처리(코드)

不言
不言앞으로
2019-02-11 10:22:422847검색

이 글의 내용은 Laravel 핵심 분석의 예외 처리(코드)에 대한 내용입니다. 필요한 친구들이 참고할 수 있기를 바랍니다.

예외 처리는 프로그래밍에서 매우 중요하지만 가장 간과되는 언어 기능입니다. 이는 개발자에게 프로그램 런타임 오류를 처리하는 메커니즘을 제공합니다. 프로그램 설계의 경우 올바른 예외 처리를 통해 프로그램 자체의 세부 정보가 사용자에게 유출되는 것을 방지할 수 있습니다. 완전한 오류 역추적 스택을 제공하는 동시에 프로그램의 견고성을 향상시킵니다.

이 기사에서는 Laravel에서 제공되는 예외 처리 기능을 간략하게 검토한 다음 개발 시 예외 처리를 사용하는 몇 가지 사례, 사용자 정의 예외를 사용하는 방법 및 Laravel의 예외 처리 기능을 확장하는 방법에 대해 설명합니다.

예외 처리기 등록

여기서 커널이 우리가 여러 번 말한 요청을 처리하기 전에 부트스트랩 단계로 돌아가야 합니다. 부트스트랩 단계의 IlluminateFoundationBootstrapHandleExceptions 섹션에서 Laravel은 시스템 예외 처리 동작을 설정하고 등록합니다. 전역 예외 처리기. ExceptionHandler. 예외 기록은 Storage/laravel.log 파일이며, 요청 유형에 따라 예외 응답을 렌더링하여 클라이언트에 출력합니다. 여기서 ExceptionHandler는 프로젝트 시작 시 서비스 컨테이너에 등록된 AppExceptionsHandler 클래스의 인스턴스입니다.

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_error_handler 함수는 오류 핸들러 함수를 등록하는 데 사용됩니다. 또는 대부분의 클래스 라이브러리는 오류를 발생시키기 위해 PHP의 Trigger_error 함수를 사용합니다. 따라서 예외 핸들러는 예외만 처리할 수 있지만 오류는 처리할 수 없습니다. 따라서 이전 클래스 라이브러리와 호환되기 위해 일반적으로 전역 오류 핸들러 메소드를 등록하는 데 set_error_handler가 사용됩니다. 메서드에 오류가 있으면 오류가 예외로 변환된 다음 다시 발생하므로 프로젝트의 모든 코드가 올바르게 실행되지 않을 때 예외 인스턴스가 발생할 수 있습니다.

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();
}

일반적으로 사용되는 Laravel 예외 예제

Laravel은 공통 프로그램 예외에 해당하는 예외 인스턴스를 발생시킵니다. 이를 통해 개발자는 이러한 런타임 예외를 캡처하고 필요에 따라 후속 처리를 수행할 수 있습니다(예: catch에서 다른 해결 방법 호출, 로그 파일에 예외 기록, 알람 이메일 보내기, 문자 메시지 보내기) 여기에서는 개발 중에 흔히 발생하는 몇 가지 예외를 나열하고 일반적인 코딩 중에 어떤 상황에서 예외가 발생하는지 설명합니다. 프로그램에서 이러한 예외를 포착하는 데 주의해야 합니다. 프로그램을 더욱 강력하게 만들기 위해 이를 잘 처리합니다.

IlluminateDatabaseQueryException 이 예외는 Laravel에서 SQL 문을 실행할 때 오류가 발생할 때 발생합니다. 또한 가장 일반적으로 사용되는 예외이며, 예를 들어 Update 문을 실행할 때 많은 사람들이 사용합니다. SQL 실행 후 수정된 행의 수를 판단하여 UPDATE 성공 여부를 판단하지만, 일부 시나리오에서는 실행된 UPDATE 문이 레코드 값을 수정하지 않는 경우 이를 통해 UPDATE 성공 여부를 판단하는 것이 불가능합니다. 또한, 트랜잭션 실행 중에 QueryException이 발생하면 코드 블록에서 트랜잭션을 롤백하는 데 사용할 수 있습니다.

IlluminateDatabaseEloquentModelNotFoundException 이 예외는 모델의 findOrFail 및 firstOrFail 메소드를 통해 단일 레코드를 찾을 수 없는 경우 발생합니다(find 및 first는 데이터를 찾을 수 없으면 NULL을 반환합니다).

IlluminateValidationValidationException 이 예외는 요청이 Laravel의 FormValidator 유효성 검사를 통과하지 못한 경우 발생합니다.

IlluminateAuthAccessAuthorizationException 사용자 요청이 Laravel의 정책(Policy) 검증을 통과하지 못한 경우 발생하는 예외입니다.

SymfonyComponentRoutingExceptionMethodNotAllowedException 라우팅을 요청할 때 HTTP 메서드가 올바르지 않습니다.

IlluminateHttpExceptionsHttpResponseException Laravel이 HTTP 요청 예외 처리에 실패할 때 발생합니다.

Laravel 확장 예외 handler

위에서 언급했듯이 Laravel은 AppExceptionsHandler를 전역 예외 처리기로 성공적으로 등록했습니다. 코드에서 포착되지 않은 예외는 결국 AppExceptionsHandler에 의해 포착됩니다. 그런 다음 응답이 클라이언트로 전송됩니다. 그러나 내장된 예외 처리 방법은 사용하기 쉽지 않습니다. 이메일이나 오류 로그 시스템에 예외를 보고하려는 경우가 많습니다. Sentry 시스템에 예외를 보고하는 경우는 매우 좋습니다. 사용:

// 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
);
및 기본 렌더링 방법 양식 확인 중에 생성된 응답의 JSON 형식은 렌더링 방법의 동작을 사용자 정의해야 하는 프로젝트의 통합

형식과 다른 경우가 많습니다.

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类的时候第四个参数$previous是一个实现了Throwable接口类实例,在这种情景下我们因为捕获到了QueryException的异常实例而抛出了UserManagerException的实例,然后通过这个参数将QueryException实例传递给PHP异常的堆栈,这提供给我们回溯整个异常的能力来获取更多上下文信息,而不是仅仅只是当前抛出的异常实例的上下文信息, 在错误收集系统可以使用类似下面的代码来获取所有异常的信息。

while($e instanceof \Exception) {
    echo $e->getMessage();
    $e = $e->getPrevious();
}

异常处理是PHP非常重要但又容易让开发者忽略的功能,这篇文章简单解释了Laravel内部异常处理的机制以及扩展Laravel异常处理的方式方法。更多的篇幅着重分享了一些异常处理的编程实践,这些正是我希望每个读者都能看明白并实践下去的一些编程习惯,包括之前分享的Interface的应用也是一样。

위 내용은 Laravel 핵심 분석 예외 처리(코드)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제