1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Php;
4:
5: use function floor;
6:
7: /**
8: * @api
9: */
10: final class PhpVersion
11: {
12:
13: public const SOURCE_RUNTIME = 1;
14: public const SOURCE_CONFIG = 2;
15: public const SOURCE_COMPOSER_PLATFORM_PHP = 3;
16: public const SOURCE_UNKNOWN = 4;
17:
18: /**
19: * @param self::SOURCE_* $source
20: */
21: public function __construct(private int $versionId, private int $source = self::SOURCE_UNKNOWN)
22: {
23: }
24:
25: public function getSourceLabel(): string
26: {
27: switch ($this->source) {
28: case self::SOURCE_RUNTIME:
29: return 'runtime';
30: case self::SOURCE_CONFIG:
31: return 'config';
32: case self::SOURCE_COMPOSER_PLATFORM_PHP:
33: return 'config.platform.php in composer.json';
34: }
35:
36: return 'unknown';
37: }
38:
39: public function getVersionId(): int
40: {
41: return $this->versionId;
42: }
43:
44: public function getVersionString(): string
45: {
46: $first = (int) floor($this->versionId / 10000);
47: $second = (int) floor(($this->versionId % 10000) / 100);
48: $third = (int) floor($this->versionId % 100);
49:
50: return $first . '.' . $second . ($third !== 0 ? '.' . $third : '');
51: }
52:
53: public function supportsNullCoalesceAssign(): bool
54: {
55: return $this->versionId >= 70400;
56: }
57:
58: public function supportsParameterContravariance(): bool
59: {
60: return $this->versionId >= 70400;
61: }
62:
63: public function supportsReturnCovariance(): bool
64: {
65: return $this->versionId >= 70400;
66: }
67:
68: public function supportsNoncapturingCatches(): bool
69: {
70: return $this->versionId >= 80000;
71: }
72:
73: public function supportsNativeUnionTypes(): bool
74: {
75: return $this->versionId >= 80000;
76: }
77:
78: public function deprecatesRequiredParameterAfterOptional(): bool
79: {
80: return $this->versionId >= 80000;
81: }
82:
83: public function deprecatesRequiredParameterAfterOptionalNullableAndDefaultNull(): bool
84: {
85: return $this->versionId >= 80100;
86: }
87:
88: public function deprecatesRequiredParameterAfterOptionalUnionOrMixed(): bool
89: {
90: return $this->versionId >= 80300;
91: }
92:
93: public function supportsLessOverridenParametersWithVariadic(): bool
94: {
95: return $this->versionId >= 80000;
96: }
97:
98: public function supportsThrowExpression(): bool
99: {
100: return $this->versionId >= 80000;
101: }
102:
103: public function supportsClassConstantOnExpression(): bool
104: {
105: return $this->versionId >= 80000;
106: }
107:
108: public function supportsLegacyConstructor(): bool
109: {
110: return $this->versionId < 80000;
111: }
112:
113: public function supportsPromotedProperties(): bool
114: {
115: return $this->versionId >= 80000;
116: }
117:
118: public function supportsParameterTypeWidening(): bool
119: {
120: return $this->versionId >= 70200;
121: }
122:
123: public function supportsUnsetCast(): bool
124: {
125: return $this->versionId < 80000;
126: }
127:
128: public function supportsNamedArguments(): bool
129: {
130: return $this->versionId >= 80000;
131: }
132:
133: public function throwsTypeErrorForInternalFunctions(): bool
134: {
135: return $this->versionId >= 80000;
136: }
137:
138: public function throwsValueErrorForInternalFunctions(): bool
139: {
140: return $this->versionId >= 80000;
141: }
142:
143: public function supportsHhPrintfSpecifier(): bool
144: {
145: return $this->versionId >= 80000;
146: }
147:
148: public function isEmptyStringValidAliasForNoneInMbSubstituteCharacter(): bool
149: {
150: return $this->versionId < 80000;
151: }
152:
153: public function supportsAllUnicodeScalarCodePointsInMbSubstituteCharacter(): bool
154: {
155: return $this->versionId >= 70200;
156: }
157:
158: public function isNumericStringValidArgInMbSubstituteCharacter(): bool
159: {
160: return $this->versionId < 80000;
161: }
162:
163: public function isNullValidArgInMbSubstituteCharacter(): bool
164: {
165: return $this->versionId >= 80000;
166: }
167:
168: public function isInterfaceConstantImplicitlyFinal(): bool
169: {
170: return $this->versionId < 80100;
171: }
172:
173: public function supportsFinalConstants(): bool
174: {
175: return $this->versionId >= 80100;
176: }
177:
178: public function supportsReadOnlyProperties(): bool
179: {
180: return $this->versionId >= 80100;
181: }
182:
183: public function supportsEnums(): bool
184: {
185: return $this->versionId >= 80100;
186: }
187:
188: public function supportsPureIntersectionTypes(): bool
189: {
190: return $this->versionId >= 80100;
191: }
192:
193: public function supportsCaseInsensitiveConstantNames(): bool
194: {
195: return $this->versionId < 80000;
196: }
197:
198: public function hasStricterRoundFunctions(): bool
199: {
200: return $this->versionId >= 80000;
201: }
202:
203: public function hasTentativeReturnTypes(): bool
204: {
205: return $this->versionId >= 80100;
206: }
207:
208: public function supportsFirstClassCallables(): bool
209: {
210: return $this->versionId >= 80100;
211: }
212:
213: public function supportsArrayUnpackingWithStringKeys(): bool
214: {
215: return $this->versionId >= 80100;
216: }
217:
218: public function throwsOnInvalidMbStringEncoding(): bool
219: {
220: return $this->versionId >= 80000;
221: }
222:
223: public function supportsPassNoneEncodings(): bool
224: {
225: return $this->versionId < 70300;
226: }
227:
228: public function producesWarningForFinalPrivateMethods(): bool
229: {
230: return $this->versionId >= 80000;
231: }
232:
233: public function deprecatesDynamicProperties(): bool
234: {
235: return $this->versionId >= 80200;
236: }
237:
238: public function strSplitReturnsEmptyArray(): bool
239: {
240: return $this->versionId >= 80200;
241: }
242:
243: public function supportsDisjunctiveNormalForm(): bool
244: {
245: return $this->versionId >= 80200;
246: }
247:
248: public function serializableRequiresMagicMethods(): bool
249: {
250: return $this->versionId >= 80100;
251: }
252:
253: public function arrayFunctionsReturnNullWithNonArray(): bool
254: {
255: return $this->versionId < 80000;
256: }
257:
258: // see https://www.php.net/manual/en/migration80.incompatible.php#migration80.incompatible.core.string-number-comparision
259: public function castsNumbersToStringsOnLooseComparison(): bool
260: {
261: return $this->versionId >= 80000;
262: }
263:
264: public function supportsCallableInstanceMethods(): bool
265: {
266: return $this->versionId < 80000;
267: }
268:
269: public function supportsJsonValidate(): bool
270: {
271: return $this->versionId >= 80300;
272: }
273:
274: public function supportsConstantsInTraits(): bool
275: {
276: return $this->versionId >= 80200;
277: }
278:
279: public function supportsNativeTypesInClassConstants(): bool
280: {
281: return $this->versionId >= 80300;
282: }
283:
284: public function supportsAbstractTraitMethods(): bool
285: {
286: return $this->versionId >= 80000;
287: }
288:
289: public function supportsOverrideAttribute(): bool
290: {
291: return $this->versionId >= 80300;
292: }
293:
294: public function supportsDynamicClassConstantFetch(): bool
295: {
296: return $this->versionId >= 80300;
297: }
298:
299: public function supportsReadOnlyClasses(): bool
300: {
301: return $this->versionId >= 80200;
302: }
303:
304: public function supportsReadOnlyAnonymousClasses(): bool
305: {
306: return $this->versionId >= 80300;
307: }
308:
309: public function supportsNeverReturnTypeInArrowFunction(): bool
310: {
311: return $this->versionId >= 80200;
312: }
313:
314: public function supportsPregUnmatchedAsNull(): bool
315: {
316: // while PREG_UNMATCHED_AS_NULL is defined in php-src since 7.2.x it starts working as expected with 7.4.x
317: // https://3v4l.org/v3HE4
318: return $this->versionId >= 70400;
319: }
320:
321: public function supportsPregCaptureOnlyNamedGroups(): bool
322: {
323: // https://php.watch/versions/8.2/preg-n-no-capture-modifier
324: return $this->versionId >= 80200;
325: }
326:
327: public function hasDateTimeExceptions(): bool
328: {
329: return $this->versionId >= 80300;
330: }
331:
332: public function isCurloptUrlCheckingFileSchemeWithOpenBasedir(): bool
333: {
334: // Before PHP 8.0, when setting CURLOPT_URL, an unparsable URL or a file:// scheme would fail if open_basedir is used
335: // https://github.com/php/php-src/blob/php-7.4.33/ext/curl/interface.c#L139-L158
336: // https://github.com/php/php-src/blob/php-8.0.0/ext/curl/interface.c#L128-L130
337: return $this->versionId < 80000;
338: }
339:
340: public function highlightStringDoesNotReturnFalse(): bool
341: {
342: return $this->versionId >= 80400;
343: }
344:
345: public function deprecatesImplicitlyNullableParameterTypes(): bool
346: {
347: return $this->versionId >= 80400;
348: }
349:
350: public function substrReturnFalseInsteadOfEmptyString(): bool
351: {
352: return $this->versionId < 80000;
353: }
354:
355: }
356: