From a30538e791c1bb9797661cadefc8c47c52fa6ad9 Mon Sep 17 00:00:00 2001 From: Filis Futsarov Date: Fri, 21 Mar 2025 20:10:19 +0100 Subject: [PATCH 1/2] php8.4 support among other improvements --- .gitattributes | 16 ++++----- .gitignore | 2 ++ .php-cs-fixer.php | 10 ++++++ .phpstan.neon | 10 ++++++ LICENSE | 2 +- composer.json | 19 +++++----- phpcs.xml | 16 +++++++++ phpunit.xml | 38 ++++++++++++++++++++ src/ErrorFormatter/AbstractFormatter.php | 4 +-- src/ErrorFormatter/ImageFormatter.php | 4 +++ src/ErrorHandler.php | 21 +++++++---- tests/ErrorHandlerTest.php | 45 +++++++++++++----------- 12 files changed, 140 insertions(+), 47 deletions(-) create mode 100644 .php-cs-fixer.php create mode 100644 .phpstan.neon create mode 100644 phpcs.xml create mode 100644 phpunit.xml diff --git a/.gitattributes b/.gitattributes index c540470..b67c7ca 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,10 +1,10 @@ * text=auto eol=lf -/tests export-ignore -.editorconfig export-ignore -.gitattributes export-ignore -.gitignore export-ignore -.php_cs export-ignore -.travis.yml export-ignore -phpcs.xml.dist export-ignore -phpunit.xml.dist export-ignore +/tests export-ignore +.editorconfig export-ignore +.gitattributes export-ignore +.gitignore export-ignore +.php-cs-fixer.php export-ignore +phpcs.xml.dist export-ignore +phpunit.xml.dist export-ignore +.phpstan.neon export-ignore diff --git a/.gitignore b/.gitignore index 364d1a4..5ae8693 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ vendor composer.lock coverage *.cache +.idea +kit diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php new file mode 100644 index 0000000..90b12a3 --- /dev/null +++ b/.php-cs-fixer.php @@ -0,0 +1,10 @@ +setFinder( + PhpCsFixer\Finder::create() + ->files() + ->name('*.php') + ->in(__DIR__.'/src') + ->in(__DIR__.'/tests') + ); \ No newline at end of file diff --git a/.phpstan.neon b/.phpstan.neon new file mode 100644 index 0000000..c13c95b --- /dev/null +++ b/.phpstan.neon @@ -0,0 +1,10 @@ +parameters: + reportUnmatchedIgnoredErrors: false + inferPrivatePropertyTypeFromConstructor: true + level: 8 + paths: + - src + - tests + ignoreErrors: + - "#expects GdImage, resource given#" + - "#should return resource but returns#" diff --git a/LICENSE b/LICENSE index 017c0cd..374fb13 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2019 +Copyright (c) 2019-2025 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/composer.json b/composer.json index cabe854..b8d4dfd 100644 --- a/composer.json +++ b/composer.json @@ -20,16 +20,17 @@ }, "require": { "php": "^7.2 || ^8.0", - "middlewares/utils": "^3.0 || ^4.0", - "psr/http-server-middleware": "^1.0" + "middlewares/utils": "^2 || ^3 || ^4", + "psr/http-server-middleware": "^1", + "ext-gd": "*" }, "require-dev": { - "phpunit/phpunit": "^8|^9", - "friendsofphp/php-cs-fixer": "^2.0", - "squizlabs/php_codesniffer": "^3.0", - "oscarotero/php-cs-fixer-config": "^1.0", - "phpstan/phpstan": "^0.12", - "laminas/laminas-diactoros": "^2|^3" + "phpunit/phpunit": "^8 || ^9", + "friendsofphp/php-cs-fixer": "^3", + "squizlabs/php_codesniffer": "^3", + "oscarotero/php-cs-fixer-config": "^2", + "phpstan/phpstan": "^1 || ^2", + "laminas/laminas-diactoros": "^2 || ^3" }, "autoload": { "psr-4": { @@ -49,4 +50,4 @@ "coverage": "phpunit --coverage-text", "coverage-html": "phpunit --coverage-html=coverage" } -} +} \ No newline at end of file diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 0000000..7814331 --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,16 @@ + + + Middlewares coding standard + + + + + + + + + + + src + tests + diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..76258d7 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,38 @@ + + + + + tests + + + + + + ./src + + ./tests + ./vendor + + + + + + + + + diff --git a/src/ErrorFormatter/AbstractFormatter.php b/src/ErrorFormatter/AbstractFormatter.php index 775e629..c839fec 100644 --- a/src/ErrorFormatter/AbstractFormatter.php +++ b/src/ErrorFormatter/AbstractFormatter.php @@ -23,8 +23,8 @@ abstract class AbstractFormatter implements FormatterInterface protected $contentTypes = []; public function __construct( - ResponseFactoryInterface $responseFactory = null, - StreamFactoryInterface $streamFactory = null + ?ResponseFactoryInterface $responseFactory = null, + ?StreamFactoryInterface $streamFactory = null ) { $this->responseFactory = $responseFactory ?? Factory::getResponseFactory(); $this->streamFactory = $streamFactory ?? Factory::getStreamFactory(); diff --git a/src/ErrorFormatter/ImageFormatter.php b/src/ErrorFormatter/ImageFormatter.php index cf68400..0072243 100644 --- a/src/ErrorFormatter/ImageFormatter.php +++ b/src/ErrorFormatter/ImageFormatter.php @@ -51,9 +51,13 @@ private function createImage(Throwable $error) $size = 200; $image = imagecreatetruecolor($size, $size); $textColor = imagecolorallocate($image, 255, 255, 255); + + /* @phpstan-ignore-next-line */ imagestring($image, 5, 10, 10, "$type $code", $textColor); + /* @phpstan-ignore-next-line */ foreach (str_split($message, intval($size / 10)) as $line => $text) { + /* @phpstan-ignore-next-line */ imagestring($image, 5, 10, ($line * 18) + 28, $text, $textColor); } diff --git a/src/ErrorHandler.php b/src/ErrorHandler.php index 49f00ef..8d6afd4 100644 --- a/src/ErrorHandler.php +++ b/src/ErrorHandler.php @@ -4,6 +4,12 @@ namespace Middlewares; use Middlewares\ErrorFormatter\FormatterInterface; +use Middlewares\ErrorFormatter\HtmlFormatter; +use Middlewares\ErrorFormatter\ImageFormatter; +use Middlewares\ErrorFormatter\JsonFormatter; +use Middlewares\ErrorFormatter\PlainFormatter; +use Middlewares\ErrorFormatter\SvgFormatter; +use Middlewares\ErrorFormatter\XmlFormatter; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; @@ -20,16 +26,16 @@ class ErrorHandler implements MiddlewareInterface * * @param FormatterInterface[] $formatters */ - public function __construct(array $formatters = null) + public function __construct(?array $formatters = null) { if (empty($formatters)) { $formatters = [ - new ErrorFormatter\PlainFormatter(), - new ErrorFormatter\HtmlFormatter(), - new ErrorFormatter\ImageFormatter(), - new ErrorFormatter\JsonFormatter(), - new ErrorFormatter\SvgFormatter(), - new ErrorFormatter\XmlFormatter(), + new PlainFormatter(), + new HtmlFormatter(), + new ImageFormatter(), + new JsonFormatter(), + new SvgFormatter(), + new XmlFormatter(), ]; } @@ -59,6 +65,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface } } + /** @var FormatterInterface $default */ $default = reset($this->formatters); return $default->handle($error, $request); diff --git a/tests/ErrorHandlerTest.php b/tests/ErrorHandlerTest.php index b5421c8..d250385 100644 --- a/tests/ErrorHandlerTest.php +++ b/tests/ErrorHandlerTest.php @@ -4,7 +4,12 @@ namespace Middlewares\Tests; use Exception; -use Middlewares\ErrorFormatter; +use Middlewares\ErrorFormatter\HtmlFormatter; +use Middlewares\ErrorFormatter\ImageFormatter; +use Middlewares\ErrorFormatter\JsonFormatter; +use Middlewares\ErrorFormatter\PlainFormatter; +use Middlewares\ErrorFormatter\SvgFormatter; +use Middlewares\ErrorFormatter\XmlFormatter; use Middlewares\ErrorHandler; use Middlewares\Utils\Dispatcher; use Middlewares\Utils\Factory; @@ -13,7 +18,7 @@ class ErrorHandlerTest extends TestCase { - public function testMiddleware() + public function testMiddleware(): void { $response = Dispatcher::run([ new ErrorHandler(), @@ -27,7 +32,7 @@ function ($request) { $this->assertStringContainsString('Something went wrong', (string) $response->getBody()); } - public function testHttpException() + public function testHttpException(): void { $response = Dispatcher::run([ new ErrorHandler(), @@ -39,7 +44,7 @@ function ($request) { $this->assertEquals(404, $response->getStatusCode()); } - public function testHttpStatusException() + public function testHttpStatusException(): void { $response = Dispatcher::run([ new ErrorHandler(), @@ -56,13 +61,13 @@ public function getStatusCode(): int $this->assertEquals(418, $response->getStatusCode()); } - public function testGifFormatter() + public function testGifFormatter(): void { $request = Factory::createServerRequest('GET', '/'); $request = $request->withheader('Accept', 'image/gif'); $response = Dispatcher::run([ - (new ErrorHandler())->addFormatters(new ErrorFormatter\ImageFormatter()), + (new ErrorHandler())->addFormatters(new ImageFormatter()), function ($request) { throw new Exception('Something went wrong'); }, @@ -71,13 +76,13 @@ function ($request) { $this->assertEquals('image/gif', $response->getHeaderLine('Content-Type')); } - public function testHtmlFormatter() + public function testHtmlFormatter(): void { $request = Factory::createServerRequest('GET', '/'); $request = $request->withheader('Accept', 'text/html'); $response = Dispatcher::run([ - (new ErrorHandler())->addFormatters(new ErrorFormatter\HtmlFormatter()), + (new ErrorHandler())->addFormatters(new HtmlFormatter()), function ($request) { throw new Exception('Something went wrong'); }, @@ -86,13 +91,13 @@ function ($request) { $this->assertEquals('text/html', $response->getHeaderLine('Content-Type')); } - public function testJpegFormatter() + public function testJpegFormatter(): void { $request = Factory::createServerRequest('GET', '/'); $request = $request->withheader('Accept', 'image/jpeg'); $response = Dispatcher::run([ - (new ErrorHandler())->addFormatters(new ErrorFormatter\ImageFormatter()), + (new ErrorHandler())->addFormatters(new ImageFormatter()), function ($request) { throw new Exception('Something went wrong'); }, @@ -101,13 +106,13 @@ function ($request) { $this->assertEquals('image/jpeg', $response->getHeaderLine('Content-Type')); } - public function testJsonFormatter() + public function testJsonFormatter(): void { $request = Factory::createServerRequest('GET', '/'); $request = $request->withheader('Accept', 'application/json'); $response = Dispatcher::run([ - (new ErrorHandler())->addFormatters(new ErrorFormatter\JsonFormatter()), + (new ErrorHandler())->addFormatters(new JsonFormatter()), function ($request) { throw new Exception('Something went wrong'); }, @@ -116,13 +121,13 @@ function ($request) { $this->assertEquals('application/json', $response->getHeaderLine('Content-Type')); } - public function testPlainFormatter() + public function testPlainFormatter(): void { $request = Factory::createServerRequest('GET', '/'); $request = $request->withheader('Accept', 'text/plain'); $response = Dispatcher::run([ - (new ErrorHandler())->addFormatters(new ErrorFormatter\PlainFormatter()), + (new ErrorHandler())->addFormatters(new PlainFormatter()), function ($request) { throw new Exception('Something went wrong'); }, @@ -131,13 +136,13 @@ function ($request) { $this->assertEquals('text/plain', $response->getHeaderLine('Content-Type')); } - public function testPngFormatter() + public function testPngFormatter(): void { $request = Factory::createServerRequest('GET', '/'); $request = $request->withheader('Accept', 'image/png'); $response = Dispatcher::run([ - (new ErrorHandler())->addFormatters(new ErrorFormatter\ImageFormatter()), + (new ErrorHandler())->addFormatters(new ImageFormatter()), function ($request) { throw new Exception('Something went wrong'); }, @@ -146,13 +151,13 @@ function ($request) { $this->assertEquals('image/png', $response->getHeaderLine('Content-Type')); } - public function testSvgFormatter() + public function testSvgFormatter(): void { $request = Factory::createServerRequest('GET', '/'); $request = $request->withheader('Accept', 'image/svg+xml'); $response = Dispatcher::run([ - (new ErrorHandler())->addFormatters(new ErrorFormatter\SvgFormatter()), + (new ErrorHandler())->addFormatters(new SvgFormatter()), function ($request) { throw new Exception('Something went wrong'); }, @@ -161,13 +166,13 @@ function ($request) { $this->assertEquals('image/svg+xml', $response->getHeaderLine('Content-Type')); } - public function testXmlFormatter() + public function testXmlFormatter(): void { $request = Factory::createServerRequest('GET', '/'); $request = $request->withheader('Accept', 'text/xml'); $response = Dispatcher::run([ - (new ErrorHandler())->addFormatters(new ErrorFormatter\XmlFormatter()), + (new ErrorHandler())->addFormatters(new XmlFormatter()), function ($request) { throw new Exception('Something went wrong'); }, From ed42065866e3f53570c97edb23798cb69412492d Mon Sep 17 00:00:00 2001 From: Filis Futsarov Date: Fri, 21 Mar 2025 22:12:46 +0100 Subject: [PATCH 2/2] update github workflow --- .github/workflows/main.yaml | 41 +++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index fb33d5d..5d14a59 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -1,10 +1,10 @@ name: "testing" on: - push: - branches: [ master ] - pull_request: - branches: [ master ] + push: + branches: [ master ] + pull_request: + branches: [ master ] jobs: qa: @@ -13,19 +13,19 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Validate composer.json and composer.lock run: composer validate - name: Cache Composer packages id: composer-cache - uses: actions/cache@v2 + uses: actions/cache@v4 with: - path: vendor - key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} - restore-keys: | - ${{ runner.os }}-php- + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- - name: Install dependencies if: steps.composer-cache.outputs.cache-hit != 'true' @@ -40,15 +40,15 @@ jobs: strategy: matrix: - php: - - 7.2 - - 7.3 - - 7.4 - - 8.0 - - 8.1 - - 8.2 - - 8.3 - - 8.4 + php: + - 7.2 + - 7.3 + - 7.4 + - 8.0 + - 8.1 + - 8.2 + - 8.3 + - 8.4 steps: - name: Checkout @@ -71,6 +71,3 @@ jobs: - name: Tests run: composer test - - - name: Tests coverage - run: composer coverage \ No newline at end of file