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