diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 5561b4a..9fb28af 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -41,6 +41,10 @@ jobs: if: success() || matrix.allow_failure run: phpcs -wps --colors + - name: PHPStan + uses: php-actions/phpstan@v3 + if: success() || matrix.allow_failure + test: name: Unit tests with php ${{ matrix.php }} on ${{ matrix.os }} runs-on: ${{ matrix.os }} diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon new file mode 100644 index 0000000..fd2c8fa --- /dev/null +++ b/phpstan-baseline.neon @@ -0,0 +1,31 @@ +parameters: + ignoreErrors: + - + message: "#^Parameter \\#1 \\$callback of function array_map expects \\(callable\\(array\\\\|string\\)\\: mixed\\)\\|null, 'strtolower' given\\.$#" + count: 1 + path: src/Locale.php + + - + message: "#^Parameter \\#1 \\$haystack of function strpos expects string, array\\\\|int\\|string\\>\\|int\\|string\\>\\|int\\|string\\>\\|string given\\.$#" + count: 1 + path: src/Locale.php + + - + message: "#^Parameter \\#1 \\$haystack of function strpos expects string, array\\\\|int\\|string\\>\\|int\\|string\\>\\|string given\\.$#" + count: 2 + path: src/Locale.php + + - + message: "#^Parameter \\#1 \\$string of function substr expects string, string\\|null given\\.$#" + count: 2 + path: src/Locale.php + + - + message: "#^Parameter \\#1 \\$subject of static method ipl\\\\Stdlib\\\\Str\\:\\:trimSplit\\(\\) expects string\\|null, array\\\\|int\\|string\\>\\|int\\|string\\>\\|int\\|string\\>\\|string given\\.$#" + count: 1 + path: src/Locale.php + + - + message: "#^Parameter \\#1 \\$subject of static method ipl\\\\Stdlib\\\\Str\\:\\:trimSplit\\(\\) expects string\\|null, array\\\\|int\\|string\\>\\|int\\|string\\>\\|string given\\.$#" + count: 2 + path: src/Locale.php diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..6bf4499 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,15 @@ +includes: + - phpstan-baseline.neon + +parameters: + level: max + + checkFunctionNameCase: true + checkInternalClassCaseSensitivity: true + treatPhpDocTypesAsCertain: false + + paths: + - src + + scanDirectories: + - vendor diff --git a/src/GettextTranslator.php b/src/GettextTranslator.php index a7a8144..288a489 100644 --- a/src/GettextTranslator.php +++ b/src/GettextTranslator.php @@ -4,6 +4,7 @@ use FilesystemIterator; use ipl\Stdlib\Contract\Translator; +use SplFileInfo; /** * Translator using PHP's native [gettext](https://www.php.net/gettext) extension @@ -43,10 +44,10 @@ class GettextTranslator implements Translator /** @var string Default locale code */ protected $defaultLocale = 'en_US'; - /** @var array Known translation directories as array[$domain] => $directory */ + /** @var array Known translation directories as array[$domain] => $directory */ protected $translationDirectories = []; - /** @var array Loaded translations as array[$domain] => $directory */ + /** @var array Loaded translations as array[$domain] => $directory */ protected $loadedTranslations = []; /** @var string Primary locale code used for translations */ @@ -103,7 +104,7 @@ public function setDefaultLocale($defaultLocale) /** * Get available translations * - * @return array Available translations as array[$domain] => $directory + * @return array Available translations as array[$domain] => $directory */ public function getTranslationDirectories() { @@ -128,7 +129,7 @@ public function addTranslationDirectory($directory, $domain = null) /** * Get loaded translations * - * @return array Loaded translations as array[$domain] => $directory + * @return array Loaded translations as array[$domain] => $directory */ public function getLoadedTranslations() { @@ -333,6 +334,7 @@ public function listLocales() FilesystemIterator::CURRENT_AS_FILEINFO | FilesystemIterator::SKIP_DOTS ); + /** @var SplFileInfo $file */ foreach ($fs as $file) { if (! $file->isDir()) { continue; diff --git a/src/Locale.php b/src/Locale.php index d9e6b05..48e345f 100644 --- a/src/Locale.php +++ b/src/Locale.php @@ -2,6 +2,9 @@ namespace ipl\I18n; +use ipl\Stdlib\Str; +use stdClass; + class Locale { /** @var string Default locale code */ @@ -35,13 +38,13 @@ public function setDefaultLocale($defaultLocale) * Return the preferred locale based on the given HTTP header and the available translations * * @param string $header The HTTP "Accept-Language" header - * @param array $available Available translations + * @param array $available Available translations * * @return string The browser's preferred locale code */ public function getPreferred($header, array $available) { - $headerValues = explode(',', $header); + $headerValues = Str::trimSplit($header, ','); for ($i = 0; $i < count($headerValues); $i++) { // In order to accomplish a stable sort we need to take the original // index into account as well during element comparison @@ -50,8 +53,8 @@ public function getPreferred($header, array $available) usort( // Sort DESC but keep equal elements ASC $headerValues, function ($a, $b) { - $tagA = explode(';', $a[0], 2); - $tagB = explode(';', $b[0], 2); + $tagA = Str::trimSplit($a[0], ';', 2); + $tagB = Str::trimSplit($b[0], ';', 2); $qValA = (float) (strpos($a[0], ';') > 0 ? substr(array_pop($tagA), 2) : 1); $qValB = (float) (strpos($b[0], ';') > 0 ? substr(array_pop($tagB), 2) : 1); @@ -65,7 +68,7 @@ function ($a, $b) { $requestedLocales = []; foreach ($headerValues as $headerValue) { if (strpos($headerValue, ';') > 0) { - $parts = explode(';', $headerValue, 2); + $parts = Str::trimSplit($headerValue, ';', 2); $headerValue = $parts[0]; } $requestedLocales[] = str_replace('-', '_', $headerValue); @@ -115,7 +118,7 @@ function ($a, $b) { * * @param string $locale * - * @return object Output of {@link \Locale::parseLocale()} converted to an object + * @return stdClass Output of {@link \Locale::parseLocale()} converted to an object */ public function parseLocale($locale) {