diff --git a/build/code_sniffer/phpbb/Sniffs/CodeLayout/UnionTypesCheckSniff.php b/build/code_sniffer/phpbb/Sniffs/CodeLayout/UnionTypesCheckSniff.php new file mode 100644 index 0000000000..64b30a859e --- /dev/null +++ b/build/code_sniffer/phpbb/Sniffs/CodeLayout/UnionTypesCheckSniff.php @@ -0,0 +1,116 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\Sniffs\CodeLayout; + +use PHP_CodeSniffer\Files\File; +use PHP_CodeSniffer\Sniffs\Sniff; + +/** + * Checks that union type declarations follows the coding guidelines. + */ +class UnionTypesCheckSniff implements Sniff +{ + /** + * {@inheritdoc} + */ + public function register() + { + return [ + T_FUNCTION, + T_CLASS, + ]; + } + + /** + * {@inheritdoc} + */ + public function process(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + if ($tokens[$stackPtr]['type'] === 'T_FUNCTION') + { + $method_params = $phpcsFile->getMethodParameters($stackPtr); + $method_params_array = array_column($method_params, 'type_hint', 'token'); + foreach ($method_params_array as $stack_pointer => $type_hint) + { + $this->check_union_type($phpcsFile, $stack_pointer, $type_hint); + } + + $method_properties = $phpcsFile->getMethodProperties($stackPtr); + $this->check_union_type($phpcsFile, $stackPtr, $method_properties['return_type']); + } + else if ($tokens[$stackPtr]['type'] === 'T_CLASS') + { + $class_token = $tokens[$stackPtr]; + $class_closer_pointer = $class_token['scope_closer']; + $first_method_pointer = $phpcsFile->findNext(T_FUNCTION, $stackPtr); + $class_members_declarations_end_pointer = $first_method_pointer ?: $class_closer_pointer; + + $stack_pointer = $stackPtr; + while(($class_member_pointer = $phpcsFile->findNext(T_VARIABLE, $stack_pointer)) !== false && ($class_member_pointer < $class_members_declarations_end_pointer)) + { + $properties = $phpcsFile->getMemberProperties($class_member_pointer); + $this->check_union_type($phpcsFile, $class_member_pointer, $properties['type']); + $stack_pointer = $class_member_pointer + 1; + } + } + } + + public function check_union_type(File $phpcsFile, $stack_pointer, $type_hint) + { + if (empty($type_hint)) + { + return; + } + + if (!strpos($type_hint, '|') && $type_hint[0] == '?') // Check nullable shortcut syntax + { + $type = substr($type_hint, 1); + $error = 'Nullable shortcut syntax must not be used. Use union type instead: %1$s|null; found %2$s'; + $data = [$type, $type_hint]; + $phpcsFile->addError($error, $stack_pointer, 'ShortNullableSyntax', $data); + } + else if ((count($types_array = explode('|', $type_hint))) > 1) // Check union type layout + { + $types_array_null_less = $types_array; + + // Check 'null' to be the last element + $null_position = array_search('null', $types_array); + if ($null_position !== false && $null_position != array_key_last($types_array)) + { + $error = 'The "null" type hint must be the last of the union type elements; found %s'; + $data = [implode('|', $types_array)]; + $phpcsFile->addError($error, $stack_pointer, 'NullAlwaysLast', $data); + } + + // Check types excepting 'null' to follow alphabetical order + if ($null_position !== false) + { + array_splice($types_array_null_less, $null_position, 1); + } + + if (count($types_array_null_less) > 1) + { + $types_array_null_less_sorted = $types_array_null_less; + sort($types_array_null_less_sorted); + if (!empty(array_diff_assoc($types_array_null_less, $types_array_null_less_sorted))) + { + $error = 'Union type elements must be sorted alphabetically excepting the "null" type hint must be the last if any; found %s'; + $data = [implode('|', $types_array)]; + $phpcsFile->addError($error, $stack_pointer, 'AlphabeticalSort', $data); + } + } + } + } +} diff --git a/build/code_sniffer/ruleset-php-strict.xml b/build/code_sniffer/ruleset-php-strict.xml index 9e2f0664d8..58fe270021 100644 --- a/build/code_sniffer/ruleset-php-strict.xml +++ b/build/code_sniffer/ruleset-php-strict.xml @@ -45,4 +45,8 @@ + + + diff --git a/phpBB/phpbb/routing/loader_resolver.php b/phpBB/phpbb/routing/loader_resolver.php index de3adcaa34..2e21d8bb3d 100644 --- a/phpBB/phpbb/routing/loader_resolver.php +++ b/phpBB/phpbb/routing/loader_resolver.php @@ -33,7 +33,7 @@ class loader_resolver implements LoaderResolverInterface /** * {@inheritdoc} */ - public function resolve($resource, $type = null): false|\Symfony\Component\Config\Loader\LoaderInterface + public function resolve($resource, $type = null): \Symfony\Component\Config\Loader\LoaderInterface|false { /** @var \Symfony\Component\Config\Loader\LoaderInterface $loader */ foreach ($this->loaders as $loader)