Maison  >  Article  >  cadre php  >  Gestion des exceptions d'analyse de base de Laravel (code)

Gestion des exceptions d'analyse de base de Laravel (code)

不言
不言avant
2019-02-11 10:22:422904parcourir

Le contenu de cet article concerne la gestion des exceptions (code) de l'analyse de base de Laravel. Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer.

La gestion des exceptions est une fonctionnalité de langage très importante mais la plus négligée dans la programmation. Elle fournit aux développeurs un mécanisme pour gérer les erreurs d'exécution du programme. Pour la conception du programme, une gestion correcte des exceptions peut empêcher les fuites. fourni aux utilisateurs, fournissant aux développeurs une pile complète de traçabilité des erreurs, tout en améliorant également la robustesse du programme.

Dans cet article, nous passerons brièvement en revue les capacités de gestion des exceptions fournies dans Laravel, puis parlerons de certaines pratiques d'utilisation de la gestion des exceptions en développement, de la façon d'utiliser les exceptions personnalisées et de la façon d'étendre les capacités de gestion des exceptions de Laravel.

Enregistrer le gestionnaire d'exceptions

Ici, nous devons revenir à l'étape d'amorçage avant que le noyau ne traite la demande que nous avons dite à plusieurs reprises dans la section IlluminateFoundationBootstrapHandleExceptions. de l'étape d'amorçage. Comportement de gestion des exceptions du système et enregistrement d'un gestionnaire d'exceptions global :

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']) enregistre la méthode handleException de HandleExceptions en tant que méthode de gestionnaire global du programme. :

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

Dans le processeur, les exceptions sont principalement signalées via la méthode de rapport d'ExceptionHandler. Ici, les exceptions sont enregistrées dans le fichier storage/laravel.log, puis la réponse d'exception est rendue en fonction de la demande. tapez pour générer une sortie pour le client. L'ExceptionHandler est ici une instance de la classe AppExceptionsHandler, qui est enregistrée dans le conteneur de service au début du projet :

// 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
);

Ici, en passant, la fonction set_error_handler est utilisée pour enregistrer le gestionnaire d'erreurs fonction, car dans certains anciens codes ou bibliothèques de classes, la fonction trigger_error de PHP est principalement utilisée pour générer des erreurs. Le gestionnaire d'exceptions ne peut gérer que les exceptions mais pas les erreurs. Par conséquent, afin d'être compatible avec les anciennes bibliothèques de classes, set_error_handler est généralement utilisé pour enregistrer. la méthode globale. La méthode du gestionnaire d'erreurs, après avoir détecté l'erreur dans la méthode, convertit l'erreur en une exception, puis la renvoie, afin que tous les codes du projet puissent lancer des instances d'exception lorsqu'ils ne sont pas exécutés correctement.

/**
 * 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);
    }
}

Instances d'exception Laravel couramment utilisées

Laravel génère des instances d'exception correspondantes pour les exceptions de programme courantes, permettant aux développeurs de capturer ces opérations. Des exceptions se produisent et un traitement de suivi est fait selon vos propres besoins (par exemple : appeler une autre méthode de remède dans catch, enregistrer des exceptions dans des fichiers journaux, envoyer des e-mails d'alarme, des messages texte)

Ici, je liste quelques rencontres courantes dans les exceptions de développement et j'explique sous quoi Dans les circonstances dans lesquelles elles sont générées. Dans le codage quotidien, vous devez faire attention à détecter ces exceptions dans le programme et à bien les gérer pour rendre le programme plus robuste.

IlluminateDatabaseQueryException Cette exception sera levée lorsqu'une erreur se produit lors de l'exécution d'une instruction SQL dans Laravel. C'est également l'exception la plus couramment utilisée et est utilisée pour capturer les erreurs d'exécution SQL. Par exemple, lors de l'exécution de l'instruction Update, De nombreuses personnes aiment juger si l'instruction SQL est exécutée après son exécution.Le nombre de lignes modifiées est utilisé pour déterminer si l'UPDATE a réussi.Cependant, dans certains scénarios, l'instruction UPDATE exécutée ne modifie pas la valeur de l'enregistrement. Dans ce cas, il est impossible de déterminer si la mise à jour a réussi via la fonction modifiée. De plus, si elle est capturée lors de l'exécution de la transaction, QueryException peut annuler la transaction dans le bloc catch.

IlluminateDatabaseEloquentModelNotFoundException Cette exception sera levée si un seul enregistrement n'est pas trouvé via les méthodes findOrFail et firstOrFail du modèle (find et first renverront NULL lorsque les données sont introuvables).

IlluminateValidationValidationException Cette exception est levée lorsque la requête ne passe pas la validation FormValidator de Laravel.

IlluminateAuthAccessAuthorizationException Cette exception est levée lorsque la demande de l'utilisateur ne réussit pas la vérification de la politique de Laravel

SymfonyComponentRoutingExceptionMethodNotAllowedException La méthode HTTP est incorrecte lors de la demande de routage

IlluminateHttpExceptionsHttpResponseException L Aravel ne gère pas les requêtes HTTP. l'exception est levée en cas de succès

Étendre le gestionnaire d'exceptions de Laravel

Comme mentionné ci-dessus, Laravel a enregistré avec succès AppExceptionsHandler en tant que gestionnaire d'exceptions global, et le code n'a pas été intercepté. Les exceptions reçues le seront éventuellement être intercepté par AppExceptionsHandler. Le processeur signale d'abord l'exception et l'enregistre dans le fichier journal, puis restitue la réponse d'exception et envoie ensuite la réponse au client. Cependant, la méthode de gestion d'exceptions intégrée n'est pas facile à utiliser. Nous souhaitons souvent signaler des exceptions aux systèmes de messagerie ou de journal d'erreurs. L'exemple suivant consiste à signaler les exceptions au système Sentry. Utilisation :

public function report(Exception $exception)
{
    if (app()->bound('sentry') && $this->shouldReport($exception)) {
        app('sentry')->captureException($exception);
    }

    parent::report($exception);
}

et la méthode de rendu par défaut. Le format JSON de la réponse générée lors de la validation du formulaire est souvent différent du format unifié JOSN de notre projet, ce qui nous oblige à personnaliser le comportement du méthode de rendu.

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的应用也是一样。

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer