/** * Declares a refined type in {@code scope} for the name represented by * {@code node}. It must be possible to refine the type of the given node in * the given scope, as determined by {@link #getTypeIfRefinable}. */ protected function declareNameInScope(LinkedFlowScope $scope, \PHPParser_Node $node, PhpType $type) { switch (true) { case $node instanceof \PHPParser_Node_Expr_Variable: if (is_string($node->name)) { $scope->inferSlotType($node->name, $type); } break; case $node instanceof \PHPParser_Node_Expr_StaticPropertyFetch: case $node instanceof \PHPParser_Node_Expr_PropertyFetch: if (null !== ($qualifiedName = TypeInference::getQualifiedName($node))) { $origType = $node->getAttribute('type') ?: $this->typeRegistry->getNativeType('unknown'); $scope->inferQualifiedSlot($node, $qualifiedName, $origType, $type); } break; // Something like: if (is_array($functions = getFunctionNames())) { } // Something like: if (is_array($functions = getFunctionNames())) { } case $node instanceof \PHPParser_Node_Expr_Assign: case $node instanceof \PHPParser_Node_Expr_AssignRef: $this->declareNameInScope($scope, $node->var, $type); break; case $node instanceof \PHPParser_Node_Expr_ArrayDimFetch: $dim = \Scrutinizer\PhpAnalyzer\PhpParser\NodeUtil::getValue($node->dim); if ($dim->isDefined() && null !== ($slotType = $node->var->getAttribute('type')) && $slotType->isArrayType()) { $newSlotType = $slotType->inferItemType($dim->get(), $type); $this->declareNameInScope($scope, $node->var, $newSlotType); } break; } }
/** * 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; }