CodeGen icon indicating copy to clipboard operation
CodeGen copied to clipboard

ChainOperator Expr

Open dafik opened this issue 9 years ago • 1 comments

How about this:

class:

<?php
namespace CodeGen\Expr;

use CodeGen\Block;
use CodeGen\Renderable;
use CodeGen\Variable;
use LogicException;

class ChainExpr implements Renderable
{
    /**
     * @var Variable|string
     */
    protected $inNewLine;
    protected $args;


    protected $op = '->';

    public function __construct($args, $inNewLine = true)
    {
        $this->inNewLine = $inNewLine;
        if (!is_array($args) || count($args) < 2) {
            throw new LogicException('to few arg');
        }
        $this->args = $args;
    }

    public function render(array $args = array())
    {
        $arguments = [];
        foreach ($this->args as $arg) {
            if ($arg instanceof Renderable) {
                $arguments[] = $arg->render($args);
            } else {
                $arguments[] = $arg;
            }
        }
        if ($this->inNewLine) {
            $block = new Block();
            $chainBlock = new Block();
            $chainBlock->increaseIndentLevel();
            foreach ($arguments as $key => $arg) {
                if ($key === 0) {
                    $block->appendLine($arg);
                } else {
                    $chainBlock->appendLine($this->op . $arg);
                }
            }
            $block->appendRenderable($chainBlock);

            return $block->render($args);
        } else {
            return implode($this->op, $arguments);
        }
    }

    public function __toString()
    {
        return $this->render();
    }

}

test:

class ChainExprTest extends CodeGenTestCase
{

    public function testChainExprOneLine()
    {
        $expr = new ChainExpr(
            [
                new MethodCall(new Variable('$someObject'), 'someMethod'),
                new FunctionCall('otherMethod')
            ],
            false
        );
        $this->assertCodeEquals('$someObject->someMethod()->otherMethod()', $expr);
    }

    public function testChainExpr()
    {
        $expr = new ChainExpr([
            new MethodCall(new Variable('$someObject'), 'someMethod'),
            new FunctionCall('otherMethod')
        ]);
        $this->assertCodeEqualsFile('tests/data/chain_expr.fixture', $expr);
    }

    public function testChainExprAssign()
    {
        $expr = new ChainExpr([
            new MethodCall(new Variable('$someObject'), 'someMethod'),
            new FunctionCall('otherMethod'),
            new FunctionCall('anotherMethod')

        ]);

        $assignExpr = new AssignExpr(
            new Variable('$someValue'),
            $expr
        );

        $this->assertCodeEqualsFile('tests/data/chain_expr_assign.fixture', $assignExpr);
    }
}

fixtures:

$someObject->someMethod()
    ->otherMethod()
$someValue = $someObject->someMethod()
    ->otherMethod()
    ->anotherMethod()

dafik avatar Nov 12 '15 10:11 dafik

I would prefer adding a ChainedMethodCall for this:

$chain = new ChainedMethodCall($object, [....]);
$chain->otherMethod($arg1, $arg2, $arg3);
$chain->anotherMethod($arg1, $arg2, $arg3);

Use __call to catch the method calls, and generates

$someObject->someMethod(...)->otherMethod(....)

c9s avatar Nov 12 '15 11:11 c9s