pnoexz / php-api-exception
PHP 异常
v1.0.0
2018-11-11 17:59 UTC
Requires (Dev)
- phpunit/phpunit: ^6.3
- squizlabs/php_codesniffer: ^3.0
This package is auto-updated.
Last update: 2024-09-18 19:27:59 UTC
README
PHP API Exception 依赖于以下原则
- 每个异常必须是唯一的。
- 消费者不得依赖于可读的错误消息来识别它。
- 遵守 PSR-3。参见: https://www.php-fig.org/psr/psr-3/
签名
/** * @param array $data Any JSON serializable data we should * send to the user along with the built-in * information * @param \Throwable|null $previous The previous exception thrown to * maintain the exception chain */ ApiExceptionL::__construct(array $data = [], \Throwable $previous = null)
示例
简单用法
<?php namespace Pnoexz\Examples\Exceptions; use Pnoexz\ApiException\Http\ClientError\NotFoundException; class ArticleNotFoundException extends NotFoundException { /** * @var string */ public $message = "Article not found"; }
将输出以下 JSON
{ "class": "Pnoexz\\Examples\\Exceptions\\ArticleNotFoundException", "message": "Article not found", "statusCode": 404 }
创建自定义异常
如果您需要支持不在标准中或缺失的 HTTP 状态码,可以从 \Pnoexz\ApiException\Http\ClientError\ClientErrorException
(默认级别 WARNING)\Pnoexz\ApiException\Http\ServerError\ServerErrorException
(默认级别 ERROR)或 Pnoexz\ApiException
(无默认级别)扩展。
<?php namespace Pnoexz\Examples\Exceptions; use Pnoexz\ApiException; class ArticleNotFoundException extends ApiException { /** * @var int */ protected $httpStatus = 701; /** * @var string */ protected $level = \Psr\Log\LogLevel::WARNING; /** * @var string */ public $message = "Article not found"; }
与异常一起向客户端发送额外数据
假设我们正在构建一个电子商务系统,其中用户不能同时拥有多个挂起的购物车。如果用户尝试创建新的购物车,我们不仅可以显示错误,还可以向客户端发送实体,让他们优雅地处理错误并向用户显示信息。
异常
<?php namespace Pnoexz\Examples\Exceptions; use Pnoexz\ApiException\Http\ClientError\ConflictException; class PendingCartExistsException extends ConflictException { /** * @var string */ public $message = "A pending cart already exists"; }
从服务中
$potentiallyPendingCart = $this->getPendingCartByUser($user); if (!empty($potentiallyPendingCart)) { throw new PendingCartExistsException(['cart' => $potentiallyPendingCart]); }
输出到客户端
{ "class": "Pnoexz\\Examples\\Exceptions\\PendingCartExistsException", "message": "A pending cart already exists", "statusCode": 409, "data": { "cart": { "id": 8, "items": [ {"id": 8931} ] } } }
保持之前抛出的异常
在这种情况下,我们将捕获包含敏感数据(原始 SQL 查询)的异常。我们需要区分客户端将看到的内容和我们应记录的内容。这可能在我们的处理程序中需要一些额外的代码,但在抛出异常时,我们只需将捕获的内容传递给第二个参数。
class FruitRepository { public function get(int $id) { try { $sth = $this->dbh->prepare(" SELECT name, colour, calories FROM fruit WHERE id = :id "); $sth->execute([':id' => $id]); return $sth->fetch(PDO::FETCH_ASSOC); } catch (\Exception $e) { throw new DatabaseException([], $e); } } }
然后我们可以从调用方法中捕获此异常并做类似的事情;保持异常跟踪并向客户端发送一个很好的输出。
处理程序
以下示例使用 Slim3 错误处理程序,但应与其他框架类似工作。
仅处理 ApiExceptions
/** * @param Request $request * @param Response $response * @param \Throwable $throwable * @return Response */ public function __invoke( Request $request, Response $response, ApiException $exception ) { $encodedBody = \json_encode($exception); $this->log( $exception->getLevel(), $exception->getMessage(), $encodedBody ); return $response->withJson($encodedBody, $exception->getStatusCode()); }
处理所有异常
/** * @param Request $request * @param Response $response * @param \Throwable $throwable * @return Response */ public function __invoke( Request $request, Response $response, \Throwable $throwable ) { $statusCode = 500; $level = LogLevel::ERROR; $message = $throwable->getMessage(); if ($throwable instanceof ApiException) { $statusCode = $throwable->getStatusCode(); $level = $throwable->getLevel(); } $body = [ 'statusCode' => $statusCode, 'level' => $level, 'message' => $message, ]; $this->log($level, $message, $body); return $response->withJson($body, $statusCode); }