|   1:  | <?php | 
|   2:  |  | 
|   3:  | declare(strict_types=1); | 
|   4:  |  | 
|   5:  | namespace PHPStan\BetterReflection\Reflection\Adapter; | 
|   6:  |  | 
|   7:  | use LogicException; | 
|   8:  | use OutOfBoundsException; | 
|   9:  | use PhpParser\Node\Expr; | 
|  10:  | use ReflectionClass as CoreReflectionClass; | 
|  11:  | use ReflectionFunctionAbstract as CoreReflectionFunctionAbstract; | 
|  12:  | use ReflectionParameter as CoreReflectionParameter; | 
|  13:  | use ReturnTypeWillChange; | 
|  14:  | use PHPStan\BetterReflection\Reflection\ReflectionAttribute as BetterReflectionAttribute; | 
|  15:  | use PHPStan\BetterReflection\Reflection\ReflectionIntersectionType as BetterReflectionIntersectionType; | 
|  16:  | use PHPStan\BetterReflection\Reflection\ReflectionMethod as BetterReflectionMethod; | 
|  17:  | use PHPStan\BetterReflection\Reflection\ReflectionNamedType as BetterReflectionNamedType; | 
|  18:  | use PHPStan\BetterReflection\Reflection\ReflectionParameter as BetterReflectionParameter; | 
|  19:  | use PHPStan\BetterReflection\Reflection\ReflectionType as BetterReflectionType; | 
|  20:  | use PHPStan\BetterReflection\Reflection\ReflectionUnionType as BetterReflectionUnionType; | 
|  21:  | use ValueError; | 
|  22:  |  | 
|  23:  | use function array_map; | 
|  24:  | use function count; | 
|  25:  | use function sprintf; | 
|  26:  | use function strtolower; | 
|  27:  |  | 
|  28:  |  | 
|  29:  | final class ReflectionParameter extends CoreReflectionParameter | 
|  30:  | { | 
|  31:  |      | 
|  32:  |  | 
|  33:  |  | 
|  34:  |     private $betterReflectionParameter; | 
|  35:  |     public function __construct(BetterReflectionParameter $betterReflectionParameter) | 
|  36:  |     { | 
|  37:  |         $this->betterReflectionParameter = $betterReflectionParameter; | 
|  38:  |         unset($this->name); | 
|  39:  |     } | 
|  40:  |  | 
|  41:  |     public function __toString(): string | 
|  42:  |     { | 
|  43:  |         return $this->betterReflectionParameter->__toString(); | 
|  44:  |     } | 
|  45:  |  | 
|  46:  |     public function getName(): string | 
|  47:  |     { | 
|  48:  |         return $this->betterReflectionParameter->getName(); | 
|  49:  |     } | 
|  50:  |  | 
|  51:  |     public function isPassedByReference(): bool | 
|  52:  |     { | 
|  53:  |         return $this->betterReflectionParameter->isPassedByReference(); | 
|  54:  |     } | 
|  55:  |  | 
|  56:  |     public function canBePassedByValue(): bool | 
|  57:  |     { | 
|  58:  |         return $this->betterReflectionParameter->canBePassedByValue(); | 
|  59:  |     } | 
|  60:  |  | 
|  61:  |      | 
|  62:  |  | 
|  63:  |  | 
|  64:  |     public function getDeclaringFunction(): CoreReflectionFunctionAbstract | 
|  65:  |     { | 
|  66:  |         $function = $this->betterReflectionParameter->getDeclaringFunction(); | 
|  67:  |  | 
|  68:  |         if ($function instanceof BetterReflectionMethod) { | 
|  69:  |             return new ReflectionMethod($function); | 
|  70:  |         } | 
|  71:  |  | 
|  72:  |         return new ReflectionFunction($function); | 
|  73:  |     } | 
|  74:  |  | 
|  75:  |      | 
|  76:  |     public function getDeclaringClass(): ?CoreReflectionClass | 
|  77:  |     { | 
|  78:  |         $declaringClass = $this->betterReflectionParameter->getDeclaringClass(); | 
|  79:  |  | 
|  80:  |         if ($declaringClass === null) { | 
|  81:  |             return null; | 
|  82:  |         } | 
|  83:  |  | 
|  84:  |         return new ReflectionClass($declaringClass); | 
|  85:  |     } | 
|  86:  |  | 
|  87:  |      | 
|  88:  |     public function getClass(): ?CoreReflectionClass | 
|  89:  |     { | 
|  90:  |         $type = $this->betterReflectionParameter->getType(); | 
|  91:  |  | 
|  92:  |         if ($type === null) { | 
|  93:  |             return null; | 
|  94:  |         } | 
|  95:  |  | 
|  96:  |         if ($type instanceof BetterReflectionIntersectionType) { | 
|  97:  |             return null; | 
|  98:  |         } | 
|  99:  |  | 
| 100:  |         if ($type instanceof BetterReflectionNamedType) { | 
| 101:  |             $classType = $type; | 
| 102:  |         } else { | 
| 103:  |             $unionTypes = $type->getTypes(); | 
| 104:  |  | 
| 105:  |             if (count($unionTypes) !== 2) { | 
| 106:  |                 return null; | 
| 107:  |             } | 
| 108:  |  | 
| 109:  |             if (! $type->allowsNull()) { | 
| 110:  |                 return null; | 
| 111:  |             } | 
| 112:  |  | 
| 113:  |             foreach ($unionTypes as $unionInnerType) { | 
| 114:  |                 if (! $unionInnerType instanceof BetterReflectionNamedType) { | 
| 115:  |                     return null; | 
| 116:  |                 } | 
| 117:  |  | 
| 118:  |                 if ($unionInnerType->allowsNull()) { | 
| 119:  |                     continue; | 
| 120:  |                 } | 
| 121:  |  | 
| 122:  |                 $classType = $unionInnerType; | 
| 123:  |                 break; | 
| 124:  |             } | 
| 125:  |         } | 
| 126:  |  | 
| 127:  |         try { | 
| 128:  |              | 
| 129:  |             return new ReflectionClass($classType->getClass()); | 
| 130:  |         } catch (LogicException $exception) { | 
| 131:  |             return null; | 
| 132:  |         } | 
| 133:  |     } | 
| 134:  |  | 
| 135:  |     public function isArray(): bool | 
| 136:  |     { | 
| 137:  |         return $this->isType($this->betterReflectionParameter->getType(), 'array'); | 
| 138:  |     } | 
| 139:  |  | 
| 140:  |     public function isCallable(): bool | 
| 141:  |     { | 
| 142:  |         return $this->isType($this->betterReflectionParameter->getType(), 'callable'); | 
| 143:  |     } | 
| 144:  |  | 
| 145:  |      | 
| 146:  |  | 
| 147:  |  | 
| 148:  |  | 
| 149:  |     private function isType($typeReflection, string $type): bool | 
| 150:  |     { | 
| 151:  |         if ($typeReflection === null) { | 
| 152:  |             return false; | 
| 153:  |         } | 
| 154:  |  | 
| 155:  |         if ($typeReflection instanceof BetterReflectionIntersectionType) { | 
| 156:  |             return false; | 
| 157:  |         } | 
| 158:  |  | 
| 159:  |         $isOneOfAllowedTypes = static function (BetterReflectionType $namedType, string ...$types): bool { | 
| 160:  |             foreach ($types as $type) { | 
| 161:  |                 if ($namedType instanceof BetterReflectionNamedType && strtolower($namedType->getName()) === $type) { | 
| 162:  |                     return true; | 
| 163:  |                 } | 
| 164:  |             } | 
| 165:  |  | 
| 166:  |             return false; | 
| 167:  |         }; | 
| 168:  |  | 
| 169:  |         if ($typeReflection instanceof BetterReflectionUnionType) { | 
| 170:  |             $unionTypes = $typeReflection->getTypes(); | 
| 171:  |  | 
| 172:  |             foreach ($unionTypes as $unionType) { | 
| 173:  |                 if (! $isOneOfAllowedTypes($unionType, $type, 'null')) { | 
| 174:  |                     return false; | 
| 175:  |                 } | 
| 176:  |             } | 
| 177:  |  | 
| 178:  |             return true; | 
| 179:  |         } | 
| 180:  |  | 
| 181:  |         return $isOneOfAllowedTypes($typeReflection, $type); | 
| 182:  |     } | 
| 183:  |  | 
| 184:  |     public function allowsNull(): bool | 
| 185:  |     { | 
| 186:  |         return $this->betterReflectionParameter->allowsNull(); | 
| 187:  |     } | 
| 188:  |  | 
| 189:  |     public function getPosition(): int | 
| 190:  |     { | 
| 191:  |         return $this->betterReflectionParameter->getPosition(); | 
| 192:  |     } | 
| 193:  |  | 
| 194:  |     public function isOptional(): bool | 
| 195:  |     { | 
| 196:  |         return $this->betterReflectionParameter->isOptional(); | 
| 197:  |     } | 
| 198:  |  | 
| 199:  |     public function isVariadic(): bool | 
| 200:  |     { | 
| 201:  |         return $this->betterReflectionParameter->isVariadic(); | 
| 202:  |     } | 
| 203:  |  | 
| 204:  |     public function isDefaultValueAvailable(): bool | 
| 205:  |     { | 
| 206:  |         return $this->betterReflectionParameter->isDefaultValueAvailable(); | 
| 207:  |     } | 
| 208:  |  | 
| 209:  |      | 
| 210:  |  | 
| 211:  |  | 
| 212:  |     #[ReturnTypeWillChange] | 
| 213:  |     public function getDefaultValue() | 
| 214:  |     { | 
| 215:  |         return $this->betterReflectionParameter->getDefaultValue(); | 
| 216:  |     } | 
| 217:  |  | 
| 218:  |      | 
| 219:  |  | 
| 220:  |  | 
| 221:  |     public function getDefaultValueExpr(): Expr | 
| 222:  |     { | 
| 223:  |         return $this->betterReflectionParameter->getDefaultValueExpression(); | 
| 224:  |     } | 
| 225:  |  | 
| 226:  |     public function getDefaultValueExpression(): Expr | 
| 227:  |     { | 
| 228:  |         return $this->betterReflectionParameter->getDefaultValueExpression(); | 
| 229:  |     } | 
| 230:  |  | 
| 231:  |     public function isDefaultValueConstant(): bool | 
| 232:  |     { | 
| 233:  |         return $this->betterReflectionParameter->isDefaultValueConstant(); | 
| 234:  |     } | 
| 235:  |  | 
| 236:  |     public function getDefaultValueConstantName(): string | 
| 237:  |     { | 
| 238:  |         return $this->betterReflectionParameter->getDefaultValueConstantName(); | 
| 239:  |     } | 
| 240:  |  | 
| 241:  |     public function hasType(): bool | 
| 242:  |     { | 
| 243:  |         return $this->betterReflectionParameter->hasType(); | 
| 244:  |     } | 
| 245:  |  | 
| 246:  |      | 
| 247:  |  | 
| 248:  |  | 
| 249:  |     public function getType(): ?\ReflectionType | 
| 250:  |     { | 
| 251:  |         return ReflectionType::fromTypeOrNull($this->betterReflectionParameter->getType()); | 
| 252:  |     } | 
| 253:  |  | 
| 254:  |     public function isPromoted(): bool | 
| 255:  |     { | 
| 256:  |         return $this->betterReflectionParameter->isPromoted(); | 
| 257:  |     } | 
| 258:  |  | 
| 259:  |      | 
| 260:  |  | 
| 261:  |  | 
| 262:  |  | 
| 263:  |  | 
| 264:  |     public function getAttributes(?string $name = null, int $flags = 0): array | 
| 265:  |     { | 
| 266:  |         if ($flags !== 0 && $flags !== ReflectionAttribute::IS_INSTANCEOF) { | 
| 267:  |             throw new ValueError('Argument #2 ($flags) must be a valid attribute filter flag'); | 
| 268:  |         } | 
| 269:  |  | 
| 270:  |         if ($name !== null && $flags !== 0) { | 
| 271:  |             $attributes = $this->betterReflectionParameter->getAttributesByInstance($name); | 
| 272:  |         } elseif ($name !== null) { | 
| 273:  |             $attributes = $this->betterReflectionParameter->getAttributesByName($name); | 
| 274:  |         } else { | 
| 275:  |             $attributes = $this->betterReflectionParameter->getAttributes(); | 
| 276:  |         } | 
| 277:  |  | 
| 278:  |         return array_map(static function (BetterReflectionAttribute $betterReflectionAttribute) { | 
| 279:  |             return ReflectionAttributeFactory::create($betterReflectionAttribute); | 
| 280:  |         }, $attributes); | 
| 281:  |     } | 
| 282:  |  | 
| 283:  |      | 
| 284:  |  | 
| 285:  |  | 
| 286:  |     public function __get(string $name) | 
| 287:  |     { | 
| 288:  |         if ($name === 'name') { | 
| 289:  |             return $this->betterReflectionParameter->getName(); | 
| 290:  |         } | 
| 291:  |  | 
| 292:  |         throw new OutOfBoundsException(sprintf('Property %s::$%s does not exist.', self::class, $name)); | 
| 293:  |     } | 
| 294:  | } | 
| 295:  |  |