diff --git a/phpstan.neon b/phpstan.neon index e8325b66935..bd692e25e0b 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,6 +1,10 @@ includes: - vendor/symplify/phpstan-rules/config/symplify-rules.neon +rules: + # @todo this rule is costly and designed for hand-on refactoring; enable only when needed + # - Rector\Utils\PHPStan\Rule\LongAndDependentComplexRectorRule + parameters: reportUnmatchedIgnoredErrors: false @@ -552,7 +556,6 @@ parameters: - '#class\-string\:\:processNode\(\)#' - - '#Access to an undefined property (.*?)\\Core\\Contract\\PhpParser\\Node\\StmtsAwareInterface\:\:\$stmts#' - '#Property Rector\\Core\\Contract\\PhpParser\\Node\\StmtsAwareInterface\:\:\$stmts \(array\|null\) does not accept array#' + - '#Class "Rector\\Utils\\PHPStan\\Rule\\LongAndDependentComplexRectorRule" is missing @see annotation with test case class reference#' diff --git a/rules/Php81/Enum/NameNullToStrictNullFunctionMap.php b/rules/Php81/Enum/NameNullToStrictNullFunctionMap.php new file mode 100644 index 00000000000..72479e004a3 --- /dev/null +++ b/rules/Php81/Enum/NameNullToStrictNullFunctionMap.php @@ -0,0 +1,301 @@ + + */ + public const FUNCTION_TO_PARAM_NAMES = [ + 'preg_split' => ['subject'], + 'preg_match' => ['subject'], + 'preg_match_all' => ['subject'], + 'preg_filter' => ['replacement', 'subject'], + 'preg_replace' => ['replacement', 'subject'], + 'preg_replace_callback' => ['subject'], + 'preg_replace_callback_array' => ['subject'], + 'explode' => ['string'], + 'strlen' => ['string'], + 'str_contains' => ['haystack', 'needle'], + 'strtotime' => ['datetime'], + 'str_replace' => ['subject'], + 'substr_replace' => ['string', 'replace'], + 'str_ireplace' => ['search', 'replace', 'subject'], + 'substr' => ['string'], + 'str_starts_with' => ['haystack', 'needle'], + 'strtoupper' => ['string'], + 'strtolower' => ['string'], + 'strpos' => ['haystack', 'needle'], + 'stripos' => ['haystack', 'needle'], + 'json_decode' => ['json'], + 'urlencode' => ['string'], + 'urldecode' => ['string'], + 'rawurlencode' => ['string'], + 'rawurldecode' => ['string'], + 'base64_encode' => ['string'], + 'base64_decode' => ['string'], + 'utf8_encode' => ['string'], + 'utf8_decode' => ['string'], + 'bin2hex' => ['string'], + 'hex2bin' => ['string'], + 'hexdec' => ['hex_string'], + 'octdec' => ['octal_string'], + 'base_convert' => ['num'], + 'htmlspecialchars' => ['string'], + 'htmlspecialchars_decode' => ['string'], + 'html_entity_decode' => ['string'], + 'htmlentities' => ['string'], + 'addslashes' => ['string'], + 'addcslashes' => ['string', 'characters'], + 'stripslashes' => ['string'], + 'stripcslashes' => ['string'], + 'quotemeta' => ['string'], + 'quoted_printable_decode' => ['string'], + 'quoted_printable_encode' => ['string'], + 'escapeshellarg' => ['arg'], + 'curl_escape' => ['string'], + 'curl_unescape' => ['string'], + 'convert_uuencode' => ['string'], + 'setcookie' => ['value', 'path', 'domain'], + 'setrawcookie' => ['value', 'path', 'domain'], + 'zlib_encode' => ['data'], + 'gzdeflate' => ['data'], + 'gzencode' => ['data'], + 'gzcompress' => ['data'], + 'gzwrite' => ['data'], + 'gzputs' => ['data'], + 'deflate_add' => ['data'], + 'inflate_add' => ['data'], + 'unpack' => ['format', 'string'], + 'iconv_mime_encode' => ['field_name', 'field_value'], + 'iconv_mime_decode' => ['string'], + 'iconv' => ['from_encoding', 'to_encoding', 'string'], + 'sodium_bin2hex' => ['string'], + 'sodium_hex2bin' => ['string', 'ignore'], + 'sodium_bin2base64' => ['string'], + 'sodium_base642bin' => ['string', 'ignore'], + 'mb_detect_encoding' => ['string'], + 'mb_encode_mimeheader' => ['string'], + 'mb_decode_mimeheader' => ['string'], + 'mb_encode_numericentity' => ['string'], + 'mb_decode_numericentity' => ['string'], + 'transliterator_transliterate' => ['string'], + 'mysqli_real_escape_string' => ['string'], + 'mysqli_escape_string' => ['string'], + 'pg_escape_bytea' => ['string'], + 'pg_escape_literal' => ['string'], + 'pg_escape_string' => ['string'], + 'pg_unescape_bytea' => ['string'], + 'ucfirst' => ['string'], + 'lcfirst' => ['string'], + 'ucwords' => ['string'], + 'trim' => ['string'], + 'ltrim' => ['string'], + 'rtrim' => ['string'], + 'chop' => ['string'], + 'str_rot13' => ['string'], + 'str_shuffle' => ['string'], + 'substr_count' => ['haystack', 'needle'], + 'strcoll' => ['string1', 'string2'], + 'str_split' => ['string'], + 'chunk_split' => ['string'], + 'wordwrap' => ['string'], + 'strrev' => ['string'], + 'str_repeat' => ['string'], + 'str_pad' => ['string'], + 'nl2br' => ['string'], + 'strip_tags' => ['string'], + 'hebrev' => ['string'], + 'iconv_substr' => ['string'], + 'mb_strtoupper' => ['string'], + 'mb_strtolower' => ['string'], + 'mb_convert_case' => ['string'], + 'mb_convert_kana' => ['string'], + 'mb_convert_encoding' => ['string'], + 'mb_scrub' => ['string'], + 'mb_substr' => ['string'], + 'mb_substr_count' => ['haystack', 'needle'], + 'mb_str_split' => ['string'], + 'mb_split' => ['pattern', 'string'], + 'sodium_pad' => ['string'], + 'grapheme_substr' => ['string'], + 'strrpos' => ['haystack', 'needle'], + 'strripos' => ['haystack', 'needle'], + 'iconv_strpos' => ['haystack', 'needle'], + 'iconv_strrpos' => ['haystack', 'needle'], + 'mb_strpos' => ['haystack', 'needle'], + 'mb_strrpos' => ['haystack', 'needle'], + 'mb_stripos' => ['haystack', 'needle'], + 'mb_strripos' => ['haystack', 'needle'], + 'grapheme_extract' => ['haystack'], + 'grapheme_strpos' => ['haystack', 'needle'], + 'grapheme_strrpos' => ['haystack', 'needle'], + 'grapheme_stripos' => ['haystack', 'needle'], + 'grapheme_strripos' => ['haystack', 'needle'], + 'strcmp' => ['string1', 'string2'], + 'strncmp' => ['string1', 'string2'], + 'strcasecmp' => ['string1', 'string2'], + 'strncasecmp' => ['string1', 'string2'], + 'strnatcmp' => ['string1', 'string2'], + 'strnatcasecmp' => ['string1', 'string2'], + 'substr_compare' => ['haystack', 'needle'], + 'str_ends_with' => ['haystack', 'needle'], + 'collator_compare' => ['string1', 'string2'], + 'collator_get_sort_key' => ['string'], + 'metaphone' => ['string'], + 'soundex' => ['string'], + 'levenshtein' => ['string1', 'string2'], + 'similar_text' => ['string1', 'string2'], + 'sodium_compare' => ['string1', 'string2'], + 'sodium_memcmp' => ['string1', 'string2'], + 'strstr' => ['haystack', 'needle'], + 'strchr' => ['haystack', 'needle'], + 'stristr' => ['haystack', 'needle'], + 'strrchr' => ['haystack', 'needle'], + 'strpbrk' => ['string', 'characters'], + 'strspn' => ['string', 'characters'], + 'strcspn' => ['string', 'characters'], + 'strtr' => ['string'], + 'strtok' => ['string'], + 'str_word_count' => ['string'], + 'count_chars' => ['string'], + 'iconv_strlen' => ['string'], + 'mb_strlen' => ['string'], + 'mb_strstr' => ['haystack', 'needle'], + 'mb_strrchr' => ['haystack', 'needle'], + 'mb_stristr' => ['haystack', 'needle'], + 'mb_strrichr' => ['haystack', 'needle'], + 'mb_strcut' => ['string'], + 'mb_strwidth' => ['string'], + 'mb_strimwidth' => ['string', 'trim_marker'], + 'grapheme_strlen' => ['string'], + 'grapheme_strstr' => ['haystack', 'needle'], + 'grapheme_stristr' => ['haystack', 'needle'], + 'preg_quote' => ['str'], + 'mb_ereg' => ['pattern', 'string'], + 'mb_eregi' => ['pattern', 'string'], + 'mb_ereg_replace' => ['pattern', 'replacement', 'string'], + 'mb_eregi_replace' => ['pattern', 'replacement', 'string'], + 'mb_ereg_replace_callback' => ['pattern', 'string'], + 'mb_ereg_match' => ['pattern', 'string'], + 'mb_ereg_search_init' => ['string'], + 'normalizer_normalize' => ['string'], + 'normalizer_is_normalized' => ['string'], + 'normalizer_get_raw_decomposition' => ['string'], + 'numfmt_parse' => ['string'], + 'hash' => ['algo', 'data'], + 'hash_hmac' => ['algo', 'data', 'key'], + 'hash_update' => ['data'], + 'hash_pbkdf2' => ['algo', 'password', 'salt'], + 'crc32' => ['string'], + 'md5' => ['string'], + 'sha1' => ['string'], + 'crypt' => ['string', 'salt'], + 'basename' => ['path'], + 'dirname' => ['path'], + 'pathinfo' => ['path'], + 'sscanf' => ['string'], + 'fwrite' => ['data'], + 'fputs' => ['data'], + 'output_add_rewrite_var' => ['name', 'value'], + 'parse_url' => ['url'], + 'parse_str' => ['string'], + 'mb_parse_str' => ['string'], + 'parse_ini_string' => ['ini_string'], + 'locale_accept_from_http' => ['header'], + 'msgfmt_parse' => ['string'], + 'msgfmt_parse_message' => ['locale', 'pattern', 'message'], + 'str_getcsv' => ['string'], + 'fgetcsv' => ['escape'], + 'fputcsv' => ['escape'], + 'password_hash' => ['password'], + 'password_verify' => ['password', 'hash'], + 'bcadd' => ['num1', 'num2'], + 'bcsub' => ['num1', 'num2'], + 'bcmul' => ['num1', 'num2'], + 'bcdiv' => ['num1', 'num2'], + 'bcmod' => ['num1', 'num2'], + 'bcpow' => ['num', 'exponent'], + 'bcpowmod' => ['num', 'exponent', 'modulus'], + 'bcsqrt' => ['num'], + 'bccomp' => ['num1', 'num2'], + 'simplexml_load_string' => ['data'], + 'xml_parse' => ['data'], + 'xml_parse_into_struct' => ['data'], + 'xml_parser_create_ns' => ['separator'], + 'xmlwriter_set_indent_string' => ['indentation'], + 'xmlwriter_write_attribute' => ['name', 'value'], + 'xmlwriter_write_attribute_ns' => ['value'], + 'xmlwriter_write_pi' => ['target', 'content'], + 'xmlwriter_write_cdata' => ['content'], + 'xmlwriter_text' => ['content'], + 'xmlwriter_write_raw' => ['content'], + 'xmlwriter_write_comment' => ['content'], + 'xmlwriter_write_dtd' => ['name'], + 'xmlwriter_write_dtd_element' => ['name', 'content'], + 'xmlwriter_write_dtd_attlist' => ['name', 'content'], + 'xmlwriter_write_dtd_entity' => ['name', 'content'], + 'sodium_crypto_aead_aes256gcm_encrypt' => ['message', 'additional_data', 'nonce', 'key'], + 'sodium_crypto_aead_aes256gcm_decrypt' => ['ciphertext', 'additional_data', 'nonce', 'key'], + 'sodium_crypto_aead_chacha20poly1305_encrypt' => ['message', 'additional_data', 'nonce', 'key'], + 'sodium_crypto_aead_chacha20poly1305_decrypt' => ['ciphertext', 'additional_data', 'nonce', 'key'], + 'sodium_crypto_aead_chacha20poly1305_ietf_encrypt' => ['message', 'additional_data', 'nonce', 'key'], + 'sodium_crypto_aead_chacha20poly1305_ietf_decrypt' => ['ciphertext', 'additional_data', 'nonce', 'key'], + 'sodium_crypto_aead_xchacha20poly1305_ietf_encrypt' => ['message', 'additional_data', 'nonce', 'key'], + 'sodium_crypto_aead_xchacha20poly1305_ietf_decrypt' => ['ciphertext', 'additional_data', 'nonce', 'key'], + 'sodium_crypto_auth' => ['message', 'key'], + 'sodium_crypto_auth_verify' => ['mac', 'message', 'key'], + 'sodium_crypto_box' => ['message', 'nonce', 'key_pair'], + 'sodium_crypto_box_seal' => ['message', 'public_key'], + 'sodium_crypto_generichash' => ['message'], + 'sodium_crypto_generichash_update' => ['message'], + 'sodium_crypto_secretbox' => ['message', 'nonce', 'key'], + 'sodium_crypto_secretstream_xchacha20poly1305_push' => ['message'], + 'sodium_crypto_secretstream_xchacha20poly1305_pull' => ['ciphertext'], + 'sodium_crypto_shorthash' => ['message', 'key'], + 'sodium_crypto_sign' => ['message', 'secret_key'], + 'sodium_crypto_sign_detached' => ['message'], + 'sodium_crypto_sign_open' => ['signed_message', 'public_key'], + 'sodium_crypto_sign_verify_detached' => ['signature', 'message', 'public_key'], + 'sodium_crypto_stream_xor' => ['message', 'nonce', 'key'], + 'sodium_crypto_stream_xchacha20_xor' => ['message', 'nonce', 'key'], + 'imagechar' => ['char'], + 'imagecharup' => ['char'], + 'imageftbbox' => ['string'], + 'imagefttext' => ['text'], + 'imagestring' => ['string'], + 'imagestringup' => ['string'], + 'imagettfbbox' => ['string'], + 'imagettftext' => ['text'], + 'pspell_add_to_personal' => ['word'], + 'pspell_add_to_session' => ['word'], + 'pspell_check' => ['word'], + 'pspell_config_create' => ['language', 'spelling', 'jargon', 'encoding'], + 'pspell_new' => ['spelling', 'jargon', 'encoding'], + 'pspell_new_personal' => ['spelling', 'jargon', 'encoding'], + 'pspell_store_replacement' => ['correct'], + 'pspell_suggest' => ['word'], + 'stream_get_line' => ['ending'], + 'stream_socket_sendto' => ['data'], + 'socket_sendto' => ['data'], + 'socket_write' => ['data'], + 'socket_send' => ['data'], + 'mail' => ['to', 'subject', 'message'], + 'mb_send_mail' => ['to', 'subject', 'message'], + 'ctype_alnum' => ['text'], + 'ctype_alpha' => ['text'], + 'ctype_cntrl' => ['text'], + 'ctype_digit' => ['text'], + 'ctype_graph' => ['text'], + 'ctype_lower' => ['text'], + 'ctype_print' => ['text'], + 'ctype_punct' => ['text'], + 'ctype_space' => ['text'], + 'ctype_upper' => ['text'], + 'ctype_xdigit' => ['text'], + 'uniqid' => ['prefix'], + ]; +} diff --git a/rules/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector.php b/rules/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector.php index bad47467733..b50ba427741 100644 --- a/rules/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector.php +++ b/rules/Php81/Rector/FuncCall/NullToStrictStringFuncCallArgRector.php @@ -28,6 +28,7 @@ use Rector\Core\ValueObject\PhpVersionFeature; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\PHPStan\ParametersAcceptorSelectorVariantsWrapper; +use Rector\Php81\Enum\NameNullToStrictNullFunctionMap; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -37,299 +38,6 @@ */ final class NullToStrictStringFuncCallArgRector extends AbstractRector implements MinPhpVersionInterface { - /** - * @var array - */ - private const ARG_POSITION_NAME_NULL_TO_STRICT_STRING = [ - 'preg_split' => ['subject'], - 'preg_match' => ['subject'], - 'preg_match_all' => ['subject'], - 'preg_filter' => ['replacement', 'subject'], - 'preg_replace' => ['replacement', 'subject'], - 'preg_replace_callback' => ['subject'], - 'preg_replace_callback_array' => ['subject'], - 'explode' => ['string'], - 'strlen' => ['string'], - 'str_contains' => ['haystack', 'needle'], - 'strtotime' => ['datetime'], - 'str_replace' => ['subject'], - 'substr_replace' => ['string', 'replace'], - 'str_ireplace' => ['search', 'replace', 'subject'], - 'substr' => ['string'], - 'str_starts_with' => ['haystack', 'needle'], - 'strtoupper' => ['string'], - 'strtolower' => ['string'], - 'strpos' => ['haystack', 'needle'], - 'stripos' => ['haystack', 'needle'], - 'json_decode' => ['json'], - 'urlencode' => ['string'], - 'urldecode' => ['string'], - 'rawurlencode' => ['string'], - 'rawurldecode' => ['string'], - 'base64_encode' => ['string'], - 'base64_decode' => ['string'], - 'utf8_encode' => ['string'], - 'utf8_decode' => ['string'], - 'bin2hex' => ['string'], - 'hex2bin' => ['string'], - 'hexdec' => ['hex_string'], - 'octdec' => ['octal_string'], - 'base_convert' => ['num'], - 'htmlspecialchars' => ['string'], - 'htmlspecialchars_decode' => ['string'], - 'html_entity_decode' => ['string'], - 'htmlentities' => ['string'], - 'addslashes' => ['string'], - 'addcslashes' => ['string', 'characters'], - 'stripslashes' => ['string'], - 'stripcslashes' => ['string'], - 'quotemeta' => ['string'], - 'quoted_printable_decode' => ['string'], - 'quoted_printable_encode' => ['string'], - 'escapeshellarg' => ['arg'], - 'curl_escape' => ['string'], - 'curl_unescape' => ['string'], - 'convert_uuencode' => ['string'], - 'setcookie' => ['value', 'path', 'domain'], - 'setrawcookie' => ['value', 'path', 'domain'], - 'zlib_encode' => ['data'], - 'gzdeflate' => ['data'], - 'gzencode' => ['data'], - 'gzcompress' => ['data'], - 'gzwrite' => ['data'], - 'gzputs' => ['data'], - 'deflate_add' => ['data'], - 'inflate_add' => ['data'], - 'unpack' => ['format', 'string'], - 'iconv_mime_encode' => ['field_name', 'field_value'], - 'iconv_mime_decode' => ['string'], - 'iconv' => ['from_encoding', 'to_encoding', 'string'], - 'sodium_bin2hex' => ['string'], - 'sodium_hex2bin' => ['string', 'ignore'], - 'sodium_bin2base64' => ['string'], - 'sodium_base642bin' => ['string', 'ignore'], - 'mb_detect_encoding' => ['string'], - 'mb_encode_mimeheader' => ['string'], - 'mb_decode_mimeheader' => ['string'], - 'mb_encode_numericentity' => ['string'], - 'mb_decode_numericentity' => ['string'], - 'transliterator_transliterate' => ['string'], - 'mysqli_real_escape_string' => ['string'], - 'mysqli_escape_string' => ['string'], - 'pg_escape_bytea' => ['string'], - 'pg_escape_literal' => ['string'], - 'pg_escape_string' => ['string'], - 'pg_unescape_bytea' => ['string'], - 'ucfirst' => ['string'], - 'lcfirst' => ['string'], - 'ucwords' => ['string'], - 'trim' => ['string'], - 'ltrim' => ['string'], - 'rtrim' => ['string'], - 'chop' => ['string'], - 'str_rot13' => ['string'], - 'str_shuffle' => ['string'], - 'substr_count' => ['haystack', 'needle'], - 'strcoll' => ['string1', 'string2'], - 'str_split' => ['string'], - 'chunk_split' => ['string'], - 'wordwrap' => ['string'], - 'strrev' => ['string'], - 'str_repeat' => ['string'], - 'str_pad' => ['string'], - 'nl2br' => ['string'], - 'strip_tags' => ['string'], - 'hebrev' => ['string'], - 'iconv_substr' => ['string'], - 'mb_strtoupper' => ['string'], - 'mb_strtolower' => ['string'], - 'mb_convert_case' => ['string'], - 'mb_convert_kana' => ['string'], - 'mb_convert_encoding' => ['string'], - 'mb_scrub' => ['string'], - 'mb_substr' => ['string'], - 'mb_substr_count' => ['haystack', 'needle'], - 'mb_str_split' => ['string'], - 'mb_split' => ['pattern', 'string'], - 'sodium_pad' => ['string'], - 'grapheme_substr' => ['string'], - 'strrpos' => ['haystack', 'needle'], - 'strripos' => ['haystack', 'needle'], - 'iconv_strpos' => ['haystack', 'needle'], - 'iconv_strrpos' => ['haystack', 'needle'], - 'mb_strpos' => ['haystack', 'needle'], - 'mb_strrpos' => ['haystack', 'needle'], - 'mb_stripos' => ['haystack', 'needle'], - 'mb_strripos' => ['haystack', 'needle'], - 'grapheme_extract' => ['haystack'], - 'grapheme_strpos' => ['haystack', 'needle'], - 'grapheme_strrpos' => ['haystack', 'needle'], - 'grapheme_stripos' => ['haystack', 'needle'], - 'grapheme_strripos' => ['haystack', 'needle'], - 'strcmp' => ['string1', 'string2'], - 'strncmp' => ['string1', 'string2'], - 'strcasecmp' => ['string1', 'string2'], - 'strncasecmp' => ['string1', 'string2'], - 'strnatcmp' => ['string1', 'string2'], - 'strnatcasecmp' => ['string1', 'string2'], - 'substr_compare' => ['haystack', 'needle'], - 'str_ends_with' => ['haystack', 'needle'], - 'collator_compare' => ['string1', 'string2'], - 'collator_get_sort_key' => ['string'], - 'metaphone' => ['string'], - 'soundex' => ['string'], - 'levenshtein' => ['string1', 'string2'], - 'similar_text' => ['string1', 'string2'], - 'sodium_compare' => ['string1', 'string2'], - 'sodium_memcmp' => ['string1', 'string2'], - 'strstr' => ['haystack', 'needle'], - 'strchr' => ['haystack', 'needle'], - 'stristr' => ['haystack', 'needle'], - 'strrchr' => ['haystack', 'needle'], - 'strpbrk' => ['string', 'characters'], - 'strspn' => ['string', 'characters'], - 'strcspn' => ['string', 'characters'], - 'strtr' => ['string'], - 'strtok' => ['string'], - 'str_word_count' => ['string'], - 'count_chars' => ['string'], - 'iconv_strlen' => ['string'], - 'mb_strlen' => ['string'], - 'mb_strstr' => ['haystack', 'needle'], - 'mb_strrchr' => ['haystack', 'needle'], - 'mb_stristr' => ['haystack', 'needle'], - 'mb_strrichr' => ['haystack', 'needle'], - 'mb_strcut' => ['string'], - 'mb_strwidth' => ['string'], - 'mb_strimwidth' => ['string', 'trim_marker'], - 'grapheme_strlen' => ['string'], - 'grapheme_strstr' => ['haystack', 'needle'], - 'grapheme_stristr' => ['haystack', 'needle'], - 'preg_quote' => ['str'], - 'mb_ereg' => ['pattern', 'string'], - 'mb_eregi' => ['pattern', 'string'], - 'mb_ereg_replace' => ['pattern', 'replacement', 'string'], - 'mb_eregi_replace' => ['pattern', 'replacement', 'string'], - 'mb_ereg_replace_callback' => ['pattern', 'string'], - 'mb_ereg_match' => ['pattern', 'string'], - 'mb_ereg_search_init' => ['string'], - 'normalizer_normalize' => ['string'], - 'normalizer_is_normalized' => ['string'], - 'normalizer_get_raw_decomposition' => ['string'], - 'numfmt_parse' => ['string'], - 'hash' => ['algo', 'data'], - 'hash_hmac' => ['algo', 'data', 'key'], - 'hash_update' => ['data'], - 'hash_pbkdf2' => ['algo', 'password', 'salt'], - 'crc32' => ['string'], - 'md5' => ['string'], - 'sha1' => ['string'], - 'crypt' => ['string', 'salt'], - 'basename' => ['path'], - 'dirname' => ['path'], - 'pathinfo' => ['path'], - 'sscanf' => ['string'], - 'fwrite' => ['data'], - 'fputs' => ['data'], - 'output_add_rewrite_var' => ['name', 'value'], - 'parse_url' => ['url'], - 'parse_str' => ['string'], - 'mb_parse_str' => ['string'], - 'parse_ini_string' => ['ini_string'], - 'locale_accept_from_http' => ['header'], - 'msgfmt_parse' => ['string'], - 'msgfmt_parse_message' => ['locale', 'pattern', 'message'], - 'str_getcsv' => ['string'], - 'fgetcsv' => ['escape'], - 'fputcsv' => ['escape'], - 'password_hash' => ['password'], - 'password_verify' => ['password', 'hash'], - 'bcadd' => ['num1', 'num2'], - 'bcsub' => ['num1', 'num2'], - 'bcmul' => ['num1', 'num2'], - 'bcdiv' => ['num1', 'num2'], - 'bcmod' => ['num1', 'num2'], - 'bcpow' => ['num', 'exponent'], - 'bcpowmod' => ['num', 'exponent', 'modulus'], - 'bcsqrt' => ['num'], - 'bccomp' => ['num1', 'num2'], - 'simplexml_load_string' => ['data'], - 'xml_parse' => ['data'], - 'xml_parse_into_struct' => ['data'], - 'xml_parser_create_ns' => ['separator'], - 'xmlwriter_set_indent_string' => ['indentation'], - 'xmlwriter_write_attribute' => ['name', 'value'], - 'xmlwriter_write_attribute_ns' => ['value'], - 'xmlwriter_write_pi' => ['target', 'content'], - 'xmlwriter_write_cdata' => ['content'], - 'xmlwriter_text' => ['content'], - 'xmlwriter_write_raw' => ['content'], - 'xmlwriter_write_comment' => ['content'], - 'xmlwriter_write_dtd' => ['name'], - 'xmlwriter_write_dtd_element' => ['name', 'content'], - 'xmlwriter_write_dtd_attlist' => ['name', 'content'], - 'xmlwriter_write_dtd_entity' => ['name', 'content'], - 'sodium_crypto_aead_aes256gcm_encrypt' => ['message', 'additional_data', 'nonce', 'key'], - 'sodium_crypto_aead_aes256gcm_decrypt' => ['ciphertext', 'additional_data', 'nonce', 'key'], - 'sodium_crypto_aead_chacha20poly1305_encrypt' => ['message', 'additional_data', 'nonce', 'key'], - 'sodium_crypto_aead_chacha20poly1305_decrypt' => ['ciphertext', 'additional_data', 'nonce', 'key'], - 'sodium_crypto_aead_chacha20poly1305_ietf_encrypt' => ['message', 'additional_data', 'nonce', 'key'], - 'sodium_crypto_aead_chacha20poly1305_ietf_decrypt' => ['ciphertext', 'additional_data', 'nonce', 'key'], - 'sodium_crypto_aead_xchacha20poly1305_ietf_encrypt' => ['message', 'additional_data', 'nonce', 'key'], - 'sodium_crypto_aead_xchacha20poly1305_ietf_decrypt' => ['ciphertext', 'additional_data', 'nonce', 'key'], - 'sodium_crypto_auth' => ['message', 'key'], - 'sodium_crypto_auth_verify' => ['mac', 'message', 'key'], - 'sodium_crypto_box' => ['message', 'nonce', 'key_pair'], - 'sodium_crypto_box_seal' => ['message', 'public_key'], - 'sodium_crypto_generichash' => ['message'], - 'sodium_crypto_generichash_update' => ['message'], - 'sodium_crypto_secretbox' => ['message', 'nonce', 'key'], - 'sodium_crypto_secretstream_xchacha20poly1305_push' => ['message'], - 'sodium_crypto_secretstream_xchacha20poly1305_pull' => ['ciphertext'], - 'sodium_crypto_shorthash' => ['message', 'key'], - 'sodium_crypto_sign' => ['message', 'secret_key'], - 'sodium_crypto_sign_detached' => ['message'], - 'sodium_crypto_sign_open' => ['signed_message', 'public_key'], - 'sodium_crypto_sign_verify_detached' => ['signature', 'message', 'public_key'], - 'sodium_crypto_stream_xor' => ['message', 'nonce', 'key'], - 'sodium_crypto_stream_xchacha20_xor' => ['message', 'nonce', 'key'], - 'imagechar' => ['char'], - 'imagecharup' => ['char'], - 'imageftbbox' => ['string'], - 'imagefttext' => ['text'], - 'imagestring' => ['string'], - 'imagestringup' => ['string'], - 'imagettfbbox' => ['string'], - 'imagettftext' => ['text'], - 'pspell_add_to_personal' => ['word'], - 'pspell_add_to_session' => ['word'], - 'pspell_check' => ['word'], - 'pspell_config_create' => ['language', 'spelling', 'jargon', 'encoding'], - 'pspell_new' => ['spelling', 'jargon', 'encoding'], - 'pspell_new_personal' => ['spelling', 'jargon', 'encoding'], - 'pspell_store_replacement' => ['correct'], - 'pspell_suggest' => ['word'], - 'stream_get_line' => ['ending'], - 'stream_socket_sendto' => ['data'], - 'socket_sendto' => ['data'], - 'socket_write' => ['data'], - 'socket_send' => ['data'], - 'mail' => ['to', 'subject', 'message'], - 'mb_send_mail' => ['to', 'subject', 'message'], - 'ctype_alnum' => ['text'], - 'ctype_alpha' => ['text'], - 'ctype_cntrl' => ['text'], - 'ctype_digit' => ['text'], - 'ctype_graph' => ['text'], - 'ctype_lower' => ['text'], - 'ctype_print' => ['text'], - 'ctype_punct' => ['text'], - 'ctype_space' => ['text'], - 'ctype_upper' => ['text'], - 'ctype_xdigit' => ['text'], - 'uniqid' => ['prefix'], - ]; - public function __construct( private readonly ReflectionResolver $reflectionResolver, private readonly ArgsAnalyzer $argsAnalyzer, @@ -429,7 +137,7 @@ public function provideMinPhpVersion(): int private function resolveNamedPositions(FuncCall $funcCall, array $args): array { $functionName = $this->nodeNameResolver->getName($funcCall); - $argNames = self::ARG_POSITION_NAME_NULL_TO_STRICT_STRING[$functionName]; + $argNames = NameNullToStrictNullFunctionMap::FUNCTION_TO_PARAM_NAMES[$functionName]; $positions = []; foreach ($args as $position => $arg) { @@ -544,7 +252,7 @@ private function resolveOriginalPositions(FuncCall $funcCall, Scope $scope): arr $scope ); $functionName = $functionReflection->getName(); - $argNames = self::ARG_POSITION_NAME_NULL_TO_STRICT_STRING[$functionName]; + $argNames = NameNullToStrictNullFunctionMap::FUNCTION_TO_PARAM_NAMES[$functionName]; $positions = []; foreach ($parametersAcceptor->getParameters() as $position => $parameterReflection) { @@ -558,7 +266,7 @@ private function resolveOriginalPositions(FuncCall $funcCall, Scope $scope): arr private function shouldSkip(FuncCall $funcCall): bool { - $functionNames = array_keys(self::ARG_POSITION_NAME_NULL_TO_STRICT_STRING); + $functionNames = array_keys(NameNullToStrictNullFunctionMap::FUNCTION_TO_PARAM_NAMES); if (! $this->nodeNameResolver->isNames($funcCall, $functionNames)) { return true; } diff --git a/utils/PHPStan/Rule/LongAndDependentComplexRectorRule.php b/utils/PHPStan/Rule/LongAndDependentComplexRectorRule.php new file mode 100644 index 00000000000..beed3af5a0c --- /dev/null +++ b/utils/PHPStan/Rule/LongAndDependentComplexRectorRule.php @@ -0,0 +1,132 @@ + + * This rule helps to find overly complex rules, that usually have little value, but are costrly to run. + */ +final class LongAndDependentComplexRectorRule implements Rule +{ + /** + * @var int + */ + private const ALLOWED_TRANSITIONAL_COMPLEXITY = 120; + + private readonly Parser $phpParser; + + private readonly NodeFinder $nodeFinder; + + public function __construct( + private readonly AstCognitiveComplexityAnalyzer $astCognitiveComplexityAnalyzer, + ) { + $parserFactory = new ParserFactory(); + $this->phpParser = $parserFactory->create(ParserFactory::PREFER_PHP7); + + $this->nodeFinder = new NodeFinder(); + } + + public function getNodeType(): string + { + return InClassNode::class; + } + + /** + * @param InClassNode $node + */ + public function processNode(Node $node, Scope $scope): array + { + // check only rector rules + $classReflection = $node->getClassReflection(); + if (! $classReflection->isSubclassOf(RectorInterface::class)) { + return []; + } + + // not much complex + if (! $classReflection->hasConstructor()) { + return []; + } + + $extendedMethodReflection = $classReflection->getConstructor(); + $parametersAcceptorWithPhpDocs = ParametersAcceptorSelector::selectSingle( + $extendedMethodReflection->getVariants() + ); + + $originalClassLike = $node->getOriginalNode(); + if (! $originalClassLike instanceof Class_) { + return []; + } + + $currentClassLikeComplexity = $this->astCognitiveComplexityAnalyzer->analyzeClassLike($originalClassLike); + $totalTransitionalComplexity = $currentClassLikeComplexity; + + foreach ($parametersAcceptorWithPhpDocs->getParameters() as $parameterReflectionWithPhpDoc) { + $parameterType = $parameterReflectionWithPhpDoc->getType(); + if (! $parameterType instanceof TypeWithClassName) { + continue; + } + + $parameterClassReflection = $parameterType->getClassReflection(); + if (! $parameterClassReflection instanceof ClassReflection) { + continue; + } + + $dependencyClass = $this->parseClassReflectionToClassNode($parameterClassReflection); + if (! $dependencyClass instanceof Class_) { + continue; + } + + $dependencyComplexity = $this->astCognitiveComplexityAnalyzer->analyzeClassLike($dependencyClass); + $totalTransitionalComplexity += $dependencyComplexity; + } + + if ($totalTransitionalComplexity < self::ALLOWED_TRANSITIONAL_COMPLEXITY) { + return []; + } + + return [sprintf( + 'Transitional dependency complexity %d is over %d, please consider splitting it up.', + $totalTransitionalComplexity, + self::ALLOWED_TRANSITIONAL_COMPLEXITY + )]; + } + + private function parseClassReflectionToClassNode(ClassReflection $classReflection): ?Class_ + { + $fileName = $classReflection->getFileName(); + if (! is_string($fileName)) { + return null; + } + + $fileContents = FileSystem::read($fileName); + + $stmts = $this->phpParser->parse($fileContents); + if ($stmts === null) { + return null; + } + + $foundNode = $this->nodeFinder->findFirstInstanceOf($stmts, Class_::class); + if (! $foundNode instanceof Class_) { + return null; + } + + return $foundNode; + } +}