/**
  * 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;
 }
 /**
  * print an associative array
  */
 public function pExpr_Array(\PHPParser_Node_Expr_Array $node)
 {
     $multiLine = FALSE;
     $startLine = $node->getAttribute('startLine');
     $endLine = $node->getAttribute('endLine');
     if ($startLine != $endLine) {
         $multiLine = TRUE;
     }
     $printedNodes = '';
     foreach ($node->items as $itemNode) {
         $glueToken = ", ";
         if ($itemNode->getAttribute('startLine') != $startLine) {
             $glueToken = ',' . LF;
             $startLine = $itemNode->getAttribute('startLine');
         }
         if (!empty($printedNodes)) {
             $printedNodes .= $glueToken . $this->p($itemNode);
         } else {
             $printedNodes .= $this->p($itemNode);
         }
     }
     if ($multiLine) {
         $multiLinedItems = $this->indentToken . preg_replace('~\\n(?!$|' . $this->noIndentToken . ')~', LF . $this->indentToken, $printedNodes);
         return 'array(' . LF . $multiLinedItems . LF . ')';
     } else {
         return parent::pExpr_Array($node);
     }
 }
 /**
  * @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());
 }