物事はうまくいかないことがあります。エラーを予測することはできませんが、予期することはできます。各 Slim Framework アプリケーションには、キャッチされないすべての PHP 例外を受け取るエラーハンドラーがあります。このエラーハンドラーは、現在の HTTP リクエストオブジェクトとレスポンスオブジェクトも受け取ります。エラーハンドラーは、HTTP クライアントに返される適切なレスポンスオブジェクトを準備して返す必要があります。
<?php
use Slim\Factory\AppFactory;
require __DIR__ . '/../vendor/autoload.php';
$app = AppFactory::create();
/**
* The routing middleware should be added earlier than the ErrorMiddleware
* Otherwise exceptions thrown from it will not be handled by the middleware
*/
$app->addRoutingMiddleware();
/**
* Add Error Middleware
*
* @param bool $displayErrorDetails -> Should be set to false in production
* @param bool $logErrors -> Parameter is passed to the default ErrorHandler
* @param bool $logErrorDetails -> Display error details in error log
* @param LoggerInterface|null $logger -> Optional PSR-3 Logger
*
* Note: This middleware should be added last. It will not handle any exceptions/errors
* for middleware added after it.
*/
$errorMiddleware = $app->addErrorMiddleware(true, true, true);
// ...
$app->run();
任意のタイプの Exception または Throwable に対してカスタムハンドラーをマップできるようになりました。
<?php
use Monolog\Handler\RotatingFileHandler;
use Monolog\Logger;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Log\LoggerInterface;
use Slim\Factory\AppFactory;
require __DIR__ . '/../vendor/autoload.php';
$app = AppFactory::create();
// Add Routing Middleware
$app->addRoutingMiddleware();
// Optional: Define custom error logger
$logger = new Logger('error');
$logger->pushHandler(new RotatingFileHandler('error.log'));
// Define Custom Error Handler
$customErrorHandler = function (
ServerRequestInterface $request,
Throwable $exception,
bool $displayErrorDetails,
bool $logErrors,
bool $logErrorDetails
) use ($app, $logger) {
if ($logger) {
$logger->error($exception->getMessage());
}
$payload = ['error' => $exception->getMessage()];
$response = $app->getResponseFactory()->createResponse();
$response->getBody()->write(
json_encode($payload, JSON_UNESCAPED_UNICODE)
);
return $response;
};
// Add Error Middleware
$errorMiddleware = $app->addErrorMiddleware(true, true, true, $logger);
$errorMiddleware->setDefaultErrorHandler($customErrorHandler);
// ...
$app->run();
Slim に付属のデフォルトの ErrorHandler
にカスタムエラーロギングをパイプしたい場合は、2つの方法があります。
最初の方法では、ErrorHandler
を拡張して logError()
メソッドをスタブするだけで済みます。
<?php
namespace MyApp\Handlers;
use Slim\Handlers\ErrorHandler;
class MyErrorHandler extends ErrorHandler
{
protected function logError(string $error): void
{
// Insert custom error logging function.
}
}
<?php
use MyApp\Handlers\MyErrorHandler;
use Slim\Factory\AppFactory;
require __DIR__ . '/../vendor/autoload.php';
$app = AppFactory::create();
// Add Routing Middleware
$app->addRoutingMiddleware();
// Instantiate Your Custom Error Handler
$myErrorHandler = new MyErrorHandler($app->getCallableResolver(), $app->getResponseFactory());
// Add Error Middleware
$errorMiddleware = $app->addErrorMiddleware(true, true, true);
$errorMiddleware->setDefaultErrorHandler($myErrorHandler);
// ...
$app->run();
2番目の方法では、人気のある Monolog ライブラリの1つなど、PSR-3 標準に準拠したロガーを提供できます。
<?php
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use MyApp\Handlers\MyErrorHandler;
use Slim\Factory\AppFactory;
require __DIR__ . '/../vendor/autoload.php';
$app = AppFactory::create();
// Add Routing Middleware
$app->addRoutingMiddleware();
// Monolog Example
$logger = new Logger('app');
$streamHandler = new StreamHandler(__DIR__ . '/var/log', 100);
$logger->pushHandler($streamHandler);
// Add Error Middleware with Logger
$errorMiddleware = $app->addErrorMiddleware(true, true, true, $logger);
// ...
$app->run();
レンダリングは、最終的に処理から切り離されました。これは、ErrorRenderers
の助けを借りて、コンテンツタイプを検出し、適切にレンダリングします。コアの ErrorHandler
は、完全にリファクタリングされた AbstractErrorHandler
クラスを拡張します。デフォルトでは、サポートされているコンテンツタイプに対して適切な ErrorRenderer
を呼び出します。コアの ErrorHandler
は、次のコンテンツタイプのレンダラーを定義します。
application/json
application/xml
および text/xml
text/html
text/plain
任意のコンテンツタイプに対して、独自のエラーレンダラーを登録できます。まず、\Slim\Interfaces\ErrorRendererInterface
を実装する新しいエラーレンダラーを定義します。
<?php
use Slim\Interfaces\ErrorRendererInterface;
use Throwable;
class MyCustomErrorRenderer implements ErrorRendererInterface
{
public function __invoke(Throwable $exception, bool $displayErrorDetails): string
{
return 'My awesome format';
}
}
次に、コアエラーハンドラーにそのエラーレンダラーを登録します。以下の例では、text/html
コンテンツタイプに使用するレンダラーを登録します。
<?php
use MyApp\Handlers\MyErrorHandler;
use Slim\Factory\AppFactory;
require __DIR__ . '/../vendor/autoload.php';
$app = AppFactory::create();
// Add Routing Middleware
$app->addRoutingMiddleware();
// Add Error Middleware
$errorMiddleware = $app->addErrorMiddleware(true, true, true);
// Get the default error handler and register my custom error renderer.
$errorHandler = $errorMiddleware->getDefaultErrorHandler();
$errorHandler->registerErrorRenderer('text/html', MyCustomErrorRenderer::class);
// ...
$app->run();
デフォルトでは、エラーハンドラーはリクエストの Accept
ヘッダーを使用してエラーレンダラーを検出しようとします。エラーハンドラーに特定のエラーレンダラーを使用するように強制する必要がある場合は、次のように記述できます。
$errorHandler->forceContentType('application/json');
アプリケーション内に名前付きの HTTP 例外を追加しました。これらの例外は、ネイティブレンダラーと適切に連携します。ネイティブ HTML レンダラーが呼び出されたときに、少し詳しい情報を提供するために、それぞれに description
および title
属性を持たせることもできます。
基本クラス HttpSpecializedException
は Exception
を拡張し、次のサブクラスが付属しています。
基本リポジトリで提供しないその他の応答コードが必要な場合は、HttpSpecializedException
クラスを拡張できます。たとえば、ネイティブのもののように動作する 504 ゲートウェイタイムアウト例外が必要な場合は、次のようにします。
class HttpGatewayTimeoutException extends HttpSpecializedException
{
protected $code = 504;
protected $message = 'Gateway Timeout.';
protected $title = '504 Gateway Timeout';
protected $description = 'Timed out before receiving response from the upstream server.';
}
HTTP 例外をスローするには、次のコードを使用します。
use Slim\Exception\HttpNotFoundException;
// ...
throw new HttpNotFoundException($request);
例外をスローするときは、必ず $request
オブジェクトを渡してください。