Skip to content

Commit 1cc1259

Browse files
authored
Bleeding edge - CallWithDeprecatedIniOptionRule
1 parent 81833b5 commit 1cc1259

File tree

4 files changed

+208
-0
lines changed

4 files changed

+208
-0
lines changed

Diff for: rules.neon

+7
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ services:
1616
tags:
1717
- phpstan.deprecations.deprecatedScopeResolver
1818

19+
-
20+
class: PHPStan\Rules\Deprecations\CallWithDeprecatedIniOptionRule
21+
1922
rules:
2023
- PHPStan\Rules\Deprecations\AccessDeprecatedPropertyRule
2124
- PHPStan\Rules\Deprecations\AccessDeprecatedStaticPropertyRule
@@ -33,3 +36,7 @@ rules:
3336
- PHPStan\Rules\Deprecations\TypeHintDeprecatedInFunctionSignatureRule
3437
- PHPStan\Rules\Deprecations\UsageOfDeprecatedCastRule
3538
- PHPStan\Rules\Deprecations\UsageOfDeprecatedTraitRule
39+
40+
conditionalTags:
41+
PHPStan\Rules\Deprecations\CallWithDeprecatedIniOptionRule:
42+
phpstan.rules.rule: %featureToggles.bleedingEdge%
+135
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Deprecations;
4+
5+
use PhpParser\Node;
6+
use PhpParser\Node\Expr\FuncCall;
7+
use PhpParser\Node\Name;
8+
use PHPStan\Analyser\Scope;
9+
use PHPStan\Broker\FunctionNotFoundException;
10+
use PHPStan\Php\PhpVersion;
11+
use PHPStan\Reflection\ReflectionProvider;
12+
use PHPStan\Rules\Rule;
13+
use PHPStan\Rules\RuleErrorBuilder;
14+
use function array_key_exists;
15+
use function count;
16+
use function in_array;
17+
use function sprintf;
18+
use function strtolower;
19+
20+
/**
21+
* @implements Rule<FuncCall>
22+
*/
23+
class CallWithDeprecatedIniOptionRule implements Rule
24+
{
25+
26+
private const INI_FUNCTIONS = [
27+
'ini_get',
28+
'ini_set',
29+
'ini_alter',
30+
'ini_restore',
31+
'get_cfg_var',
32+
];
33+
34+
private const DEPRECATED_OPTIONS = [
35+
// deprecated since unknown version
36+
'mbstring.http_input' => 0,
37+
'mbstring.http_output' => 0,
38+
'mbstring.internal_encoding' => 0,
39+
'pdo_odbc.db2_instance_name' => 0,
40+
'enable_dl' => 0,
41+
42+
'iconv.input_encoding' => 50600,
43+
'iconv.output_encoding' => 50600,
44+
'iconv.internal_encoding' => 50600,
45+
46+
'mbstring.func_overload' => 70200,
47+
'track_errors' => 70200,
48+
49+
'allow_url_include' => 70400,
50+
51+
'assert.quiet_eval' => 80000,
52+
53+
'filter.default' => 80100,
54+
'oci8.old_oci_close_semantics' => 80100,
55+
56+
'assert.active' => 80300,
57+
'assert.exception' => 80300,
58+
'assert.bail' => 80300,
59+
'assert.warning' => 80300,
60+
61+
'session.sid_length' => 80400,
62+
'session.sid_bits_per_character' => 80400,
63+
];
64+
65+
private ReflectionProvider $reflectionProvider;
66+
67+
private DeprecatedScopeHelper $deprecatedScopeHelper;
68+
69+
private PhpVersion $phpVersion;
70+
71+
public function __construct(
72+
ReflectionProvider $reflectionProvider,
73+
DeprecatedScopeHelper $deprecatedScopeHelper,
74+
PhpVersion $phpVersion
75+
)
76+
{
77+
$this->reflectionProvider = $reflectionProvider;
78+
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
79+
$this->phpVersion = $phpVersion;
80+
}
81+
82+
public function getNodeType(): string
83+
{
84+
return FuncCall::class;
85+
}
86+
87+
public function processNode(Node $node, Scope $scope): array
88+
{
89+
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
90+
return [];
91+
}
92+
93+
if (!($node->name instanceof Name)) {
94+
return [];
95+
}
96+
97+
if (count($node->getArgs()) < 1) {
98+
return [];
99+
}
100+
101+
try {
102+
$function = $this->reflectionProvider->getFunction($node->name, $scope);
103+
} catch (FunctionNotFoundException $e) {
104+
// Other rules will notify if the function is not found
105+
return [];
106+
}
107+
108+
if (!in_array(strtolower($function->getName()), self::INI_FUNCTIONS, true)) {
109+
return [];
110+
}
111+
112+
$phpVersionId = $this->phpVersion->getVersionId();
113+
$iniType = $scope->getType($node->getArgs()[0]->value);
114+
foreach ($iniType->getConstantStrings() as $string) {
115+
if (!array_key_exists($string->getValue(), self::DEPRECATED_OPTIONS)) {
116+
continue;
117+
}
118+
119+
if ($phpVersionId < self::DEPRECATED_OPTIONS[$string->getValue()]) {
120+
continue;
121+
}
122+
123+
return [
124+
RuleErrorBuilder::message(sprintf(
125+
"Call to function %s() with deprecated option '%s'.",
126+
$function->getName(),
127+
$string->getValue(),
128+
))->identifier('function.deprecated')->build(),
129+
];
130+
}
131+
132+
return [];
133+
}
134+
135+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Deprecations;
4+
5+
use PHPStan\Php\PhpVersion;
6+
use PHPStan\Rules\Rule;
7+
use PHPStan\Testing\RuleTestCase;
8+
use const PHP_VERSION_ID;
9+
10+
/**
11+
* @extends RuleTestCase<CallWithDeprecatedIniOptionRule>
12+
*/
13+
class CallWithDeprecatedIniOptionRuleTest extends RuleTestCase
14+
{
15+
16+
protected function getRule(): Rule
17+
{
18+
return new CallWithDeprecatedIniOptionRule(
19+
$this->createReflectionProvider(),
20+
new DeprecatedScopeHelper([new DefaultDeprecatedScopeResolver()]),
21+
self::getContainer()->getByType(PhpVersion::class),
22+
);
23+
}
24+
25+
public function testRule(): void
26+
{
27+
$expectedErrors = [];
28+
if (PHP_VERSION_ID >= 80300) {
29+
$expectedErrors = [
30+
[
31+
"Call to function ini_set() with deprecated option 'assert.active'.",
32+
11,
33+
],
34+
[
35+
"Call to function ini_get() with deprecated option 'assert.active'.",
36+
12,
37+
],
38+
];
39+
}
40+
41+
$this->analyse(
42+
[__DIR__ . '/data/call-with-deprecation-ini-option.php'],
43+
$expectedErrors,
44+
);
45+
}
46+
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace CallWithDeprecatedIniOption;
4+
5+
function doFooBar(): void {
6+
var_dump(ini_set('memory_limit', '2048M'));
7+
var_dump(ini_get('memory_limit'));
8+
}
9+
10+
function doFoo(): void {
11+
var_dump(ini_set('assert.active', false));
12+
var_dump(ini_get('assert.active'));
13+
}
14+
15+
/** @deprecated */
16+
function inDeprecatedFunction(): void {
17+
var_dump(ini_set('assert.active', false));
18+
var_dump(ini_get('assert.active'));
19+
}

0 commit comments

Comments
 (0)