RSS
 

PHP: Using functions/expressions in HEREDOC strings

29 Mar2011

If you have any experience with PHP at all, you’ve probably used HEREDOC strings. The syntax is quite useful for long strings that span on multiple lines, but I always disliked the fact that if you wanted to use the result of a function, you need to store it in a variable as an intermediate step:

1
2
3
4
$time = date('r'); // example value: Tue, 29 Mar 2011 17:04:28 +0300
$string = <<<HEREDOC
Now is {$time}
HEREDOC;

Unfortunately, PHP doesn’t provide any direct means for calling functions or outputting expression results in HEREDOC strings. The problem here is that, unless the thing in the curly braces starts with a dollar sign, it can’t be recognized by PHP as something to be replaced – you can’t just put {date(‘r’)}. Functions doesn’t start with a dollar sign, so you can’t use them. Object methods work fine though, because the object variable does start with a “$”.

We can use this behavior in order to devise a very neat workaround to our problem: if PHP wants a $ in the beginning, why don’t we just store the function name in a variable? That’s one of the less popular features of PHP – and for good reasons – but in this particular case it could be very useful. So, we can change our example above to this, and it will work:

1
2
3
4
$date_func = 'date'; // the name of the function as a string
$string = <<<HEREDOC
Now is {$date_func('r')}
HEREDOC;

Now, let’s step this up a bit, shall we? Take a look at this example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function heredoc($param) {
    // just return whatever has been passed to us
    return $param;
}
 
$heredoc = 'heredoc';
 
$string = <<<HEREDOC
\$heredoc is now a generic function that can be used in all sorts of ways:
Output the result of a function: {$heredoc(date('r'))}
Output the value of a constant: {$heredoc(__FILE__)}
Static methods work just as well: {$heredoc(MyClass::getSomething())}
2 + 2 equals {$heredoc(2+2)}
HEREDOC;
 
// The same works not only with HEREDOC strings,
// but with double-quoted strings as well:
$string = "{$heredoc(2+2)}";

Told you it was neat, isn’t it? :) The “heredoc” function is just a wrapper – it accepts a single argument which is returned unmodified, and the name of this function is stored in a global variable that can be used anywhere in your application. The name of the function and the name of the global variable are completely customizable, so the “heredoc” name is just a suggestion. $heredoc() will be called as a function, and the expression in the parentheses will be evaluated as any other PHP code. The result of that expression will be given to the heredoc function which, in its turn, just returns it and puts it in the HEREDOC string.

 
 

Leave a Reply

 

Current ye@r *

 
  1. zamiere

    5 September 2011 at 10:40

    It’s the best trick for PHP since phpHAML!

    My suggestion for variable name is $echo.

     
  2. Rosko

    30 January 2012 at 14:31

    That’s a very good suggestion! Shorter and probably more meaningful.

    Since PHP 5.3 we could also do:

    $echo = function($param){return $param;};

     
  3. Some1

    15 July 2012 at 11:24

    This is an awesome hack, thanks!

    For those who need more than functions,
    but arbitrary PHP code executed in a
    heredoc, try this:

    $exec = function($parm){eval ($parm);};

    $testvar = 42;

    echo <<<EOF
    blah1
    {$exec("echo '{$testvar}foobarbaz';")}
    {$exec("if ($testvar == 42) echo 'It works!';")}
    blah2
    EOF

     
  4. Rosko

    16 August 2012 at 09:21

    Thanks for the suggestion! Although I would avoid using “eval” unless I really need to. Most of the time I’m perfectly OK with using expressions (for which you don’t need eval), and if I needed to put a whole statement there, I’d probably be better off just taking the thing off and moving it before the HEREDOC string. Otherwise I’d have to take extra care to use only safe strings in the eval – *cough* php injection *cough*

     
  5. Dale P

    23 July 2014 at 15:11

    Thanks for this – great tip.

    And +1 for Rosko’s suggestion. This works well in OOP class.