diff --git a/.envrc b/.envrc index 1d953f4..1b5ce28 100644 --- a/.envrc +++ b/.envrc @@ -1 +1,2 @@ use nix +watch_file flake.nix flake.lock diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 5279933..3f37c41 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -10,16 +10,16 @@ env: jobs: tests: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest strategy: matrix: php: + - '8.4' + - '8.3' - '8.2' - '8.1' - '8.0' - '7.4' - - '7.3' - - '7.2' include: - php: '7.4' cs_fixer: true @@ -27,13 +27,13 @@ jobs: phpstan: true name: 'Check with PHP ${{ matrix.php }}' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Nix - uses: cachix/install-nix-action@v20 + uses: cachix/install-nix-action@v31 - name: Set up Nix cache - uses: cachix/cachix-action@v12 + uses: cachix/cachix-action@v16 with: # Use cache from https://github.com/fossar/nix-phps name: fossar @@ -46,7 +46,7 @@ jobs: run: | echo "dir=$(composer config cache-files-dir)" >> "$GITHUB_OUTPUT" - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} diff --git a/.gitignore b/.gitignore index 57872d0..08b8e95 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ /vendor/ +.direnv/ +.php-cs-fixer.cache +.phpunit.result.cache +phpstan.neon diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index 67ba003..5c1fa19 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -6,15 +6,22 @@ $rules = [ '@Symfony' => true, '@Symfony:risky' => true, - '@PHP71Migration' => true, - '@PHP71Migration:risky' => true, + '@PHP74Migration' => true, + '@PHP74Migration:risky' => true, + 'phpdoc_to_property_type' => true, // 'phpdoc_to_param_type' => true, - 'phpdoc_to_return_type' => true, + // 'phpdoc_to_return_type' => true, 'phpdoc_types_order' => false, // overwrite some Symfony rules - 'braces' => ['position_after_functions_and_oop_constructs' => 'same'], - 'function_declaration' => ['closure_function_spacing' => 'none'], + 'braces_position' => [ + 'functions_opening_brace' => 'same_line', + 'classes_opening_brace' => 'same_line', + ], + 'function_declaration' => [ + 'closure_function_spacing' => 'none', + 'closure_fn_spacing' => 'none', + ], 'concat_space' => ['spacing' => 'one'], 'phpdoc_align' => false, 'yoda_style' => false, diff --git a/NEWS.md b/NEWS.md index 1598c41..f1427c5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,8 @@ # guzzle-transcoder changes +## 0.3.1 – 2025-03-15 +- Raise minimum PHP version to 7.4.0. + ## 0.3.0 – 2023-03-06 - Raise minimum PHP version to 7.2.5. - Add support for fossar/transcoder 2.0. diff --git a/composer.json b/composer.json index aad6654..6e8ab3e 100644 --- a/composer.json +++ b/composer.json @@ -3,15 +3,16 @@ "description": "Guzzle plugin that converts responses to UTF-8", "type": "library", "require": { - "php": ">=7.2.5", + "php": ">=7.4.0", "guzzlehttp/guzzle": "^6.3 || ^7.0", - "fossar/transcoder": "^1.0 || ^2.0", + "fossar/transcoder": "^1.0 || ^2.0 || ^3.0", "guzzlehttp/psr7": "^1.7 || ^2.0" }, "require-dev" : { "friendsofphp/php-cs-fixer": "^3.0", "php-parallel-lint/php-parallel-lint": "^1.0", - "symfony/phpunit-bridge": "^6.2" + "symfony/phpunit-bridge": "^6.2 || ^7.0", + "phpstan/phpstan": "^2.1" }, "autoload": { "psr-4": { "Fossar\\GuzzleTranscoder\\": "src/" } @@ -28,7 +29,7 @@ ], "config": { "platform": { - "php": "7.2.5" + "php": "7.4.0" } }, "scripts": { diff --git a/flake.lock b/flake.lock index 6b23d4c..ed532a6 100644 --- a/flake.lock +++ b/flake.lock @@ -3,11 +3,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1673956053, - "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "lastModified": 1733328505, + "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", "owner": "edolstra", "repo": "flake-compat", - "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", "type": "github" }, "original": { @@ -18,11 +18,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1677852945, - "narHash": "sha256-liiVJjkBTuBTAkRW3hrI8MbPD2ImYzwUpa7kvteiKhM=", + "lastModified": 1741310760, + "narHash": "sha256-aizILFrPgq/W53Jw8i0a1h1GZAAKtlYOrG/A5r46gVM=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "f5ffd5787786dde3a8bf648c7a1b5f78c4e01abb", + "rev": "de0fe301211c267807afd11b12613f5511ff7433", "type": "github" }, "original": { @@ -39,11 +39,11 @@ "utils": "utils" }, "locked": { - "lastModified": 1678028979, - "narHash": "sha256-qFSN1sKlavyqUl9Vz60+iniYbTLfXtdAwFmTDdlLN5o=", + "lastModified": 1741586946, + "narHash": "sha256-EI5TFGNt2ObkK8MdBASS9LVkWT0qTqOljFFP2+RjZI0=", "owner": "fossar", "repo": "nix-phps", - "rev": "181dee32f3d47312674462cff30fcca661c6c643", + "rev": "e48727edd2dbbbd7b9181bd284569554f6e4e5a6", "type": "github" }, "original": { @@ -57,13 +57,31 @@ "phps": "phps" } }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, "utils": { + "inputs": { + "systems": "systems" + }, "locked": { - "lastModified": 1676283394, - "narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=", + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", "owner": "numtide", "repo": "flake-utils", - "rev": "3db36a8b464d0c4532ba1c7dda728f4576d6d073", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 40ad678..2907829 100644 --- a/flake.nix +++ b/flake.nix @@ -34,7 +34,7 @@ # Composer and PHP. php php.packages.composer - php.packages.phpstan + pkgs.phpactor ]; }; }; diff --git a/phpstan.neon b/phpstan.dist.neon similarity index 100% rename from phpstan.neon rename to phpstan.dist.neon diff --git a/src/GuzzleTranscoder.php b/src/GuzzleTranscoder.php index a07bb05..3786f60 100644 --- a/src/GuzzleTranscoder.php +++ b/src/GuzzleTranscoder.php @@ -12,17 +12,13 @@ use Psr\Http\Message\ResponseInterface; class GuzzleTranscoder { - /** @var ?TranscoderInterface */ - private $transcoder; + private ?TranscoderInterface $transcoder; - /** @var string */ - private $targetEncoding; + private string $targetEncoding; - /** @var bool */ - private $replaceHeaders; + private bool $replaceHeaders; - /** @var bool */ - private $replaceContent; + private bool $replaceContent; /** * Constructs a class for transcoding Responses. @@ -56,7 +52,7 @@ private function createTranscoder(): TranscoderInterface { public function convert(ResponseInterface $response): ResponseInterface { $stream = $response->getBody(); - /** @var array */ + /** @var array> */ $headers = $response->getHeaders(); $result = $this->convertResponse($headers, (string) $stream); if ($result !== null) { @@ -81,11 +77,10 @@ public function convert(ResponseInterface $response): ResponseInterface { */ public function __invoke(callable $handler): callable { return function(RequestInterface $request, array $options) use ($handler): PromiseInterface { + /** @var array $options */ $promise = $handler($request, $options); - return $promise->then(function(ResponseInterface $response): ResponseInterface { - return $this->convert($response); - }); + return $promise->then(fn(ResponseInterface $response): ResponseInterface => $this->convert($response)); }; } @@ -100,9 +95,9 @@ public function __invoke(callable $handler): callable { * * Otherwise an array containing the new headers and content is returned. * - * @param array $headers + * @param array> $headers * - * @return ?array{headers: array, content: string} + * @return ?array{headers: array>, content: string} */ public function convertResponse(array $headers, string $content): ?array { $headerDeclaredEncoding = null; diff --git a/src/Utils.php b/src/Utils.php index b808994..70f9199 100644 --- a/src/Utils.php +++ b/src/Utils.php @@ -72,7 +72,7 @@ class Utils { * [Basic=>null, realm=>'"foo\bar"'] * ["" => null, "rel" => "pre,vious", "title*" => "UTF-8'de'letztes%20Kapitel" ], ["" => null, "rel" => "next", "title*" => "UTF-8'de'n%c3%a4chstes%20Kapitel" ] * - * @param string[]|string $headerValues + * @param list|string $headerValues * * @throws \Exception * @@ -174,7 +174,7 @@ public static function joinHttpHeaderWords(array $headerValues): string { } /** @var array> $headerValues */ $spaces = '\\s'; - $ctls = '\\x00-\\x1F\\x7F'; //@see http://stackoverflow.com/a/1497928/413531 + $ctls = '\\x00-\\x1F\\x7F'; // @see http://stackoverflow.com/a/1497928/413531 $tspecials = '()<>@,;:<>/[\\]?.="\\\\'; $tokenPattern = "#^[^{$spaces}{$ctls}{$tspecials}]+$#"; $result = []; diff --git a/tests/ContentTypeExtractorTest.php b/tests/ContentTypeExtractorTest.php index 0de2e9e..c2bee1d 100644 --- a/tests/ContentTypeExtractorTest.php +++ b/tests/ContentTypeExtractorTest.php @@ -27,8 +27,7 @@ public function contentTypes(): iterable { yield 'HTML5, double quotes' => [ << -HTML - , + HTML, 'iso-8859-1', [ '' => '', @@ -38,8 +37,7 @@ public function contentTypes(): iterable { yield 'HTML5, single quotes' => [ << -HTML - , + HTML, 'iso-8859-1', [ "" => "", @@ -49,8 +47,7 @@ public function contentTypes(): iterable { yield 'HTML5, unquoted' => [ << -HTML - , + HTML, 'iso-8859-1', [ '' => '', @@ -60,8 +57,7 @@ public function contentTypes(): iterable { yield 'HTML5, unquoted, spaces around' => [ << -HTML - , + HTML, 'iso-8859-1', [ '' => '', @@ -71,8 +67,7 @@ public function contentTypes(): iterable { yield 'HTML5, unquoted, extra attributes' => [ << -HTML - , + HTML, 'iso-8859-1', [ '' => '', @@ -82,8 +77,7 @@ public function contentTypes(): iterable { yield 'HTML5, random case' => [ << -HTML - , + HTML, 'ISo-8859-1', [ '' => '', @@ -93,8 +87,7 @@ public function contentTypes(): iterable { yield '(X)HTML5, unquoted' => [ << -HTML - , + HTML, 'iso-8859-1', [ '' => '', @@ -104,8 +97,7 @@ public function contentTypes(): iterable { yield '(X)HTML5, tight' => [ << -HTML - , + HTML, 'iso-8859-1', [ '' => '', @@ -117,8 +109,7 @@ public function contentTypes(): iterable { yield '(X)HTML5, unquoted, misplaced solidus' => [ << -HTML - , + HTML, 'iso-8859-1/', [ '' => '', @@ -127,9 +118,8 @@ public function contentTypes(): iterable { yield 'HTML4, double quotes' => [ << -HTML - , + + HTML, 'ISO-8859-1', [ '' => '', @@ -138,9 +128,8 @@ public function contentTypes(): iterable { yield 'HTML4, double quotes, other way around' => [ << -HTML - , + + HTML, 'ISO-8859-1', [ '' => '', @@ -149,9 +138,8 @@ public function contentTypes(): iterable { yield 'HTML4, double quotes, extra attributes, other way around' => [ << -HTML - , + + HTML, 'ISO-8859-1', [ '' => '', @@ -160,9 +148,8 @@ public function contentTypes(): iterable { yield 'HTML4, single quotes' => [ << -HTML - , + + HTML, 'ISO-8859-1', [ "" => "", @@ -171,9 +158,8 @@ public function contentTypes(): iterable { yield 'HTML4, unquoted+single quotes' => [ << -HTML - , + + HTML, 'ISO-8859-1', [ "" => "", @@ -183,9 +169,8 @@ public function contentTypes(): iterable { // https://httpwg.org/specs/rfc9110.html#field.content-type yield 'HTML4, internally quoted, extra parameters' => [ << -HTML - , + + HTML, 'ISO-8859-1', [ "" => "", @@ -194,9 +179,8 @@ public function contentTypes(): iterable { yield 'HTML4, single quotes+double quotes+spaces around' => [ << -HTML - , + + HTML, 'ISO-8859-1', [ '' => '', @@ -205,9 +189,8 @@ public function contentTypes(): iterable { yield 'HTML4, random case' => [ << -HTML - , + + HTML, 'ISO-8859-1', [ '' => '', @@ -216,9 +199,8 @@ public function contentTypes(): iterable { yield '(X)HTML4' => [ << -HTML - , + + HTML, 'ISO-8859-1', [ '' => '', @@ -227,11 +209,10 @@ public function contentTypes(): iterable { yield 'multiple declarations' => [ << - - -HTML - , + + + + HTML, 'ISO-8859-1', [ '' => '', diff --git a/tests/GuzzleTranscoderTest.php b/tests/GuzzleTranscoderTest.php index cceb8c7..fac9e20 100644 --- a/tests/GuzzleTranscoderTest.php +++ b/tests/GuzzleTranscoderTest.php @@ -214,7 +214,7 @@ public function testConvertResponse(): void { * Gets the headers from a HTTP response as one dimensional associative array * with header names as keys. The header values will not be parsed but saved as-is! * - * @return array{headers: array, body: string} + * @return array{headers: array>, body: string} */ private function splitHeadersAndContentFromHttpResponseString(string $responseString): array { $lines = explode("\n", $responseString); diff --git a/tests/ReadmeTest.php b/tests/ReadmeTest.php index 1ebcd7c..2bfb0e6 100644 --- a/tests/ReadmeTest.php +++ b/tests/ReadmeTest.php @@ -36,8 +36,8 @@ public function readmeExamples(): iterable { foreach ($matches as $match) { $mockedSourceBody = file_get_contents(__DIR__ . '/resources/' . $match['response']); \assert($mockedSourceBody !== false); // For PHPStan. + /** @var array $mockedHeaders */ $mockedHeaders = json_decode($match['headers'], true); - \assert(\is_array($mockedHeaders)); // For PHPStan. $expectedBody = file_get_contents(__DIR__ . '/resources/' . $match['expected']); \assert($expectedBody !== false); // For PHPStan.