From 143e3ca55400b64af30076a847aee1ecd70fe4d1 Mon Sep 17 00:00:00 2001 From: Matheus Pereira Date: Mon, 8 Apr 2024 23:40:19 -0300 Subject: [PATCH] Feat: Adding max level on PHP Stan (#4) feat: patterns, phpstan, tests and git actions --- .editorconfig | 2 +- .gitattributes | 12 +++++++++ .github/hooks/commit-msg | 13 ++++++++++ .github/hooks/pre-push | 5 ++++ .gitignore | 2 +- README.md | 23 +++++++++-------- composer.json | 12 ++++++--- phpstan.neon | 10 ------- src/Contracts/RuleContract.php | 33 ++++++++++++++++++++++++ src/Providers/ValidationProvider.php | 19 +++++++++----- src/Rules/CepRule.php | 24 +++++++---------- src/Rules/DocumentNumberRule.php | 24 +++++++---------- src/Rules/NotHtmlRule.php | 22 +++++++--------- src/Services/DocumentCnpjService.php | 4 +-- src/Services/DocumentCpfService.php | 4 +-- tests/Feature/CepRuleTest.php | 2 +- tests/Helpers/FakeCepHelper.php | 2 +- tests/Helpers/MaskHelper.php | 16 ++++-------- tests/TestCase.php | 14 +++++----- tests/Unit/CepRuleTest.php | 4 ++- tests/Unit/DocumentNumberRuleTest.php | 5 +++- tests/Unit/NotHtmlRuleTest.php | 17 +++++++++--- tests/app/Controllers/TestController.php | 11 +++++--- 23 files changed, 175 insertions(+), 105 deletions(-) create mode 100644 .gitattributes create mode 100755 .github/hooks/commit-msg create mode 100755 .github/hooks/pre-push delete mode 100644 phpstan.neon create mode 100644 src/Contracts/RuleContract.php diff --git a/.editorconfig b/.editorconfig index edacd7e..6e159e5 100644 --- a/.editorconfig +++ b/.editorconfig @@ -12,5 +12,5 @@ trim_trailing_whitespace = true indent_size = unset trim_trailing_whitespace = false -[*.{yml,yaml,json,xml}] +[*.{yml,yaml,xml}] indent_size = 2 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..89b2c58 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,12 @@ +# Ignore all test and documentation with "export-ignore". +/.editorconfig export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore +/Makefile export-ignore +/mkdocs.yml export-ignore +/phpcs.xml export-ignore +/phpunit.xml export-ignore + +/.github export-ignore +/docs export-ignore +/tests export-ignore diff --git a/.github/hooks/commit-msg b/.github/hooks/commit-msg new file mode 100755 index 0000000..b677de0 --- /dev/null +++ b/.github/hooks/commit-msg @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +message="$(cat $1)" +requiredPattern="^(feat|fix|refactor|docs|test|chore|style|build|revert): .*$" + +if ! [[ $message =~ $requiredPattern ]]; +then + echo "Invalid commit message, follow the pattern below" + echo ": " + echo "Example - feat: adding new loading button" + echo "Allowed commit types: feat, fix, refactor, docs, test, chore, style, build, revert" + exit 1 +fi diff --git a/.github/hooks/pre-push b/.github/hooks/pre-push new file mode 100755 index 0000000..da3bb8b --- /dev/null +++ b/.github/hooks/pre-push @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +composer phpcs +composer phpstan +composer tests diff --git a/.gitignore b/.gitignore index 287bd71..a2a6b0c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,4 @@ composer.phar /coverage/ /logs/ /site/ -.DS_Store \ No newline at end of file +.DS_Store diff --git a/README.md b/README.md index 2e46414..8fe627b 100644 --- a/README.md +++ b/README.md @@ -2,17 +2,18 @@ Composer package for request brazilian field validation for Laravel -

- - PRs Welcome - - - License MIT - - - License MIT - -

+ + PRs Welcome + + + Packagist + + + Github Actions + + + License MIT + # Dependences diff --git a/composer.json b/composer.json index df1911c..ebe9e8e 100644 --- a/composer.json +++ b/composer.json @@ -47,11 +47,17 @@ "@phpstan", "@tests" ], - "tests": "@php vendor/bin/phpunit -d memory_limit=256M --do-not-cache-result --configuration phpunit.xml", "phpcbf": "@php vendor/bin/phpcbf", "phpcs": "@php vendor/bin/phpcs", - "phpstan": "@php vendor/bin/phpstan analyse --memory-limit 256M", - "post-autoload-dump": "@php vendor/bin/testbench package:discover --ansi" + "phpstan": "@php vendor/bin/phpstan analyse --level 9 ./src ./tests", + "tests": "@php vendor/bin/phpunit -d memory_limit=256M --do-not-cache-result --configuration phpunit.xml", + "post-autoload-dump": [ + "@php vendor/bin/testbench package:discover --ansi" + ], + "post-install-cmd": [ + "git config --local core.hooksPath .github/hooks", + "chmod +x .github/hooks/*" + ] }, "extra": { "laravel": { diff --git a/phpstan.neon b/phpstan.neon deleted file mode 100644 index 79b1fcd..0000000 --- a/phpstan.neon +++ /dev/null @@ -1,10 +0,0 @@ -parameters: - - paths: - - ./src/ - - excludePaths: - - ./vendor/ - - ./tests/Unit/ - - level: 4 \ No newline at end of file diff --git a/src/Contracts/RuleContract.php b/src/Contracts/RuleContract.php new file mode 100644 index 0000000..e104bb5 --- /dev/null +++ b/src/Contracts/RuleContract.php @@ -0,0 +1,33 @@ + $params + * @return self + */ + public function params(array $params): self; + + /** + * Determine if the validation rule passes. + * + * @param string $attribute + * @param mixed $value + * @return bool + */ + public function passes(string $attribute, mixed $value): bool; + + /** + * Get the validation error message. + * + * @return string|array + */ + public function message(): string|array; +} diff --git a/src/Providers/ValidationProvider.php b/src/Providers/ValidationProvider.php index 140ac69..c75517e 100644 --- a/src/Providers/ValidationProvider.php +++ b/src/Providers/ValidationProvider.php @@ -6,6 +6,11 @@ class ValidationProvider extends ServiceProvider { + /** + * @var \Illuminate\Contracts\Foundation\Application + */ + protected $app; + /** * Indicates if loading of the provider is deferred. * @@ -20,15 +25,17 @@ class ValidationProvider extends ServiceProvider */ public function boot(): void { + /** + * @var array<\Matmper\Contracts\RuleContract> $services + */ $services = [ - 'cep' => \Matmper\Rules\CepRule::class, - 'document' => \Matmper\Rules\DocumentNumberRule::class, - 'not_html' => \Matmper\Rules\NotHtmlRule::class, + 'cep' => $this->app->make(\Matmper\Rules\CepRule::class), + 'document' => $this->app->make(\Matmper\Rules\DocumentNumberRule::class), + 'not_html' => $this->app->make(\Matmper\Rules\NotHtmlRule::class), ]; - foreach ($services as $name => $service) { - $class = app($service); - + foreach ($services as $name => $class) { + // @phpstan-ignore-next-line $this->app['validator']->extend($name, function ($attribute, $value, $params) use ($class) { return $class->params($params)->passes($attribute, $value); }, $class->message()); diff --git a/src/Rules/CepRule.php b/src/Rules/CepRule.php index a868656..3ebf6c0 100644 --- a/src/Rules/CepRule.php +++ b/src/Rules/CepRule.php @@ -2,9 +2,9 @@ namespace Matmper\Rules; -use Illuminate\Contracts\Validation\Rule; +use Matmper\Contracts\RuleContract; -class CepRule implements Rule +class CepRule implements RuleContract { /** * Set true if check mask and value @@ -14,9 +14,7 @@ class CepRule implements Rule private $checkMask; /** - * Set rule params - * - * @return self + * @inheritDoc */ public function params(array $params): self { @@ -28,25 +26,23 @@ public function params(array $params): self /** * Determine if the validation rule passes. * - * @param string $attribute - * @param mixed $value - * @return bool + * @param string $attribute + * @param string $value + * @return bool */ - public function passes($attribute, $value): bool + public function passes(string $attribute, mixed $value): bool { if ($this->checkMask && !preg_match('/\d{5}\-\d{3}/', $value)) { return false; } - return strlen(preg_replace("/[^\d]/", "", $value)) === 8; + return strlen((string) preg_replace("/[^\d]/", "", $value)) === 8; } /** - * Get the validation error message. - * - * @return string + * @inheritDoc */ - public function message() + public function message(): string { return ':attribute não é um CEP válido'; } diff --git a/src/Rules/DocumentNumberRule.php b/src/Rules/DocumentNumberRule.php index 2cf7ff5..0d9c09a 100644 --- a/src/Rules/DocumentNumberRule.php +++ b/src/Rules/DocumentNumberRule.php @@ -2,10 +2,10 @@ namespace Matmper\Rules; -use Illuminate\Contracts\Validation\Rule; +use Matmper\Contracts\RuleContract; use Matmper\Enums\DocumentType; -class DocumentNumberRule implements Rule +class DocumentNumberRule implements RuleContract { /** * Set true if check mask and value @@ -17,14 +17,12 @@ class DocumentNumberRule implements Rule /** * List to documents to validate * - * @var array + * @var array */ private $documents; /** - * Set rule params - * - * @return self + * @inheritDoc */ public function params(array $params): self { @@ -37,11 +35,11 @@ public function params(array $params): self /** * Determine if the validation rule passes. * - * @param string $attribute - * @param mixed $value - * @return bool + * @param string $attribute + * @param string $value + * @return bool */ - public function passes($attribute, $value): bool + public function passes(string $attribute, mixed $value): bool { $cpfRule = new \Matmper\Services\DocumentCpfService; @@ -59,11 +57,9 @@ public function passes($attribute, $value): bool } /** - * Get the validation error message. - * - * @return string + * @inheritDoc */ - public function message() + public function message(): string { return ':attribute não é um documento válido'; } diff --git a/src/Rules/NotHtmlRule.php b/src/Rules/NotHtmlRule.php index 86e277f..b46f295 100644 --- a/src/Rules/NotHtmlRule.php +++ b/src/Rules/NotHtmlRule.php @@ -2,14 +2,12 @@ namespace Matmper\Rules; -use Illuminate\Contracts\Validation\Rule; +use Matmper\Contracts\RuleContract; -class NotHtmlRule implements Rule +class NotHtmlRule implements RuleContract { /** - * Set rule params - * - * @return self + * @inheritDoc */ public function params(array $params): self { @@ -19,11 +17,11 @@ public function params(array $params): self /** * Determine if the validation rule passes. * - * @param string $attribute - * @param mixed $value - * @return bool + * @param string $attribute + * @param string $value + * @return bool */ - public function passes($attribute, $value): bool + public function passes(string $attribute, mixed $value): bool { if (!is_string($value)) { throw new \Matmper\Exceptions\ValueIsNotStringException($attribute); @@ -37,11 +35,9 @@ public function passes($attribute, $value): bool } /** - * Get the validation error message. - * - * @return string + * @inheritDoc */ - public function message() + public function message(): string { return ':attribute não deve conter código HTML'; } diff --git a/src/Services/DocumentCnpjService.php b/src/Services/DocumentCnpjService.php index 9780520..7f524f4 100644 --- a/src/Services/DocumentCnpjService.php +++ b/src/Services/DocumentCnpjService.php @@ -7,10 +7,10 @@ class DocumentCnpjService /** * Determine if the validation rule passes. * - * @param mixed $value + * @param string $value * @return bool */ - public function passes(mixed $value, bool $checkMask): bool + public function passes(string $value, bool $checkMask): bool { $cnpjValue = (string) preg_replace("/[^0-9]/", "", $value); diff --git a/src/Services/DocumentCpfService.php b/src/Services/DocumentCpfService.php index 81c884b..dbf80cb 100644 --- a/src/Services/DocumentCpfService.php +++ b/src/Services/DocumentCpfService.php @@ -7,10 +7,10 @@ class DocumentCpfService /** * Determine if the validation rule passes. * - * @param mixed $value + * @param string $value * @return bool */ - public function passes(mixed $value, bool $checkMask): bool + public function passes(string $value, bool $checkMask): bool { $cpfValue = (string) preg_replace("/[^0-9]/", "", $value); diff --git a/tests/Feature/CepRuleTest.php b/tests/Feature/CepRuleTest.php index 6a966a8..31c0609 100644 --- a/tests/Feature/CepRuleTest.php +++ b/tests/Feature/CepRuleTest.php @@ -26,7 +26,7 @@ public function testCepRequest(string $cep, int $assertStatus): void /** * Data Provider: testCepRequest * - * @return array + * @return array> */ public function testCepDataProvider(): array { diff --git a/tests/Helpers/FakeCepHelper.php b/tests/Helpers/FakeCepHelper.php index 849a759..28d5355 100644 --- a/tests/Helpers/FakeCepHelper.php +++ b/tests/Helpers/FakeCepHelper.php @@ -12,7 +12,7 @@ class FakeCepHelper */ public static function cep(bool $mask = false): string { - $cep = random_int(10000000, 99999999); + $cep = (string) random_int(10000000, 99999999); return $mask ? MaskHelper::create($cep, '#####-###') : $cep; } } diff --git a/tests/Helpers/MaskHelper.php b/tests/Helpers/MaskHelper.php index d944b3b..9caf7bf 100644 --- a/tests/Helpers/MaskHelper.php +++ b/tests/Helpers/MaskHelper.php @@ -7,19 +7,13 @@ class MaskHelper /** * Create a string mask * - * @param string $value 123 - * @param string $mask #.## - * @return string 1.23 + * @param string $value + * @param string $mask + * @return string */ public static function create(string $value, string $mask): string { - $char = str_replace(' ', '', $value); - $strlen = strlen($char); - - for ($i = 0; $i < $strlen; $i++) { - $mask[strpos($mask, '#')] = $char[$i]; - } - - return $mask; + $mask = str_replace('#', '%s', $mask); + return vsprintf($mask, str_split($value)); } } diff --git a/tests/TestCase.php b/tests/TestCase.php index 2382765..3809492 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,14 +2,15 @@ namespace Tests; -use Illuminate\Foundation\Application; - class TestCase extends \Orchestra\Testbench\TestCase { - protected $app = null; + /** + * @var \Illuminate\Foundation\Application; + */ + protected $app; /** - * @param Application $app + * @param \Illuminate\Foundation\Application $app * @return void */ protected function getEnvironmentSetUp($app): void @@ -18,6 +19,7 @@ protected function getEnvironmentSetUp($app): void $this->app->setBasePath(__DIR__ . '/..'); + /** @var \Illuminate\Support\Facades\Route $router */ $router = $this->app['router']; $router->post('test', "\App\Controllers\TestController@index"); } @@ -25,8 +27,8 @@ protected function getEnvironmentSetUp($app): void /** * Load custom package providers * - * @param Application $app - * @return array + * @param \Illuminate\Foundation\Application $app + * @return array */ protected function getPackageProviders($app): array { diff --git a/tests/Unit/CepRuleTest.php b/tests/Unit/CepRuleTest.php index c72d510..5b487c9 100644 --- a/tests/Unit/CepRuleTest.php +++ b/tests/Unit/CepRuleTest.php @@ -14,6 +14,7 @@ public function test_invalid_validation_parameter_exception(): void { $this->expectException(\Matmper\Exceptions\InvalidValidationParameterException::class); + /** @var \Matmper\Rules\CepRule $rule */ $rule = app(\Matmper\Rules\CepRule::class); $rule->params(['wrong_value']); } @@ -24,6 +25,7 @@ public function test_invalid_validation_parameter_exception(): void */ public function test_document_number(string $type, string $cep, bool $assert): void { + /** @var \Matmper\Rules\CepRule $rule */ $rule = app(\Matmper\Rules\CepRule::class); $rule->params([$type]); @@ -35,7 +37,7 @@ public function test_document_number(string $type, string $cep, bool $assert): v /** * Data Provider: test_document_number * - * @return array + * @return array> */ public function testCepDataProvider(): array { diff --git a/tests/Unit/DocumentNumberRuleTest.php b/tests/Unit/DocumentNumberRuleTest.php index 69acbec..ae5a81c 100644 --- a/tests/Unit/DocumentNumberRuleTest.php +++ b/tests/Unit/DocumentNumberRuleTest.php @@ -14,6 +14,7 @@ public function test_invalid_document_type_exception(): void { $this->expectException(\Matmper\Exceptions\InvalidDocumentTypeException::class); + /** @var \Matmper\Rules\DocumentNumberRule $rule */ $rule = app(\Matmper\Rules\DocumentNumberRule::class); $rule->params(['wrong_document', 'value']); } @@ -25,6 +26,7 @@ public function test_invalid_validation_parameter_exception(): void { $this->expectException(\Matmper\Exceptions\InvalidValidationParameterException::class); + /** @var \Matmper\Rules\DocumentNumberRule $rule */ $rule = app(\Matmper\Rules\DocumentNumberRule::class); $rule->params(['cpf', 'wrong_value']); } @@ -35,6 +37,7 @@ public function test_invalid_validation_parameter_exception(): void */ public function test_document_number(string $type, string $mask, string $document, bool $assert): void { + /** @var \Matmper\Rules\DocumentNumberRule $rule */ $rule = app(\Matmper\Rules\DocumentNumberRule::class); $rule->params([$type, $mask]); @@ -46,7 +49,7 @@ public function test_document_number(string $type, string $mask, string $documen /** * Data Provider: test_document_number * - * @return array + * @return array> */ public function testDocumentNumberDataProvider(): array { diff --git a/tests/Unit/NotHtmlRuleTest.php b/tests/Unit/NotHtmlRuleTest.php index baaed8e..d046efb 100644 --- a/tests/Unit/NotHtmlRuleTest.php +++ b/tests/Unit/NotHtmlRuleTest.php @@ -7,11 +7,15 @@ class NotHtmlRuleTest extends TestCase { /** + * @param string $value + * @param bool $assert + * * @test * @dataProvider testNotHtmlDataProvider */ public function test_not_html(mixed $value, bool $assert): void { + /** @var \Matmper\Rules\NotHtmlRule $rule */ $rule = app(\Matmper\Rules\NotHtmlRule::class); $passes = $rule->passes(fake()->word(), $value); @@ -21,7 +25,7 @@ public function test_not_html(mixed $value, bool $assert): void /** * Data Provider: test_not_html * - * @return array + * @return array> */ public function testNotHtmlDataProvider(): array { @@ -32,11 +36,13 @@ public function testNotHtmlDataProvider(): array 'false_string_03' => ["", false], 'false_string_04' => ["

", false], 'false_string_05' => ["$value
", false], - 'false_string_05' => ["
$value", false], + 'false_string_06' => ["
$value", false], ]; } /** + * @param int|int[] $value + * * @test * @dataProvider testNotHtmlExceptionDataProvider */ @@ -44,21 +50,24 @@ public function test_not_html_string_exception(mixed $value): void { $this->expectException(\Matmper\Exceptions\ValueIsNotStringException::class); + /** @var \Matmper\Rules\NotHtmlRule $rule */ $rule = app(\Matmper\Rules\NotHtmlRule::class); + + // @phpstan-ignore-next-line argument.type $rule->passes(fake()->word(), $value); } /** * Data Provider: test_not_html_string_exception * - * @return array + * @return array> */ public function testNotHtmlExceptionDataProvider(): array { return [ 'false_int' => [fake()->randomDigitNotZero()], 'false_float' => [fake()->randomFloat(2)], - 'false_array' => [[1, 2, 3]], + 'false_array' => [[1,2,3]], ]; } } diff --git a/tests/app/Controllers/TestController.php b/tests/app/Controllers/TestController.php index 59c3304..cb6d79e 100644 --- a/tests/app/Controllers/TestController.php +++ b/tests/app/Controllers/TestController.php @@ -12,17 +12,22 @@ class TestController * * @param Request $request * @return \Illuminate\Http\JsonResponse + * @throws \Throwable */ public function index(Request $request): \Illuminate\Http\JsonResponse { try { - $validate = Validator::make($request->except('rules'), $request->rules); + $rules = (array) $request->get('rules'); + $validate = Validator::make($request->except('rules'), $rules); + + /** @var \Illuminate\Contracts\Routing\ResponseFactory $response */ + $response = response(); if ($validate->fails()) { - return response()->json(['errors' => $validate->errors()], 400); + return $response->json(['errors' => $validate->errors()], 400); } - return response()->json(['success' => true], 200); + return $response->json(['success' => true], 200); } catch (\Throwable $th) { throw $th; }