diff --git a/rules.neon b/rules.neon index 704579b..df499a8 100644 --- a/rules.neon +++ b/rules.neon @@ -16,6 +16,9 @@ services: tags: - phpstan.deprecations.deprecatedScopeResolver + - + class: PHPStan\Rules\Deprecations\CallWithDeprecatedIniOptionRule + rules: - PHPStan\Rules\Deprecations\AccessDeprecatedPropertyRule - PHPStan\Rules\Deprecations\AccessDeprecatedStaticPropertyRule @@ -33,3 +36,7 @@ rules: - PHPStan\Rules\Deprecations\TypeHintDeprecatedInFunctionSignatureRule - PHPStan\Rules\Deprecations\UsageOfDeprecatedCastRule - PHPStan\Rules\Deprecations\UsageOfDeprecatedTraitRule + +conditionalTags: + PHPStan\Rules\Deprecations\CallWithDeprecatedIniOptionRule: + phpstan.rules.rule: %featureToggles.bleedingEdge% diff --git a/src/Rules/Deprecations/CallWithDeprecatedIniOptionRule.php b/src/Rules/Deprecations/CallWithDeprecatedIniOptionRule.php new file mode 100644 index 0000000..aa80968 --- /dev/null +++ b/src/Rules/Deprecations/CallWithDeprecatedIniOptionRule.php @@ -0,0 +1,135 @@ + + */ +class CallWithDeprecatedIniOptionRule implements Rule +{ + + private const INI_FUNCTIONS = [ + 'ini_get', + 'ini_set', + 'ini_alter', + 'ini_restore', + 'get_cfg_var', + ]; + + private const DEPRECATED_OPTIONS = [ + // deprecated since unknown version + 'mbstring.http_input' => 0, + 'mbstring.http_output' => 0, + 'mbstring.internal_encoding' => 0, + 'pdo_odbc.db2_instance_name' => 0, + 'enable_dl' => 0, + + 'iconv.input_encoding' => 50600, + 'iconv.output_encoding' => 50600, + 'iconv.internal_encoding' => 50600, + + 'mbstring.func_overload' => 70200, + 'track_errors' => 70200, + + 'allow_url_include' => 70400, + + 'assert.quiet_eval' => 80000, + + 'filter.default' => 80100, + 'oci8.old_oci_close_semantics' => 80100, + + 'assert.active' => 80300, + 'assert.exception' => 80300, + 'assert.bail' => 80300, + 'assert.warning' => 80300, + + 'session.sid_length' => 80400, + 'session.sid_bits_per_character' => 80400, + ]; + + private ReflectionProvider $reflectionProvider; + + private DeprecatedScopeHelper $deprecatedScopeHelper; + + private PhpVersion $phpVersion; + + public function __construct( + ReflectionProvider $reflectionProvider, + DeprecatedScopeHelper $deprecatedScopeHelper, + PhpVersion $phpVersion + ) + { + $this->reflectionProvider = $reflectionProvider; + $this->deprecatedScopeHelper = $deprecatedScopeHelper; + $this->phpVersion = $phpVersion; + } + + public function getNodeType(): string + { + return FuncCall::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) { + return []; + } + + if (!($node->name instanceof Name)) { + return []; + } + + if (count($node->getArgs()) < 1) { + return []; + } + + try { + $function = $this->reflectionProvider->getFunction($node->name, $scope); + } catch (FunctionNotFoundException $e) { + // Other rules will notify if the function is not found + return []; + } + + if (!in_array(strtolower($function->getName()), self::INI_FUNCTIONS, true)) { + return []; + } + + $phpVersionId = $this->phpVersion->getVersionId(); + $iniType = $scope->getType($node->getArgs()[0]->value); + foreach ($iniType->getConstantStrings() as $string) { + if (!array_key_exists($string->getValue(), self::DEPRECATED_OPTIONS)) { + continue; + } + + if ($phpVersionId < self::DEPRECATED_OPTIONS[$string->getValue()]) { + continue; + } + + return [ + RuleErrorBuilder::message(sprintf( + "Call to function %s() with deprecated option '%s'.", + $function->getName(), + $string->getValue(), + ))->identifier('function.deprecated')->build(), + ]; + } + + return []; + } + +} diff --git a/tests/Rules/Deprecations/CallWithDeprecatedIniOptionRuleTest.php b/tests/Rules/Deprecations/CallWithDeprecatedIniOptionRuleTest.php new file mode 100644 index 0000000..66236ba --- /dev/null +++ b/tests/Rules/Deprecations/CallWithDeprecatedIniOptionRuleTest.php @@ -0,0 +1,47 @@ + + */ +class CallWithDeprecatedIniOptionRuleTest extends RuleTestCase +{ + + protected function getRule(): Rule + { + return new CallWithDeprecatedIniOptionRule( + $this->createReflectionProvider(), + new DeprecatedScopeHelper([new DefaultDeprecatedScopeResolver()]), + self::getContainer()->getByType(PhpVersion::class), + ); + } + + public function testRule(): void + { + $expectedErrors = []; + if (PHP_VERSION_ID >= 80300) { + $expectedErrors = [ + [ + "Call to function ini_set() with deprecated option 'assert.active'.", + 11, + ], + [ + "Call to function ini_get() with deprecated option 'assert.active'.", + 12, + ], + ]; + } + + $this->analyse( + [__DIR__ . '/data/call-with-deprecation-ini-option.php'], + $expectedErrors, + ); + } + +} diff --git a/tests/Rules/Deprecations/data/call-with-deprecation-ini-option.php b/tests/Rules/Deprecations/data/call-with-deprecation-ini-option.php new file mode 100644 index 0000000..7f1b4cc --- /dev/null +++ b/tests/Rules/Deprecations/data/call-with-deprecation-ini-option.php @@ -0,0 +1,19 @@ +