From 1fe4eeff8a0227949cc469367b2e53912ece07ad Mon Sep 17 00:00:00 2001 From: Pablo Largo Mohedano Date: Sat, 19 Apr 2025 20:29:33 +0200 Subject: [PATCH 01/13] Add PHPStan --- composer.json | 6 ++++-- phpstan.dist.neon | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 phpstan.dist.neon diff --git a/composer.json b/composer.json index 16bdde90..77d20eba 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,8 @@ "squizlabs/php_codesniffer": "^3.0", "brick/varexporter": "^0.3.5", "friendsofphp/php-cs-fixer": "^3.2", - "oscarotero/php-cs-fixer-config": "^2.0" + "oscarotero/php-cs-fixer-config": "^2.0", + "phpstan/phpstan": "^2.1" }, "autoload": { "psr-4": { @@ -41,7 +42,8 @@ "scripts": { "test": [ "phpunit", - "phpcs" + "phpcs", + "phpstan" ], "cs-fix": "php-cs-fixer fix" } diff --git a/phpstan.dist.neon b/phpstan.dist.neon new file mode 100644 index 00000000..e1bb8c65 --- /dev/null +++ b/phpstan.dist.neon @@ -0,0 +1,5 @@ +parameters: + level: 0 + paths: + - src + - tests From cb287b23c97d7e7df3c1cb38a7b199aac33723c6 Mon Sep 17 00:00:00 2001 From: Pablo Largo Mohedano Date: Sun, 20 Apr 2025 00:11:50 +0200 Subject: [PATCH 02/13] Consistent constructor --- src/Comments.php | 2 ++ src/Flags.php | 2 ++ src/Headers.php | 2 ++ src/References.php | 6 ++++++ src/Translation.php | 2 ++ src/Translations.php | 2 ++ 6 files changed, 16 insertions(+) diff --git a/src/Comments.php b/src/Comments.php index cd706b93..ca44c97e 100644 --- a/src/Comments.php +++ b/src/Comments.php @@ -11,6 +11,8 @@ /** * Class to manage the comments of a translation. + * + * @phpstan-consistent-constructor */ class Comments implements JsonSerializable, Countable, IteratorAggregate { diff --git a/src/Flags.php b/src/Flags.php index 49add0be..e8826bb8 100644 --- a/src/Flags.php +++ b/src/Flags.php @@ -11,6 +11,8 @@ /** * Class to manage the flags of a translation. + * + * @phpstan-consistent-constructor */ class Flags implements JsonSerializable, Countable, IteratorAggregate { diff --git a/src/Headers.php b/src/Headers.php index 87c782e3..b6ad091f 100644 --- a/src/Headers.php +++ b/src/Headers.php @@ -12,6 +12,8 @@ /** * Class to manage the headers of translations. + * + * @phpstan-consistent-constructor */ class Headers implements JsonSerializable, Countable, IteratorAggregate { diff --git a/src/References.php b/src/References.php index b6758b17..274219fe 100644 --- a/src/References.php +++ b/src/References.php @@ -11,6 +11,8 @@ /** * Class to manage the references of a translation. + * + * @phpstan-consistent-constructor */ class References implements JsonSerializable, Countable, IteratorAggregate { @@ -24,6 +26,10 @@ public static function __set_state(array $state): References return $references; } + public function __construct() + { + } + public function __debugInfo() { return $this->toArray(); diff --git a/src/Translation.php b/src/Translation.php index f4c86adb..69cb6b90 100644 --- a/src/Translation.php +++ b/src/Translation.php @@ -5,6 +5,8 @@ /** * Class to manage an individual translation. + * + * @phpstan-consistent-constructor */ class Translation { diff --git a/src/Translations.php b/src/Translations.php index 761851b5..11913ef5 100644 --- a/src/Translations.php +++ b/src/Translations.php @@ -12,6 +12,8 @@ /** * Class to manage a collection of translations under the same domain. + * + * @phpstan-consistent-constructor */ class Translations implements Countable, IteratorAggregate { From d5d48d987060ef120462864d45a9a94799e8a104 Mon Sep 17 00:00:00 2001 From: Pablo Largo Mohedano Date: Sun, 20 Apr 2025 00:20:41 +0200 Subject: [PATCH 03/13] Fixed `arguments.count` errors --- phpstan.dist.neon | 2 +- tests/MoGeneratorTest.php | 2 +- tests/PoGeneratorTest.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/phpstan.dist.neon b/phpstan.dist.neon index e1bb8c65..8883a8a1 100644 --- a/phpstan.dist.neon +++ b/phpstan.dist.neon @@ -1,5 +1,5 @@ parameters: - level: 0 + level: 1 paths: - src - tests diff --git a/tests/MoGeneratorTest.php b/tests/MoGeneratorTest.php index a3e246dc..4a0bd27e 100644 --- a/tests/MoGeneratorTest.php +++ b/tests/MoGeneratorTest.php @@ -26,7 +26,7 @@ public function testMoGenerator() $translation->translate('Orixinal'); $translations->add($translation); - $translation = Translation::create('context-1', 'Other comment', 'Other comments'); + $translation = Translation::create('context-1', 'Other comment'); $translation->translate('Outro comentario'); $translation->translatePlural('Outros comentarios'); $translations->add($translation); diff --git a/tests/PoGeneratorTest.php b/tests/PoGeneratorTest.php index f2b6726e..11da0a9e 100644 --- a/tests/PoGeneratorTest.php +++ b/tests/PoGeneratorTest.php @@ -33,7 +33,7 @@ public function testPoLoader() $translation->getReferences()->add('/my/template.php', 45); $translations->add($translation); - $translation = Translation::create('context-1', 'Other comment', 'Other comments'); + $translation = Translation::create('context-1', 'Other comment'); $translation->translate('Outro comentario'); $translation->translatePlural('Outros comentarios'); $translation->getExtractedComments()->add('Not sure about this'); From 7d7f1f92aac800e6e4507db1c882afdb43428be3 Mon Sep 17 00:00:00 2001 From: Pablo Largo Mohedano Date: Sun, 20 Apr 2025 01:08:42 +0200 Subject: [PATCH 04/13] Checking level 4 errors --- phpstan.dist.neon | 2 +- src/Scanner/ParsedFunction.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/phpstan.dist.neon b/phpstan.dist.neon index 8883a8a1..8360a50c 100644 --- a/phpstan.dist.neon +++ b/phpstan.dist.neon @@ -1,5 +1,5 @@ parameters: - level: 1 + level: 4 paths: - src - tests diff --git a/src/Scanner/ParsedFunction.php b/src/Scanner/ParsedFunction.php index 255df4d1..4f4477d0 100644 --- a/src/Scanner/ParsedFunction.php +++ b/src/Scanner/ParsedFunction.php @@ -24,7 +24,7 @@ public function __construct(string $name, string $filename, int $line, ?int $las $this->lastLine = isset($lastLine) ? $lastLine : $line; } - public function __debugInfo() + public function __debugInfo(): array { return $this->toArray(); } From 19c8de224cf8c78972601baaf47bc1c15a4f92fa Mon Sep 17 00:00:00 2001 From: Pablo Largo Mohedano Date: Sun, 20 Apr 2025 13:43:34 +0200 Subject: [PATCH 05/13] `MoLoader::read()` does never return `false` --- src/Loader/MoLoader.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Loader/MoLoader.php b/src/Loader/MoLoader.php index 57406813..1e2fb68b 100644 --- a/src/Loader/MoLoader.php +++ b/src/Loader/MoLoader.php @@ -125,9 +125,7 @@ private function seekTo(int $position): void private function readInt(string $byteOrder): int { - if (($read = $this->read(4)) === false) { - return 0; - } + $read = $this->read(4); $read = (array) unpack($byteOrder, $read); From 01a3ccfce589ad278002629a1ad790930e634e41 Mon Sep 17 00:00:00 2001 From: Pablo Largo Mohedano Date: Sun, 20 Apr 2025 13:45:14 +0200 Subject: [PATCH 06/13] Property `Gettext\Loader\StrictPoLoader::$displayLineColumn` is unused. --- src/Loader/StrictPoLoader.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Loader/StrictPoLoader.php b/src/Loader/StrictPoLoader.php index 5a9c1152..49de25c6 100644 --- a/src/Loader/StrictPoLoader.php +++ b/src/Loader/StrictPoLoader.php @@ -35,8 +35,6 @@ final class StrictPoLoader extends Loader private $warnings = []; /** @var bool */ private $isDisabled; - /** @var bool */ - private $displayLineColumn; /** * Generates a Translations object from a .po based string From 7895b1964568f7a6ffb382302ac98b52c1a05e74 Mon Sep 17 00:00:00 2001 From: Pablo Largo Mohedano Date: Sun, 20 Apr 2025 13:47:57 +0200 Subject: [PATCH 07/13] Right side of && is always true --- src/Loader/StrictPoLoader.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Loader/StrictPoLoader.php b/src/Loader/StrictPoLoader.php index 49de25c6..c36b4865 100644 --- a/src/Loader/StrictPoLoader.php +++ b/src/Loader/StrictPoLoader.php @@ -323,8 +323,15 @@ private function readIdentifier(string $identifier, bool $throwIfNotFound = fals */ private function readContext(): bool { - return ($data = $this->readIdentifier('msgctxt')) !== null - && ($this->translation = $this->translation->withContext($data)); + $data = $this->readIdentifier('msgctxt'); + + if ($data === null) { + return false; + } + + $this->translation = $this->translation->withContext($data); + + return true; } /** From b14f73680eae78638da9eafbfb8ae8d29a4ba774 Mon Sep 17 00:00:00 2001 From: Pablo Largo Mohedano Date: Sun, 20 Apr 2025 13:51:13 +0200 Subject: [PATCH 08/13] Right side of && is always true --- src/Loader/StrictPoLoader.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Loader/StrictPoLoader.php b/src/Loader/StrictPoLoader.php index c36b4865..cad528ee 100644 --- a/src/Loader/StrictPoLoader.php +++ b/src/Loader/StrictPoLoader.php @@ -347,7 +347,15 @@ private function readOriginal(): void */ private function readPlural(): bool { - return ($data = $this->readIdentifier('msgid_plural')) !== null && $this->translation->setPlural($data); + $data = $this->readIdentifier('msgid_plural'); + + if ($data === null) { + return false; + } + + $this->translation->setPlural($data); + + return true; } /** From 5b6604fb1e4663616e95f6e233cc27f23aecfb19 Mon Sep 17 00:00:00 2001 From: Pablo Largo Mohedano Date: Sun, 20 Apr 2025 13:56:10 +0200 Subject: [PATCH 09/13] Set PHPStan level to 5 --- phpstan.dist.neon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpstan.dist.neon b/phpstan.dist.neon index 8360a50c..d2554326 100644 --- a/phpstan.dist.neon +++ b/phpstan.dist.neon @@ -1,5 +1,5 @@ parameters: - level: 4 + level: 5 paths: - src - tests From 4e19ecff4ef7b42719e7647eaba005e6976446c6 Mon Sep 17 00:00:00 2001 From: Pablo Largo Mohedano Date: Sun, 20 Apr 2025 15:31:49 +0200 Subject: [PATCH 10/13] Ignore `trait.unused` --- src/Scanner/FunctionsHandlersTrait.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Scanner/FunctionsHandlersTrait.php b/src/Scanner/FunctionsHandlersTrait.php index 47301427..75e4c13a 100644 --- a/src/Scanner/FunctionsHandlersTrait.php +++ b/src/Scanner/FunctionsHandlersTrait.php @@ -7,6 +7,8 @@ /** * Trait with common gettext function handlers + * + * @phpstan-ignore trait.unused */ trait FunctionsHandlersTrait { From 389b7ce3788136bb568e3418315c5f91cbe04cbb Mon Sep 17 00:00:00 2001 From: Pablo Largo Mohedano Date: Sun, 20 Apr 2025 15:47:26 +0200 Subject: [PATCH 11/13] Coding style --- src/Loader/PoLoader.php | 4 ++-- src/Loader/StrictPoLoader.php | 7 ++++--- src/Merge.php | 2 +- tests/BasePoLoaderTestCase.php | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Loader/PoLoader.php b/src/Loader/PoLoader.php index e7e706c7..6816b5b3 100644 --- a/src/Loader/PoLoader.php +++ b/src/Loader/PoLoader.php @@ -24,8 +24,8 @@ public function loadString(string $string, ?Translations $translations = null): $nextLine = next($lines); // Treat empty comments as empty lines https://github.com/php-gettext/Gettext/pull/296 - if ($line === "#") { - $line = ""; + if ($line === '#') { + $line = ''; } //Multiline diff --git a/src/Loader/StrictPoLoader.php b/src/Loader/StrictPoLoader.php index cad528ee..2be8208b 100644 --- a/src/Loader/StrictPoLoader.php +++ b/src/Loader/StrictPoLoader.php @@ -167,7 +167,7 @@ private function readCommentString(): string private function readQuotedString(?string $context = null): string { $this->readWhitespace(); - for ($data = '', $isNewPart = true, $checkpoint = null;;) { + for ($data = '', $isNewPart = true, $checkpoint = null; ;) { if ($isNewPart && !$this->readChar('"')) { // The data is over (e.g. beginning of an identifier) or perhaps there's an error // Restore the checkpoint and let the next parser handle it @@ -192,11 +192,11 @@ private function readQuotedString(?string $context = null): string case '\\': $data .= $this->readEscape(); break; - // Unexpected newline + // Unexpected newline case "\r": case "\n": throw new Exception("Newline character must be escaped{$this->getErrorPosition()}"); - // Unexpected end of file + // Unexpected end of file case null: throw new Exception("Expected a closing quote{$this->getErrorPosition()}"); } @@ -227,6 +227,7 @@ private function readEscape(): string return chr($decimal); case 'x': $value = $this->readCharset($hexDigits, 1, PHP_INT_MAX, 'hexadecimal'); + // GNU reads all valid hexadecimal chars, but only uses the last pair return hex2bin(str_pad(substr($value, -2), 2, '0', STR_PAD_LEFT)); case 'U': diff --git a/src/Merge.php b/src/Merge.php index 0406b81d..e69fb908 100644 --- a/src/Merge.php +++ b/src/Merge.php @@ -30,7 +30,7 @@ final class Merge //Merge strategies public const SCAN_AND_LOAD = - Merge::HEADERS_OVERRIDE + Merge::HEADERS_OVERRIDE | Merge::TRANSLATIONS_OURS | Merge::TRANSLATIONS_OVERRIDE | Merge::EXTRACTED_COMMENTS_OURS diff --git a/tests/BasePoLoaderTestCase.php b/tests/BasePoLoaderTestCase.php index 76cdac63..4029d72b 100644 --- a/tests/BasePoLoaderTestCase.php +++ b/tests/BasePoLoaderTestCase.php @@ -24,7 +24,7 @@ public function testPoLoader() This file is distributed under the same license as the PACKAGE package. FIRST AUTHOR , YEAR. EOT - , + , $description ); From cb6dcb55a976a2c8340af8d1cf91466c864cd58a Mon Sep 17 00:00:00 2001 From: Pablo Largo Mohedano Date: Tue, 22 Apr 2025 20:34:42 +0200 Subject: [PATCH 12/13] Make PHPStan dependency broader --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 77d20eba..81f1e205 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,7 @@ "brick/varexporter": "^0.3.5", "friendsofphp/php-cs-fixer": "^3.2", "oscarotero/php-cs-fixer-config": "^2.0", - "phpstan/phpstan": "^2.1" + "phpstan/phpstan": "^1|^2" }, "autoload": { "psr-4": { From 61282ae24027974a7c84bec9a9cf38fe34b92776 Mon Sep 17 00:00:00 2001 From: Pablo Largo Mohedano Date: Tue, 22 Apr 2025 23:21:10 +0200 Subject: [PATCH 13/13] Make phpcs happy --- src/Loader/StrictPoLoader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Loader/StrictPoLoader.php b/src/Loader/StrictPoLoader.php index 2be8208b..6ed0d9e9 100644 --- a/src/Loader/StrictPoLoader.php +++ b/src/Loader/StrictPoLoader.php @@ -167,7 +167,7 @@ private function readCommentString(): string private function readQuotedString(?string $context = null): string { $this->readWhitespace(); - for ($data = '', $isNewPart = true, $checkpoint = null; ;) { + for ($data = '', $isNewPart = true, $checkpoint = null;;) { if ($isNewPart && !$this->readChar('"')) { // The data is over (e.g. beginning of an identifier) or perhaps there's an error // Restore the checkpoint and let the next parser handle it