1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Type;
4:
5: use PHPStan\Php\PhpVersion;
6: use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
7: use PHPStan\PhpDocParser\Ast\Type\TypeNode;
8: use PHPStan\TrinaryLogic;
9: use PHPStan\Type\Constant\ConstantArrayType;
10: use PHPStan\Type\Constant\ConstantBooleanType;
11: use PHPStan\Type\Constant\ConstantFloatType;
12: use PHPStan\Type\Constant\ConstantIntegerType;
13: use PHPStan\Type\Constant\ConstantStringType;
14: use PHPStan\Type\Traits\FalseyBooleanTypeTrait;
15: use PHPStan\Type\Traits\NonArrayTypeTrait;
16: use PHPStan\Type\Traits\NonCallableTypeTrait;
17: use PHPStan\Type\Traits\NonGenericTypeTrait;
18: use PHPStan\Type\Traits\NonIterableTypeTrait;
19: use PHPStan\Type\Traits\NonObjectTypeTrait;
20: use PHPStan\Type\Traits\NonRemoveableTypeTrait;
21:
22: /** @api */
23: class NullType implements ConstantScalarType
24: {
25:
26: use NonArrayTypeTrait;
27: use NonCallableTypeTrait;
28: use NonIterableTypeTrait;
29: use NonObjectTypeTrait;
30: use FalseyBooleanTypeTrait;
31: use NonGenericTypeTrait;
32: use NonRemoveableTypeTrait;
33:
34: /** @api */
35: public function __construct()
36: {
37: }
38:
39: public function getReferencedClasses(): array
40: {
41: return [];
42: }
43:
44: public function getObjectClassNames(): array
45: {
46: return [];
47: }
48:
49: public function getObjectClassReflections(): array
50: {
51: return [];
52: }
53:
54: public function getConstantStrings(): array
55: {
56: return [];
57: }
58:
59: /**
60: * @return null
61: */
62: public function getValue()
63: {
64: return null;
65: }
66:
67: public function generalize(GeneralizePrecision $precision): Type
68: {
69: return $this;
70: }
71:
72: public function accepts(Type $type, bool $strictTypes): AcceptsResult
73: {
74: if ($type instanceof self) {
75: return AcceptsResult::createYes();
76: }
77:
78: if ($type instanceof CompoundType) {
79: return $type->isAcceptedBy($this, $strictTypes);
80: }
81:
82: return AcceptsResult::createNo();
83: }
84:
85: public function isSuperTypeOf(Type $type): IsSuperTypeOfResult
86: {
87: if ($type instanceof self) {
88: return IsSuperTypeOfResult::createYes();
89: }
90:
91: if ($type instanceof CompoundType) {
92: return $type->isSubTypeOf($this);
93: }
94:
95: return IsSuperTypeOfResult::createNo();
96: }
97:
98: public function equals(Type $type): bool
99: {
100: return $type instanceof self;
101: }
102:
103: public function isSmallerThan(Type $otherType, PhpVersion $phpVersion): TrinaryLogic
104: {
105: if ($otherType instanceof ConstantScalarType) {
106: return TrinaryLogic::createFromBoolean(null < $otherType->getValue());
107: }
108:
109: if ($otherType instanceof CompoundType) {
110: return $otherType->isGreaterThan($this, $phpVersion);
111: }
112:
113: if ($otherType->isObject()->yes()) {
114: return TrinaryLogic::createYes();
115: }
116:
117: return TrinaryLogic::createMaybe();
118: }
119:
120: public function isSmallerThanOrEqual(Type $otherType, PhpVersion $phpVersion): TrinaryLogic
121: {
122: if ($otherType instanceof ConstantScalarType) {
123: return TrinaryLogic::createFromBoolean(null <= $otherType->getValue());
124: }
125:
126: if ($otherType instanceof CompoundType) {
127: return $otherType->isGreaterThanOrEqual($this, $phpVersion);
128: }
129:
130: if ($otherType->isObject()->yes()) {
131: return TrinaryLogic::createYes();
132: }
133:
134: return TrinaryLogic::createMaybe();
135: }
136:
137: public function describe(VerbosityLevel $level): string
138: {
139: return 'null';
140: }
141:
142: public function toNumber(): Type
143: {
144: return new ConstantIntegerType(0);
145: }
146:
147: public function toAbsoluteNumber(): Type
148: {
149: return $this->toNumber()->toAbsoluteNumber();
150: }
151:
152: public function toString(): Type
153: {
154: return new ConstantStringType('');
155: }
156:
157: public function toInteger(): Type
158: {
159: return $this->toNumber();
160: }
161:
162: public function toFloat(): Type
163: {
164: return $this->toNumber()->toFloat();
165: }
166:
167: public function toArray(): Type
168: {
169: return new ConstantArrayType([], []);
170: }
171:
172: public function toArrayKey(): Type
173: {
174: return new ConstantStringType('');
175: }
176:
177: public function toCoercedArgumentType(bool $strictTypes): Type
178: {
179: return $this;
180: }
181:
182: public function isOffsetAccessible(): TrinaryLogic
183: {
184: return TrinaryLogic::createYes();
185: }
186:
187: public function isOffsetAccessLegal(): TrinaryLogic
188: {
189: return TrinaryLogic::createYes();
190: }
191:
192: public function hasOffsetValueType(Type $offsetType): TrinaryLogic
193: {
194: return TrinaryLogic::createNo();
195: }
196:
197: public function getOffsetValueType(Type $offsetType): Type
198: {
199: return new ErrorType();
200: }
201:
202: public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $unionValues = true): Type
203: {
204: $array = new ConstantArrayType([], []);
205: return $array->setOffsetValueType($offsetType, $valueType, $unionValues);
206: }
207:
208: public function setExistingOffsetValueType(Type $offsetType, Type $valueType): Type
209: {
210: return $this;
211: }
212:
213: public function unsetOffset(Type $offsetType): Type
214: {
215: return $this;
216: }
217:
218: public function traverse(callable $cb): Type
219: {
220: return $this;
221: }
222:
223: public function traverseSimultaneously(Type $right, callable $cb): Type
224: {
225: return $this;
226: }
227:
228: public function isNull(): TrinaryLogic
229: {
230: return TrinaryLogic::createYes();
231: }
232:
233: public function isConstantValue(): TrinaryLogic
234: {
235: return TrinaryLogic::createYes();
236: }
237:
238: public function isConstantScalarValue(): TrinaryLogic
239: {
240: return TrinaryLogic::createYes();
241: }
242:
243: public function getConstantScalarTypes(): array
244: {
245: return [$this];
246: }
247:
248: public function getConstantScalarValues(): array
249: {
250: return [$this->getValue()];
251: }
252:
253: public function isTrue(): TrinaryLogic
254: {
255: return TrinaryLogic::createNo();
256: }
257:
258: public function isFalse(): TrinaryLogic
259: {
260: return TrinaryLogic::createNo();
261: }
262:
263: public function isBoolean(): TrinaryLogic
264: {
265: return TrinaryLogic::createNo();
266: }
267:
268: public function isFloat(): TrinaryLogic
269: {
270: return TrinaryLogic::createNo();
271: }
272:
273: public function isInteger(): TrinaryLogic
274: {
275: return TrinaryLogic::createNo();
276: }
277:
278: public function isString(): TrinaryLogic
279: {
280: return TrinaryLogic::createNo();
281: }
282:
283: public function isNumericString(): TrinaryLogic
284: {
285: return TrinaryLogic::createNo();
286: }
287:
288: public function isNonEmptyString(): TrinaryLogic
289: {
290: return TrinaryLogic::createNo();
291: }
292:
293: public function isNonFalsyString(): TrinaryLogic
294: {
295: return TrinaryLogic::createNo();
296: }
297:
298: public function isLiteralString(): TrinaryLogic
299: {
300: return TrinaryLogic::createNo();
301: }
302:
303: public function isLowercaseString(): TrinaryLogic
304: {
305: return TrinaryLogic::createNo();
306: }
307:
308: public function isUppercaseString(): TrinaryLogic
309: {
310: return TrinaryLogic::createNo();
311: }
312:
313: public function isClassString(): TrinaryLogic
314: {
315: return TrinaryLogic::createNo();
316: }
317:
318: public function getClassStringObjectType(): Type
319: {
320: return new ErrorType();
321: }
322:
323: public function getObjectTypeOrClassStringObjectType(): Type
324: {
325: return new ErrorType();
326: }
327:
328: public function isVoid(): TrinaryLogic
329: {
330: return TrinaryLogic::createNo();
331: }
332:
333: public function isScalar(): TrinaryLogic
334: {
335: return TrinaryLogic::createNo();
336: }
337:
338: public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
339: {
340: if ($type instanceof ConstantScalarType) {
341: return LooseComparisonHelper::compareConstantScalars($this, $type, $phpVersion);
342: }
343:
344: if ($type->isConstantArray()->yes() && $type->isIterableAtLeastOnce()->no()) {
345: // @phpstan-ignore equal.alwaysTrue, equal.notAllowed
346: return new ConstantBooleanType($this->getValue() == []); // phpcs:ignore
347: }
348:
349: if ($type instanceof CompoundType) {
350: return $type->looseCompare($this, $phpVersion);
351: }
352:
353: return new BooleanType();
354: }
355:
356: public function getSmallerType(PhpVersion $phpVersion): Type
357: {
358: return new NeverType();
359: }
360:
361: public function getSmallerOrEqualType(PhpVersion $phpVersion): Type
362: {
363: // All falsey types except '0'
364: return new UnionType([
365: new NullType(),
366: new ConstantBooleanType(false),
367: new ConstantIntegerType(0),
368: new ConstantFloatType(0.0),
369: new ConstantStringType(''),
370: new ConstantArrayType([], []),
371: ]);
372: }
373:
374: public function getGreaterType(PhpVersion $phpVersion): Type
375: {
376: // All truthy types, but also '0'
377: return new MixedType(subtractedType: new UnionType([
378: new NullType(),
379: new ConstantBooleanType(false),
380: new ConstantIntegerType(0),
381: new ConstantFloatType(0.0),
382: new ConstantStringType(''),
383: new ConstantArrayType([], []),
384: ]));
385: }
386:
387: public function getGreaterOrEqualType(PhpVersion $phpVersion): Type
388: {
389: return new MixedType();
390: }
391:
392: public function getFiniteTypes(): array
393: {
394: return [$this];
395: }
396:
397: public function exponentiate(Type $exponent): Type
398: {
399: return new UnionType(
400: [
401: new ConstantIntegerType(0),
402: new ConstantIntegerType(1),
403: ],
404: );
405: }
406:
407: public function toPhpDocNode(): TypeNode
408: {
409: return new IdentifierTypeNode('null');
410: }
411:
412: }
413: