/** * Refines an array type with the information about the used key, and assigned element type. * * This also defines the type for the item key if available and non-dynamic. * * @param PhpType $varType * @param PhpType $resultType * @param \PHPParser_Node_Expr_ArrayDimFetch $node * * @return ArrayType */ private function getRefinedArrayType(PhpType $varType, PhpType $resultType, \PHPParser_Node_Expr_ArrayDimFetch $node) { $usedKeyType = $this->getKeyTypeForDimFetch($node); $keyType = $varType->getKeyType(); if ($keyType === $this->typeRegistry->getNativeType('generic_array_key')) { $refinedKeyType = $usedKeyType; } else { $refinedKeyType = $keyType->getLeastSupertype($usedKeyType); } $elementType = $varType->getElementType(); if ($elementType === $this->typeRegistry->getNativeType('generic_array_value')) { $refinedElementType = $resultType; } else { $refinedElementType = $elementType->getLeastSupertype($resultType); } $refinedType = $this->typeRegistry->getArrayType($refinedElementType, $refinedKeyType); // Infer the type of the key if a concrete value is available. NodeUtil::getValue($node->dim)->map(function ($keyName) use(&$refinedType, $resultType) { $refinedType = $refinedType->inferItemType($keyName, $resultType); }); return $refinedType; }
private function getStringRepr(PhpType $type) { switch (true) { case $type instanceof AllType: return TypeRegistry::NATIVE_ALL; // This handles the generic array type specially. // This handles the generic array type specially. case $type === self::$typeRegistry->getNativeType('array'): return 'array'; case $type instanceof ArrayType: $itemTypes = $type->getItemTypes(); if (empty($itemTypes)) { return TypeRegistry::NATIVE_ARRAY . '<' . $this->getStringRepr($type->getKeyType()) . ',' . $this->getStringRepr($type->getElementType()) . '>'; } return sprintf('array<%s,%s,%s>', $this->getStringRepr($type->getKeyType()), $this->getStringRepr($type->getElementType()), $this->dumpJsonLike($itemTypes, true)); case $type instanceof FalseType: return TypeRegistry::NATIVE_BOOLEAN_FALSE; case $type instanceof BooleanType: return TypeRegistry::NATIVE_BOOLEAN; case $type instanceof CallableType: return TypeRegistry::NATIVE_CALLABLE; case $type instanceof ResourceType: return TypeRegistry::NATIVE_RESOURCE; case $type instanceof DoubleType: return TypeRegistry::NATIVE_DOUBLE; case $type instanceof IntegerType: return TypeRegistry::NATIVE_INTEGER; case $type instanceof ThisType: return 'this<' . $type->getReferenceName() . '>'; case $type instanceof NamedType: // If this type has been resolved, we can get the representation // of the resolved type instead of using the reference name. if (!$type->isNoResolvedType()) { return $this->getStringRepr($type->getReferencedType()); } return 'object<' . $type->getReferenceName() . '>'; case $type instanceof NoObjectType: return TypeRegistry::NATIVE_OBJECT; case $type instanceof NoType: return TypeRegistry::NATIVE_NONE; case $type instanceof NullType: return TypeRegistry::NATIVE_NULL; case $type instanceof ObjectType: return 'object<' . $type->getName() . '>'; case $type instanceof StringType: return TypeRegistry::NATIVE_STRING; case $type instanceof UnionType: $alt = array(); foreach ($type->getAlternates() as $t) { $alt[] = $this->getStringRepr($t); } return implode('|', $alt); case $type instanceof UnknownType: return $type->isChecked() ? TypeRegistry::NATIVE_UNKNOWN_CHECKED : TypeRegistry::NATIVE_UNKNOWN; } throw new \InvalidArgumentException(sprintf('The SWITCH statement is exhaustive, but got "%s".', get_class($type))); }