1: <?php declare(strict_types=1);
2:
3: namespace PhpParser;
4:
5: /**
6: * A PHP version, representing only the major and minor version components.
7: */
8: class PhpVersion {
9: /** @var int Version ID in PHP_VERSION_ID format */
10: public int $id;
11:
12: /** @var int[] Minimum versions for builtin types */
13: private const BUILTIN_TYPE_VERSIONS = [
14: 'array' => 50100,
15: 'callable' => 50400,
16: 'bool' => 70000,
17: 'int' => 70000,
18: 'float' => 70000,
19: 'string' => 70000,
20: 'iterable' => 70100,
21: 'void' => 70100,
22: 'object' => 70200,
23: 'null' => 80000,
24: 'false' => 80000,
25: 'mixed' => 80000,
26: 'never' => 80100,
27: 'true' => 80200,
28: ];
29:
30: private function __construct(int $id) {
31: $this->id = $id;
32: }
33:
34: /**
35: * Create a PhpVersion object from major and minor version components.
36: */
37: public static function fromComponents(int $major, int $minor): self {
38: return new self($major * 10000 + $minor * 100);
39: }
40:
41: /**
42: * Get the newest PHP version supported by this library. Support for this version may be partial,
43: * if it is still under development.
44: */
45: public static function getNewestSupported(): self {
46: return self::fromComponents(8, 4);
47: }
48:
49: /**
50: * Get the host PHP version, that is the PHP version we're currently running on.
51: */
52: public static function getHostVersion(): self {
53: return self::fromComponents(\PHP_MAJOR_VERSION, \PHP_MINOR_VERSION);
54: }
55:
56: /**
57: * Parse the version from a string like "8.1".
58: */
59: public static function fromString(string $version): self {
60: if (!preg_match('/^(\d+)\.(\d+)/', $version, $matches)) {
61: throw new \LogicException("Invalid PHP version \"$version\"");
62: }
63: return self::fromComponents((int) $matches[1], (int) $matches[2]);
64: }
65:
66: /**
67: * Check whether two versions are the same.
68: */
69: public function equals(PhpVersion $other): bool {
70: return $this->id === $other->id;
71: }
72:
73: /**
74: * Check whether this version is greater than or equal to the argument.
75: */
76: public function newerOrEqual(PhpVersion $other): bool {
77: return $this->id >= $other->id;
78: }
79:
80: /**
81: * Check whether this version is older than the argument.
82: */
83: public function older(PhpVersion $other): bool {
84: return $this->id < $other->id;
85: }
86:
87: /**
88: * Check whether this is the host PHP version.
89: */
90: public function isHostVersion(): bool {
91: return $this->equals(self::getHostVersion());
92: }
93:
94: /**
95: * Check whether this PHP version supports the given builtin type. Type name must be lowercase.
96: */
97: public function supportsBuiltinType(string $type): bool {
98: $minVersion = self::BUILTIN_TYPE_VERSIONS[$type] ?? null;
99: return $minVersion !== null && $this->id >= $minVersion;
100: }
101:
102: /**
103: * Whether this version supports [] array literals.
104: */
105: public function supportsShortArraySyntax(): bool {
106: return $this->id >= 50400;
107: }
108:
109: /**
110: * Whether this version supports [] for destructuring.
111: */
112: public function supportsShortArrayDestructuring(): bool {
113: return $this->id >= 70100;
114: }
115:
116: /**
117: * Whether this version supports flexible heredoc/nowdoc.
118: */
119: public function supportsFlexibleHeredoc(): bool {
120: return $this->id >= 70300;
121: }
122:
123: /**
124: * Whether this version supports trailing commas in parameter lists.
125: */
126: public function supportsTrailingCommaInParamList(): bool {
127: return $this->id >= 80000;
128: }
129:
130: /**
131: * Whether this version allows "$var =& new Obj".
132: */
133: public function allowsAssignNewByReference(): bool {
134: return $this->id < 70000;
135: }
136:
137: /**
138: * Whether this version allows invalid octals like "08".
139: */
140: public function allowsInvalidOctals(): bool {
141: return $this->id < 70000;
142: }
143:
144: /**
145: * Whether this version allows DEL (\x7f) to occur in identifiers.
146: */
147: public function allowsDelInIdentifiers(): bool {
148: return $this->id < 70100;
149: }
150:
151: /**
152: * Whether this version supports yield in expression context without parentheses.
153: */
154: public function supportsYieldWithoutParentheses(): bool {
155: return $this->id >= 70000;
156: }
157:
158: /**
159: * Whether this version supports unicode escape sequences in strings.
160: */
161: public function supportsUnicodeEscapes(): bool {
162: return $this->id >= 70000;
163: }
164: }
165: