Skip to content

Commit 064c784

Browse files
committed
Updated Rector to commit a72065d1676018c9fa346f11ff0cdd2dd7bbe27d
rectorphp/rector-src@a72065d [php 8.3] Add json_validate rule. (#7213)
1 parent 4b2b1f3 commit 064c784

File tree

7 files changed

+150
-3
lines changed

7 files changed

+150
-3
lines changed

config/set/php83.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
namespace RectorPrefix202509;
55

66
use Rector\Config\RectorConfig;
7+
use Rector\Php83\Rector\BooleanAnd\JsonValidateRector;
78
use Rector\Php83\Rector\Class_\ReadOnlyAnonymousClassRector;
89
use Rector\Php83\Rector\ClassConst\AddTypeToConstRector;
910
use Rector\Php83\Rector\ClassMethod\AddOverrideAttributeToOverriddenMethodsRector;
1011
use Rector\Php83\Rector\FuncCall\CombineHostPortLdapUriRector;
1112
use Rector\Php83\Rector\FuncCall\DynamicClassConstFetchRector;
1213
use Rector\Php83\Rector\FuncCall\RemoveGetClassGetParentClassNoArgsRector;
1314
return static function (RectorConfig $rectorConfig) : void {
14-
$rectorConfig->rules([AddOverrideAttributeToOverriddenMethodsRector::class, AddTypeToConstRector::class, CombineHostPortLdapUriRector::class, RemoveGetClassGetParentClassNoArgsRector::class, ReadOnlyAnonymousClassRector::class, DynamicClassConstFetchRector::class]);
15+
$rectorConfig->rules([AddOverrideAttributeToOverriddenMethodsRector::class, AddTypeToConstRector::class, CombineHostPortLdapUriRector::class, RemoveGetClassGetParentClassNoArgsRector::class, ReadOnlyAnonymousClassRector::class, DynamicClassConstFetchRector::class, JsonValidateRector::class]);
1516
};
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
<?php
2+
3+
declare (strict_types=1);
4+
namespace Rector\Php83\Rector\BooleanAnd;
5+
6+
use PhpParser\Node;
7+
use PhpParser\Node\Arg;
8+
use PhpParser\Node\Expr\BinaryOp\BooleanAnd;
9+
use PhpParser\Node\Expr\BinaryOp\Identical;
10+
use PhpParser\Node\Expr\BinaryOp\NotIdentical;
11+
use PhpParser\Node\Expr\ConstFetch;
12+
use PhpParser\Node\Expr\FuncCall;
13+
use PhpParser\Node\Name;
14+
use Rector\NodeManipulator\BinaryOpManipulator;
15+
use Rector\Php71\ValueObject\TwoNodeMatch;
16+
use Rector\PhpParser\Node\Value\ValueResolver;
17+
use Rector\Rector\AbstractRector;
18+
use Rector\ValueObject\PhpVersionFeature;
19+
use Rector\ValueObject\PolyfillPackage;
20+
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
21+
use Rector\VersionBonding\Contract\RelatedPolyfillInterface;
22+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
23+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
24+
/**
25+
* @see \Rector\Tests\Php83\Rector\BooleanAnd\JsonValidateRector\JsonValidateRectorTest
26+
*/
27+
final class JsonValidateRector extends AbstractRector implements MinPhpVersionInterface, RelatedPolyfillInterface
28+
{
29+
/**
30+
* @readonly
31+
*/
32+
private BinaryOpManipulator $binaryOpManipulator;
33+
private ValueResolver $valueResolver;
34+
protected const ARG_NAMES = ['json', 'associative', 'depth', 'flags'];
35+
private const JSON_MAX_DEPTH = 0x7fffffff;
36+
public function __construct(BinaryOpManipulator $binaryOpManipulator, ValueResolver $valueResolver)
37+
{
38+
$this->binaryOpManipulator = $binaryOpManipulator;
39+
$this->valueResolver = $valueResolver;
40+
}
41+
public function provideMinPhpVersion() : int
42+
{
43+
return PhpVersionFeature::JSON_VALIDATE;
44+
}
45+
public function getRuleDefinition() : RuleDefinition
46+
{
47+
return new RuleDefinition('Replace json_decode($json, true) !== null && json_last_error() === JSON_ERROR_NONE with json_validate()', [new CodeSample(<<<'CODE_SAMPLE'
48+
if (json_decode($json, true) !== null && json_last_error() === JSON_ERROR_NONE) {
49+
}
50+
51+
CODE_SAMPLE
52+
, <<<'CODE_SAMPLE'
53+
if (json_validate($json)) {
54+
}
55+
CODE_SAMPLE
56+
)]);
57+
}
58+
/**
59+
* @return array<class-string<Node>>
60+
*/
61+
public function getNodeTypes() : array
62+
{
63+
return [BooleanAnd::class];
64+
}
65+
/**
66+
* @param BooleanAnd $node
67+
*/
68+
public function refactor(Node $node) : ?Node
69+
{
70+
$funcCall = $this->matchJsonValidateArg($node);
71+
if (!$funcCall instanceof FuncCall) {
72+
return null;
73+
}
74+
if ($funcCall->isFirstClassCallable()) {
75+
return null;
76+
}
77+
$args = $funcCall->getArgs();
78+
if (\count($args) < 1) {
79+
return null;
80+
}
81+
if (!$this->validateArgs($funcCall)) {
82+
return null;
83+
}
84+
$funcCall->name = new Name('json_validate');
85+
$funcCall->args = $args;
86+
return $funcCall;
87+
}
88+
public function providePolyfillPackage() : string
89+
{
90+
return PolyfillPackage::PHP_83;
91+
}
92+
public function matchJsonValidateArg(BooleanAnd $booleanAnd) : ?FuncCall
93+
{
94+
// match: json_decode(...) !== null OR null !== json_decode(...)
95+
if (!$booleanAnd->left instanceof NotIdentical) {
96+
return null;
97+
}
98+
$decodeMatch = $this->binaryOpManipulator->matchFirstAndSecondConditionNode($booleanAnd->left, fn($node) => $node instanceof FuncCall && $this->isName($node->name, 'json_decode'), fn($node) => $node instanceof ConstFetch && $this->isName($node->name, 'null'));
99+
if (!$decodeMatch instanceof TwoNodeMatch) {
100+
return null;
101+
}
102+
// match: json_last_error() === JSON_ERROR_NONE OR JSON_ERROR_NONE === json_last_error()
103+
if (!$booleanAnd->right instanceof Identical) {
104+
return null;
105+
}
106+
$errorMatch = $this->binaryOpManipulator->matchFirstAndSecondConditionNode($booleanAnd->right, fn($node) => $node instanceof FuncCall && $this->isName($node->name, 'json_last_error'), fn($node) => $node instanceof ConstFetch && $this->isName($node->name, 'JSON_ERROR_NONE'));
107+
if (!$errorMatch instanceof TwoNodeMatch) {
108+
return null;
109+
}
110+
// always return the json_decode(...) call
111+
$funcCall = $decodeMatch->getFirstExpr();
112+
if (!$funcCall instanceof FuncCall) {
113+
return null;
114+
}
115+
return $funcCall;
116+
}
117+
protected function validateArgs(FuncCall $funcCall) : bool
118+
{
119+
$depth = $funcCall->getArg('depth', 2);
120+
$flags = $funcCall->getArg('flags', 3);
121+
if ($flags instanceof Arg) {
122+
$flagsValue = $this->valueResolver->getValue($flags);
123+
if ($flagsValue !== \JSON_INVALID_UTF8_IGNORE) {
124+
return \false;
125+
}
126+
}
127+
if ($depth instanceof Arg) {
128+
$depthValue = $this->valueResolver->getValue($depth);
129+
if ($depthValue <= 0 || $depthValue > self::JSON_MAX_DEPTH) {
130+
return \false;
131+
}
132+
}
133+
return \true;
134+
}
135+
}

src/Application/VersionResolver.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ final class VersionResolver
1919
* @api
2020
* @var string
2121
*/
22-
public const PACKAGE_VERSION = 'ea27d15f03aa75e5a68e80bb188a9acca0dfa81f';
22+
public const PACKAGE_VERSION = 'a72065d1676018c9fa346f11ff0cdd2dd7bbe27d';
2323
/**
2424
* @api
2525
* @var string
2626
*/
27-
public const RELEASE_DATE = '2025-09-04 20:28:19';
27+
public const RELEASE_DATE = '2025-09-04 20:31:57';
2828
/**
2929
* @var int
3030
*/

src/ValueObject/PhpVersionFeature.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,11 @@ final class PhpVersionFeature
519519
* @var int
520520
*/
521521
public const READONLY_ANONYMOUS_CLASS = \Rector\ValueObject\PhpVersion::PHP_83;
522+
/**
523+
* @var int
524+
* @see https://wiki.php.net/rfc/json_validate
525+
*/
526+
public const JSON_VALIDATE = \Rector\ValueObject\PhpVersion::PHP_83;
522527
/**
523528
* @see https://wiki.php.net/rfc/mixed_type_v2
524529
* @var int

src/ValueObject/PolyfillPackage.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
*/
99
final class PolyfillPackage
1010
{
11+
/**
12+
* @var string
13+
*/
14+
public const PHP_83 = 'symfony/polyfill-php83';
1115
/**
1216
* @var string
1317
*/

vendor/composer/autoload_classmap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2166,6 +2166,7 @@
21662166
'Rector\\Php82\\Rector\\FuncCall\\Utf8DecodeEncodeToMbConvertEncodingRector' => $baseDir . '/rules/Php82/Rector/FuncCall/Utf8DecodeEncodeToMbConvertEncodingRector.php',
21672167
'Rector\\Php82\\Rector\\New_\\FilesystemIteratorSkipDotsRector' => $baseDir . '/rules/Php82/Rector/New_/FilesystemIteratorSkipDotsRector.php',
21682168
'Rector\\Php82\\Rector\\Param\\AddSensitiveParameterAttributeRector' => $baseDir . '/rules/Php82/Rector/Param/AddSensitiveParameterAttributeRector.php',
2169+
'Rector\\Php83\\Rector\\BooleanAnd\\JsonValidateRector' => $baseDir . '/rules/Php83/Rector/BooleanAnd/JsonValidateRector.php',
21692170
'Rector\\Php83\\Rector\\ClassConst\\AddTypeToConstRector' => $baseDir . '/rules/Php83/Rector/ClassConst/AddTypeToConstRector.php',
21702171
'Rector\\Php83\\Rector\\ClassMethod\\AddOverrideAttributeToOverriddenMethodsRector' => $baseDir . '/rules/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector.php',
21712172
'Rector\\Php83\\Rector\\Class_\\ReadOnlyAnonymousClassRector' => $baseDir . '/rules/Php83/Rector/Class_/ReadOnlyAnonymousClassRector.php',

vendor/composer/autoload_static.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2414,6 +2414,7 @@ class ComposerStaticInit2c278beb4e563f4f2550db2567226f71
24142414
'Rector\\Php82\\Rector\\FuncCall\\Utf8DecodeEncodeToMbConvertEncodingRector' => __DIR__ . '/../..' . '/rules/Php82/Rector/FuncCall/Utf8DecodeEncodeToMbConvertEncodingRector.php',
24152415
'Rector\\Php82\\Rector\\New_\\FilesystemIteratorSkipDotsRector' => __DIR__ . '/../..' . '/rules/Php82/Rector/New_/FilesystemIteratorSkipDotsRector.php',
24162416
'Rector\\Php82\\Rector\\Param\\AddSensitiveParameterAttributeRector' => __DIR__ . '/../..' . '/rules/Php82/Rector/Param/AddSensitiveParameterAttributeRector.php',
2417+
'Rector\\Php83\\Rector\\BooleanAnd\\JsonValidateRector' => __DIR__ . '/../..' . '/rules/Php83/Rector/BooleanAnd/JsonValidateRector.php',
24172418
'Rector\\Php83\\Rector\\ClassConst\\AddTypeToConstRector' => __DIR__ . '/../..' . '/rules/Php83/Rector/ClassConst/AddTypeToConstRector.php',
24182419
'Rector\\Php83\\Rector\\ClassMethod\\AddOverrideAttributeToOverriddenMethodsRector' => __DIR__ . '/../..' . '/rules/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector.php',
24192420
'Rector\\Php83\\Rector\\Class_\\ReadOnlyAnonymousClassRector' => __DIR__ . '/../..' . '/rules/Php83/Rector/Class_/ReadOnlyAnonymousClassRector.php',

0 commit comments

Comments
 (0)