Repository navigation
Expand file tree
/
Copy pathTargetRange.php
More file actions
110 lines (96 loc) · 4.06 KB
/
Copy pathTargetRange.php
File metadata and controls
110 lines (96 loc) · 4.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
<?php
declare(strict_types=1);
/*
* This file is part of the PHPRegex package.
*
* (c) Younes ENNAJI <younes.ennaji.pro@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPRegex\Rector;
use PhpParser\Node\Expr\CallLike;
use PHPRegex\Parser\ParserOptions;
use PHPRegex\Parser\PcreTarget;
use PHPRegex\Parser\Printer\NodeDumper;
use PHPRegex\Parser\Printer\PatternPrinter;
use PHPRegex\Parser\RegexParser;
use PHPRegex\Parser\Validation\RangeDiff;
use Rector\Reflection\ReflectionResolver;
/**
* What the rules that ready a pattern for the PHP versions a project runs
* on share: the range they judge, from the PHP the code targets to the
* newest the library knows, and the pattern literals a call hands to a
* regex engine.
*
* @internal
*/
final class TargetRange
{
/**
* The functions whose first argument is a pattern.
*/
public const FUNCTIONS = ['preg_match', 'preg_match_all', 'preg_replace', 'preg_replace_callback', 'preg_split', 'preg_grep', 'preg_filter'];
/**
* @var array<int, RangeDiff>
*/
private static array $ranges = [];
private function __construct() {}
/**
* The call with each pattern literal it hands to a regex engine
* replaced by its rewrite (see PatternLiterals); null when none is
* rewritten.
*
* @template T of CallLike
*
* @param T $call
* @param bool $pregCall whether the call is a function call named in FUNCTIONS
* @param bool $callbackArray whether the call is preg_replace_callback_array()
* @param \Closure(string): ?string $rewrite the rewritten pattern, or null to leave it
*
* @return T|null
*/
public static function rewriteEach(CallLike $call, bool $pregCall, bool $callbackArray, ReflectionResolver $reflection, \Closure $rewrite): ?CallLike
{
$changed = false;
foreach (PatternLiterals::of($call, $pregCall, $callbackArray, $reflection) as [$literal, $replace]) {
$rewritten = $rewrite($literal->value);
if (null !== $rewritten) {
$replace(PregCall::string($rewritten));
$changed = true;
}
}
return $changed ? $call : null;
}
/**
* From the PHP the code targets to the newest the library knows,
* through every PHP version a verdict may change at: never a single
* target. One range per PHP version, kept for the run, each target read
* by the parser of the PHP the code targets (PregCall::targetParser()).
* Rector takes targets from PHP 5.3 to PHP 10.0, which the library
* judges from PHP 7.0 to PHP 9.99.
*/
public static function from(int $phpVersion): RangeDiff
{
$phpVersion = min(max($phpVersion, ParserOptions::OLDEST_PHP_VERSION_ID), ParserOptions::LATEST_PHP_VERSION_ID);
$boundaries = PcreTarget::phpVersionBoundaries();
return self::$ranges[$phpVersion] ??= RangeDiff::between($phpVersion, max($phpVersion, $boundaries[\count($boundaries) - 1]), PregCall::targetParser($phpVersion));
}
/**
* Whether the rewritten pattern is read alike by every PHP of the
* range, and as $reader, which accepts both, read the pattern it
* replaces: the same tree, or two trees the printer writes the same
* once escapes are normalized, as a NUL byte and "\x00".
*/
public static function keepsTheReading(RangeDiff $range, RegexParser $reader, string $pattern, string $rewritten): bool
{
$verdict = $range->diff($rewritten);
if (!$verdict->from->validation->isValid || null !== $verdict->refusedBy() || null !== $verdict->readsOtherwise) {
return false;
}
$before = $reader->parse($pattern);
$after = $reader->parse($rewritten);
return $after->accept(new NodeDumper()) === $before->accept(new NodeDumper())
|| $after->accept(PatternPrinter::pretty()) === $before->accept(PatternPrinter::pretty());
}
}