src/Eloquent/Typhoon/Generator/TyphaxASTGenerator.php
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;
    }
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;
    }
@param TraversableType $type

@return Call|Closure