| Conditions | 139 |
| Paths | 1510 |
| Total Lines | 775 |
| Lines | 78 |
| Ratio | 10.06 % |
| Changes | 0 | ||
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
| 1 | <?php |
||
| 114 | public static function getTypeFromTree( |
||
| 115 | ParseTree $parse_tree, |
||
| 116 | Codebase $codebase, |
||
| 117 | array $php_version = null, |
||
| 118 | array $template_type_map = [], |
||
| 119 | array $type_aliases = [] |
||
| 120 | ) { |
||
| 121 | if ($parse_tree instanceof ParseTree\GenericTree) { |
||
| 122 | $generic_type = $parse_tree->value; |
||
| 123 | |||
| 124 | $generic_params = []; |
||
| 125 | |||
| 126 | foreach ($parse_tree->children as $i => $child_tree) { |
||
| 127 | $tree_type = self::getTypeFromTree( |
||
| 128 | $child_tree, |
||
| 129 | $codebase, |
||
| 130 | null, |
||
| 131 | $template_type_map, |
||
| 132 | $type_aliases |
||
| 133 | ); |
||
| 134 | |||
| 135 | if ($generic_type === 'class-string-map' |
||
| 136 | && $i === 0 |
||
| 137 | ) { |
||
| 138 | if ($tree_type instanceof TTemplateParam) { |
||
| 139 | $template_type_map[$tree_type->param_name] = ['class-string-map' => [$tree_type->as]]; |
||
| 140 | } elseif ($tree_type instanceof TNamedObject) { |
||
| 141 | $template_type_map[$tree_type->value] = ['class-string-map' => [\Psalm\Type::getObject()]]; |
||
| 142 | } |
||
| 143 | } |
||
| 144 | |||
| 145 | $generic_params[] = $tree_type instanceof Union ? $tree_type : new Union([$tree_type]); |
||
| 146 | } |
||
| 147 | |||
| 148 | $generic_type_value = TypeTokenizer::fixScalarTerms($generic_type); |
||
| 149 | |||
| 150 | if (($generic_type_value === 'array' |
||
| 151 | || $generic_type_value === 'non-empty-array' |
||
| 152 | || $generic_type_value === 'associative-array') |
||
| 153 | && count($generic_params) === 1 |
||
| 154 | ) { |
||
| 155 | array_unshift($generic_params, new Union([new TArrayKey])); |
||
| 156 | } elseif (in_array($generic_type_value, ['iterable', 'Traversable', 'Iterator', 'IteratorAggregate'], true) |
||
| 157 | && count($generic_params) === 1 |
||
| 158 | ) { |
||
| 159 | array_unshift($generic_params, new Union([new TMixed])); |
||
| 160 | } elseif ($generic_type_value === 'Generator') { |
||
| 161 | if (count($generic_params) === 1) { |
||
| 162 | array_unshift($generic_params, new Union([new TMixed])); |
||
| 163 | } |
||
| 164 | |||
| 165 | for ($i = 0, $l = 4 - count($generic_params); $i < $l; ++$i) { |
||
| 166 | $generic_params[] = new Union([new TMixed]); |
||
| 167 | } |
||
| 168 | } |
||
| 169 | |||
| 170 | if (!$generic_params) { |
||
| 171 | throw new TypeParseTreeException('No generic params provided for type'); |
||
| 172 | } |
||
| 173 | |||
| 174 | if ($generic_type_value === 'array' || $generic_type_value === 'associative-array') { |
||
| 175 | if ($generic_params[0]->isMixed()) { |
||
| 176 | $generic_params[0] = \Psalm\Type::getArrayKey(); |
||
| 177 | } |
||
| 178 | |||
| 179 | return new TArray($generic_params); |
||
| 180 | } |
||
| 181 | |||
| 182 | if ($generic_type_value === 'non-empty-array') { |
||
| 183 | if ($generic_params[0]->isMixed()) { |
||
| 184 | $generic_params[0] = \Psalm\Type::getArrayKey(); |
||
| 185 | } |
||
| 186 | |||
| 187 | return new TNonEmptyArray($generic_params); |
||
| 188 | } |
||
| 189 | |||
| 190 | if ($generic_type_value === 'iterable') { |
||
| 191 | return new TIterable($generic_params); |
||
| 192 | } |
||
| 193 | |||
| 194 | if ($generic_type_value === 'list') { |
||
| 195 | return new TList($generic_params[0]); |
||
| 196 | } |
||
| 197 | |||
| 198 | if ($generic_type_value === 'non-empty-list') { |
||
| 199 | return new TNonEmptyList($generic_params[0]); |
||
| 200 | } |
||
| 201 | |||
| 202 | if ($generic_type_value === 'class-string') { |
||
| 203 | $class_name = (string) $generic_params[0]; |
||
| 204 | |||
| 205 | View Code Duplication | if (isset($template_type_map[$class_name])) { |
|
| 206 | $first_class = array_keys($template_type_map[$class_name])[0]; |
||
| 207 | |||
| 208 | return self::getGenericParamClass( |
||
| 209 | $class_name, |
||
| 210 | $template_type_map[$class_name][$first_class][0], |
||
| 211 | $first_class |
||
| 212 | ); |
||
| 213 | } |
||
| 214 | |||
| 215 | $param_union_types = array_values($generic_params[0]->getAtomicTypes()); |
||
| 216 | |||
| 217 | if (count($param_union_types) > 1) { |
||
| 218 | throw new TypeParseTreeException('Union types are not allowed in class string param'); |
||
| 219 | } |
||
| 220 | |||
| 221 | if (!$param_union_types[0] instanceof TNamedObject) { |
||
| 222 | throw new TypeParseTreeException('Class string param should be a named object'); |
||
| 223 | } |
||
| 224 | |||
| 225 | return new TClassString($class_name, $param_union_types[0]); |
||
| 226 | } |
||
| 227 | |||
| 228 | if ($generic_type_value === 'class-string-map') { |
||
| 229 | if (count($generic_params) !== 2) { |
||
| 230 | throw new TypeParseTreeException( |
||
| 231 | 'There should only be two params for class-string-map, ' |
||
| 232 | . count($generic_params) . ' provided' |
||
| 233 | ); |
||
| 234 | } |
||
| 235 | |||
| 236 | $template_marker_parts = array_values($generic_params[0]->getAtomicTypes()); |
||
| 237 | |||
| 238 | $template_marker = $template_marker_parts[0]; |
||
| 239 | |||
| 240 | $template_as_type = null; |
||
| 241 | |||
| 242 | if ($template_marker instanceof TNamedObject) { |
||
| 243 | $template_param_name = $template_marker->value; |
||
| 244 | } elseif ($template_marker instanceof Atomic\TTemplateParam) { |
||
| 245 | $template_param_name = $template_marker->param_name; |
||
| 246 | $template_as_type = array_values($template_marker->as->getAtomicTypes())[0]; |
||
| 247 | |||
| 248 | if (!$template_as_type instanceof TNamedObject) { |
||
| 249 | throw new TypeParseTreeException( |
||
| 250 | 'Unrecognised as type' |
||
| 251 | ); |
||
| 252 | } |
||
| 253 | } else { |
||
| 254 | throw new TypeParseTreeException( |
||
| 255 | 'Unrecognised class-string-map templated param' |
||
| 256 | ); |
||
| 257 | } |
||
| 258 | |||
| 259 | return new TClassStringMap( |
||
| 260 | $template_param_name, |
||
| 261 | $template_as_type, |
||
| 262 | $generic_params[1] |
||
| 263 | ); |
||
| 264 | } |
||
| 265 | |||
| 266 | View Code Duplication | if ($generic_type_value === 'key-of') { |
|
| 267 | $param_name = (string) $generic_params[0]; |
||
| 268 | |||
| 269 | if (isset($template_type_map[$param_name])) { |
||
| 270 | $defining_class = array_keys($template_type_map[$param_name])[0]; |
||
| 271 | |||
| 272 | return new Atomic\TTemplateKeyOf( |
||
| 273 | $param_name, |
||
| 274 | $defining_class, |
||
| 275 | $template_type_map[$param_name][$defining_class][0] |
||
| 276 | ); |
||
| 277 | } |
||
| 278 | |||
| 279 | $param_union_types = array_values($generic_params[0]->getAtomicTypes()); |
||
| 280 | |||
| 281 | if (count($param_union_types) > 1) { |
||
| 282 | throw new TypeParseTreeException('Union types are not allowed in key-of type'); |
||
| 283 | } |
||
| 284 | |||
| 285 | if (!$param_union_types[0] instanceof Atomic\TScalarClassConstant) { |
||
| 286 | throw new TypeParseTreeException( |
||
| 287 | 'Untemplated key-of param ' . $param_name . ' should be a class constant' |
||
| 288 | ); |
||
| 289 | } |
||
| 290 | |||
| 291 | return new Atomic\TKeyOfClassConstant( |
||
| 292 | $param_union_types[0]->fq_classlike_name, |
||
| 293 | $param_union_types[0]->const_name |
||
| 294 | ); |
||
| 295 | } |
||
| 296 | |||
| 297 | View Code Duplication | if ($generic_type_value === 'value-of') { |
|
| 298 | $param_name = (string) $generic_params[0]; |
||
| 299 | |||
| 300 | if (isset($template_type_map[$param_name])) { |
||
| 301 | $defining_class = array_keys($template_type_map[$param_name])[0]; |
||
| 302 | |||
| 303 | return new Atomic\TTemplateKeyOf( |
||
| 304 | $param_name, |
||
| 305 | $defining_class, |
||
| 306 | $template_type_map[$param_name][$defining_class][0] |
||
| 307 | ); |
||
| 308 | } |
||
| 309 | |||
| 310 | $param_union_types = array_values($generic_params[0]->getAtomicTypes()); |
||
| 311 | |||
| 312 | if (count($param_union_types) > 1) { |
||
| 313 | throw new TypeParseTreeException('Union types are not allowed in value-of type'); |
||
| 314 | } |
||
| 315 | |||
| 316 | if (!$param_union_types[0] instanceof Atomic\TScalarClassConstant) { |
||
| 317 | throw new TypeParseTreeException( |
||
| 318 | 'Untemplated value-of param ' . $param_name . ' should be a class constant' |
||
| 319 | ); |
||
| 320 | } |
||
| 321 | |||
| 322 | return new Atomic\TValueOfClassConstant( |
||
| 323 | $param_union_types[0]->fq_classlike_name, |
||
| 324 | $param_union_types[0]->const_name |
||
| 325 | ); |
||
| 326 | } |
||
| 327 | |||
| 328 | if (isset(TypeTokenizer::PSALM_RESERVED_WORDS[$generic_type_value]) |
||
| 329 | && $generic_type_value !== 'self' |
||
| 330 | && $generic_type_value !== 'static' |
||
| 331 | ) { |
||
| 332 | throw new TypeParseTreeException('Cannot create generic object with reserved word'); |
||
| 333 | } |
||
| 334 | |||
| 335 | return new TGenericObject($generic_type_value, $generic_params); |
||
| 336 | } |
||
| 337 | |||
| 338 | if ($parse_tree instanceof ParseTree\UnionTree) { |
||
| 339 | $has_null = false; |
||
| 340 | |||
| 341 | $atomic_types = []; |
||
| 342 | |||
| 343 | foreach ($parse_tree->children as $child_tree) { |
||
| 344 | if ($child_tree instanceof ParseTree\NullableTree) { |
||
| 345 | if (!isset($child_tree->children[0])) { |
||
| 346 | throw new TypeParseTreeException('Invalid ? character'); |
||
| 347 | } |
||
| 348 | |||
| 349 | $atomic_type = self::getTypeFromTree( |
||
| 350 | $child_tree->children[0], |
||
| 351 | $codebase, |
||
| 352 | null, |
||
| 353 | $template_type_map, |
||
| 354 | $type_aliases |
||
| 355 | ); |
||
| 356 | $has_null = true; |
||
| 357 | } else { |
||
| 358 | $atomic_type = self::getTypeFromTree( |
||
| 359 | $child_tree, |
||
| 360 | $codebase, |
||
| 361 | null, |
||
| 362 | $template_type_map, |
||
| 363 | $type_aliases |
||
| 364 | ); |
||
| 365 | } |
||
| 366 | |||
| 367 | if ($atomic_type instanceof Union) { |
||
| 368 | foreach ($atomic_type->getAtomicTypes() as $type) { |
||
| 369 | $atomic_types[] = $type; |
||
| 370 | } |
||
| 371 | |||
| 372 | continue; |
||
| 373 | } |
||
| 374 | |||
| 375 | $atomic_types[] = $atomic_type; |
||
| 376 | } |
||
| 377 | |||
| 378 | if ($has_null) { |
||
| 379 | $atomic_types[] = new TNull; |
||
| 380 | } |
||
| 381 | |||
| 382 | if (!$atomic_types) { |
||
| 383 | throw new TypeParseTreeException( |
||
| 384 | 'No atomic types found' |
||
| 385 | ); |
||
| 386 | } |
||
| 387 | |||
| 388 | return TypeCombination::combineTypes($atomic_types); |
||
| 389 | } |
||
| 390 | |||
| 391 | if ($parse_tree instanceof ParseTree\IntersectionTree) { |
||
| 392 | $intersection_types = array_map( |
||
| 393 | /** |
||
| 394 | * @return Atomic |
||
| 395 | */ |
||
| 396 | function (ParseTree $child_tree) use ($codebase, $template_type_map, $type_aliases) { |
||
| 397 | $atomic_type = self::getTypeFromTree( |
||
| 398 | $child_tree, |
||
| 399 | $codebase, |
||
| 400 | null, |
||
| 401 | $template_type_map, |
||
| 402 | $type_aliases |
||
| 403 | ); |
||
| 404 | |||
| 405 | if (!$atomic_type instanceof Atomic) { |
||
| 406 | throw new TypeParseTreeException( |
||
| 407 | 'Intersection types cannot contain unions' |
||
| 408 | ); |
||
| 409 | } |
||
| 410 | |||
| 411 | return $atomic_type; |
||
| 412 | }, |
||
| 413 | $parse_tree->children |
||
| 414 | ); |
||
| 415 | |||
| 416 | $onlyObjectLike = true; |
||
| 417 | foreach ($intersection_types as $intersection_type) { |
||
| 418 | if (!$intersection_type instanceof ObjectLike) { |
||
| 419 | $onlyObjectLike = false; |
||
| 420 | break; |
||
| 421 | } |
||
| 422 | } |
||
| 423 | |||
| 424 | if ($onlyObjectLike) { |
||
| 425 | /** @var non-empty-array<string|int, Union> */ |
||
| 426 | $properties = []; |
||
| 427 | /** @var ObjectLike $intersection_type */ |
||
| 428 | foreach ($intersection_types as $intersection_type) { |
||
| 429 | foreach ($intersection_type->properties as $property => $property_type) { |
||
| 430 | if (!array_key_exists($property, $properties)) { |
||
| 431 | $properties[$property] = clone $property_type; |
||
| 432 | continue; |
||
| 433 | } |
||
| 434 | |||
| 435 | $intersection_type = \Psalm\Type::intersectUnionTypes( |
||
| 436 | $properties[$property], |
||
| 437 | $property_type, |
||
| 438 | $codebase |
||
| 439 | ); |
||
| 440 | if ($intersection_type === null) { |
||
| 441 | throw new TypeParseTreeException( |
||
| 442 | 'Incompatible intersection types for "' . $property . '", ' |
||
| 443 | . $properties[$property] . ' and ' . $property_type |
||
| 444 | . ' provided' |
||
| 445 | ); |
||
| 446 | } |
||
| 447 | $properties[$property] = $intersection_type; |
||
| 448 | } |
||
| 449 | } |
||
| 450 | return new ObjectLike($properties); |
||
| 451 | } |
||
| 452 | |||
| 453 | $keyed_intersection_types = []; |
||
| 454 | |||
| 455 | if ($intersection_types[0] instanceof TTypeAlias) { |
||
| 456 | foreach ($intersection_types as $intersection_type) { |
||
| 457 | if (!$intersection_type instanceof TTypeAlias) { |
||
| 458 | throw new TypeParseTreeException( |
||
| 459 | 'Intersection types with a type alias can only be comprised of other type aliases, ' |
||
| 460 | . get_class($intersection_type) . ' provided' |
||
| 461 | ); |
||
| 462 | } |
||
| 463 | |||
| 464 | $keyed_intersection_types[$intersection_type->getKey()] = $intersection_type; |
||
| 465 | } |
||
| 466 | |||
| 467 | $first_type = array_shift($keyed_intersection_types); |
||
| 468 | |||
| 469 | if ($keyed_intersection_types) { |
||
| 470 | $first_type->extra_types = $keyed_intersection_types; |
||
| 471 | } |
||
| 472 | } else { |
||
| 473 | foreach ($intersection_types as $intersection_type) { |
||
| 474 | if (!$intersection_type instanceof TIterable |
||
| 475 | && !$intersection_type instanceof TNamedObject |
||
| 476 | && !$intersection_type instanceof TTemplateParam |
||
| 477 | && !$intersection_type instanceof TObjectWithProperties |
||
| 478 | ) { |
||
| 479 | throw new TypeParseTreeException( |
||
| 480 | 'Intersection types must be all objects or all object-like arrays, ' |
||
| 481 | . get_class($intersection_type) . ' provided' |
||
| 482 | ); |
||
| 483 | } |
||
| 484 | |||
| 485 | $keyed_intersection_types[ |
||
| 486 | $intersection_type instanceof TIterable |
||
| 487 | ? $intersection_type->getId() |
||
| 488 | : $intersection_type->getKey() |
||
| 489 | ] = $intersection_type; |
||
| 490 | } |
||
| 491 | |||
| 492 | $intersect_static = false; |
||
| 493 | |||
| 494 | if (isset($keyed_intersection_types['static'])) { |
||
| 495 | unset($keyed_intersection_types['static']); |
||
| 496 | $intersect_static = true; |
||
| 497 | } |
||
| 498 | |||
| 499 | if (!$keyed_intersection_types && $intersect_static) { |
||
| 500 | return new TNamedObject('static'); |
||
| 501 | } |
||
| 502 | |||
| 503 | $first_type = array_shift($keyed_intersection_types); |
||
| 504 | |||
| 505 | if ($intersect_static |
||
| 506 | && $first_type instanceof TNamedObject |
||
| 507 | ) { |
||
| 508 | $first_type->was_static = true; |
||
| 509 | } |
||
| 510 | |||
| 511 | if ($keyed_intersection_types) { |
||
| 512 | $first_type->extra_types = $keyed_intersection_types; |
||
| 513 | } |
||
| 514 | } |
||
| 515 | |||
| 516 | return $first_type; |
||
| 517 | } |
||
| 518 | |||
| 519 | if ($parse_tree instanceof ParseTree\ObjectLikeTree) { |
||
| 520 | $properties = []; |
||
| 521 | |||
| 522 | $type = $parse_tree->value; |
||
| 523 | |||
| 524 | $is_tuple = true; |
||
| 525 | |||
| 526 | foreach ($parse_tree->children as $i => $property_branch) { |
||
| 527 | if (!$property_branch instanceof ParseTree\ObjectLikePropertyTree) { |
||
| 528 | $property_type = self::getTypeFromTree( |
||
| 529 | $property_branch, |
||
| 530 | $codebase, |
||
| 531 | null, |
||
| 532 | $template_type_map, |
||
| 533 | $type_aliases |
||
| 534 | ); |
||
| 535 | $property_maybe_undefined = false; |
||
| 536 | $property_key = (string)$i; |
||
| 537 | } elseif (count($property_branch->children) === 1) { |
||
| 538 | $property_type = self::getTypeFromTree( |
||
| 539 | $property_branch->children[0], |
||
| 540 | $codebase, |
||
| 541 | null, |
||
| 542 | $template_type_map, |
||
| 543 | $type_aliases |
||
| 544 | ); |
||
| 545 | $property_maybe_undefined = $property_branch->possibly_undefined; |
||
| 546 | $property_key = $property_branch->value; |
||
| 547 | $is_tuple = false; |
||
| 548 | } else { |
||
| 549 | throw new TypeParseTreeException( |
||
| 550 | 'Missing property type' |
||
| 551 | ); |
||
| 552 | } |
||
| 553 | |||
| 554 | if ($property_key[0] === '\'' || $property_key[0] === '"') { |
||
| 555 | $property_key = \stripslashes(substr($property_key, 1, -1)); |
||
| 556 | } |
||
| 557 | |||
| 558 | if (!$property_type instanceof Union) { |
||
| 559 | $property_type = new Union([$property_type]); |
||
| 560 | } |
||
| 561 | |||
| 562 | if ($property_maybe_undefined) { |
||
| 563 | $property_type->possibly_undefined = true; |
||
| 564 | } |
||
| 565 | |||
| 566 | $properties[$property_key] = $property_type; |
||
| 567 | } |
||
| 568 | |||
| 569 | if ($type !== 'array' && $type !== 'object' && $type !== 'callable-array') { |
||
| 570 | throw new TypeParseTreeException('Unexpected brace character'); |
||
| 571 | } |
||
| 572 | |||
| 573 | if (!$properties) { |
||
| 574 | throw new TypeParseTreeException('No properties supplied for ObjectLike'); |
||
| 575 | } |
||
| 576 | |||
| 577 | if ($type === 'object') { |
||
| 578 | return new TObjectWithProperties($properties); |
||
| 579 | } |
||
| 580 | |||
| 581 | if ($type === 'callable-array') { |
||
| 582 | return new Atomic\TCallableObjectLikeArray($properties); |
||
| 583 | } |
||
| 584 | |||
| 585 | $object_like = new ObjectLike($properties); |
||
| 586 | |||
| 587 | if ($is_tuple) { |
||
| 588 | $object_like->sealed = true; |
||
| 589 | $object_like->is_list = true; |
||
| 590 | } |
||
| 591 | |||
| 592 | return $object_like; |
||
| 593 | } |
||
| 594 | |||
| 595 | if ($parse_tree instanceof ParseTree\CallableWithReturnTypeTree) { |
||
| 596 | $callable_type = self::getTypeFromTree( |
||
| 597 | $parse_tree->children[0], |
||
| 598 | $codebase, |
||
| 599 | null, |
||
| 600 | $template_type_map, |
||
| 601 | $type_aliases |
||
| 602 | ); |
||
| 603 | |||
| 604 | if (!$callable_type instanceof TCallable && !$callable_type instanceof TFn) { |
||
| 605 | throw new \InvalidArgumentException('Parsing callable tree node should return TCallable'); |
||
| 606 | } |
||
| 607 | |||
| 608 | if (!isset($parse_tree->children[1])) { |
||
| 609 | throw new TypeParseTreeException('Invalid return type'); |
||
| 610 | } |
||
| 611 | |||
| 612 | $return_type = self::getTypeFromTree( |
||
| 613 | $parse_tree->children[1], |
||
| 614 | $codebase, |
||
| 615 | null, |
||
| 616 | $template_type_map, |
||
| 617 | $type_aliases |
||
| 618 | ); |
||
| 619 | |||
| 620 | $callable_type->return_type = $return_type instanceof Union ? $return_type : new Union([$return_type]); |
||
| 621 | |||
| 622 | return $callable_type; |
||
| 623 | } |
||
| 624 | |||
| 625 | if ($parse_tree instanceof ParseTree\CallableTree) { |
||
| 626 | $params = array_map( |
||
| 627 | /** |
||
| 628 | * @return FunctionLikeParameter |
||
| 629 | */ |
||
| 630 | function (ParseTree $child_tree) use ($codebase, $template_type_map, $type_aliases) { |
||
| 631 | $is_variadic = false; |
||
| 632 | $is_optional = false; |
||
| 633 | |||
| 634 | if ($child_tree instanceof ParseTree\CallableParamTree) { |
||
| 635 | $tree_type = self::getTypeFromTree( |
||
| 636 | $child_tree->children[0], |
||
| 637 | $codebase, |
||
| 638 | null, |
||
| 639 | $template_type_map, |
||
| 640 | $type_aliases |
||
| 641 | ); |
||
| 642 | $is_variadic = $child_tree->variadic; |
||
| 643 | $is_optional = $child_tree->has_default; |
||
| 644 | } else { |
||
| 645 | if ($child_tree instanceof ParseTree\Value && strpos($child_tree->value, '$') > 0) { |
||
| 646 | $child_tree->value = preg_replace('/(.+)\$.*/', '$1', $child_tree->value); |
||
| 647 | } |
||
| 648 | |||
| 649 | $tree_type = self::getTypeFromTree( |
||
| 650 | $child_tree, |
||
| 651 | $codebase, |
||
| 652 | null, |
||
| 653 | $template_type_map, |
||
| 654 | $type_aliases |
||
| 655 | ); |
||
| 656 | } |
||
| 657 | |||
| 658 | $tree_type = $tree_type instanceof Union ? $tree_type : new Union([$tree_type]); |
||
| 659 | |||
| 660 | $param = new FunctionLikeParameter( |
||
| 661 | '', |
||
| 662 | false, |
||
| 663 | $tree_type, |
||
| 664 | null, |
||
| 665 | null, |
||
| 666 | $is_optional, |
||
| 667 | false, |
||
| 668 | $is_variadic |
||
| 669 | ); |
||
| 670 | |||
| 671 | // type is not authoratative |
||
| 672 | $param->signature_type = null; |
||
| 673 | |||
| 674 | return $param; |
||
| 675 | }, |
||
| 676 | $parse_tree->children |
||
| 677 | ); |
||
| 678 | |||
| 679 | if (in_array(strtolower($parse_tree->value), ['closure', '\closure'], true)) { |
||
| 680 | return new TFn('Closure', $params); |
||
| 681 | } |
||
| 682 | |||
| 683 | return new TCallable($parse_tree->value, $params); |
||
| 684 | } |
||
| 685 | |||
| 686 | if ($parse_tree instanceof ParseTree\EncapsulationTree) { |
||
| 687 | return self::getTypeFromTree( |
||
| 688 | $parse_tree->children[0], |
||
| 689 | $codebase, |
||
| 690 | null, |
||
| 691 | $template_type_map, |
||
| 692 | $type_aliases |
||
| 693 | ); |
||
| 694 | } |
||
| 695 | |||
| 696 | if ($parse_tree instanceof ParseTree\NullableTree) { |
||
| 697 | if (!isset($parse_tree->children[0])) { |
||
| 698 | throw new TypeParseTreeException('Misplaced question mark'); |
||
| 699 | } |
||
| 700 | |||
| 701 | $non_nullable_type = self::getTypeFromTree( |
||
| 702 | $parse_tree->children[0], |
||
| 703 | $codebase, |
||
| 704 | null, |
||
| 705 | $template_type_map, |
||
| 706 | $type_aliases |
||
| 707 | ); |
||
| 708 | |||
| 709 | if ($non_nullable_type instanceof Union) { |
||
| 710 | $non_nullable_type->addType(new TNull); |
||
| 711 | |||
| 712 | return $non_nullable_type; |
||
| 713 | } |
||
| 714 | |||
| 715 | return TypeCombination::combineTypes([ |
||
| 716 | new TNull, |
||
| 717 | $non_nullable_type, |
||
| 718 | ]); |
||
| 719 | } |
||
| 720 | |||
| 721 | if ($parse_tree instanceof ParseTree\MethodTree |
||
| 722 | || $parse_tree instanceof ParseTree\MethodWithReturnTypeTree |
||
| 723 | ) { |
||
| 724 | throw new TypeParseTreeException('Misplaced brackets'); |
||
| 725 | } |
||
| 726 | |||
| 727 | if ($parse_tree instanceof ParseTree\IndexedAccessTree) { |
||
| 728 | if (!isset($parse_tree->children[0]) || !$parse_tree->children[0] instanceof ParseTree\Value) { |
||
| 729 | throw new TypeParseTreeException('Unrecognised indexed access'); |
||
| 730 | } |
||
| 731 | |||
| 732 | $offset_param_name = $parse_tree->value; |
||
| 733 | $array_param_name = $parse_tree->children[0]->value; |
||
| 734 | |||
| 735 | if (!isset($template_type_map[$offset_param_name])) { |
||
| 736 | throw new TypeParseTreeException('Unrecognised template param ' . $offset_param_name); |
||
| 737 | } |
||
| 738 | |||
| 739 | if (!isset($template_type_map[$array_param_name])) { |
||
| 740 | throw new TypeParseTreeException('Unrecognised template param ' . $array_param_name); |
||
| 741 | } |
||
| 742 | |||
| 743 | $offset_template_data = $template_type_map[$offset_param_name]; |
||
| 744 | |||
| 745 | $offset_defining_class = array_keys($offset_template_data)[0]; |
||
| 746 | |||
| 747 | if (!$offset_defining_class |
||
| 748 | && isset($offset_template_data['']) |
||
| 749 | && $offset_template_data[''][0]->isSingle() |
||
| 750 | ) { |
||
| 751 | $offset_template_type = array_values($offset_template_data[''][0]->getAtomicTypes())[0]; |
||
| 752 | |||
| 753 | if ($offset_template_type instanceof Atomic\TTemplateKeyOf) { |
||
| 754 | $offset_defining_class = (string) $offset_template_type->defining_class; |
||
| 755 | } |
||
| 756 | } |
||
| 757 | |||
| 758 | $array_defining_class = array_keys($template_type_map[$array_param_name])[0]; |
||
| 759 | |||
| 760 | if ($offset_defining_class !== $array_defining_class |
||
| 761 | && substr($offset_defining_class, 0, 3) !== 'fn-' |
||
| 762 | ) { |
||
| 763 | throw new TypeParseTreeException('Template params are defined in different locations'); |
||
| 764 | } |
||
| 765 | |||
| 766 | return new Atomic\TTemplateIndexedAccess( |
||
| 767 | $array_param_name, |
||
| 768 | $offset_param_name, |
||
| 769 | $array_defining_class |
||
| 770 | ); |
||
| 771 | } |
||
| 772 | |||
| 773 | if ($parse_tree instanceof ParseTree\TemplateAsTree) { |
||
| 774 | return new Atomic\TTemplateParam( |
||
| 775 | $parse_tree->param_name, |
||
| 776 | new Union([new TNamedObject($parse_tree->as)]), |
||
| 777 | 'class-string-map' |
||
| 778 | ); |
||
| 779 | } |
||
| 780 | |||
| 781 | if ($parse_tree instanceof ParseTree\ConditionalTree) { |
||
| 782 | $template_param_name = $parse_tree->condition->param_name; |
||
| 783 | |||
| 784 | if (!isset($template_type_map[$template_param_name])) { |
||
| 785 | throw new TypeParseTreeException('Unrecognized template \'' . $template_param_name . '\''); |
||
| 786 | } |
||
| 787 | |||
| 788 | if (count($parse_tree->children) !== 2) { |
||
| 789 | throw new TypeParseTreeException('Invalid conditional'); |
||
| 790 | } |
||
| 791 | |||
| 792 | $first_class = array_keys($template_type_map[$template_param_name])[0]; |
||
| 793 | |||
| 794 | $conditional_type = self::getTypeFromTree( |
||
| 795 | $parse_tree->condition->children[0], |
||
| 796 | $codebase, |
||
| 797 | null, |
||
| 798 | $template_type_map, |
||
| 799 | $type_aliases |
||
| 800 | ); |
||
| 801 | |||
| 802 | $if_type = self::getTypeFromTree( |
||
| 803 | $parse_tree->children[0], |
||
| 804 | $codebase, |
||
| 805 | null, |
||
| 806 | $template_type_map, |
||
| 807 | $type_aliases |
||
| 808 | ); |
||
| 809 | |||
| 810 | $else_type = self::getTypeFromTree( |
||
| 811 | $parse_tree->children[1], |
||
| 812 | $codebase, |
||
| 813 | null, |
||
| 814 | $template_type_map, |
||
| 815 | $type_aliases |
||
| 816 | ); |
||
| 817 | |||
| 818 | if ($conditional_type instanceof Atomic) { |
||
| 819 | $conditional_type = new Union([$conditional_type]); |
||
| 820 | } |
||
| 821 | |||
| 822 | if ($if_type instanceof Atomic) { |
||
| 823 | $if_type = new Union([$if_type]); |
||
| 824 | } |
||
| 825 | |||
| 826 | if ($else_type instanceof Atomic) { |
||
| 827 | $else_type = new Union([$else_type]); |
||
| 828 | } |
||
| 829 | |||
| 830 | return new Atomic\TConditional( |
||
| 831 | $template_param_name, |
||
| 832 | $first_class, |
||
| 833 | $template_type_map[$template_param_name][$first_class][0], |
||
| 834 | $conditional_type, |
||
| 835 | $if_type, |
||
| 836 | $else_type |
||
| 837 | ); |
||
| 838 | } |
||
| 839 | |||
| 840 | if (!$parse_tree instanceof ParseTree\Value) { |
||
| 841 | throw new \InvalidArgumentException('Unrecognised parse tree type ' . get_class($parse_tree)); |
||
| 842 | } |
||
| 843 | |||
| 844 | if ($parse_tree->value[0] === '"' || $parse_tree->value[0] === '\'') { |
||
| 845 | return new TLiteralString(substr($parse_tree->value, 1, -1)); |
||
| 846 | } |
||
| 847 | |||
| 848 | if (strpos($parse_tree->value, '::')) { |
||
| 849 | list($fq_classlike_name, $const_name) = explode('::', $parse_tree->value); |
||
| 850 | |||
| 851 | View Code Duplication | if (isset($template_type_map[$fq_classlike_name]) && $const_name === 'class') { |
|
| 852 | $first_class = array_keys($template_type_map[$fq_classlike_name])[0]; |
||
| 853 | |||
| 854 | return self::getGenericParamClass( |
||
| 855 | $fq_classlike_name, |
||
| 856 | $template_type_map[$fq_classlike_name][$first_class][0], |
||
| 857 | $first_class |
||
| 858 | ); |
||
| 859 | } |
||
| 860 | |||
| 861 | if ($const_name === 'class') { |
||
| 862 | return new Atomic\TLiteralClassString($fq_classlike_name); |
||
| 863 | } |
||
| 864 | |||
| 865 | return new Atomic\TScalarClassConstant($fq_classlike_name, $const_name); |
||
| 866 | } |
||
| 867 | |||
| 868 | if (preg_match('/^\-?(0|[1-9][0-9]*)(\.[0-9]{1,})$/', $parse_tree->value)) { |
||
| 869 | return new TLiteralFloat((float) $parse_tree->value); |
||
| 870 | } |
||
| 871 | |||
| 872 | if (preg_match('/^\-?(0|[1-9][0-9]*)$/', $parse_tree->value)) { |
||
| 873 | return new TLiteralInt((int) $parse_tree->value); |
||
| 874 | } |
||
| 875 | |||
| 876 | if (!preg_match('@^(\$this|\\\\?[a-zA-Z_\x7f-\xff][\\\\\-0-9a-zA-Z_\x7f-\xff]*)$@', $parse_tree->value)) { |
||
| 877 | throw new TypeParseTreeException('Invalid type \'' . $parse_tree->value . '\''); |
||
| 878 | } |
||
| 879 | |||
| 880 | $atomic_type_string = TypeTokenizer::fixScalarTerms($parse_tree->value, $php_version); |
||
| 881 | |||
| 882 | $atomic_type = Atomic::create($atomic_type_string, $php_version, $template_type_map, $type_aliases); |
||
| 883 | |||
| 884 | $atomic_type->offset_start = $parse_tree->offset_start; |
||
| 885 | $atomic_type->offset_end = $parse_tree->offset_end; |
||
| 886 | |||
| 887 | return $atomic_type; |
||
| 888 | } |
||
| 889 | |||
| 969 |
This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.