From 4d8ff2b775fee4ba095baf6c72a5224805ebddb9 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 27 May 2026 21:26:34 +0000 Subject: [PATCH 1/6] Require PHP 8.2 and migrate ninjify to contributte tooling Replace ninjify/nunjuck and ninjify/qa with contributte/tester and contributte/qa, bump the PHP requirement to ^8.2, and adapt the codebase to the stricter contributte/qa coding standard. https://claude.ai/code/session_01UPxf8e9GsykRoeqXM4j871 --- .github/workflows/main.yaml | 18 ++++--- Makefile | 7 +-- composer.json | 6 +-- ruleset.xml | 14 ++++-- src/FallbackResolver.php | 1 + src/Helpers.php | 6 +-- src/Latte/Filters.php | 9 +--- src/Latte/Macros.php | 19 ++++---- src/Latte/Nodes/TranslateNode.php | 33 ++++++------- src/Latte/Nodes/TranslatorNode.php | 3 +- src/Latte/TranslatorExtension.php | 10 ++++ src/LocaleResolver.php | 2 +- src/LocalesResolvers/Cookie.php | 2 +- src/LocalesResolvers/Session.php | 2 + src/PrefixedTranslator.php | 9 ++-- src/Tracy/Panel.php | 49 ++++++++++---------- src/Translator.php | 19 ++++---- src/Wrappers/Message.php | 6 +-- src/Wrappers/NotTranslate.php | 1 - tests/Tests/DI/TranslationExtensionTest.phpt | 2 +- tests/Tests/Loaders/NetteDatabaseTest.phpt | 26 +++++------ tests/Tests/LocaleResolverMock.php | 1 + tests/Tests/LocalesResolvers/CookieTest.phpt | 9 ++-- tests/Tests/PsrLoggerMock.php | 1 + tests/Toolkit/Container.php | 5 +- 25 files changed, 126 insertions(+), 134 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 1c97b558..c4213e60 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -28,7 +28,7 @@ jobs: id: "extcache" uses: "shivammathur/cache-extensions@v1" with: - php-version: "8.0" + php-version: "8.2" extensions: "${{ env.extensions }}" key: "${{ env.cache-version }}" @@ -42,7 +42,7 @@ jobs: - name: "Install PHP" uses: "shivammathur/setup-php@v2" with: - php-version: "8.0" + php-version: "8.2" extensions: "${{ env.extensions }}" tools: "composer" @@ -90,7 +90,7 @@ jobs: id: "extcache" uses: "shivammathur/cache-extensions@v1" with: - php-version: "8.1" + php-version: "8.2" extensions: "${{ env.extensions }}" key: "${{ env.cache-version }}" @@ -104,7 +104,7 @@ jobs: - name: "Install PHP" uses: "shivammathur/setup-php@v2" with: - php-version: "8.1" + php-version: "8.2" extensions: "${{ env.extensions }}" tools: "composer" @@ -148,12 +148,10 @@ jobs: strategy: matrix: - php-version: [ "8.0", "8.1", "8.2", "8.3", "8.4", "8.5" ] + php-version: [ "8.2", "8.3", "8.4", "8.5" ] composer-args: [ "" ] include: - - php-version: "8.0" - composer-args: "--prefer-lowest" - - php-version: "8.1" + - php-version: "8.2" composer-args: "--prefer-lowest" fail-fast: false @@ -232,7 +230,7 @@ jobs: id: "extcache" uses: "shivammathur/cache-extensions@v1" with: - php-version: "8.0" + php-version: "8.2" extensions: "${{ env.extensions }}" key: "${{ env.cache-version }}" @@ -246,7 +244,7 @@ jobs: - name: "Install PHP" uses: "shivammathur/setup-php@v2" with: - php-version: "8.0" + php-version: "8.2" coverage: "xdebug" extensions: "${{ env.extensions }}" tools: "composer" diff --git a/Makefile b/Makefile index fc4c02c6..737e0a04 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: qa lint cs csf phpstan tests coverage +.PHONY: qa cs csf phpstan tests coverage all: @$(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | sort | egrep -v -e '^[^[:alnum:]]' -e '^$@$$' | xargs @@ -6,10 +6,7 @@ all: vendor: composer.json composer.lock composer install -qa: lint phpstan cs - -lint: vendor - vendor/bin/linter src tests +qa: phpstan cs cs: vendor vendor/bin/codesniffer src tests diff --git a/composer.json b/composer.json index 93250f45..c5f683fc 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ } ], "require": { - "php": "^8.0.2", + "php": "^8.2", "latte/latte": "^2.6|^3.0.25", "nette/di": "^3.0.6", "nette/http": "^3.0.7", @@ -27,6 +27,8 @@ "symfony/config": "^6.0|^7.0|^8.0" }, "require-dev": { + "contributte/qa": "^0.4.0", + "contributte/tester": "^0.4.1", "doctrine/orm": "^2.8", "mockery/mockery": "^1.4", "nette/application": "^3.1.0", @@ -34,8 +36,6 @@ "nette/database": "^3.1.1", "nette/robot-loader": "^3.4.0|^4.0.0", "nette/tester": "^2.3.1", - "ninjify/nunjuck": "^0.4.0", - "ninjify/qa": "^0.13", "phpstan/phpstan": "^2.1", "phpstan/phpstan-deprecation-rules": "^2.0", "phpstan/phpstan-nette": "^2.0", diff --git a/ruleset.xml b/ruleset.xml index a1d48e0f..725ed8c6 100644 --- a/ruleset.xml +++ b/ruleset.xml @@ -1,12 +1,13 @@ - + - - - + + + + src/Latte/Nodes/* @@ -18,7 +19,10 @@ - + + + + diff --git a/src/FallbackResolver.php b/src/FallbackResolver.php index f3ccddb7..a928356c 100644 --- a/src/FallbackResolver.php +++ b/src/FallbackResolver.php @@ -18,6 +18,7 @@ public function setFallbackLocales( ): self { $this->fallbackLocales = $array; + return $this; } diff --git a/src/Helpers.php b/src/Helpers.php index 49aadbba..38ccf936 100644 --- a/src/Helpers.php +++ b/src/Helpers.php @@ -43,14 +43,12 @@ public static function isAbsoluteMessage( } /** - * @param mixed $message * @param array|null $prefix - * @return mixed */ public static function prefixMessage( - $message, + mixed $message, ?array $prefix - ) + ): mixed { if (is_string($message) && $prefix !== null && !self::isAbsoluteMessage($message)) { $message = implode('.', $prefix) . '.' . $message; diff --git a/src/Latte/Filters.php b/src/Latte/Filters.php index 3c077a11..06ebd0af 100644 --- a/src/Latte/Filters.php +++ b/src/Latte/Filters.php @@ -17,15 +17,10 @@ public function __construct( $this->translator = $translator; } - /** - * @param \Latte\Runtime\FilterInfo $filterInfo - * @param mixed $message - * @param mixed ...$parameters - */ public function translate( FilterInfo $filterInfo, - $message, - ...$parameters + mixed $message, + mixed ...$parameters ): string { return $this->translator->translate($message, ...$parameters); diff --git a/src/Latte/Macros.php b/src/Latte/Macros.php index 87c3c2c9..9e108c84 100644 --- a/src/Latte/Macros.php +++ b/src/Latte/Macros.php @@ -31,6 +31,16 @@ public static function install( $me->addMacro('translator', [$me, 'macroPrefix'], [$me, 'macroPrefix']); } + public static function macroWithoutParameters( + MacroNode $node + ): bool + { + $result = Strings::trim($node->tokenizer->joinUntil(',')) === Strings::trim($node->args); + $node->tokenizer->reset(); + + return $result; + } + /** * {_ ...} * @@ -124,13 +134,4 @@ public function macroPrefix( ', $tempPrefixProp, $tempPrefixProp, $prefixProp, $tempPrefixProp, $prefixProp, $prefixProp)); } - public static function macroWithoutParameters( - MacroNode $node - ): bool - { - $result = Strings::trim($node->tokenizer->joinUntil(',')) === Strings::trim($node->args); - $node->tokenizer->reset(); - return $result; - } - } diff --git a/src/Latte/Nodes/TranslateNode.php b/src/Latte/Nodes/TranslateNode.php index 26e158f6..92ba78d2 100644 --- a/src/Latte/Nodes/TranslateNode.php +++ b/src/Latte/Nodes/TranslateNode.php @@ -45,26 +45,21 @@ public static function create( return $node; } - public function print( PrintContext $context ): string { - if ($this->content instanceof TextNode) { - return $context->format( - ' + return $this->content instanceof TextNode ? $context->format( + ' $ʟ_fi = new LR\FilterInfo(%dump); echo %modifyContent(%dump) %line; ', - $context->getEscaper()->export(), - $this->modifier, - $this->content->content, - $this->position, - ); - - } else { - return $context->format( - ' + $context->getEscaper()->export(), + $this->modifier, + $this->content->content, + $this->position, + ) : $context->format( + ' ob_start(fn() => ""); try { %node } finally { @@ -73,15 +68,13 @@ public function print( $ʟ_fi = new LR\FilterInfo(%dump); echo %modifyContent($ʟ_tmp) %line; ', - $this->content, - $context->getEscaper()->export(), - $this->modifier, - $this->position, - ); - } + $this->content, + $context->getEscaper()->export(), + $this->modifier, + $this->position, + ); } - public function &getIterator(): \Generator { yield $this->content; diff --git a/src/Latte/Nodes/TranslatorNode.php b/src/Latte/Nodes/TranslatorNode.php index 4364dee0..69354292 100644 --- a/src/Latte/Nodes/TranslatorNode.php +++ b/src/Latte/Nodes/TranslatorNode.php @@ -27,10 +27,10 @@ public static function create( $node = new TranslatorNode(); $node->prefix = $variable; [$node->content] = yield; + return $node; } - public function print( PrintContext $context ): string @@ -54,7 +54,6 @@ public function print( ); } - public function &getIterator(): \Generator { yield $this->prefix; diff --git a/src/Latte/TranslatorExtension.php b/src/Latte/TranslatorExtension.php index e233801c..e787654e 100644 --- a/src/Latte/TranslatorExtension.php +++ b/src/Latte/TranslatorExtension.php @@ -33,6 +33,9 @@ public function __construct( $this->translator = $translator; } + /** + * @return array + */ public function getTags(): array { return [ @@ -42,6 +45,9 @@ public function getTags(): array ]; } + /** + * @return array + */ public function getFilters(): array { return [ @@ -57,6 +63,9 @@ public function getFilters(): array ]; } + /** + * @return array + */ public function getProviders(): array { return [ @@ -92,6 +101,7 @@ public function parseTranslate( $outputNode->modifier->escape = $outputNode->modifier->removeFilter('noescape') === null; $outputNode->expression = $messageNode; array_unshift($outputNode->modifier->filters, new FilterNode(new IdentifierNode('translate'), $args->toArguments())); + return $outputNode; } diff --git a/src/LocaleResolver.php b/src/LocaleResolver.php index 4215b470..61073b65 100644 --- a/src/LocaleResolver.php +++ b/src/LocaleResolver.php @@ -32,13 +32,13 @@ public function getResolvers(): array /** * @param class-string $resolver - * @return self */ public function addResolver( string $resolver ): self { $this->resolvers[] = $resolver; + return $this; } diff --git a/src/LocalesResolvers/Cookie.php b/src/LocalesResolvers/Cookie.php index 1953de0e..1cfb3ab8 100644 --- a/src/LocalesResolvers/Cookie.php +++ b/src/LocalesResolvers/Cookie.php @@ -1,4 +1,4 @@ -sessionSection[self::$parameter] = $locale; + return $this; } @@ -42,6 +43,7 @@ public function resolve( { if (!$this->session->isStarted() && $this->httpResponse->isSent()) { trigger_error('The advice of session locale resolver is required but the session has not been started and headers had been already sent. Either start your sessions earlier or disable the SessionResolver.', E_USER_WARNING); + return null; } diff --git a/src/PrefixedTranslator.php b/src/PrefixedTranslator.php index bddb7545..538114ca 100644 --- a/src/PrefixedTranslator.php +++ b/src/PrefixedTranslator.php @@ -30,18 +30,15 @@ public function getPrefix(): string return $this->prefix; } - /** - * @param mixed $message - * @param mixed ...$parameters - */ public function translate( - $message, - ...$parameters + mixed $message, + mixed ...$parameters ): string { $this->translator->addPrefix($this->prefix); $message = $this->translator->translate($message, ...$parameters); $this->translator->removePrefix(); + return $message; } diff --git a/src/Tracy/Panel.php b/src/Tracy/Panel.php index 09eb84b8..33618a45 100644 --- a/src/Tracy/Panel.php +++ b/src/Tracy/Panel.php @@ -43,6 +43,7 @@ public function getTab(): string // https://postimg.cc/jC4Nq2wg $icon = ''; $errMsg = ($this->missingTranslationCount > 1) ? 'errors' : 'error'; + return '' . $icon . ' ' . @@ -123,30 +124,6 @@ public function getPanel(): string return implode('', $panel); } - /** - * @param array> $resources - */ - private static function createResourcePanelHelper( - array $resources, - string $class - ): string - { - $string = ''; - $string .= ''; - - foreach ($resources as $k1 => $v1) { - foreach ($v1 as $k2 => $v2) { - $string .= ''; - $string .= ''; - $string .= ''; - $string .= ''; - $string .= ''; - } - } - - return $string . '
LocaleDomainFile name
' . htmlspecialchars($k1) . '' . htmlspecialchars($v2) . '' . htmlspecialchars(dirname($k2)) . '/' . htmlspecialchars(basename($k2)) . '
'; - } - public function addMissingTranslation( string $id, string $domain @@ -220,4 +197,28 @@ public function getIgnoredResources(): array return $this->ignoredResources; } + /** + * @param array> $resources + */ + private static function createResourcePanelHelper( + array $resources, + string $class + ): string + { + $string = ''; + $string .= ''; + + foreach ($resources as $k1 => $v1) { + foreach ($v1 as $k2 => $v2) { + $string .= ''; + $string .= ''; + $string .= ''; + $string .= ''; + $string .= ''; + } + } + + return $string . '
LocaleDomainFile name
' . htmlspecialchars($k1) . '' . htmlspecialchars($v2) . '' . htmlspecialchars(dirname($k2)) . '/' . htmlspecialchars(basename($k2)) . '
'; + } + } diff --git a/src/Translator.php b/src/Translator.php index 521d4f2a..bd924e38 100644 --- a/src/Translator.php +++ b/src/Translator.php @@ -16,6 +16,8 @@ class Translator extends SymfonyTranslator implements ITranslator { + public bool $returnOriginalMessage = true; + private LocaleResolver $localeResolver; private FallbackResolver $fallbackResolver; @@ -26,8 +28,6 @@ class Translator extends SymfonyTranslator implements ITranslator private bool $debug; - public bool $returnOriginalMessage = true; - /** @var array|null */ private ?array $localesWhitelist = null; @@ -155,6 +155,7 @@ public function addPrefix( ): self { $this->prefix[] = $string; + return $this; } @@ -182,6 +183,7 @@ public function setTranslateKeyGenerator( ): self { $this->translateKeyGenerator = $generator; + return $this; } @@ -230,12 +232,13 @@ public function getAvailableLocales(): array { $locales = array_keys($this->resourcesLocales); sort($locales); + return $locales; } public function addResource( string $format, - $resource, + mixed $resource, string $locale, ?string $domain = null ): void @@ -277,6 +280,7 @@ public function setPsrLogger( ): self { $this->psrLogger = $psrLogger; + return $this; } @@ -290,16 +294,13 @@ public function setTracyPanel( ): self { $this->tracyPanel = $tracyPanel; + return $this; } - /** - * @param mixed $message - * @param mixed ...$parameters - */ public function translate( - $message, - ...$parameters + mixed $message, + mixed ...$parameters ): string { if ($message === null || $message === '') { diff --git a/src/Wrappers/Message.php b/src/Wrappers/Message.php index 5c51406e..272a9072 100644 --- a/src/Wrappers/Message.php +++ b/src/Wrappers/Message.php @@ -12,19 +12,15 @@ class Message implements Stringable /** @var array */ public array $parameters; - /** - * @param array ...$parameters - */ public function __construct( string $message, - ...$parameters + mixed ...$parameters ) { $this->message = $message; $this->parameters = $parameters; } - public function __toString(): string { return $this->message; diff --git a/src/Wrappers/NotTranslate.php b/src/Wrappers/NotTranslate.php index fafad06c..d810a979 100644 --- a/src/Wrappers/NotTranslate.php +++ b/src/Wrappers/NotTranslate.php @@ -16,7 +16,6 @@ public function __construct( $this->message = $message; } - public function __toString(): string { return $this->message; diff --git a/tests/Tests/DI/TranslationExtensionTest.phpt b/tests/Tests/DI/TranslationExtensionTest.phpt index cf771b18..9a9e85b6 100644 --- a/tests/Tests/DI/TranslationExtensionTest.phpt +++ b/tests/Tests/DI/TranslationExtensionTest.phpt @@ -297,7 +297,7 @@ final class TranslationExtensionTest extends TestAbstract public function test12(): void { - Assert::exception(static function () { + Assert::exception(static function (): void { Container::of() ->withDefaults() ->withCompiler(function (Compiler $compiler): void { diff --git a/tests/Tests/Loaders/NetteDatabaseTest.phpt b/tests/Tests/Loaders/NetteDatabaseTest.phpt index db60dace..bfdef69b 100644 --- a/tests/Tests/Loaders/NetteDatabaseTest.phpt +++ b/tests/Tests/Loaders/NetteDatabaseTest.phpt @@ -27,7 +27,19 @@ final class NetteDatabaseTest extends TestAbstract private Connection $connection; - protected function setUp() + public function test01(): void + { + $this->connection->query(file_get_contents(__DIR__ . '/../../sql.sql')); + $this->translator->setLocale('cs_CZ'); + + Assert::same('Ahoj', $this->translator->translate('db_table.hello')); + + $this->translator->setLocale('en_US'); + + Assert::same('Hello', $this->translator->translate('db_table.hello')); + } + + protected function setUp(): void { parent::setUp(); @@ -60,18 +72,6 @@ final class NetteDatabaseTest extends TestAbstract } } - public function test01(): void - { - $this->connection->query(file_get_contents(__DIR__ . '/../../sql.sql')); - $this->translator->setLocale('cs_CZ'); - - Assert::same('Ahoj', $this->translator->translate('db_table.hello')); - - $this->translator->setLocale('en_US'); - - Assert::same('Hello', $this->translator->translate('db_table.hello')); - } - } (new NetteDatabaseTest($container))->run(); diff --git a/tests/Tests/LocaleResolverMock.php b/tests/Tests/LocaleResolverMock.php index aaaf72c3..0846c8ae 100644 --- a/tests/Tests/LocaleResolverMock.php +++ b/tests/Tests/LocaleResolverMock.php @@ -15,6 +15,7 @@ public function setLocale( ): self { $this->locale = $locale; + return $this; } diff --git a/tests/Tests/LocalesResolvers/CookieTest.phpt b/tests/Tests/LocalesResolvers/CookieTest.phpt index 9bb7a6e7..0a8dad7f 100644 --- a/tests/Tests/LocalesResolvers/CookieTest.phpt +++ b/tests/Tests/LocalesResolvers/CookieTest.phpt @@ -17,6 +17,7 @@ $container = require __DIR__ . '/../../bootstrap.php'; final class CookieTest extends TestAbstract { + /** @var array */ private static array $cookies; public function test01(): void @@ -34,18 +35,16 @@ final class CookieTest extends TestAbstract ): ?string { $response = Mockery::mock(IResponse::class); - $response->shouldReceive('setCookie')->andReturnUsing(function ($name, $value) { + $response->shouldReceive('setCookie')->andReturnUsing(function ($name, $value): void { self::$cookies[$name] = $value; self::$cookies = array_filter(self::$cookies); }); - $response->shouldReceive('deleteCookie')->andReturnUsing(function ($name) { + $response->shouldReceive('deleteCookie')->andReturnUsing(function ($name): void { unset(self::$cookies[$name]); }); $request = Mockery::mock(IRequest::class); - $request->shouldReceive('getCookie')->andReturnUsing(function ($name) { - return self::$cookies[$name] ?? null; - }); + $request->shouldReceive('getCookie')->andReturnUsing(fn ($name) => self::$cookies[$name] ?? null); $resolver = new Cookie( $request, diff --git a/tests/Tests/PsrLoggerMock.php b/tests/Tests/PsrLoggerMock.php index 826c1f37..bd07ae91 100644 --- a/tests/Tests/PsrLoggerMock.php +++ b/tests/Tests/PsrLoggerMock.php @@ -16,6 +16,7 @@ public function log( array $context = [] ): void { + // noop mock logger } } diff --git a/tests/Toolkit/Container.php b/tests/Toolkit/Container.php index 4cc11710..d34a8aac 100644 --- a/tests/Toolkit/Container.php +++ b/tests/Toolkit/Container.php @@ -11,11 +11,10 @@ final class Container { - /** @var string */ - private $key; + private string $key; /** @var callable[] */ - private $onCompile = []; + private array $onCompile = []; public function __construct(string $key) { From fefdef69be7000ebf67cb732fb7478abe4bf716c Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 27 May 2026 21:31:52 +0000 Subject: [PATCH 2/6] Drop Latte 2.x support Require latte/latte ^3.0.25 and remove the Latte 2 compatibility layer: delete the MacroSet-based Macros and the Filters class, simplify the DI extension to register the Latte 3 TranslatorExtension unconditionally, and switch from the deprecated Nette\Localization\ITranslator to Nette\Localization\Translator. Inspired by https://github.com/contributte/translation/pull/140 https://claude.ai/code/session_01UPxf8e9GsykRoeqXM4j871 --- composer.json | 2 +- phpstan.lowest.neon | 50 ------- phpstan.neon | 77 ----------- src/DI/TranslationExtension.php | 32 ++--- src/Latte/Filters.php | 29 ---- src/Latte/Macros.php | 137 ------------------- src/Latte/TranslatorExtension.php | 6 +- src/PrefixedTranslator.php | 4 +- src/Translator.php | 4 +- tests/Tests/DI/TranslationExtensionTest.phpt | 18 +-- tests/Tests/Latte/FiltersTest.phpt | 32 ----- 11 files changed, 25 insertions(+), 366 deletions(-) delete mode 100644 src/Latte/Filters.php delete mode 100644 src/Latte/Macros.php delete mode 100644 tests/Tests/Latte/FiltersTest.phpt diff --git a/composer.json b/composer.json index c5f683fc..561cf636 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,7 @@ ], "require": { "php": "^8.2", - "latte/latte": "^2.6|^3.0.25", + "latte/latte": "^3.0.25", "nette/di": "^3.0.6", "nette/http": "^3.0.7", "nette/neon": "^3.3.1", diff --git a/phpstan.lowest.neon b/phpstan.lowest.neon index 553ab4c2..f9a25919 100644 --- a/phpstan.lowest.neon +++ b/phpstan.lowest.neon @@ -18,53 +18,3 @@ parameters: count: 2 message: '#^Variable property access on object\.$#' path: 'src/Loaders/Doctrine.php' - - # ------------------------------------------------------------------- - # for back compatibility with old packages - will be remove in future - # ------------------------------------------------------------------- - - - - message: """ - #^Fetching class constant class of deprecated (class|interface) Nette\\\\Localization\\\\ITranslator\\: - use Nette\\\\Localization\\\\Translator$# - """ - path: src/DI/TranslationExtension.php - - - - count: 1 - message: """ - #^Parameter \\$translator of method Contributte\\\\Translation\\\\Latte\\\\Filters\\:\\:__construct\\(\\) has typehint with deprecated interface Nette\\\\Localization\\\\ITranslator\\: - use Nette\\\\Localization\\\\Translator$# - """ - path: src/Latte/Filters.php - - - - count: 1 - message: """ - #^Parameter \\$translator of method Contributte\\\\Translation\\\\Latte\\\\TranslatorExtension\\:\\:__construct\\(\\) has typehint with deprecated interface Nette\\\\Localization\\\\ITranslator\\: - use Nette\\\\Localization\\\\Translator$# - """ - path: src/Latte/TranslatorExtension.php - - - - count: 1 - message: """ - #^Class Contributte\\\\Translation\\\\PrefixedTranslator implements deprecated interface Nette\\\\Localization\\\\ITranslator\\: - use Nette\\\\Localization\\\\Translator$# - """ - path: src/PrefixedTranslator.php - - - - count: 1 - message: """ - #^Class Contributte\\\\Translation\\\\Translator implements deprecated interface Nette\\\\Localization\\\\ITranslator\\: - use Nette\\\\Localization\\\\Translator$# - """ - path: src/Translator.php - - - - message: """ - #^Fetching class constant class of deprecated (class|interface) Nette\\\\Bridges\\\\ApplicationLatte\\\\ILatteFactory\\: - use Nette\\\\Bridges\\\\ApplicationLatte\\\\LatteFactory$# - """ - path: src/DI/TranslationExtension.php diff --git a/phpstan.neon b/phpstan.neon index ae8bf0a2..53429826 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -9,9 +9,6 @@ parameters: paths: - src - tests - excludePaths: - - src/Latte/Filters.php - - src/Latte/Macros.php ignoreErrors: - count: 2 @@ -24,77 +21,3 @@ parameters: message: '#^Instanceof between Nette\\Utils\\FileInfo and SplFileInfo will always evaluate to true\.$#' count: 1 path: src/DI/TranslationExtension.php - - - - count: 2 - message: '#^Comparison operation "\<" between \d+ and \d+ is always false\.$#' - path: 'src/Latte/Macros.php' - - - - count: 1 - message: '#^Comparison operation "\>=" between \d+ and \d+ is always true\.$#' - path: 'src/Latte/Macros.php' - - - - count: 1 - message: '#^Result of && is always false\.$#' - path: 'src/Latte/Macros.php' - - # ------------------------------------------------------------------- - # for back compatibility with old packages - will be remove in future - # ------------------------------------------------------------------- - - - message: """ - #^Access to constant on deprecated interface Nette\\\\Bridges\\\\ApplicationLatte\\\\ILatteFactory\\: - use Nette\\\\Bridges\\\\ApplicationLatte\\\\LatteFactory$# - """ - count: 1 - path: src/DI/TranslationExtension.php - - - - message: """ - #^Access to constant on deprecated interface Nette\\\\Localization\\\\ITranslator\\: - use Nette\\\\Localization\\\\Translator$# - """ - count: 2 - path: src/DI/TranslationExtension.php - - - - count: 1 - message: """ - #^Parameter \\$translator of method Contributte\\\\Translation\\\\Latte\\\\Filters\\:\\:__construct\\(\\) has typehint with deprecated interface Nette\\\\Localization\\\\ITranslator\\: - use Nette\\\\Localization\\\\Translator$# - """ - path: src/Latte/Filters.php - - - - count: 1 - message: """ - #^Parameter \\$translator of method Contributte\\\\Translation\\\\Latte\\\\TranslatorExtension\\:\\:__construct\\(\\) has typehint with deprecated interface Nette\\\\Localization\\\\ITranslator\\: - use Nette\\\\Localization\\\\Translator$# - """ - path: src/Latte/TranslatorExtension.php - - - - count: 1 - message: """ - #^Class Contributte\\\\Translation\\\\PrefixedTranslator implements deprecated interface Nette\\\\Localization\\\\ITranslator\\: - use Nette\\\\Localization\\\\Translator$# - """ - path: src/PrefixedTranslator.php - - - - count: 1 - message: """ - #^Class Contributte\\\\Translation\\\\Translator implements deprecated interface Nette\\\\Localization\\\\ITranslator\\: - use Nette\\\\Localization\\\\Translator$# - """ - path: src/Translator.php - - - - message: """ - #^Instantiation of deprecated class Nette\\\\PhpGenerator\\\\PhpLiteral\\: - use Nette\\\\PhpGenerator\\\\Literal$# - """ - count: 1 - path: src/DI/TranslationExtension.php diff --git a/src/DI/TranslationExtension.php b/src/DI/TranslationExtension.php index 02720a00..fdf4bf62 100644 --- a/src/DI/TranslationExtension.php +++ b/src/DI/TranslationExtension.php @@ -6,8 +6,6 @@ use Contributte\Translation\Exceptions\InvalidArgument; use Contributte\Translation\FallbackResolver; use Contributte\Translation\Helpers; -use Contributte\Translation\Latte\Filters; -use Contributte\Translation\Latte\Macros; use Contributte\Translation\Latte\TranslatorExtension; use Contributte\Translation\Loaders\Neon; use Contributte\Translation\LocaleResolver; @@ -18,13 +16,12 @@ use Contributte\Translation\LocalesResolvers\Session; use Contributte\Translation\Tracy\Panel; use Contributte\Translation\Translator; -use Nette\Bridges\ApplicationLatte\ILatteFactory; +use Nette\Bridges\ApplicationLatte\LatteFactory; use Nette\DI\CompilerExtension; use Nette\DI\Definitions\Statement; use Nette\DI\MissingServiceException; -use Nette\Localization\ITranslator; +use Nette\Localization\Translator as NetteTranslator; use Nette\PhpGenerator\ClassType; -use Nette\PhpGenerator\PhpLiteral; use Nette\Schema\Expect; use Nette\Schema\Schema; use Nette\Utils\Finder; @@ -126,7 +123,7 @@ static function ( 'translatorFactory' => Expect::string()->default(null), 'returnOriginalMessage' => Expect::bool()->default(true), 'autowired' => Expect::type('bool|array')->default(true), - 'latteFactory' => Expect::string(ILatteFactory::class)->nullable(), + 'latteFactory' => Expect::string(LatteFactory::class)->nullable(), ])->castTo('array'); } @@ -184,7 +181,7 @@ public function loadConfiguration(): void if ($config['autowired'] === true) { $autowired = [ - ITranslator::class, + NetteTranslator::class, TranslatorInterface::class, Translator::class, ]; @@ -270,26 +267,13 @@ public function beforeCompile(): void $latteFactoryName = $config['latteFactory'] !== null ? $builder->getByType($config['latteFactory']) : null; if ($latteFactoryName !== null) { - $iTranslator = $builder->getDefinitionByType(ITranslator::class); - - $latteFilters = $builder->addDefinition($this->prefix('latte.filters')) - ->setFactory(Filters::class); - /** @var \Nette\DI\Definitions\FactoryDefinition $latteFactory */ $latteFactory = $builder->getDefinition($latteFactoryName); - /** @phpstan-ignore-next-line */ - if (version_compare(\Latte\Engine::VERSION, '3', '<')) { - $latteFactory->getResultDefinition() - ->addSetup('?->onCompile[] = function (Latte\\Engine $engine): void { ?::install($engine->getCompiler()); }', ['@self', new PhpLiteral(Macros::class)]) - ->addSetup('addProvider', ['translator', $iTranslator]) - ->addSetup('addFilter', ['translate', [$latteFilters, 'translate']]); - } else { - $latteExtension = $builder->addDefinition($this->prefix('latte.extension')) - ->setFactory(TranslatorExtension::class); - $latteFactory->getResultDefinition() - ->addSetup('addExtension', [$latteExtension]); - } + $latteExtension = $builder->addDefinition($this->prefix('latte.extension')) + ->setFactory(TranslatorExtension::class); + $latteFactory->getResultDefinition() + ->addSetup('addExtension', [$latteExtension]); } /** @var array $providers */ diff --git a/src/Latte/Filters.php b/src/Latte/Filters.php deleted file mode 100644 index 06ebd0af..00000000 --- a/src/Latte/Filters.php +++ /dev/null @@ -1,29 +0,0 @@ -translator = $translator; - } - - public function translate( - FilterInfo $filterInfo, - mixed $message, - mixed ...$parameters - ): string - { - return $this->translator->translate($message, ...$parameters); - } - -} diff --git a/src/Latte/Macros.php b/src/Latte/Macros.php deleted file mode 100644 index 9e108c84..00000000 --- a/src/Latte/Macros.php +++ /dev/null @@ -1,137 +0,0 @@ -addMacro('_', [$me, 'macroTranslate'], [$me, 'macroTranslate']); - $me->addMacro('translator', [$me, 'macroPrefix'], [$me, 'macroPrefix']); - } - - public static function macroWithoutParameters( - MacroNode $node - ): bool - { - $result = Strings::trim($node->tokenizer->joinUntil(',')) === Strings::trim($node->args); - $node->tokenizer->reset(); - - return $result; - } - - /** - * {_ ...} - * - * @throws \Latte\CompileException - */ - public function macroTranslate( - MacroNode $node, - PhpWriter $writer - ): string - { - if ($node->closing) { - if (strpos($node->content, 'content, true); - $node->content = ''; - - } else { - $node->openingCode = '' . $node->openingCode; - $value = 'ob_get_clean()'; - } - - /** @phpstan-ignore-next-line */ - if (!defined(Engine::class . '::VERSION_ID') || Engine::VERSION_ID < 20900) { - $latteProp = '$_fi'; - /** @phpstan-ignore-next-line */ - } elseif (Engine::VERSION_ID >= 20900 && Engine::VERSION_ID < 20902) { - $latteProp = '$__fi'; - } else { - $latteProp = '$ʟ_fi'; - } - - return $writer->write(sprintf('%s = new LR\FilterInfo(%%var); echo %%modifyContent($this->filters->filterContent("translate", %s, %%raw))', $latteProp, $latteProp), $node->context[0], $value); - } - - if ($node->empty = ($node->args !== '')) { - $messageProp = Helpers::createLatteProperty('Message'); - $prefixProp = Helpers::createLatteProperty('Prefix'); - - $macroCodeEcho = self::macroWithoutParameters($node) - ? sprintf('echo %%modify(call_user_func($this->filters->translate, %s))', $messageProp) - : sprintf('echo %%modify(call_user_func($this->filters->translate, %s, %%node.args))', $messageProp); - - return $writer->write(sprintf(' - %s = %%node.word; - - if (is_string(%s)) { - %s = isset(%s) && !\Contributte\Translation\Helpers::isAbsoluteMessage(%%node.word) ? implode(".", %s) . "." . %%node.word : %%node.word; - } - - %s; - ', $messageProp, $messageProp, $messageProp, $prefixProp, $prefixProp, $macroCodeEcho)); - } - - return ''; - } - - /** - * {translate ...} - * - * @throws \Latte\CompileException - */ - public function macroPrefix( - MacroNode $node, - PhpWriter $writer - ): string - { - $prefixProp = Helpers::createLatteProperty('Prefix'); - $tempPrefixProp = Helpers::createLatteProperty('TempPrefix'); - - if ($node->closing) { - if ($node->content !== null && $node->content !== '') { - return $writer->write(sprintf('%s = array_pop(%s);', $prefixProp, $tempPrefixProp)); - } - - return ''; - } - - if ($node->args === '') { - throw new CompileException('Expected message prefix, none given.'); - } - - return $writer->write(sprintf(' - if (!isset(%s)) { - %s = []; - } - - if (isset(%s)) { - %s[] = %s; - } - - %s = [%%node.word]; - ', $tempPrefixProp, $tempPrefixProp, $prefixProp, $tempPrefixProp, $prefixProp, $prefixProp)); - } - -} diff --git a/src/Latte/TranslatorExtension.php b/src/Latte/TranslatorExtension.php index e787654e..1777a08e 100644 --- a/src/Latte/TranslatorExtension.php +++ b/src/Latte/TranslatorExtension.php @@ -19,15 +19,15 @@ use Latte\Compiler\Tag; use Latte\Extension; use Latte\Runtime\FilterInfo; -use Nette\Localization\ITranslator; +use Nette\Localization\Translator; class TranslatorExtension extends Extension { - private ITranslator $translator; + private Translator $translator; public function __construct( - ITranslator $translator + Translator $translator ) { $this->translator = $translator; diff --git a/src/PrefixedTranslator.php b/src/PrefixedTranslator.php index 538114ca..93d2bc91 100644 --- a/src/PrefixedTranslator.php +++ b/src/PrefixedTranslator.php @@ -2,9 +2,9 @@ namespace Contributte\Translation; -use Nette\Localization\ITranslator; +use Nette\Localization\Translator as NetteTranslator; -class PrefixedTranslator implements ITranslator +class PrefixedTranslator implements NetteTranslator { private Translator $translator; diff --git a/src/Translator.php b/src/Translator.php index bd924e38..ff763f65 100644 --- a/src/Translator.php +++ b/src/Translator.php @@ -7,13 +7,13 @@ use Contributte\Translation\Wrappers\Message; use Contributte\Translation\Wrappers\NotTranslate; use Generator; -use Nette\Localization\ITranslator; +use Nette\Localization\Translator as NetteTranslator; use Nette\Utils\Strings; use Nette\Utils\Validators; use Psr\Log\LoggerInterface; use Symfony\Component\Translation\Translator as SymfonyTranslator; -class Translator extends SymfonyTranslator implements ITranslator +class Translator extends SymfonyTranslator implements NetteTranslator { public bool $returnOriginalMessage = true; diff --git a/tests/Tests/DI/TranslationExtensionTest.phpt b/tests/Tests/DI/TranslationExtensionTest.phpt index 9a9e85b6..4db2ccb1 100644 --- a/tests/Tests/DI/TranslationExtensionTest.phpt +++ b/tests/Tests/DI/TranslationExtensionTest.phpt @@ -12,9 +12,9 @@ use Nette\DI\Compiler; use Nette\DI\CompilerExtension; use Nette\DI\ContainerLoader; use Nette\DI\MissingServiceException; +use Nette\DI\ServiceCreationException; use Nette\InvalidStateException; -use Nette\Localization\ITranslator; -use Nette\Utils\Strings; +use Nette\Localization\Translator as NetteTranslator; use Psr\Log\LoggerInterface; use stdClass; use Symfony\Component\Config\ConfigCacheFactoryInterface; @@ -140,7 +140,7 @@ final class TranslationExtensionTest extends TestAbstract $compiler->addConfig(['parameters' => $this->container->getParameters(), 'translation' => ['dirs' => [__DIR__ . '__config_dir__']]]); }); }, $this->isNewNetteUtils ? InvalidStateException::class : UnexpectedValueException::class); - Assert::true(Strings::contains($e->getMessage(), __DIR__ . '/__translation_provider_dir__'));// translation provider dirs first !! + Assert::true(str_contains($e->getMessage(), __DIR__ . '/__translation_provider_dir__'));// translation provider dirs first !! } public function test03(): void @@ -155,7 +155,7 @@ final class TranslationExtensionTest extends TestAbstract $panel = $container->getByType(Panel::class); /** @var Translator $translator */ - $translator = $container->getByType(ITranslator::class); + $translator = $container->getByType(NetteTranslator::class); $tracyPanel = $translator->getTracyPanel(); @@ -195,7 +195,7 @@ final class TranslationExtensionTest extends TestAbstract ]); /** @var Translator $translator */ - $translator = $container->getByType(ITranslator::class); + $translator = $container->getByType(NetteTranslator::class); Assert::same($translator->getFallbackLocales(), ['cs_CZ']); } @@ -210,7 +210,7 @@ final class TranslationExtensionTest extends TestAbstract ]); /** @var \Contributte\Translation\Translator $translator */ - $translator = $container->getByType(ITranslator::class); + $translator = $container->getByType(NetteTranslator::class); Assert::type(CustomTranslatorMock::class, $translator); @@ -227,7 +227,7 @@ final class TranslationExtensionTest extends TestAbstract ]); /** @var \Contributte\Translation\Translator $translator */ - $translator = $container->getByType(ITranslator::class); + $translator = $container->getByType(NetteTranslator::class); Assert::false($translator->returnOriginalMessage); } @@ -241,7 +241,7 @@ final class TranslationExtensionTest extends TestAbstract ]); /** @var \Contributte\Translation\Translator $translator */ - $translator = $container->getByType(ITranslator::class); + $translator = $container->getByType(NetteTranslator::class); Assert::false($translator->returnOriginalMessage); } @@ -256,7 +256,7 @@ final class TranslationExtensionTest extends TestAbstract ], ]); }, - MissingServiceException::class + ServiceCreationException::class ); } diff --git a/tests/Tests/Latte/FiltersTest.phpt b/tests/Tests/Latte/FiltersTest.phpt deleted file mode 100644 index c92d4139..00000000 --- a/tests/Tests/Latte/FiltersTest.phpt +++ /dev/null @@ -1,32 +0,0 @@ -shouldReceive('translate') - ->once() - ->withArgs(['message', 'parameters']) - ->andReturn(''); - - $filters = new Filters($translatorMock); - Assert::same('', $filters->translate(new FilterInfo(), 'message', 'parameters')); - } - -} - -(new FiltersTest($container))->run(); From 7228ee210714d07c6e98b6b0305a78d16bb60947 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 27 May 2026 21:32:45 +0000 Subject: [PATCH 3/6] Use contributte/phpstan meta-package Replace the individual phpstan/phpstan and phpstan-* rule dependencies with contributte/phpstan, and include its bundled phpstan.neon in both config files. https://claude.ai/code/session_01UPxf8e9GsykRoeqXM4j871 --- composer.json | 5 +---- phpstan.lowest.neon | 5 +---- phpstan.neon | 5 +---- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/composer.json b/composer.json index 561cf636..3d251e32 100644 --- a/composer.json +++ b/composer.json @@ -27,6 +27,7 @@ "symfony/config": "^6.0|^7.0|^8.0" }, "require-dev": { + "contributte/phpstan": "^0.3.1", "contributte/qa": "^0.4.0", "contributte/tester": "^0.4.1", "doctrine/orm": "^2.8", @@ -36,10 +37,6 @@ "nette/database": "^3.1.1", "nette/robot-loader": "^3.4.0|^4.0.0", "nette/tester": "^2.3.1", - "phpstan/phpstan": "^2.1", - "phpstan/phpstan-deprecation-rules": "^2.0", - "phpstan/phpstan-nette": "^2.0", - "phpstan/phpstan-strict-rules": "^2.0", "psr/log": "^1.1|^2.0|^3.0", "tracy/tracy": "^2.8" }, diff --git a/phpstan.lowest.neon b/phpstan.lowest.neon index f9a25919..966bca3e 100644 --- a/phpstan.lowest.neon +++ b/phpstan.lowest.neon @@ -1,8 +1,5 @@ includes: - - vendor/phpstan/phpstan-deprecation-rules/rules.neon - - vendor/phpstan/phpstan-nette/extension.neon - - vendor/phpstan/phpstan-nette/rules.neon - - vendor/phpstan/phpstan-strict-rules/rules.neon + - vendor/contributte/phpstan/phpstan.neon parameters: level: max diff --git a/phpstan.neon b/phpstan.neon index 53429826..ab5210cd 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,8 +1,5 @@ includes: - - vendor/phpstan/phpstan-deprecation-rules/rules.neon - - vendor/phpstan/phpstan-nette/extension.neon - - vendor/phpstan/phpstan-nette/rules.neon - - vendor/phpstan/phpstan-strict-rules/rules.neon + - vendor/contributte/phpstan/phpstan.neon parameters: level: max From c10d44df6a6dd75a8f57b7ff5931a0e7e6d59f43 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 27 May 2026 21:37:09 +0000 Subject: [PATCH 4/6] Align tooling files with contributte/kernel Adopt the kernel-style Makefile (direct phpcs/phpcbf with cs2pr under CI, neon-driven phpstan, install target), use the PHP 8.2 contributte/qa ruleset and exclude generated test artifacts, scope phpstan to src, refresh .gitignore/.gitattributes/.editorconfig, and install cs2pr in CI. https://claude.ai/code/session_01UPxf8e9GsykRoeqXM4j871 --- .editorconfig | 2 +- .gitattributes | 8 +++++-- .github/workflows/main.yaml | 8 +++---- .gitignore | 13 +++++++++- Makefile | 47 +++++++++++++++++++++++-------------- phpstan.lowest.neon | 1 - phpstan.neon | 1 - ruleset.xml | 5 +++- 8 files changed, 56 insertions(+), 29 deletions(-) diff --git a/.editorconfig b/.editorconfig index 3faf1497..5e5b915a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,6 +11,6 @@ indent_style = tab indent_size = tab tab_width = 4 -[{*.json, *.yaml, *.yml, *.md}] +[*.{json,yaml,yml,md}] indent_style = space indent_size = 2 diff --git a/.gitattributes b/.gitattributes index 8573f3c1..19a492ca 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,7 +1,11 @@ .docs export-ignore -tests export-ignore +.editorconfig export-ignore .gitattributes export-ignore .gitignore export-ignore +.github export-ignore +Makefile export-ignore +README.md export-ignore +phpstan.lowest.neon export-ignore phpstan.neon export-ignore -readme.md export-ignore ruleset.xml export-ignore +tests export-ignore diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index c4213e60..c232a3f4 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -44,7 +44,7 @@ jobs: with: php-version: "8.2" extensions: "${{ env.extensions }}" - tools: "composer" + tools: "composer, cs2pr" - name: "Setup problem matchers for PHP" run: 'echo "::add-matcher::${{ runner.tool_cache }}/php.json"' @@ -106,7 +106,7 @@ jobs: with: php-version: "8.2" extensions: "${{ env.extensions }}" - tools: "composer" + tools: "composer, cs2pr" - name: "Setup problem matchers for PHP" run: 'echo "::add-matcher::${{ runner.tool_cache }}/php.json"' @@ -179,7 +179,7 @@ jobs: with: php-version: "${{ matrix.php-version }}" extensions: "${{ env.extensions }}" - tools: "composer" + tools: "composer, cs2pr" - name: "Setup problem matchers for PHP" run: 'echo "::add-matcher::${{ runner.tool_cache }}/php.json"' @@ -247,7 +247,7 @@ jobs: php-version: "8.2" coverage: "xdebug" extensions: "${{ env.extensions }}" - tools: "composer" + tools: "composer, cs2pr" - name: "Setup problem matchers for PHP" run: 'echo "::add-matcher::${{ runner.tool_cache }}/php.json"' diff --git a/.gitignore b/.gitignore index a633de2e..8f8c2472 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,15 @@ +# IDE /.idea -/temp + +# Composer /vendor /composer.lock + +# Tests +/temp +/tests/tmp +/coverage.* +/tests/**/*.log +/tests/**/*.html +/tests/**/*.expected +/tests/**/*.actual diff --git a/Makefile b/Makefile index 737e0a04..9fe02ccc 100644 --- a/Makefile +++ b/Makefile @@ -1,27 +1,38 @@ -.PHONY: qa cs csf phpstan tests coverage - -all: - @$(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | sort | egrep -v -e '^[^[:alnum:]]' -e '^$@$$' | xargs - -vendor: composer.json composer.lock - composer install +.PHONY: install +install: + composer update +.PHONY: qa qa: phpstan cs -cs: vendor - vendor/bin/codesniffer src tests +.PHONY: cs +cs: +ifdef GITHUB_ACTION + vendor/bin/phpcs --standard=ruleset.xml --encoding=utf-8 --extensions="php,phpt" --colors -nsp -q --report=checkstyle src tests | cs2pr +else + vendor/bin/phpcs --standard=ruleset.xml --encoding=utf-8 --extensions="php,phpt" --colors -nsp src tests +endif -csf: vendor - vendor/bin/codefixer src tests +.PHONY: csf +csf: + vendor/bin/phpcbf --standard=ruleset.xml --encoding=utf-8 --extensions="php,phpt" --colors -nsp src tests -phpstan: vendor - vendor/bin/phpstan analyse -l max -c phpstan.neon src +.PHONY: phpstan +phpstan: + vendor/bin/phpstan analyse -c phpstan.neon -phpstan-lowest: vendor - vendor/bin/phpstan analyse -l max -c phpstan.lowest.neon src +.PHONY: phpstan-lowest +phpstan-lowest: + vendor/bin/phpstan analyse -c phpstan.lowest.neon -tests: vendor +.PHONY: tests +tests: vendor/bin/tester -s -p php --colors 1 -C tests/Tests -coverage: vendor - vendor/bin/tester -s --colors 1 -C --coverage ./coverage.xml --coverage-src ./src tests/Tests +.PHONY: coverage +coverage: +ifdef GITHUB_ACTION + vendor/bin/tester -s -p php --colors 1 -C --coverage coverage.xml --coverage-src src tests/Tests +else + vendor/bin/tester -s -p php --colors 1 -C --coverage coverage.html --coverage-src src tests/Tests +endif diff --git a/phpstan.lowest.neon b/phpstan.lowest.neon index 966bca3e..68ffeb52 100644 --- a/phpstan.lowest.neon +++ b/phpstan.lowest.neon @@ -5,7 +5,6 @@ parameters: level: max paths: - src - - tests excludePaths: - src/Latte/TranslatorExtension.php - src/Latte/Nodes/* diff --git a/phpstan.neon b/phpstan.neon index ab5210cd..2f62e33d 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -5,7 +5,6 @@ parameters: level: max paths: - src - - tests ignoreErrors: - count: 2 diff --git a/ruleset.xml b/ruleset.xml index 725ed8c6..0964e8ae 100644 --- a/ruleset.xml +++ b/ruleset.xml @@ -1,6 +1,9 @@ - + tests/tmp + temp + + From 3c038a951dc818979c1a31b1ababb56f7c2dbbbb Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 28 May 2026 19:17:42 +0000 Subject: [PATCH 5/6] Import classes used in PHPDoc annotations Drop the project-level FullyQualifiedClassNameInAnnotation rule (kernel inherits contributte/qa's default that does not require FQ class names in annotations) and convert the remaining fully-qualified throws/use sites across src and tests to imported short names. https://claude.ai/code/session_01UPxf8e9GsykRoeqXM4j871 --- ruleset.xml | 3 --- src/DI/TranslationExtension.php | 12 +++++++----- src/Latte/TranslatorExtension.php | 5 ++++- src/Loaders/DatabaseAbstract.php | 8 +++++--- src/Loaders/Doctrine.php | 2 +- src/Loaders/Neon.php | 8 +++++--- src/Loaders/NetteDatabase.php | 2 +- src/Tracy/Panel.php | 2 +- src/Translator.php | 2 +- tests/Tests/DI/TranslationExtensionTest.phpt | 6 +++--- tests/Tests/LocaleResolverTest.phpt | 5 +++-- tests/Tests/TranslatorTest.phpt | 13 +++++++------ 12 files changed, 38 insertions(+), 30 deletions(-) diff --git a/ruleset.xml b/ruleset.xml index 0964e8ae..5a81fd44 100644 --- a/ruleset.xml +++ b/ruleset.xml @@ -12,9 +12,6 @@ src/Latte/Nodes/* - - - diff --git a/src/DI/TranslationExtension.php b/src/DI/TranslationExtension.php index fdf4bf62..31908faf 100644 --- a/src/DI/TranslationExtension.php +++ b/src/DI/TranslationExtension.php @@ -18,6 +18,8 @@ use Contributte\Translation\Translator; use Nette\Bridges\ApplicationLatte\LatteFactory; use Nette\DI\CompilerExtension; +use Nette\DI\Definitions\FactoryDefinition; +use Nette\DI\Definitions\ServiceDefinition; use Nette\DI\Definitions\Statement; use Nette\DI\MissingServiceException; use Nette\Localization\Translator as NetteTranslator; @@ -128,7 +130,7 @@ static function ( } /** - * @throws \Contributte\Translation\Exceptions\InvalidArgument|\ReflectionException + * @throws InvalidArgument|\ReflectionException */ public function loadConfiguration(): void { @@ -248,26 +250,26 @@ public function loadConfiguration(): void } /** - * @throws \Contributte\Translation\Exceptions\InvalidArgument|\ReflectionException + * @throws InvalidArgument|\ReflectionException */ public function beforeCompile(): void { $builder = $this->getContainerBuilder(); $config = $this->config; - /** @var \Nette\DI\Definitions\ServiceDefinition $translator */ + /** @var ServiceDefinition $translator */ $translator = $builder->getDefinition($this->prefix('translator')); $whitelistRegexp = Helpers::whitelistRegexp($config['locales']['whitelist']); if ($config['debug'] && $config['debugger']) { - /** @var \Nette\DI\Definitions\ServiceDefinition $tracyPanel */ + /** @var ServiceDefinition $tracyPanel */ $tracyPanel = $builder->getDefinition($this->prefix('tracyPanel')); } $latteFactoryName = $config['latteFactory'] !== null ? $builder->getByType($config['latteFactory']) : null; if ($latteFactoryName !== null) { - /** @var \Nette\DI\Definitions\FactoryDefinition $latteFactory */ + /** @var FactoryDefinition $latteFactory */ $latteFactory = $builder->getDefinition($latteFactoryName); $latteExtension = $builder->addDefinition($this->prefix('latte.extension')) diff --git a/src/Latte/TranslatorExtension.php b/src/Latte/TranslatorExtension.php index 1777a08e..11abd01a 100644 --- a/src/Latte/TranslatorExtension.php +++ b/src/Latte/TranslatorExtension.php @@ -5,6 +5,7 @@ use Contributte\Translation\Helpers; use Contributte\Translation\Latte\Nodes\TranslateNode; use Contributte\Translation\Latte\Nodes\TranslatorNode; +use Generator; use Latte\Compiler\Node; use Latte\Compiler\Nodes\Php\ArgumentNode; use Latte\Compiler\Nodes\Php\Expression\ArrayNode; @@ -17,9 +18,11 @@ use Latte\Compiler\Nodes\Php\Scalar\NullNode; use Latte\Compiler\Nodes\PrintNode; use Latte\Compiler\Tag; +use Latte\Compiler\TemplateParser; use Latte\Extension; use Latte\Runtime\FilterInfo; use Nette\Localization\Translator; +use stdClass; class TranslatorExtension extends Extension { @@ -34,7 +37,7 @@ public function __construct( } /** - * @return array + * @return array */ public function getTags(): array { diff --git a/src/Loaders/DatabaseAbstract.php b/src/Loaders/DatabaseAbstract.php index 8176c8bb..0cf9a0de 100644 --- a/src/Loaders/DatabaseAbstract.php +++ b/src/Loaders/DatabaseAbstract.php @@ -8,6 +8,8 @@ use Symfony\Component\Translation\Loader\ArrayLoader; use Symfony\Component\Translation\Loader\LoaderInterface; use Symfony\Component\Translation\MessageCatalogue; +use function is_readable; +use function is_string; abstract class DatabaseAbstract extends ArrayLoader implements LoaderInterface { @@ -22,7 +24,7 @@ abstract class DatabaseAbstract extends ArrayLoader implements LoaderInterface /** * {@inheritdoc} * - * @throws \Contributte\Translation\Exceptions\InvalidArgument + * @throws InvalidArgument */ public function load( mixed $resource, @@ -30,11 +32,11 @@ public function load( string $domain = 'messages' ): MessageCatalogue { - if (!\is_string($resource)) { + if (!is_string($resource)) { throw new InvalidArgument('Parameter resource must be string.'); } - if (!\is_readable($resource)) { + if (!is_readable($resource)) { throw new InvalidArgument('Something wrong with resource file "' . $resource . '".'); } diff --git a/src/Loaders/Doctrine.php b/src/Loaders/Doctrine.php index 4495c553..6baf8382 100644 --- a/src/Loaders/Doctrine.php +++ b/src/Loaders/Doctrine.php @@ -20,7 +20,7 @@ public function __construct( /** * @inheritdoc - * @throws \Contributte\Translation\Exceptions\InvalidState + * @throws InvalidState */ protected function getMessages( array $config, diff --git a/src/Loaders/Neon.php b/src/Loaders/Neon.php index 4ffc8e6c..622a8514 100644 --- a/src/Loaders/Neon.php +++ b/src/Loaders/Neon.php @@ -8,6 +8,8 @@ use Symfony\Component\Translation\Loader\ArrayLoader; use Symfony\Component\Translation\Loader\LoaderInterface; use Symfony\Component\Translation\MessageCatalogue; +use function is_readable; +use function is_string; class Neon extends ArrayLoader implements LoaderInterface { @@ -15,7 +17,7 @@ class Neon extends ArrayLoader implements LoaderInterface /** * {@inheritdoc} * - * @throws \Contributte\Translation\Exceptions\InvalidArgument + * @throws InvalidArgument */ public function load( mixed $resource, @@ -23,11 +25,11 @@ public function load( string $domain = 'messages' ): MessageCatalogue { - if (!\is_string($resource)) { + if (!is_string($resource)) { throw new InvalidArgument('Parameter resource must be string.'); } - if (!\is_readable($resource)) { + if (!is_readable($resource)) { throw new InvalidArgument('Something wrong with resource file "' . $resource . '".'); } diff --git a/src/Loaders/NetteDatabase.php b/src/Loaders/NetteDatabase.php index 8cc07dca..16ecedd5 100644 --- a/src/Loaders/NetteDatabase.php +++ b/src/Loaders/NetteDatabase.php @@ -20,7 +20,7 @@ public function __construct( /** * @inheritdoc - * @throws \Contributte\Translation\Exceptions\InvalidState + * @throws InvalidState */ protected function getMessages( array $config, diff --git a/src/Tracy/Panel.php b/src/Tracy/Panel.php index 33618a45..4cbabf46 100644 --- a/src/Tracy/Panel.php +++ b/src/Tracy/Panel.php @@ -17,7 +17,7 @@ class Panel implements IBarPanel private int $missingTranslationCount = 0; - /** @var array<\Contributte\Translation\LocalesResolvers\ResolverInterface> */ + /** @var array */ private array $localeResolvers = []; /** @var array> */ diff --git a/src/Translator.php b/src/Translator.php index ff763f65..2e98c547 100644 --- a/src/Translator.php +++ b/src/Translator.php @@ -188,7 +188,7 @@ public function setTranslateKeyGenerator( } /** - * @throws \Contributte\Translation\Exceptions\InvalidArgument + * @throws InvalidArgument */ public function removePrefix( ?string $string = null diff --git a/tests/Tests/DI/TranslationExtensionTest.phpt b/tests/Tests/DI/TranslationExtensionTest.phpt index 4db2ccb1..7d6ed88d 100644 --- a/tests/Tests/DI/TranslationExtensionTest.phpt +++ b/tests/Tests/DI/TranslationExtensionTest.phpt @@ -209,7 +209,7 @@ final class TranslationExtensionTest extends TestAbstract ], ]); - /** @var \Contributte\Translation\Translator $translator */ + /** @var Translator $translator */ $translator = $container->getByType(NetteTranslator::class); Assert::type(CustomTranslatorMock::class, $translator); @@ -226,7 +226,7 @@ final class TranslationExtensionTest extends TestAbstract ], ]); - /** @var \Contributte\Translation\Translator $translator */ + /** @var Translator $translator */ $translator = $container->getByType(NetteTranslator::class); Assert::false($translator->returnOriginalMessage); @@ -240,7 +240,7 @@ final class TranslationExtensionTest extends TestAbstract ], ]); - /** @var \Contributte\Translation\Translator $translator */ + /** @var Translator $translator */ $translator = $container->getByType(NetteTranslator::class); Assert::false($translator->returnOriginalMessage); diff --git a/tests/Tests/LocaleResolverTest.phpt b/tests/Tests/LocaleResolverTest.phpt index 666fea7a..6a29db3b 100644 --- a/tests/Tests/LocaleResolverTest.phpt +++ b/tests/Tests/LocaleResolverTest.phpt @@ -2,6 +2,7 @@ namespace Tests; +use Contributte\Translation\Translator; use Nette\Localization\ITranslator; use Tester\Assert; @@ -21,10 +22,10 @@ final class LocaleResolverTest extends TestAbstract ], ]); - /** @var \Contributte\Translation\Translator $translator */ + /** @var Translator $translator */ $translator = $container->getByType(ITranslator::class); - /** @var \Tests\LocaleResolverMock $mockResolver */ + /** @var LocaleResolverMock $mockResolver */ $mockResolver = $container->getByType(LocaleResolverMock::class); $localeResolver = $translator->getLocaleResolver(); diff --git a/tests/Tests/TranslatorTest.phpt b/tests/Tests/TranslatorTest.phpt index fb135bbb..3c12a9ab 100644 --- a/tests/Tests/TranslatorTest.phpt +++ b/tests/Tests/TranslatorTest.phpt @@ -10,6 +10,7 @@ use Contributte\Translation\Translator; use Contributte\Translation\Wrappers\Message; use Contributte\Translation\Wrappers\NotTranslate; use Latte\CompileException; +use Latte\Engine; use Mockery; use Nette\Bridges\ApplicationLatte\ILatteFactory; use Nette\DI\Container; @@ -91,7 +92,7 @@ final class TranslatorTest extends TestAbstract { $container = Helpers::createContainerFromConfigurator($this->container->getParameters()['tempDir']); - /** @var \Contributte\Translation\Translator $translator */ + /** @var Translator $translator */ $translator = $container->getByType(ITranslator::class); Assert::throws(static function () use ($translator): void { @@ -223,10 +224,10 @@ final class TranslatorTest extends TestAbstract { $container = Helpers::createContainerFromConfigurator($this->container->getParameters()['tempDir']); - /** @var \Latte\Engine $latte */ + /** @var Engine $latte */ $latte = $container->getByType(ILatteFactory::class)->create(); - /** @var \Contributte\Translation\Translator $translator */ + /** @var Translator $translator */ $translator = $container->getByType(ITranslator::class); Assert::same('Hello', $latte->renderToString(FileMock::create('{_messages.hello}'))); @@ -244,7 +245,7 @@ final class TranslatorTest extends TestAbstract Assert::same('Hello', $latte->renderToString(FileMock::create('{_hello, null, [], messages, en}'))); Assert::same('Hello', $latte->renderToString(FileMock::create('{php $message = "hello"}{$message|translate: null, [], messages, en}'))); - if (version_compare(\Latte\Engine::VERSION, '3', '<')) { + if (version_compare(Engine::VERSION, '3', '<')) { Assert::same('Hello', $latte->renderToString(FileMock::create('{_}messages.hello{/_}'))); Assert::same('Hello', $latte->renderToString(FileMock::create('{_}{php $message = "messages.hello"}{$message}{/_}'))); @@ -314,10 +315,10 @@ final class TranslatorTest extends TestAbstract ], ]); - /** @var \Contributte\Translation\Tracy\Panel $panel */ + /** @var Panel $panel */ $panel = $container->getByType(Panel::class); - /** @var \Contributte\Translation\Translator $translator */ + /** @var Translator $translator */ $translator = $container->getByType(ITranslator::class); $translator->translate('untranslated');// add missing translation From f019ec65ea3307be832f08fd67acbfee86db6b45 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 29 May 2026 21:16:54 +0000 Subject: [PATCH 6/6] ci: surface tester output on test job failure (temporary) Tees tester.out and posts it as a PR comment when the PHP 8.4 tests job fails, so the failure cause is visible from outside the runner. https://claude.ai/code/session_01UPxf8e9GsykRoeqXM4j871 --- .github/workflows/main.yaml | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index c232a3f4..f10f3e21 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -202,7 +202,30 @@ jobs: run: 'echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"' - name: "Tests" - run: "make tests" + run: "make tests 2>&1 | tee tester.out" + + - name: "Surface test failure" + if: failure() && matrix.php-version == '8.4' && matrix.composer-args == '' + env: + GH_TOKEN: ${{ github.token }} + run: | + { + echo '
tester output (PHP ${{ matrix.php-version }})' + echo + echo '```' + sed -E 's/\x1b\[[0-9;]*m//g' tester.out | tail -200 + echo '```' + for f in $(find tests -name '*.actual' 2>/dev/null); do + echo + echo "**$f**" + echo + echo '```' + cat "$f" + echo '```' + done + echo '
' + } > comment.md + gh pr comment ${{ github.event.pull_request.number }} --body-file comment.md tests-code-coverage: if: "github.event_name == 'push'"