/** * @param TraversableType $type * * @return Call|Closure */ public function visitTraversableType(TraversableType $type) { $this->typeCheck->visitTraversableType(func_get_args()); $primaryExpression = $type->primaryType()->accept($this); if ($type->primaryType() instanceof ArrayType && $type->keyType() instanceof MixedType && $type->valueType() instanceof MixedType) { return $primaryExpression; } $closure = new Closure(); $closure->addParameter(new Parameter($this->valueIdentifier)); $valueVariable = new Variable($this->valueIdentifier); $notTraversableObjectExpression = new LogicalNot(new InstanceOfType($valueVariable, QualifiedIdentifier::fromString('\\Traversable'))); $isArrayCall = new Call(QualifiedIdentifier::fromString('\\is_array')); $isArrayCall->add($valueVariable); $notArrayExpression = new LogicalNot($isArrayCall); if ($type->primaryType() instanceof ArrayType) { $notTraversableExpression = $notArrayExpression; } elseif ($type->primaryType() instanceof ObjectType) { $notTraversableExpression = $notTraversableObjectExpression; } else { $notTraversableExpression = new LogicalAnd($notArrayExpression, $notTraversableObjectExpression); } $ifStatement = new IfStatement($notTraversableExpression); $ifStatement->trueBranch()->add(new ReturnStatement(new Literal(false))); $closure->statementBlock()->add($ifStatement); $keyIdentifier = new Identifier('key'); $subValueIdentifier = new Identifier('subValue'); $loopStatement = new StatementBlock(); $keyVariable = new Variable($keyIdentifier); $oldValueIdentifier = $this->valueIdentifier; $this->valueIdentifier = $keyIdentifier; $keyExpression = $type->keyType()->accept($this); $this->valueIdentifier = $oldValueIdentifier; if ($keyExpression instanceof Closure) { $keyCheckVariable = new Variable(new Identifier('keyCheck')); $closure->statementBlock()->add(new ExpressionStatement(new Assign($keyCheckVariable, $keyExpression))); $keyExpression = new Call($keyCheckVariable); $keyExpression->add($keyVariable); } if (null !== $keyExpression) { $ifStatement = new IfStatement(new LogicalNot($keyExpression)); $ifStatement->trueBranch()->add(new ReturnStatement(new Literal(false))); $loopStatement->add($ifStatement); } $subValueVariable = new Variable($subValueIdentifier); $oldValueIdentifier = $this->valueIdentifier; $this->valueIdentifier = $subValueIdentifier; $valueExpression = $type->valueType()->accept($this); $this->valueIdentifier = $oldValueIdentifier; if ($valueExpression instanceof Closure) { $valueCheckVariable = new Variable(new Identifier('valueCheck')); $closure->statementBlock()->add(new ExpressionStatement(new Assign($valueCheckVariable, $valueExpression))); $valueExpression = new Call($valueCheckVariable); $valueExpression->add($subValueVariable); } if (null !== $valueExpression) { $ifStatement = new IfStatement(new LogicalNot($valueExpression)); $ifStatement->trueBranch()->add(new ReturnStatement(new Literal(false))); $loopStatement->add($ifStatement); } $closure->statementBlock()->add(new ForeachStatement($valueVariable, new Parameter($keyIdentifier), new Parameter($subValueIdentifier), $loopStatement)); $closure->statementBlock()->add(new ReturnStatement(new Literal(true))); return $closure; }
/** * Visit a traversable type. * * @param TraversableType $type The type. * * @return mixed The result of visitation. */ public function visitTraversableType(TraversableType $type) { $primaryType = $type->primaryType()->accept($this); $subTypes = array(); if ($type->keyType()) { $subTypes[] = $type->keyType()->accept($this); $subTypes[] = $type->valueType()->accept($this); } elseif (!$type->primaryType() instanceof ArrayType || !$type->valueType() instanceof MixedType) { $subTypes[] = $type->valueType()->accept($this); } if (count($subTypes) < 1) { return $primaryType; } return sprintf('%s<%s>', $primaryType, implode(',', $subTypes)); }