PocketMine-MP 5.36.1 git-eaa7c4834c8fe2f379d24e7f0ee6cc63cfb18ccc
Loading...
Searching...
No Matches
MatchTester.php
1<?php declare(strict_types=1);
2
3namespace DaveRandom\CallbackValidator;
4
6use DaveRandom\CallbackValidator\Type\BuiltInType;
10
11final class MatchTester{
15 private function __construct(){}
16
17 public static function isCovariant(?BaseType $acceptingType, ?BaseType $givenType) : bool{
18 // If the super type is unspecified, anything is a match
19 if($acceptingType === null || ($acceptingType instanceof NamedType && $acceptingType->type === BuiltInType::VOID)){
20 return true;
21 }
22
23 // If the sub type is unspecified, nothing is a match
24 if($givenType === null){
25 return false;
26 }
27
28 //composite type acceptance:
29 //super named type -> named, union (all parts must be accepted by super), intersection (at least 1 part must be accepted by super)
30 //super union -> named (must be accepted by at least 1 super), union (all parts must be accepted by at least 1 super), intersection (at least 1 part must be accepted by at least 1 super?)
31 //super intersection -> named (must be accepted by all supers), union (all parts must be accepted by all supers), intersection (all parts must be accepted by all supers)
32
33 //given union is handled the same no matter what the accepting type is
34 //ensure all parts are covariant with the accepting type
35 if($givenType instanceof UnionType){
36 foreach($givenType->types as $type){
37 if(!self::isCovariant($acceptingType, $type)){
38 return false;
39 }
40 }
41
42 return true;
43 }
44
45 if($acceptingType instanceof NamedType){
46 //at least 1 part of a given intersection must be covariant with the accepting type
47 //given intersection can only be compared with a named type to validate variance - the parts cannot
48 //be individually tested against a composite type
49 if($givenType instanceof IntersectionType){
50 foreach($givenType->types as $type){
51 if(self::isCovariant($acceptingType, $type)){
52 return true;
53 }
54 }
55
56 return false;
57 }
58
59 if($givenType instanceof NamedType){
60 $acceptingTypeName = $acceptingType->type;
61 $givenTypeName = $givenType->type;
62 if($acceptingTypeName === $givenTypeName){
63 // Exact match
64 return true;
65 }
66
67 if($acceptingTypeName === BuiltInType::MIXED && $givenTypeName !== BuiltInType::VOID){
68 //anything is covariant with mixed except void
69 return true;
70 }
71
72 if($acceptingTypeName === BuiltInType::FLOAT && $givenTypeName === BuiltInType::INT){
73 //int is covariant with float even in strict mode
74 return true;
75 }
76
77 // Check iterable
78 if($acceptingTypeName === BuiltInType::ITERABLE){
79 return $givenTypeName === BuiltInType::ARRAY
80 || $givenTypeName === \Traversable::class
81 || \is_subclass_of($givenTypeName, \Traversable::class);
82 }
83
84 // Check callable
85 if($acceptingTypeName === BuiltInType::CALLABLE){
86 return $givenTypeName === \Closure::class
87 || \method_exists($givenTypeName, '__invoke')
88 || \is_subclass_of($givenTypeName, \Closure::class);
89 }
90
91 if($acceptingTypeName === BuiltInType::OBJECT){
92 //a class type is covariant with object
93 return !$givenTypeName instanceof BuiltInType;
94 }
95
96 return is_string($givenTypeName) && is_string($acceptingTypeName) && \is_subclass_of($givenTypeName, $acceptingTypeName);
97 }
98
99 throw new \AssertionError("Unhandled reflection type " . get_class($givenType));
100 }
101
102 if($acceptingType instanceof UnionType){
103 //accepting union - the given type must be covariant with at least 1 part
104 foreach($acceptingType->types as $type){
105 if(self::isCovariant($type, $givenType)){
106 return true;
107 }
108 }
109
110 return false;
111 }
112
113 if($acceptingType instanceof IntersectionType){
114 //accepting intersection - the given type must be covariant with all parts
115 foreach($acceptingType->types as $type){
116 if(!self::isCovariant($type, $givenType)){
117 return false;
118 }
119 }
120
121 return true;
122 }
123
124 return false;
125 }
126}