-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from lcobucci/provide-initial-implementation
Provide initial implementation
- Loading branch information
Showing
34 changed files
with
1,213 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
/vendor/ | ||
/composer.lock | ||
/.phpcs.cache | ||
/infection-log.txt | ||
/infection.log | ||
/.phpunit.result.cache |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
build: | ||
environment: | ||
mysql: false | ||
postgresql: false | ||
redis: false | ||
rabbitmq: false | ||
mongodb: false | ||
php: | ||
version: 7.3 | ||
|
||
cache: | ||
disabled: false | ||
directories: | ||
- ~/.composer/cache | ||
|
||
dependencies: | ||
override: | ||
- composer install --no-interaction --prefer-dist | ||
|
||
nodes: | ||
analysis: | ||
project_setup: | ||
override: true | ||
tests: | ||
override: | ||
- php-scrutinizer-run | ||
- phpcs-run | ||
|
||
checks: | ||
php : true | ||
|
||
tools: | ||
external_code_coverage: true | ||
|
||
build_failure_conditions: | ||
- 'elements.rating(<= C).new.exists' | ||
- 'issues.severity(>= MAJOR).new.exists' | ||
- 'project.metric_change("scrutinizer.test_coverage", < -0.01)' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
dist: trusty | ||
sudo: false | ||
language: php | ||
|
||
php: | ||
- 7.3 | ||
- 7.4snapshot | ||
- nightly | ||
|
||
cache: | ||
directories: | ||
- $HOME/.composer/cache | ||
|
||
before_install: | ||
- mv ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini{,.disabled} || echo "xdebug not available" | ||
- composer self-update | ||
|
||
install: travis_retry composer install | ||
|
||
script: | ||
- ./vendor/bin/phpunit | ||
|
||
jobs: | ||
allow_failures: | ||
- php: 7.4snapshot | ||
- php: nightly | ||
|
||
include: | ||
- stage: Code Quality | ||
env: TEST_COVERAGE=1 | ||
before_script: | ||
- mv ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini{.disabled,} | ||
- if [[ ! $(php -m | grep -si xdebug) ]]; then echo "xdebug required for coverage"; exit 1; fi | ||
script: | ||
- ./vendor/bin/phpunit --coverage-clover ./clover.xml | ||
after_script: | ||
- wget https://scrutinizer-ci.com/ocular.phar | ||
- php ocular.phar code-coverage:upload --format=php-clover ./clover.xml | ||
|
||
- stage: Code Quality | ||
env: CODE_STANDARD=1 | ||
script: | ||
- ./vendor/bin/phpcs | ||
|
||
- stage: Code Quality | ||
env: STATIC_ANALYSIS=1 | ||
script: | ||
- ./vendor/bin/phpstan analyse | ||
|
||
- stage: Code Quality | ||
env: MUTATION_TESTS=1 | ||
before_script: | ||
- mv ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini{.disabled,} | ||
- if [[ ! $(php -m | grep -si xdebug) ]]; then echo "xdebug required for mutation tests"; exit 1; fi | ||
script: | ||
- ./vendor/bin/infection --threads=$(nproc) --min-msi=100 --min-covered-msi=100 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"timeout": 1, | ||
"source": { | ||
"directories": ["src"] | ||
}, | ||
"logs": { | ||
"text": "infection.log" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?xml version="1.0"?> | ||
<ruleset> | ||
<arg name="basepath" value="." /> | ||
<arg name="extensions" value="php" /> | ||
<arg name="parallel" value="80" /> | ||
<arg name="colors" /> | ||
<arg name="cache" value=".phpcs.cache" /> | ||
<arg value="p" /> | ||
|
||
<file>src</file> | ||
<file>tests</file> | ||
|
||
<rule ref="Lcobucci" /> | ||
</ruleset> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
includes: | ||
- vendor/phpstan/phpstan-phpunit/extension.neon | ||
- vendor/phpstan/phpstan-phpunit/rules.neon | ||
- vendor/phpstan/phpstan-deprecation-rules/rules.neon | ||
- vendor/phpstan/phpstan-strict-rules/rules.neon | ||
|
||
parameters: | ||
level: 7 | ||
paths: | ||
- src | ||
- tests |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd" | ||
colors="true" | ||
verbose="true" | ||
beStrictAboutOutputDuringTests="true" | ||
beStrictAboutTodoAnnotatedTests="true" | ||
beStrictAboutChangesToGlobalState="true" | ||
beStrictAboutCoversAnnotation="true" | ||
beStrictAboutResourceUsageDuringSmallTests="true" | ||
forceCoversAnnotation="true" | ||
> | ||
<testsuites> | ||
<testsuite name="unit"> | ||
<directory>tests</directory> | ||
</testsuite> | ||
</testsuites> | ||
|
||
<filter> | ||
<whitelist> | ||
<directory>src</directory> | ||
</whitelist> | ||
</filter> | ||
</phpunit> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?php | ||
declare(strict_types=1); | ||
|
||
namespace Lcobucci\ErrorHandling; | ||
|
||
use Throwable; | ||
|
||
interface DebugInfoStrategy | ||
{ | ||
/** | ||
* @return array<string, mixed>|null | ||
*/ | ||
public function extractDebugInfo(Throwable $error): ?array; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<?php | ||
declare(strict_types=1); | ||
|
||
namespace Lcobucci\ErrorHandling\DebugInfoStrategy; | ||
|
||
use Lcobucci\ErrorHandling\DebugInfoStrategy; | ||
use Throwable; | ||
|
||
final class NoDebugInfo implements DebugInfoStrategy | ||
{ | ||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public function extractDebugInfo(Throwable $error): ?array | ||
{ | ||
return null; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
<?php | ||
declare(strict_types=1); | ||
|
||
namespace Lcobucci\ErrorHandling\DebugInfoStrategy; | ||
|
||
use Generator; | ||
use Lcobucci\ErrorHandling\DebugInfoStrategy; | ||
use Throwable; | ||
use function get_class; | ||
use function iterator_to_array; | ||
|
||
final class NoTrace implements DebugInfoStrategy | ||
{ | ||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public function extractDebugInfo(Throwable $error): ?array | ||
{ | ||
$debugInfo = $this->format($error); | ||
$stack = iterator_to_array($this->streamStack($error->getPrevious()), false); | ||
|
||
if ($stack !== []) { | ||
$debugInfo['stack'] = $stack; | ||
} | ||
|
||
return $debugInfo; | ||
} | ||
|
||
private function streamStack(?Throwable $previous): Generator | ||
{ | ||
if ($previous === null) { | ||
return; | ||
} | ||
|
||
yield $this->format($previous); | ||
yield from $this->streamStack($previous->getPrevious()); | ||
} | ||
|
||
/** | ||
* @return array<string, string|int> | ||
*/ | ||
private function format(Throwable $error): array | ||
{ | ||
return [ | ||
'class' => get_class($error), | ||
'code' => $error->getCode(), | ||
'message' => $error->getMessage(), | ||
'file' => $error->getFile(), | ||
'line' => $error->getLine(), | ||
]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
<?php | ||
declare(strict_types=1); | ||
|
||
namespace Lcobucci\ErrorHandling; | ||
|
||
use Lcobucci\ContentNegotiation\UnformattedResponse; | ||
use Lcobucci\ErrorHandling\Problem\Detailed; | ||
use Lcobucci\ErrorHandling\Problem\Titled; | ||
use Lcobucci\ErrorHandling\Problem\Typed; | ||
use Psr\Http\Message\ResponseFactoryInterface; | ||
use Psr\Http\Message\ResponseInterface; | ||
use Psr\Http\Message\ServerRequestInterface; | ||
use Psr\Http\Server\MiddlewareInterface; | ||
use Psr\Http\Server\RequestHandlerInterface; | ||
use Throwable; | ||
use function array_key_exists; | ||
|
||
final class ErrorConversionMiddleware implements MiddlewareInterface | ||
{ | ||
private const CONTENT_TYPE_CONVERSION = [ | ||
'application/json' => 'application/problem+json', | ||
'application/xml' => 'application/problem+xml', | ||
]; | ||
|
||
private const STATUS_URL = 'https://httpstatuses.com/'; | ||
|
||
/** | ||
* @var ResponseFactoryInterface | ||
*/ | ||
private $responseFactory; | ||
|
||
/** | ||
* @var DebugInfoStrategy | ||
*/ | ||
private $debugInfoStrategy; | ||
|
||
/** | ||
* @var StatusCodeExtractionStrategy | ||
*/ | ||
private $statusCodeExtractor; | ||
|
||
public function __construct( | ||
ResponseFactoryInterface $responseFactory, | ||
DebugInfoStrategy $debugInfoStrategy, | ||
StatusCodeExtractionStrategy $statusCodeExtractor | ||
) { | ||
$this->responseFactory = $responseFactory; | ||
$this->debugInfoStrategy = $debugInfoStrategy; | ||
$this->statusCodeExtractor = $statusCodeExtractor; | ||
} | ||
|
||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface | ||
{ | ||
try { | ||
return $handler->handle($request); | ||
} catch (Throwable $error) { | ||
$response = $this->generateResponse($request, $error); | ||
|
||
return new UnformattedResponse( | ||
$response, | ||
$this->extractData($error, $response), | ||
['error' => $error] | ||
); | ||
} | ||
} | ||
|
||
private function generateResponse(ServerRequestInterface $request, Throwable $error): ResponseInterface | ||
{ | ||
$response = $this->responseFactory->createResponse($this->statusCodeExtractor->extractStatusCode($error)); | ||
|
||
$accept = $request->getHeaderLine('Accept'); | ||
|
||
if (! array_key_exists($accept, self::CONTENT_TYPE_CONVERSION)) { | ||
return $response; | ||
} | ||
|
||
return $response->withAddedHeader( | ||
'Content-Type', | ||
self::CONTENT_TYPE_CONVERSION[$accept] . '; charset=' . $request->getHeaderLine('Accept-Charset') | ||
); | ||
} | ||
|
||
/** | ||
* @return array<string, mixed> | ||
*/ | ||
private function extractData(Throwable $error, ResponseInterface $response): array | ||
{ | ||
$data = [ | ||
'type' => $error instanceof Typed ? $error->getTypeUri() : self::STATUS_URL . $response->getStatusCode(), | ||
'title' => $error instanceof Titled ? $error->getTitle() : $response->getReasonPhrase(), | ||
'details' => $error->getMessage(), | ||
]; | ||
|
||
if ($error instanceof Detailed) { | ||
$data += $error->getExtraDetails(); | ||
} | ||
|
||
$debugInfo = $this->debugInfoStrategy->extractDebugInfo($error); | ||
|
||
if ($debugInfo !== null) { | ||
$data['_debug'] = $debugInfo; | ||
} | ||
|
||
return $data; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
<?php | ||
declare(strict_types=1); | ||
|
||
namespace Lcobucci\ErrorHandling; | ||
|
||
use Psr\Http\Message\ResponseInterface; | ||
use Psr\Http\Message\ServerRequestInterface; | ||
use Psr\Http\Server\MiddlewareInterface; | ||
use Psr\Http\Server\RequestHandlerInterface; | ||
use Psr\Log\LoggerInterface; | ||
use Throwable; | ||
|
||
final class ErrorLoggingMiddleware implements MiddlewareInterface | ||
{ | ||
/** | ||
* @var LoggerInterface | ||
*/ | ||
private $logger; | ||
|
||
public function __construct(LoggerInterface $logger) | ||
{ | ||
$this->logger = $logger; | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
* | ||
* @throws Throwable | ||
*/ | ||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface | ||
{ | ||
try { | ||
return $handler->handle($request); | ||
} catch (Throwable $error) { | ||
$this->logger->debug('Error happened while processing request', ['exception' => $error]); | ||
|
||
throw $error; | ||
} | ||
} | ||
} |
Oops, something went wrong.