diff --git a/.github/workflows/rector-cs.yml b/.github/workflows/rector-cs.yml new file mode 100644 index 0000000..161baf1 --- /dev/null +++ b/.github/workflows/rector-cs.yml @@ -0,0 +1,26 @@ +name: Rector + PHP CS Fixer + +on: + pull_request: + paths: + - '.github/workflows/rector-cs.yml' + - 'src/**' + - 'tests/**' + - 'rector.php' + - '.php-cs-fixer.dist.php' + - 'composer.json' + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + rector-cs: + permissions: + contents: write # Required to commit formatting fixes back to the PR + uses: yiisoft/actions/.github/workflows/rector-cs.yml@master + with: + php: '8.0' diff --git a/.github/workflows/rector.yml b/.github/workflows/rector.yml deleted file mode 100644 index 7314d24..0000000 --- a/.github/workflows/rector.yml +++ /dev/null @@ -1,24 +0,0 @@ -on: - pull_request: - paths-ignore: - - 'docs/**' - - 'README.md' - - 'CHANGELOG.md' - - '.gitignore' - - '.gitattributes' - - 'infection.json.dist' - - 'psalm.xml' - -name: rector - -permissions: - contents: read - -jobs: - rector: - uses: yiisoft/actions/.github/workflows/rector.yml@master - with: - os: >- - ['ubuntu-latest'] - php: >- - ['8.4'] diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..0e03959 --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,22 @@ +in([ + __DIR__ . '/src', + __DIR__ . '/tests', +]); + +return ConfigBuilder::build() + ->setCacheFile(__DIR__ . '/runtime/.php-cs-fixer.cache') + ->setRiskyAllowed(true) + ->setParallelConfig(ParallelConfigFactory::detect()) + ->setRules([ + '@Yiisoft/Core' => true, + '@Yiisoft/Core:risky' => true, + ]) + ->setFinder($finder); diff --git a/.styleci.yml b/.styleci.yml deleted file mode 100644 index 1ab379b..0000000 --- a/.styleci.yml +++ /dev/null @@ -1,85 +0,0 @@ -preset: psr12 -risky: true - -version: 8.1 - -finder: - exclude: - - docs - - vendor - -enabled: - - alpha_ordered_traits - - array_indentation - - array_push - - combine_consecutive_issets - - combine_consecutive_unsets - - combine_nested_dirname - - declare_strict_types - - dir_constant - - fully_qualified_strict_types - - function_to_constant - - hash_to_slash_comment - - is_null - - logical_operators - - magic_constant_casing - - magic_method_casing - - method_separation - - modernize_types_casting - - native_function_casing - - native_function_type_declaration_casing - - no_alias_functions - - no_empty_comment - - no_empty_phpdoc - - no_empty_statement - - no_extra_block_blank_lines - - no_short_bool_cast - - no_superfluous_elseif - - no_unneeded_control_parentheses - - no_unneeded_curly_braces - - no_unneeded_final_method - - no_unset_cast - - no_unused_imports - - no_unused_lambda_imports - - no_useless_else - - no_useless_return - - normalize_index_brace - - php_unit_dedicate_assert - - php_unit_dedicate_assert_internal_type - - php_unit_expectation - - php_unit_mock - - php_unit_mock_short_will_return - - php_unit_namespaced - - php_unit_no_expectation_annotation - - phpdoc_no_empty_return - - phpdoc_no_useless_inheritdoc - - phpdoc_order - - phpdoc_property - - phpdoc_scalar - - phpdoc_singular_inheritdoc - - phpdoc_trim - - phpdoc_trim_consecutive_blank_line_separation - - phpdoc_type_to_var - - phpdoc_types - - phpdoc_types_order - - print_to_echo - - regular_callable_call - - return_assignment - - self_accessor - - self_static_accessor - - set_type_to_cast - - short_array_syntax - - short_list_syntax - - simplified_if_return - - single_quote - - standardize_not_equals - - ternary_to_null_coalescing - - trailing_comma_in_multiline_array - - unalign_double_arrow - - unalign_equals - - empty_loop_body_braces - - integer_literal_case - - union_type_without_spaces - -disabled: - - function_declaration diff --git a/CHANGELOG.md b/CHANGELOG.md index 46e2aa4..5776fd9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## 1.0.3 under development - Chg #44: Change PHP constraint in `composer.json` to `8.0 - 8.4` (@vjik) +- Enh #55: Explicitly import constants in "use" section (@vjik) ## 1.0.2 August 06, 2024 diff --git a/composer.json b/composer.json index 288311a..d0efb3c 100644 --- a/composer.json +++ b/composer.json @@ -38,13 +38,15 @@ "yiisoft/network-utilities": "^1.1" }, "require-dev": { + "bamarni/composer-bin-plugin": "^1.9.1", + "friendsofphp/php-cs-fixer": "^3.95", "httpsoft/http-message": "^1.1.6", - "maglnet/composer-require-checker": "^4.4", "phpunit/phpunit": "^9.6.22", - "rector/rector": "^2.0.10", + "rector/rector": "^2.5.8", "roave/infection-static-analysis-plugin": "^1.25", "spatie/phpunit-watcher": "^1.23.6", "vimeo/psalm": "^4.30 || ^5.26.1 || ^6.9.1", + "yiisoft/code-style": "^1.1", "yiisoft/test-support": "^3.0.2" }, "autoload": { @@ -61,10 +63,20 @@ "sort-packages": true, "allow-plugins": { "infection/extension-installer": true, - "composer/package-versions-deprecated": true + "composer/package-versions-deprecated": true, + "bamarni/composer-bin-plugin": true + } + }, + "extra": { + "bamarni-bin": { + "bin-links": true, + "target-directory": "tools", + "forward-command": true } }, "scripts": { + "cs-fix": "php-cs-fixer fix", + "rector": "rector", "test": "phpunit --testdox --no-interaction", "test-watch": "phpunit-watcher watch" } diff --git a/rector.php b/rector.php index c2226bf..49cebf2 100644 --- a/rector.php +++ b/rector.php @@ -2,9 +2,9 @@ declare(strict_types=1); -use Rector\CodeQuality\Rector\Class_\InlineConstructorDefaultToPropertyRector; use Rector\Config\RectorConfig; use Rector\Php74\Rector\Closure\ClosureToArrowFunctionRector; +use Yiisoft\CodeStyle\Rector\SetList; return RectorConfig::configure() ->withPaths([ @@ -12,8 +12,8 @@ __DIR__ . '/tests', ]) ->withPhpSets(php80: true) - ->withRules([ - InlineConstructorDefaultToPropertyRector::class, + ->withSets([ + SetList::YII_CORE, ]) ->withSkip([ ClosureToArrowFunctionRector::class, diff --git a/runtime/.gitignore b/runtime/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/runtime/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/src/Exception/InvalidConnectionChainItemException.php b/src/Exception/InvalidConnectionChainItemException.php index 2ff7b73..3400f70 100644 --- a/src/Exception/InvalidConnectionChainItemException.php +++ b/src/Exception/InvalidConnectionChainItemException.php @@ -11,6 +11,4 @@ * Used by {@see TrustedHostsNetworkResolver}. Thrown when resolved data of connection chain item - either IP, protocol, * host or port is invalid. */ -final class InvalidConnectionChainItemException extends Exception -{ -} +final class InvalidConnectionChainItemException extends Exception {} diff --git a/src/Exception/RfcProxyParseException.php b/src/Exception/RfcProxyParseException.php index 2cdd591..83a1372 100644 --- a/src/Exception/RfcProxyParseException.php +++ b/src/Exception/RfcProxyParseException.php @@ -12,6 +12,4 @@ * * @link https://datatracker.ietf.org/doc/html/rfc7239 */ -final class RfcProxyParseException extends Exception -{ -} +final class RfcProxyParseException extends Exception {} diff --git a/src/TrustedHostsNetworkResolver.php b/src/TrustedHostsNetworkResolver.php index 19877ca..2609304 100644 --- a/src/TrustedHostsNetworkResolver.php +++ b/src/TrustedHostsNetworkResolver.php @@ -22,6 +22,9 @@ use function is_callable; use function is_string; +use const FILTER_FLAG_HOSTNAME; +use const FILTER_VALIDATE_DOMAIN; + /** * Scans the entire connection chain and resolves the data from forwarded headers taking into account trusted IPs. * Additionally, all items' structure is thoroughly validated because headers' data can't be trusted. The following data @@ -146,9 +149,7 @@ class TrustedHostsNetworkResolver implements MiddlewareInterface */ private ?string $connectionChainItemsAttribute = null; - public function __construct() - { - } + public function __construct() {} /** * Returns a new instance with changed list of connection chain trusted IPs @@ -229,8 +230,8 @@ public function withForwardedHeaderGroups(array $headerGroups): self $this->assertIsAllowedProtocol($value, 'Value in mapping for protocol header'); } } elseif (!is_callable($headerGroup['protocol'][1])) { - $message = 'Protocol header resolving must be specified either via an associative array or a ' . - 'callable.'; + $message = 'Protocol header resolving must be specified either via an associative array or a ' + . 'callable.'; throw new InvalidArgumentException($message); } @@ -244,8 +245,8 @@ public function withForwardedHeaderGroups(array $headerGroups): self $validatedHeaderGroups[] = $validatedHeaderGroup; } else { - $message = 'Forwarded header group must be either an associative array or ' . - 'TrustedHostsNetworkResolver::FORWARDED_HEADER_GROUP_RFC constant.'; + $message = 'Forwarded header group must be either an associative array or ' + . 'TrustedHostsNetworkResolver::FORWARDED_HEADER_GROUP_RFC constant.'; throw new InvalidArgumentException($message); } @@ -641,8 +642,8 @@ private function parseProxiesFromRfcHeader(array $proxyItems): array foreach ($directiveMap as $name => $value) { if (!in_array($name, self::ALLOWED_RFC_HEADER_DIRECTIVES)) { $allowedDirectivesStr = implode('", "', self::ALLOWED_RFC_HEADER_DIRECTIVES); - $message = "\"$name\" is not a valid directive. Allowed values are: \"$allowedDirectivesStr\" " . - '(case-insensitive).'; + $message = "\"$name\" is not a valid directive. Allowed values are: \"$allowedDirectivesStr\" " + . '(case-insensitive).'; throw new RfcProxyParseException($message); } @@ -731,8 +732,8 @@ private function getConnectionChainItem( if ($protocol !== null && $validateProtocol && !$this->isProtocol($protocol)) { $allowedProtocolsStr = implode('", "', self::ALLOWED_PROTOCOLS); - $message = "\"$protocol\" protocol is not allowed. Allowed values are: \"$allowedProtocolsStr\" " . - '(case-sensitive).'; + $message = "\"$protocol\" protocol is not allowed. Allowed values are: \"$allowedProtocolsStr\" " + . '(case-sensitive).'; throw new InvalidConnectionChainItemException($message); } diff --git a/tests/Support/MockRequestHandler.php b/tests/Support/MockRequestHandler.php index 61dfb4e..f63a5ba 100644 --- a/tests/Support/MockRequestHandler.php +++ b/tests/Support/MockRequestHandler.php @@ -16,9 +16,7 @@ final class MockRequestHandler implements RequestHandlerInterface public ?ServerRequestInterface $processedRequest = null; private ?Throwable $handleException = null; - public function __construct(private int $responseStatusCode = Status::OK) - { - } + public function __construct(private int $responseStatusCode = Status::OK) {} public function handle(ServerRequestInterface $request): ResponseInterface { diff --git a/tests/TrustedHeaderProtocolResolverTest.php b/tests/TrustedHeaderProtocolResolverTest.php index 24607eb..d650bf5 100644 --- a/tests/TrustedHeaderProtocolResolverTest.php +++ b/tests/TrustedHeaderProtocolResolverTest.php @@ -122,7 +122,7 @@ public function schemeDataProvider(): array ], [ 'x-forwarded-proto-1' => ['http' => 'http'], - 'x-forwarded-proto-2' => static fn () => null, + 'x-forwarded-proto-2' => static fn() => null, 'x-forwarded-proto-3' => ['https' => 'https'], 'x-forwarded-proto-4' => ['http' => 'http'], ], @@ -200,7 +200,7 @@ public function schemeCallableFailureDataProvider(): array 'empty-array' => [[]], 'empty-string' => [['']], 'object' => [[new stdClass()]], - 'callable' => [[static fn () => 'https']], + 'callable' => [[static fn() => 'https']], ]; } @@ -214,7 +214,7 @@ public function testCallableSchemeFailure(array $scheme): void $request->method('getHeader')->willReturn($scheme); $middleware = (new TrustedHeaderProtocolResolver()) - ->withAddedProtocolHeader('x-forwarded-proto', static fn () => $scheme) + ->withAddedProtocolHeader('x-forwarded-proto', static fn() => $scheme) ; $this->expectException(RuntimeException::class); diff --git a/tests/TrustedHostsNetworkResolver/ConfigurationExceptionTest.php b/tests/TrustedHostsNetworkResolver/ConfigurationExceptionTest.php index 07eccc5..c9d8392 100644 --- a/tests/TrustedHostsNetworkResolver/ConfigurationExceptionTest.php +++ b/tests/TrustedHostsNetworkResolver/ConfigurationExceptionTest.php @@ -52,8 +52,8 @@ public function dataWithForwardedHeaderGroupsException(): array 1, TrustedHostsNetworkResolver::FORWARDED_HEADER_GROUP_X_PREFIX, ], - 'Forwarded header group must be either an associative array or ' . - 'TrustedHostsNetworkResolver::FORWARDED_HEADER_GROUP_RFC constant.', + 'Forwarded header group must be either an associative array or ' + . 'TrustedHostsNetworkResolver::FORWARDED_HEADER_GROUP_RFC constant.', ], 'group: string, non-allowed' => [ [ @@ -61,8 +61,8 @@ public function dataWithForwardedHeaderGroupsException(): array 'test', TrustedHostsNetworkResolver::FORWARDED_HEADER_GROUP_X_PREFIX, ], - 'Forwarded header group must be either an associative array or ' . - 'TrustedHostsNetworkResolver::FORWARDED_HEADER_GROUP_RFC constant.', + 'Forwarded header group must be either an associative array or ' + . 'TrustedHostsNetworkResolver::FORWARDED_HEADER_GROUP_RFC constant.', ], 'group: array, empty' => [ [ @@ -83,8 +83,8 @@ public function dataWithForwardedHeaderGroupsException(): array ], TrustedHostsNetworkResolver::FORWARDED_HEADER_GROUP_X_PREFIX, ], - 'Invalid array keys for forwarded header group. The allowed and required keys are: "ip", "protocol", ' . - '"host", "port".', + 'Invalid array keys for forwarded header group. The allowed and required keys are: "ip", "protocol", ' + . '"host", "port".', ], 'group: array, incomplete keys' => [ [ @@ -96,8 +96,8 @@ public function dataWithForwardedHeaderGroupsException(): array ], TrustedHostsNetworkResolver::FORWARDED_HEADER_GROUP_X_PREFIX, ], - 'Invalid array keys for forwarded header group. The allowed and required keys are: "ip", "protocol", ' . - '"host", "port".', + 'Invalid array keys for forwarded header group. The allowed and required keys are: "ip", "protocol", ' + . '"host", "port".', ], 'group: array, value is not a string' => [ [ @@ -346,8 +346,8 @@ public function dataWithForwardedHeaderGroupsException(): array ], TrustedHostsNetworkResolver::FORWARDED_HEADER_GROUP_X_PREFIX, ], - 'Value in mapping for protocol header must be a valid protocol. Allowed values are: "http", "https" ' . - '(case-sensitive).', + 'Value in mapping for protocol header must be a valid protocol. Allowed values are: "http", "https" ' + . '(case-sensitive).', ], ]; } @@ -416,6 +416,4 @@ public function testWithConnectionChainItemsAttributeException(): void } } -function testCallableFunction(): void -{ -} +function testCallableFunction(): void {} diff --git a/tests/TrustedHostsNetworkResolver/ProcessTest.php b/tests/TrustedHostsNetworkResolver/ProcessTest.php index 22f2205..4673c51 100644 --- a/tests/TrustedHostsNetworkResolver/ProcessTest.php +++ b/tests/TrustedHostsNetworkResolver/ProcessTest.php @@ -1032,7 +1032,7 @@ public function dataProcess(): iterable 'ip' => 'y-forwarded-for', 'protocol' => [ 'front-end-https', - static fn (string $protocol): ?string => $protocol === 'On' ? 'https' : 'http', + static fn(string $protocol): ?string => $protocol === 'On' ? 'https' : 'http', ], 'host' => 'y-forwarded-host', 'port' => 'y-forwarded-port', @@ -1325,7 +1325,7 @@ public function dataProcess(): iterable ], ], yield 'RFC header, IP related data, hidden IP, obfuscated, reverse-obfuscating' => [ - (new class () extends TrustedHostsNetworkResolver { + (new class extends TrustedHostsNetworkResolver { protected function reverseObfuscateIpIdentifier( string $ipIdentifier, array $validatedConnectionChainItems, diff --git a/tests/TrustedHostsNetworkResolver/RuntimeExceptionTest.php b/tests/TrustedHostsNetworkResolver/RuntimeExceptionTest.php index 8ac15ad..a3a7138 100644 --- a/tests/TrustedHostsNetworkResolver/RuntimeExceptionTest.php +++ b/tests/TrustedHostsNetworkResolver/RuntimeExceptionTest.php @@ -442,7 +442,7 @@ public function dataGetProtocolException(): iterable 'ip' => 'y-forwarded-for', 'protocol' => [ 'front-end-https', - static fn (string $protocol): ?string => null, + static fn(string $protocol): ?string => null, ], 'host' => 'y-forwarded-host', 'port' => 'y-forwarded-port', @@ -468,7 +468,7 @@ public function dataGetProtocolException(): iterable 'ip' => 'y-forwarded-for', 'protocol' => [ 'front-end-https', - static fn (string $protocol): ?string => 'test', + static fn(string $protocol): ?string => 'test', ], 'host' => 'y-forwarded-host', 'port' => 'y-forwarded-port', @@ -483,8 +483,8 @@ public function dataGetProtocolException(): iterable ], serverParams: ['REMOTE_ADDR' => '18.18.18.18'], ), - 'Value returned from callable for protocol header must be a valid protocol. Allowed values are: ' . - '"http", "https" (case-sensitive).', + 'Value returned from callable for protocol header must be a valid protocol. Allowed values are: ' + . '"http", "https" (case-sensitive).', ], ]; } @@ -508,7 +508,7 @@ public function dataReverseObfuscateIpIdentifierException(): iterable { return [ yield 'empty array' => [ - (new class () extends TrustedHostsNetworkResolver { + (new class extends TrustedHostsNetworkResolver { protected function reverseObfuscateIpIdentifier( string $ipIdentifier, array $validatedConnectionChainItems, @@ -532,7 +532,7 @@ protected function reverseObfuscateIpIdentifier( 'Reverse-obfuscated IP data can\'t be empty.', ], yield 'wrong items count' => [ - (new class () extends TrustedHostsNetworkResolver { + (new class extends TrustedHostsNetworkResolver { protected function reverseObfuscateIpIdentifier( string $ipIdentifier, array $validatedConnectionChainItems, @@ -556,7 +556,7 @@ protected function reverseObfuscateIpIdentifier( 'Invalid array keys for reverse-obfuscated IP data. The allowed and required keys are: "0", "1".', ], yield 'IP: not a string' => [ - (new class () extends TrustedHostsNetworkResolver { + (new class extends TrustedHostsNetworkResolver { protected function reverseObfuscateIpIdentifier( string $ipIdentifier, array $validatedConnectionChainItems, @@ -580,7 +580,7 @@ protected function reverseObfuscateIpIdentifier( 'IP returned from reverse-obfuscated IP data must be non-empty string.', ], yield 'IP: empty string' => [ - (new class () extends TrustedHostsNetworkResolver { + (new class extends TrustedHostsNetworkResolver { protected function reverseObfuscateIpIdentifier( string $ipIdentifier, array $validatedConnectionChainItems, @@ -604,7 +604,7 @@ protected function reverseObfuscateIpIdentifier( 'IP returned from reverse-obfuscated IP data must be non-empty string.', ], yield 'IP: invalid' => [ - (new class () extends TrustedHostsNetworkResolver { + (new class extends TrustedHostsNetworkResolver { protected function reverseObfuscateIpIdentifier( string $ipIdentifier, array $validatedConnectionChainItems, @@ -628,7 +628,7 @@ protected function reverseObfuscateIpIdentifier( 'IP returned from reverse-obfuscated IP data is not valid.', ], yield 'port: empty string' => [ - (new class () extends TrustedHostsNetworkResolver { + (new class extends TrustedHostsNetworkResolver { protected function reverseObfuscateIpIdentifier( string $ipIdentifier, array $validatedConnectionChainItems, @@ -652,7 +652,7 @@ protected function reverseObfuscateIpIdentifier( 'Port returned from reverse-obfuscated IP data must be non-empty string.', ], yield 'IP: valid port instead of IP, port: invalid' => [ - (new class () extends TrustedHostsNetworkResolver { + (new class extends TrustedHostsNetworkResolver { protected function reverseObfuscateIpIdentifier( string $ipIdentifier, array $validatedConnectionChainItems, @@ -684,7 +684,7 @@ protected function reverseObfuscateIpIdentifier( public function testReverseObfuscateIpIdentifierException( TrustedHostsNetworkResolver $middleware, ServerRequestInterface $request, - string $expectedExceptionMessage + string $expectedExceptionMessage, ): void { $requestHandler = new MockRequestHandler(); diff --git a/tools/.gitignore b/tools/.gitignore new file mode 100644 index 0000000..cf452dc --- /dev/null +++ b/tools/.gitignore @@ -0,0 +1,2 @@ +/*/vendor +/*/composer.lock diff --git a/tools/composer-require-checker/composer.json b/tools/composer-require-checker/composer.json new file mode 100644 index 0000000..498425c --- /dev/null +++ b/tools/composer-require-checker/composer.json @@ -0,0 +1,5 @@ +{ + "require-dev": { + "maglnet/composer-require-checker": "^4.4" + } +}