/**
  * On the first run of the type inference engine, arrays always have
  * the generic array type at this point. This is assigned by the
  * {@see AbstractScopeBuilder::attachLiteralTypes}.
  *
  * We try to make this more specific by introspecting the values of
  * the items which are assigned.
  *
  * @param \PHPParser_Node_Expr_Array $node
  * @param LinkedFlowScope $scope
  *
  * @return LinkedFlowScope
  */
 private function traverseArray(\PHPParser_Node_Expr_Array $node, LinkedFlowScope $scope)
 {
     $keyTypes = $elementTypes = $itemTypes = array();
     $lastNumericKey = -1;
     $containsDynamicKey = false;
     foreach ($node->items as $item) {
         assert($item instanceof \PHPParser_Node_Expr_ArrayItem);
         $scope = $this->traverse($item, $scope);
         if (null === $item->key) {
             $keyTypes[] = 'integer';
         } else {
             if (null !== ($type = $item->key->getAttribute('type'))) {
                 $keyTypes[] = $type;
             } else {
                 // If the key type is not available, then we will just assume both
                 // possible types. It's not that bad after all.
                 $keyTypes[] = 'string';
                 $keyTypes[] = 'integer';
             }
         }
         $itemName = null;
         $keyValue = NodeUtil::getValue($item->key);
         if (null === $item->key && !$containsDynamicKey) {
             $itemName = ++$lastNumericKey;
         } else {
             if ($keyValue->isDefined()) {
                 // We cast everything to a string (even integers), and then process
                 // both cases: 1) numeric key, and 2) anything else
                 $keyValue = (string) $keyValue->get();
                 if (ctype_digit($keyValue)) {
                     // Case 1)
                     $itemName = (int) $keyValue;
                     // PHP will always use the next highest number starting from the
                     // greatest numeric value that the array contains when no explicit
                     // key has been defined; so, we need to keep track of this.
                     if ($itemName > $lastNumericKey) {
                         $lastNumericKey = $itemName;
                     }
                 } else {
                     // Case 2)
                     $itemName = $keyValue;
                 }
             } else {
                 $containsDynamicKey = true;
             }
         }
         if (null !== ($type = $item->value->getAttribute('type'))) {
             if (null !== $itemName) {
                 $itemTypes[$itemName] = $type;
             }
             $elementTypes[] = $type;
         }
     }
     $node->setAttribute('type', $this->typeRegistry->getArrayType($elementTypes ? $this->typeRegistry->createUnionType($elementTypes) : null, $keyTypes ? $this->typeRegistry->createUnionType($keyTypes) : null, $itemTypes));
     return $scope;
 }
 /**
  * @group non-refinable
  */
 public function testArrayExprNonRefinable()
 {
     $n = new \PHPParser_Node_Expr_Array();
     $n->setAttribute('type', $this->registry->getNativeType('array'));
     $this->doTestTypeFunction($this->newScope(), 'is_callable', $n, true, array(), array());
 }