From 4edaf94eeaf58033e0d34baa54bcb34a70b78c91 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sun, 3 Aug 2025 01:33:34 +0800 Subject: [PATCH] refactor: fix phpdoc and improve code in `Language` --- psalm-baseline.xml | 8 +- system/Common.php | 2 + system/Language/Language.php | 142 ++++++------ system/Language/en/Language.php | 1 + system/Test/Mock/MockLanguage.php | 11 +- .../_support/Language/SecondMockLanguage.php | 11 +- tests/system/Language/LanguageTest.php | 210 ++++++++---------- utils/phpstan-baseline/loader.neon | 2 +- .../missingType.iterableValue.neon | 62 +----- 9 files changed, 198 insertions(+), 251 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 778bdd654739..160ba4d97cd8 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,5 +1,5 @@ - + |parser_callable_string|parser_callable>]]> @@ -68,6 +68,12 @@ + + + + + + |parser_callable_string|parser_callable>]]> diff --git a/system/Common.php b/system/Common.php index 3f69838e0b80..7e49231c6a18 100644 --- a/system/Common.php +++ b/system/Common.php @@ -729,6 +729,8 @@ function is_windows(?bool $mock = null): bool * A convenience method to translate a string or array of them and format * the result with the intl extension's MessageFormatter. * + * @param array $args + * * @return list|string */ function lang(string $line, array $args = [], ?string $locale = null) diff --git a/system/Language/Language.php b/system/Language/Language.php index a4034bdf6b90..652d17a5b68d 100644 --- a/system/Language/Language.php +++ b/system/Language/Language.php @@ -21,6 +21,8 @@ * * Locale-based, built on top of PHP internationalization. * + * @phpstan-type LoadedStrings array|string>|string|list> + * * @see \CodeIgniter\Language\LanguageTest */ class Language @@ -30,20 +32,19 @@ class Language * from files for faster retrieval on * second use. * - * @var array + * @var array> */ protected $language = []; /** - * The current language/locale to work with. + * The current locale to work with. * - * @var string + * @var non-empty-string */ protected $locale; /** - * Boolean value whether the intl - * libraries exist on the system. + * Boolean value whether the `intl` extension exists on the system. * * @var bool */ @@ -53,10 +54,13 @@ class Language * Stores filenames that have been * loaded so that we don't load them again. * - * @var array + * @var array> */ protected $loadedFiles = []; + /** + * @param non-empty-string $locale + */ public function __construct(string $locale) { $this->locale = $locale; @@ -69,6 +73,8 @@ public function __construct(string $locale) /** * Sets the current locale to use when performing string lookups. * + * @param non-empty-string|null $locale + * * @return $this */ public function setLocale(?string $locale = null) @@ -89,22 +95,24 @@ public function getLocale(): string * Parses the language string for a file, loads the file, if necessary, * getting the line. * + * @param array $args + * * @return list|string */ public function getLine(string $line, array $args = []) { - // if no file is given, just parse the line + // 1. Format the line as-is if it does not have a file. if (! str_contains($line, '.')) { return $this->formatMessage($line, $args); } - // Parse out the file name and the actual alias. - // Will load the language file and strings. + // 2. Get the formatted line using the file and line extracted from $line and the current locale. [$file, $parsedLine] = $this->parseLine($line, $this->locale); $output = $this->getTranslationOutput($this->locale, $file, $parsedLine); - if ($output === null && strpos($this->locale, '-')) { + // 3. If not found, try the locale without region (e.g., 'en-US' -> 'en'). + if ($output === null && str_contains($this->locale, '-')) { [$locale] = explode('-', $this->locale, 2); [$file, $parsedLine] = $this->parseLine($line, $locale); @@ -112,57 +120,66 @@ public function getLine(string $line, array $args = []) $output = $this->getTranslationOutput($locale, $file, $parsedLine); } - // if still not found, try English + // 4. If still not found, try English. if ($output === null) { [$file, $parsedLine] = $this->parseLine($line, 'en'); $output = $this->getTranslationOutput('en', $file, $parsedLine); } + // 5. Fallback to the original line if no translation was found. $output ??= $line; return $this->formatMessage($output, $args); } /** - * @return array|string|null + * @return list|string|null */ protected function getTranslationOutput(string $locale, string $file, string $parsedLine) { $output = $this->language[$locale][$file][$parsedLine] ?? null; + if ($output !== null) { return $output; } - foreach (explode('.', $parsedLine) as $row) { - if (! isset($current)) { - $current = $this->language[$locale][$file] ?? null; - } + // Fallback: try to traverse dot notation + $current = $this->language[$locale][$file] ?? null; + + if (is_array($current)) { + foreach (explode('.', $parsedLine) as $segment) { + $output = $current[$segment] ?? null; + + if ($output === null) { + break; + } - $output = $current[$row] ?? null; - if (is_array($output)) { - $current = $output; + if (is_array($output)) { + $current = $output; + } } - } - if ($output !== null) { - return $output; + if ($output !== null && ! is_array($output)) { + return $output; + } } - $row = current(explode('.', $parsedLine)); - $key = substr($parsedLine, strlen($row) + 1); + // Final fallback: try two-level access manually + [$first, $rest] = explode('.', $parsedLine, 2) + ['', '']; - return $this->language[$locale][$file][$row][$key] ?? null; + return $this->language[$locale][$file][$first][$rest] ?? null; } /** * Parses the language string which should include the * filename as the first segment (separated by period). + * + * @return array{non-empty-string, non-empty-string} */ protected function parseLine(string $line, string $locale): array { - $file = substr($line, 0, strpos($line, '.')); - $line = substr($line, strlen($file) + 1); + [$file, $line] = explode('.', $line, 2); if (! isset($this->language[$locale][$file]) || ! array_key_exists($line, $this->language[$locale][$file])) { $this->load($file, $locale); @@ -174,10 +191,10 @@ protected function parseLine(string $line, string $locale): array /** * Advanced message formatting. * - * @param array|string $message - * @param list $args + * @param list|string $message + * @param array $args * - * @return array|string + * @return ($message is list ? list : string) */ protected function formatMessage($message, array $args = []) { @@ -194,32 +211,27 @@ protected function formatMessage($message, array $args = []) } $formatted = MessageFormatter::formatMessage($this->locale, $message, $args); + if ($formatted === false) { // Format again to get the error message. try { - $fmt = new MessageFormatter($this->locale, $message); - $formatted = $fmt->format($args); - $fmtError = '"' . $fmt->getErrorMessage() . '" (' . $fmt->getErrorCode() . ')'; + $formatter = new MessageFormatter($this->locale, $message); + $formatted = $formatter->format($args); + $fmtError = sprintf('"%s" (%d)', $formatter->getErrorMessage(), $formatter->getErrorCode()); } catch (IntlException $e) { - $fmtError = '"' . $e->getMessage() . '" (' . $e->getCode() . ')'; + $fmtError = sprintf('"%s" (%d)', $e->getMessage(), $e->getCode()); } - $argsString = implode( - ', ', - array_map(static fn ($element): string => '"' . $element . '"', $args), - ); - $argsUrlEncoded = implode( - ', ', - array_map(static fn ($element): string => '"' . rawurlencode($element) . '"', $args), - ); - - log_message( - 'error', - 'Language.invalidMessageFormat: $message: "' . $message - . '", $args: ' . $argsString - . ' (urlencoded: ' . $argsUrlEncoded . '),' - . ' MessageFormatter Error: ' . $fmtError, - ); + $argsAsString = sprintf('"%s"', implode('", "', $args)); + $urlEncodedArgs = sprintf('"%s"', implode('", "', array_map(rawurlencode(...), $args))); + + log_message('error', sprintf( + 'Invalid message format: $message: "%s", $args: %s (urlencoded: %s), MessageFormatter Error: %s', + $message, + $argsAsString, + $urlEncodedArgs, + $fmtError, + )); return $message . "\n【Warning】Also, invalid string(s) was passed to the Language class. See log file for details."; } @@ -232,7 +244,7 @@ protected function formatMessage($message, array $args = []) * will return the file's contents, otherwise will merge with * the existing language lines. * - * @return list|null + * @return ($return is true ? LoadedStrings : null) */ protected function load(string $file, string $locale, bool $return = false) { @@ -270,8 +282,9 @@ protected function load(string $file, string $locale, bool $return = false) } /** - * A simple method for including files that can be - * overridden during testing. + * A simple method for including files that can be overridden during testing. + * + * @return LoadedStrings */ protected function requireFile(string $path): array { @@ -279,20 +292,25 @@ protected function requireFile(string $path): array $strings = []; foreach ($files as $file) { - // On some OS's we were seeing failures - // on this command returning boolean instead - // of array during testing, so we've removed - // the require_once for now. if (is_file($file)) { - $strings[] = require $file; + // On some OS, we were seeing failures on this command returning boolean instead + // of array during testing, so we've removed the require_once for now. + $loadedStrings = require $file; + + if (is_array($loadedStrings)) { + /** @var LoadedStrings $loadedStrings */ + $strings[] = $loadedStrings; + } } } - if (isset($strings[1])) { - $string = array_shift($strings); + $count = count($strings); + + if ($count > 1) { + $base = array_shift($strings); - $strings = array_replace_recursive($string, ...$strings); - } elseif (isset($strings[0])) { + $strings = array_replace_recursive($base, ...$strings); + } elseif ($count === 1) { $strings = $strings[0]; } diff --git a/system/Language/en/Language.php b/system/Language/en/Language.php index 16456c3cc472..2d638c8e3f54 100644 --- a/system/Language/en/Language.php +++ b/system/Language/en/Language.php @@ -13,5 +13,6 @@ // "Language" language settings return [ + // @deprecated v4.6.3 - never used 'invalidMessageFormat' => 'Invalid message format: "{0}", args: "{1}"', ]; diff --git a/system/Test/Mock/MockLanguage.php b/system/Test/Mock/MockLanguage.php index 4ae1196ca053..3102bb91755b 100644 --- a/system/Test/Mock/MockLanguage.php +++ b/system/Test/Mock/MockLanguage.php @@ -15,13 +15,16 @@ use CodeIgniter\Language\Language; +/** + * @phpstan-import-type LoadedStrings from Language + */ class MockLanguage extends Language { /** * Stores the data that should be * returned by the 'requireFile()' method. * - * @var mixed + * @var LoadedStrings|null */ protected $data; @@ -30,18 +33,24 @@ class MockLanguage extends Language * 'requireFile()' method to allow easy overrides * during testing. * + * @param LoadedStrings $data + * * @return $this */ public function setData(string $file, array $data, ?string $locale = null) { $this->language[$locale ?? $this->locale][$file] = $data; + $this->data = $data; + return $this; } /** * Provides an override that allows us to set custom * data to be returned easily during testing. + * + * @return LoadedStrings */ protected function requireFile(string $path): array { diff --git a/tests/_support/Language/SecondMockLanguage.php b/tests/_support/Language/SecondMockLanguage.php index 2f667312e499..cf7da6ff1599 100644 --- a/tests/_support/Language/SecondMockLanguage.php +++ b/tests/_support/Language/SecondMockLanguage.php @@ -15,20 +15,27 @@ use CodeIgniter\Language\Language; +/** + * @phpstan-import-type LoadedStrings from Language + */ class SecondMockLanguage extends Language { /** * Expose the protected *load* method + * + * @return ($return is true ? LoadedStrings : null) */ - public function loadem(string $file, string $locale = 'en', bool $return = false) + public function loadem(string $file, string $locale = 'en', bool $return = false): ?array { return $this->load($file, $locale, $return); } /** * Expose the loaded language files + * + * @return list */ - public function loaded(string $locale = 'en') + public function loaded(string $locale = 'en'): array { return $this->loadedFiles[$locale]; } diff --git a/tests/system/Language/LanguageTest.php b/tests/system/Language/LanguageTest.php index 751cc7532484..749a30e88b3d 100644 --- a/tests/system/Language/LanguageTest.php +++ b/tests/system/Language/LanguageTest.php @@ -15,9 +15,9 @@ use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\Mock\MockLanguage; -use MessageFormatter; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\RequiresPhpExtension; use Tests\Support\Language\SecondMockLanguage; /** @@ -26,16 +26,10 @@ #[Group('Others')] final class LanguageTest extends CIUnitTestCase { - private Language $lang; - - protected function setUp(): void - { - $this->lang = new Language('en'); - } - public function testReturnsStringWithNoFileInMessage(): void { - $this->assertSame('something', $this->lang->getLine('something')); + $lang = new MockLanguage('en'); + $this->assertSame('something', $lang->getLine('something')); } /** @@ -43,88 +37,84 @@ public function testReturnsStringWithNoFileInMessage(): void */ public function testReturnParsedStringWithNoFileInMessage(): void { - $this->lang->setLocale('en-GB'); + $lang = new MockLanguage('en'); + $lang->setLocale('en-GB'); $line = '{price, number, currency}'; - $this->assertSame('£7.41', $this->lang->getLine($line, ['price' => '7.41'])); + $this->assertSame('£7.41', $lang->getLine($line, ['price' => '7.41'])); $this->assertSame('£7.41', lang($line, ['price' => '7.41'], 'en-GB')); } public function testGetLineReturnsLine(): void { - $this->lang = new MockLanguage('en'); - - $this->lang->setData('books', [ + $lang = new MockLanguage('en'); + $lang->setData('books', [ 'bookSaved' => 'We kept the book free from the boogeyman', 'booksSaved' => 'We saved some more', ]); - $this->assertSame('We saved some more', $this->lang->getLine('books.booksSaved')); + $this->assertSame('We saved some more', $lang->getLine('books.booksSaved')); } public function testGetLineReturnsLineWithKeyWithDots(): void { - $this->lang = new MockLanguage('en'); - - $this->lang->setData('books', [ + $lang = new MockLanguage('en'); + $lang->setData('books', [ 'bookSaved.foo' => 'We kept the book free from the boogeyman', 'booksSaved.bar.baz' => 'We saved some more', ]); $this->assertSame( 'We kept the book free from the boogeyman', - $this->lang->getLine('books.bookSaved.foo'), + $lang->getLine('books.bookSaved.foo'), ); $this->assertSame( 'We saved some more', - $this->lang->getLine('books.booksSaved.bar.baz'), + $lang->getLine('books.booksSaved.bar.baz'), ); } public function testGetLineCannotUseKeysWithLeadingDot(): void { - $this->lang = new MockLanguage('en'); - - $this->lang->setData('books', [ + $lang = new MockLanguage('en'); + $lang->setData('books', [ '.bookSaved.foo.' => 'We kept the book free from the boogeyman', '.booksSaved.bar.baz.' => 'We saved some more', ]); $this->assertSame( 'books.bookSaved.foo', // Can't get the message. - $this->lang->getLine('books.bookSaved.foo'), + $lang->getLine('books.bookSaved.foo'), ); $this->assertSame( 'books.booksSaved.bar.baz', // Can't get the message. - $this->lang->getLine('books.booksSaved.bar.baz'), + $lang->getLine('books.booksSaved.bar.baz'), ); } public function testGetLineCannotUseKeysWithTrailingDot(): void { - $this->lang = new MockLanguage('en'); - - $this->lang->setData('books', [ + $lang = new MockLanguage('en'); + $lang->setData('books', [ 'bookSaved.foo.' => 'We kept the book free from the boogeyman', 'booksSaved.bar.baz.' => 'We saved some more', ]); $this->assertSame( 'books.bookSaved.foo', // Can't get the message. - $this->lang->getLine('books.bookSaved.foo'), + $lang->getLine('books.bookSaved.foo'), ); $this->assertSame( 'books.booksSaved.bar.baz', // Can't get the message. - $this->lang->getLine('books.booksSaved.bar.baz'), + $lang->getLine('books.booksSaved.bar.baz'), ); } public function testGetLineReturnsFallbackLine(): void { - $this->lang = new MockLanguage('en'); - - $this->lang + $lang = new MockLanguage('en'); + $lang ->setLocale('en-US') ->setData('equivalent', [ 'touchWood' => 'touch wood', @@ -137,82 +127,58 @@ public function testGetLineReturnsFallbackLine(): void 'slowcoach' => 'slowpoke', ], 'en-US'); - $this->assertSame('lay of the land', $this->lang->getLine('equivalent.lieOfLand')); - $this->assertSame('slowpoke', $this->lang->getLine('equivalent.slowcoach')); - $this->assertSame('a new lease of life', $this->lang->getLine('equivalent.leaseOfLife')); - $this->assertSame('touch wood', $this->lang->getLine('equivalent.touchWood')); - $this->assertSame('equivalent.unknown', $this->lang->getLine('equivalent.unknown')); + $this->assertSame('lay of the land', $lang->getLine('equivalent.lieOfLand')); + $this->assertSame('slowpoke', $lang->getLine('equivalent.slowcoach')); + $this->assertSame('a new lease of life', $lang->getLine('equivalent.leaseOfLife')); + $this->assertSame('touch wood', $lang->getLine('equivalent.touchWood')); + $this->assertSame('equivalent.unknown', $lang->getLine('equivalent.unknown')); } public function testGetLineArrayReturnsLineArray(): void { - $this->lang = new MockLanguage('en'); - - $this->lang->setData('books', [ - 'booksList' => [ - 'The Boogeyman', - 'We Saved', - ], - ]); + $lang = new MockLanguage('en'); + $lang->setData('books', ['booksList' => ['The Boogeyman', 'We Saved']]); - $this->assertSame([ - 'The Boogeyman', - 'We Saved', - ], $this->lang->getLine('books.booksList')); + $this->assertSame(['The Boogeyman', 'We Saved'], $lang->getLine('books.booksList')); } + #[RequiresPhpExtension('intl')] public function testGetLineFormatsMessage(): void { - // No intl extension? then we can't test this - go away.... - if (! class_exists(MessageFormatter::class)) { - $this->markTestSkipped('No intl support.'); - } - - $this->lang = new MockLanguage('en'); - - $this->lang->setData('books', [ + $lang = new MockLanguage('en'); + $lang->setData('books', [ 'bookCount' => '{0, number, integer} books have been saved.', ]); - $this->assertSame('45 books have been saved.', $this->lang->getLine('books.bookCount', [91 / 2])); + $this->assertSame('45 books have been saved.', $lang->getLine('books.bookCount', [91 / 2])); } + #[RequiresPhpExtension('intl')] public function testGetLineArrayFormatsMessages(): void { - // No intl extension? Then we can't test this - go away... - if (! class_exists(MessageFormatter::class)) { - $this->markTestSkipped('No intl support.'); - } - - $this->lang = new MockLanguage('en'); - - $this->lang->setData('books', [ + $lang = new MockLanguage('en'); + $lang->setData('books', [ 'bookList' => [ '{0, number, integer} related books.', ], ]); - $this->assertSame(['45 related books.'], $this->lang->getLine('books.bookList', [91 / 2])); + $this->assertSame(['45 related books.'], $lang->getLine('books.bookList', [91 / 2])); } /** * @see https://github.com/codeigniter4/shield/issues/851 */ + #[RequiresPhpExtension('intl')] public function testGetLineInvalidFormatMessage(): void { - // No intl extension? then we can't test this - go away.... - if (! class_exists(MessageFormatter::class)) { - $this->markTestSkipped('No intl support.'); - } - - $this->lang = new MockLanguage('en'); - - $this->lang->setLocale('ar'); + $lang = new MockLanguage('en'); + $lang->setLocale('ar'); $line = 'تم الكشف عن كلمة المرور {0} بسبب اختراق البيانات وشوهدت {1 ، عدد} مرة في {2} في كلمات المرور المخترقة.'; - $this->lang->setData('Auth', ['errorPasswordPwned' => $line]); + $lang->setData('Auth', ['errorPasswordPwned' => $line]); - $output = $this->lang->getLine('Auth.errorPasswordPwned', ['password', 'hits', 'wording']); + $output = $lang->getLine('Auth.errorPasswordPwned', ['password', 'hits', 'wording']); $this->assertSame($line . "\n【Warning】Also, invalid string(s) was passed to the Language class. See log file for details.", $output); } @@ -231,26 +197,24 @@ public function testLangAllowsOtherLocales(): void public function testLangDoesntFormat(): void { - $this->lang = new MockLanguage('en'); - - $this->lang->disableIntlSupport(); - - $this->lang->setData('books', [ + $lang = new MockLanguage('en'); + $lang->disableIntlSupport(); + $lang->setData('books', [ 'bookList' => [ '{0, number, integer} related books.', ], ]); - $this->assertSame(['{0, number, integer} related books.'], $this->lang->getLine('books.bookList', [15])); + $this->assertSame(['{0, number, integer} related books.'], $lang->getLine('books.bookList', [15])); } public function testLanguageDuplicateKey(): void { - $this->lang = new Language('en'); - $this->assertSame('These are not the droids you are looking for', $this->lang->getLine('More.strongForce', [])); - $this->assertSame('I have a very bad feeling about this', $this->lang->getLine('More.cannotMove', [])); - $this->assertSame('Could not move file "{0}" to "{1}". Reason: {2}', $this->lang->getLine('Files.cannotMove', [])); - $this->assertSame('I have a very bad feeling about this', $this->lang->getLine('More.cannotMove', [])); + $lang = new Language('en'); + $this->assertSame('These are not the droids you are looking for', $lang->getLine('More.strongForce', [])); + $this->assertSame('I have a very bad feeling about this', $lang->getLine('More.cannotMove', [])); + $this->assertSame('Could not move file "{0}" to "{1}". Reason: {2}', $lang->getLine('Files.cannotMove', [])); + $this->assertSame('I have a very bad feeling about this', $lang->getLine('More.cannotMove', [])); } public function testLanguageFileLoading(): void @@ -295,8 +259,8 @@ public function testLanguageSameKeyAndFileName(): void public function testGetLocale(): void { - $this->lang = service('language', 'en', false); - $this->assertSame('en', $this->lang->getLocale()); + $lang = service('language', 'en', false); + $this->assertSame('en', $lang->getLocale()); } public function testPrioritizedLocator(): void @@ -311,19 +275,21 @@ public function testPrioritizedLocator(): void /** * There's not a whole lot that can be done with message bundles, * but we can at least try loading them ... more accurate code coverage? - * - * @param mixed $bundle */ #[DataProvider('provideBundleUniqueKeys')] - public function testBundleUniqueKeys($bundle): void + public function testBundleUniqueKeys(string $bundle): void { $messages = require SYSTEMPATH . 'Language/en/' . $bundle . '.php'; + $this->assertIsArray($messages); $this->assertGreaterThan(0, count($messages)); } + /** + * @return iterable + */ public static function provideBundleUniqueKeys(): iterable { - return [ + yield from [ ['CLI'], ['Cache'], ['Cast'], @@ -348,22 +314,20 @@ public static function provideBundleUniqueKeys(): iterable ]; } - // Testing base locale vs variants - - public function testBaseFallbacks(): void + public function testBaseLocaleVsVariants(): void { - $this->lang = service('language', 'en-ZZ', false); + $lang = service('language', 'en-ZZ', false); // key is in both base and variant; should pick variant - $this->assertSame("It's made of cheese", $this->lang->getLine('More.notaMoon')); + $this->assertSame("It's made of cheese", $lang->getLine('More.notaMoon')); // key is in base but not variant; should pick base - $this->assertSame('I have a very bad feeling about this', $this->lang->getLine('More.cannotMove')); + $this->assertSame('I have a very bad feeling about this', $lang->getLine('More.cannotMove')); // key is in variant but not base; should pick variant - $this->assertSame('There is no try', $this->lang->getLine('More.wisdom')); + $this->assertSame('There is no try', $lang->getLine('More.wisdom')); // key isn't in either base or variant; should return bad key - $this->assertSame('More.shootMe', $this->lang->getLine('More.shootMe')); + $this->assertSame('More.shootMe', $lang->getLine('More.shootMe')); } /** @@ -371,16 +335,16 @@ public function testBaseFallbacks(): void */ public function testLangKeepLocale(): void { - $this->lang = service('language', 'en', true); + $lang = service('language', 'en', true); lang('Language.languageGetLineInvalidArgumentException'); - $this->assertSame('en', $this->lang->getLocale()); + $this->assertSame('en', $lang->getLocale()); lang('Language.languageGetLineInvalidArgumentException', [], 'ru'); - $this->assertSame('en', $this->lang->getLocale()); + $this->assertSame('en', $lang->getLocale()); lang('Language.languageGetLineInvalidArgumentException'); - $this->assertSame('en', $this->lang->getLocale()); + $this->assertSame('en', $lang->getLocale()); } /** @@ -398,31 +362,31 @@ public function testLangKeepLocale(): void */ public function testAllTheWayFallbacks(): void { - $this->lang = service('language', 'ab-CD', false); - $this->assertSame('Allin.none', $this->lang->getLine('Allin.none')); - $this->assertSame('Pyramid of Giza', $this->lang->getLine('Allin.one')); - $this->assertSame('gluttony', $this->lang->getLine('Allin.two')); - $this->assertSame('Colossus of Rhodes', $this->lang->getLine('Allin.tre')); - $this->assertSame('four calling birds', $this->lang->getLine('Allin.for')); - $this->assertSame('Temple of Artemis', $this->lang->getLine('Allin.fiv')); - $this->assertSame('envy', $this->lang->getLine('Allin.six')); - $this->assertSame('Hanging Gardens of Babylon', $this->lang->getLine('Allin.sev')); + $lang = service('language', 'ab-CD', false); + $this->assertSame('Allin.none', $lang->getLine('Allin.none')); + $this->assertSame('Pyramid of Giza', $lang->getLine('Allin.one')); + $this->assertSame('gluttony', $lang->getLine('Allin.two')); + $this->assertSame('Colossus of Rhodes', $lang->getLine('Allin.tre')); + $this->assertSame('four calling birds', $lang->getLine('Allin.for')); + $this->assertSame('Temple of Artemis', $lang->getLine('Allin.fiv')); + $this->assertSame('envy', $lang->getLine('Allin.six')); + $this->assertSame('Hanging Gardens of Babylon', $lang->getLine('Allin.sev')); } public function testLanguageNestedArrayDefinition(): void { - $this->lang = new SecondMockLanguage('en'); - $this->lang->loadem('Nested', 'en'); + $lang = new SecondMockLanguage('en'); + $lang->loadem('Nested', 'en'); - $this->assertSame('e', $this->lang->getLine('Nested.a.b.c.d')); + $this->assertSame('e', $lang->getLine('Nested.a.b.c.d')); } public function testLanguageKeySeparatedByDot(): void { - $this->lang = new SecondMockLanguage('en'); - $this->lang->loadem('Foo', 'en'); + $lang = new SecondMockLanguage('en'); + $lang->loadem('Foo', 'en'); - $this->assertSame('The fieldname field is very short.', $this->lang->getLine('Foo.bar.min_length1', ['field' => 'fieldname'])); - $this->assertSame('The fieldname field is very short.', $this->lang->getLine('Foo.baz.min_length3.short', ['field' => 'fieldname'])); + $this->assertSame('The fieldname field is very short.', $lang->getLine('Foo.bar.min_length1', ['field' => 'fieldname'])); + $this->assertSame('The fieldname field is very short.', $lang->getLine('Foo.baz.min_length3.short', ['field' => 'fieldname'])); } } diff --git a/utils/phpstan-baseline/loader.neon b/utils/phpstan-baseline/loader.neon index cbafddf4c147..91cc3a9cc25d 100644 --- a/utils/phpstan-baseline/loader.neon +++ b/utils/phpstan-baseline/loader.neon @@ -1,4 +1,4 @@ -# total 2865 errors +# total 2853 errors includes: - argument.type.neon - assign.propertyType.neon diff --git a/utils/phpstan-baseline/missingType.iterableValue.neon b/utils/phpstan-baseline/missingType.iterableValue.neon index f8a533b18a39..42156edc3aff 100644 --- a/utils/phpstan-baseline/missingType.iterableValue.neon +++ b/utils/phpstan-baseline/missingType.iterableValue.neon @@ -1,4 +1,4 @@ -# total 1435 errors +# total 1423 errors parameters: ignoreErrors: @@ -367,11 +367,6 @@ parameters: count: 1 path: ../../system/Common.php - - - message: '#^Function lang\(\) has parameter \$args with no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/Common.php - - message: '#^Function log_message\(\) has parameter \$context with no value type specified in iterable type array\.$#' count: 1 @@ -4157,46 +4152,6 @@ parameters: count: 1 path: ../../system/Images/Image.php - - - message: '#^Method CodeIgniter\\Language\\Language\:\:formatMessage\(\) has parameter \$message with no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/Language/Language.php - - - - message: '#^Method CodeIgniter\\Language\\Language\:\:formatMessage\(\) return type has no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/Language/Language.php - - - - message: '#^Method CodeIgniter\\Language\\Language\:\:getLine\(\) has parameter \$args with no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/Language/Language.php - - - - message: '#^Method CodeIgniter\\Language\\Language\:\:getTranslationOutput\(\) return type has no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/Language/Language.php - - - - message: '#^Method CodeIgniter\\Language\\Language\:\:parseLine\(\) return type has no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/Language/Language.php - - - - message: '#^Method CodeIgniter\\Language\\Language\:\:requireFile\(\) return type has no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/Language/Language.php - - - - message: '#^Property CodeIgniter\\Language\\Language\:\:\$language type has no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/Language/Language.php - - - - message: '#^Property CodeIgniter\\Language\\Language\:\:\$loadedFiles type has no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/Language/Language.php - - message: '#^Method CodeIgniter\\Model\:\:__call\(\) has parameter \$params with no value type specified in iterable type array\.$#' count: 1 @@ -4812,16 +4767,6 @@ parameters: count: 1 path: ../../system/Test/Fabricator.php - - - message: '#^Method CodeIgniter\\Test\\Mock\\MockLanguage\:\:requireFile\(\) return type has no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/Test/Mock/MockLanguage.php - - - - message: '#^Method CodeIgniter\\Test\\Mock\\MockLanguage\:\:setData\(\) has parameter \$data with no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/Test/Mock/MockLanguage.php - - message: '#^Method CodeIgniter\\Test\\TestResponse\:\:assertJSONExact\(\) has parameter \$test with no value type specified in iterable type array\.$#' count: 1 @@ -6282,11 +6227,6 @@ parameters: count: 1 path: ../../tests/system/I18n/TimeTest.php - - - message: '#^Method CodeIgniter\\Language\\LanguageTest\:\:provideBundleUniqueKeys\(\) return type has no value type specified in iterable type iterable\.$#' - count: 1 - path: ../../tests/system/Language/LanguageTest.php - - message: '#^Method CodeIgniter\\Models\\DeleteModelTest\:\:emptyPkValues\(\) return type has no value type specified in iterable type iterable\.$#' count: 1