PocketMine-MP 5.16.1 git-585aee9386a787c95e73dd0a05ffca8329606b68
CallbackType.php
1<?php declare(strict_types = 1);
2
3namespace DaveRandom\CallbackValidator;
4
5final class CallbackType
6{
10 private $returnType;
11
15 private $parameters;
16
27 private static function reflectCallable($target)
28 {
29 if ($target instanceof \Closure) {
30 return new \ReflectionFunction($target);
31 }
32
33 if (\is_array($target) && isset($target[0], $target[1])) {
34 return new \ReflectionMethod($target[0], $target[1]);
35 }
36
37 if (\is_object($target) && \method_exists($target, '__invoke')) {
38 return new \ReflectionMethod($target, '__invoke');
39 }
40
41 if (\is_string($target)) {
42 return \strpos($target, '::') !== false
43 ? new \ReflectionMethod($target)
44 : new \ReflectionFunction($target);
45 }
46
47 throw new \UnexpectedValueException("Unknown callable type");
48 }
49
56 public static function createFromCallable($callable, $flags = ParameterType::CONTRAVARIANT | ReturnType::COVARIANT)
57 {
58 try {
59 $reflection = self::reflectCallable($callable);
60 } catch (\ReflectionException $e) {
61 throw new InvalidCallbackException('Failed to reflect the supplied callable', 0, $e);
62 }
63
64 $returnType = ReturnType::createFromReflectionFunctionAbstract($reflection, $flags);
65
66 $parameters = [];
67
68 foreach ($reflection->getParameters() as $parameterReflection) {
69 $parameters[] = ParameterType::createFromReflectionParameter($parameterReflection, $flags);
70 }
71
72 return new CallbackType($returnType, ...$parameters);
73 }
74
75 public function __construct(ReturnType $returnType, ParameterType ...$parameters)
76 {
77 $this->returnType = $returnType;
78 $this->parameters = $parameters;
79 }
80
85 public function isSatisfiedBy($callable)
86 {
87 try {
88 $candidate = self::reflectCallable($callable);
89 } catch (\ReflectionException $e) {
90 throw new InvalidCallbackException('Failed to reflect the supplied callable', 0, $e);
91 }
92
93 $byRef = $candidate->returnsReference();
94 $returnType = $candidate->getReturnType();
95
96 if ($returnType instanceof \ReflectionNamedType) {
97 $typeName = $returnType->getName();
98 $nullable = $returnType->allowsNull();
99 } elseif ($returnType !== null) {
100 throw new \LogicException("Unsupported reflection type " . get_class($returnType));
101 } else {
102 $typeName = null;
103 $nullable = false;
104 }
105
106 if (!$this->returnType->isSatisfiedBy($typeName, $nullable, $byRef)) {
107 return false;
108 }
109
110 $last = null;
111
112 foreach ($candidate->getParameters() as $position => $parameter) {
113 $byRef = $parameter->isPassedByReference();
114
115 if (($type = $parameter->getType()) instanceof \ReflectionNamedType) {
116 $typeName = $type->getName();
117 $nullable = $type->allowsNull();
118 } elseif ($type !== null) {
119 throw new \LogicException("Unsupported reflection type " . get_class($type));
120 } else {
121 $typeName = null;
122 $nullable = false;
123 }
124
125 // Parameters that exist in the prototype must always be satisfied directly
126 if (isset($this->parameters[$position])) {
127 if (!$this->parameters[$position]->isSatisfiedBy($typeName, $nullable, $byRef)) {
128 return false;
129 }
130
131 $last = $this->parameters[$position];
132 continue;
133 }
134
135 // Candidates can accept additional args that are not in the prototype as long as they are not mandatory
136 if (!$parameter->isOptional() && !$parameter->isVariadic()) {
137 return false;
138 }
139
140 // If the last arg of the prototype is variadic, any additional args the candidate accepts must satisfy it
141 if ($last !== null && $last->isVariadic && !$last->isSatisfiedBy($typeName, $nullable, $byRef)) {
142 return false;
143 }
144 }
145
146 return true;
147 }
148
152 public function __toString()
153 {
154 $string = 'function ';
155
156 if ($this->returnType->isByReference) {
157 $string .= '& ';
158 }
159
160 $string .= '( ';
161
162 $i = $o = 0;
163 $l = count($this->parameters) - 1;
164 for (; $i < $l; $i++) {
165 $string .= $this->parameters[$i];
166
167 if ($o === 0 && !($this->parameters[$i + 1]->isOptional)) {
168 $string .= ', ';
169 continue;
170 }
171
172 $string .= ' [, ';
173 $o++;
174 }
175
176 if (isset($this->parameters[$l])) {
177 $string .= $this->parameters[$i] . ' ';
178 }
179
180 if ($o !== 0) {
181 $string .= str_repeat(']', $o) . ' ';
182 }
183
184 $string .= ')';
185
186 if ($this->returnType->typeName !== null) {
187 $string .= ' : ' . $this->returnType;
188 }
189
190 return $string;
191 }
192}
static createFromCallable($callable, $flags=ParameterType::CONTRAVARIANT|ReturnType::COVARIANT)
static createFromReflectionParameter($reflection, $flags=0)
static createFromReflectionFunctionAbstract($reflection, $flags=0)
Definition: ReturnType.php:23