pnoexz/php-api-exception

v1.0.0 2018-11-11 17:59 UTC

This package is auto-updated.

Last update: 2024-09-18 19:27:59 UTC


README

PHP API Exception 依赖于以下原则

签名

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