Following the official explanation, I want to create my custom MySQL function ROUND() that can take (not mandatory) another second argument.
Sor far I've done this:
<?php
namespace HQF\Bundle\PizzasBundle\DQL;
use \Doctrine\ORM\Query\AST\Functions\FunctionNode;
use \Doctrine\ORM\Query\Lexer;
class MysqlRound extends FunctionNode
{
    public $simpleArithmeticExpression;
    public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
    {
        return 'ROUND(' . $sqlWalker->walkSimpleArithmeticExpression(
            $this->simpleArithmeticExpression
        ) . ')';
    }
    public function parse(\Doctrine\ORM\Query\Parser $parser)
    {
        $lexer = $parser->getLexer();
        $parser->match(Lexer::T_IDENTIFIER);
        $parser->match(Lexer::T_OPEN_PARENTHESIS);
        $this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression();
        $parser->match(Lexer::T_CLOSE_PARENTHESIS);
    }
}
But how to implement the fact that ROUND(XX) is ok, and ROUND(XX, YY) is ok too?
You need to declare a second argument, and use the Lexer like this:
namespace HQF\Bundle\PizzasBundle\DQL;
use \Doctrine\ORM\Query\AST\Functions\FunctionNode;
use \Doctrine\ORM\Query\Lexer;
class MysqlRound extends FunctionNode
{
    private $firstExpression = null;
    private $secondExpression = null;
    public function parse(\Doctrine\ORM\Query\Parser $parser)
    {
        $lexer = $parser->getLexer();
        $parser->match(Lexer::T_IDENTIFIER);
        $parser->match(Lexer::T_OPEN_PARENTHESIS);
        $this->firstExpression = $parser->ArithmeticPrimary();
        // parse second parameter if available
        if(Lexer::T_COMMA === $lexer->lookahead['type']){
            $parser->match(Lexer::T_COMMA);
            $this->secondExpression = $parser->ArithmeticPrimary();
        }
        $parser->match(Lexer::T_CLOSE_PARENTHESIS);
    }
    public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
    {
        // use second parameter if parsed
        if (null !== $this->secondExpression){
            return 'ROUND(' 
                . $this->firstExpression->dispatch($sqlWalker)
                . ', '
                . $this->secondExpression->dispatch($sqlWalker)
                . ')';
        }
        return 'ROUND(' . $this->firstExpression->dispatch($sqlWalker) . ')';
    }
}
Many Doctrine2 extensions have been written here. All credits to @beberlei for his great work. Many functions are available (IFELSE, IFNULL, NULLIF, COS, ACOS, etc...) but not all of them (ROUND, GREATEST or LEAST are missing but you can still write them yourself if you need them.)
I also found GREATEST implementation here https://raw.githubusercontent.com/rodgermd/mura-show.com/master/src/Rodger/GalleryBundle/DoctrineExtension/Greatest.php (c) rodgermd@github :
namespace Rodger\GalleryBundle\DoctrineExtension;
use Doctrine\ORM\Query\Lexer,
    Doctrine\ORM\Query\AST\Functions;
class Greatest extends Functions\FunctionNode {
  protected $firstExpression, $secondExpression;
  public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
  {
    return sprintf("GREATEST(%s, %s)", 
            $this->firstExpression->dispatch($sqlWalker), 
            $this->secondExpression->dispatch($sqlWalker));
  }
  public function parse(\Doctrine\ORM\Query\Parser $parser)
    {
        $parser->match(Lexer::T_IDENTIFIER); // (2)
        $parser->match(Lexer::T_OPEN_PARENTHESIS); // (3)
        $this->firstExpression = $parser->ArithmeticPrimary(); // (4)
        $parser->match(Lexer::T_COMMA); // (5)
        $this->secondExpression = $parser->ArithmeticPrimary(); // (6)
        $parser->match(Lexer::T_CLOSE_PARENTHESIS); // (3)
    }
}
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With