104

In PHP, the HEREDOC string declarations are really useful for outputting a block of html. You can have it parse in variables just by prefixing them with $, but for more complicated syntax (like $var[2][3]), you have to put your expression inside {} braces.

In PHP 5, it is possible to actually make function calls within {} braces inside a HEREDOC string, but you have to go through a bit of work. The function name itself has to be stored in a variable, and you have to call it like it is a dynamically-named function. For example:

$fn = 'testfunction';
function testfunction() { return 'ok'; }
$string = <<< heredoc
plain text and now a function: {$fn()}
heredoc;

As you can see, this is a bit more messy than just:

$string = <<< heredoc
plain text and now a function: {testfunction()}
heredoc;

There are other ways besides the first code example, such as breaking out of the HEREDOC to call the function, or reversing the issue and doing something like:

?>
<!-- directly output html and only breaking into php for the function -->
plain text and now a function: <?PHP print testfunction(); ?>

The latter has the disadvantage that the output is directly put into the output stream (unless I'm using output buffering), which might not be what I want.

So, the essence of my question is: is there a more elegant way to approach this?

Edit based on responses: It certainly does seem like some kind of template engine would make my life much easier, but it would require me basically invert my usual PHP style. Not that that's a bad thing, but it explains my inertia.. I'm up for figuring out ways to make life easier though, so I'm looking into templates now.

Doug Kavendek
  • 3,624
  • 4
  • 31
  • 43
  • 3
    This isn't strictly an answer to your question, but given the poor support for function calls in heredoc statements, I usually just generate the strings I'll need before printing the heredoc. Then, I can simply use something like `Text {$string1} Text {$string2} Text` in the heredoc. – rinogo Mar 06 '18 at 00:21

17 Answers17

79

If you really want to do this but a bit simpler than using a class you can use:

function fn($data) {
  return $data;
}
$fn = 'fn';

$my_string = <<<EOT
Number of seconds since the Unix Epoch: {$fn(time())}
EOT;
CJ Dennis
  • 4,226
  • 2
  • 40
  • 69
  • Great @CJDennis! That is the best and cleanest solution for using functions call inside HEREDOC. There is nice resource in some situations. In my site, I use HEREDOC for generate forms with 22 lines of field sets (a HEREDOC block inside a FOR loop ), with function call in order to generate tabindex position. – Paulo Buchsbaum Jan 18 '13 at 04:12
  • You can even do this: `$my_string = "Half the number of seconds since the Unix Epoch: {$fn(time() / 2 . ' Yes! Really!')}";` – CJ Dennis Nov 29 '13 at 04:33
  • 2
    a more compact definition: `$fn = function fn($data) { return $data; };` – devsmt Sep 19 '16 at 13:16
  • 1
    @devsmt You're right. And even shorter is: `$fn = function ($data) { return $data; };` – CJ Dennis Sep 20 '16 at 02:31
  • oh, godegolf? okay, let me in: `$fn=function($data){return $data;};` rhis should be the shortest – My1 Dec 25 '16 at 22:42
  • @My1 No, `$f=function($d){return$d;};` is shorter, but this is not Code Golf. – CJ Dennis Dec 27 '16 at 07:08
  • I didnt even know you could return without space – My1 Dec 27 '16 at 16:57
  • @My1 Golf? `$fn='strval';` – Pinke Helga Dec 16 '18 at 12:46
  • @Olivier `"{${'fn'}(time())}"` and `"{$fn(time())}"` work exactly the same way. Both need a variable defined. – CJ Dennis Jan 05 '19 at 12:48
  • @CJDennis _After understanding your answer, I'm sharing my thoughts-_ It means If we want to call function**s** inside **heredoc** then we have to first initialize variable**s** having their values same as the name of the function. **Eg :-** `function phpf1(){}, function phpf2(), function phpf3(){}` so if we want to call those functions inside `heredoc` then we have to make variables like `$f1= 'phpf1'; $f2= 'phpf2'; $f3= 'phpf3';`. _Am I right_ ? – Abhishek Kamal Feb 11 '20 at 04:27
  • @AbhishekKamal You could do it that way but having a wrapper function as I set up in my answer makes that unnecessary. `"{$fn(phpf1())}, {$fn(phpf2())}, {$fn(phpf3())}"` is all you need. Anything that can be a valid function parameter can go inside the parentheses of `{$fn()}`. – CJ Dennis Feb 11 '20 at 04:46
  • Since PHP 7.4, `fn` is a reserved word. https://www.php.net/manual/en/migration74.incompatible.php – CJ Dennis May 05 '20 at 03:16
55

I would not use HEREDOC at all for this, personally. It just doesn't make for a good "template building" system. All your HTML is locked down in a string which has several disadvantages

  • No option for WYSIWYG
  • No code completion for HTML from IDEs
  • Output (HTML) locked to logic files
  • You end up having to use hacks like what you're trying to do now to achieve more complex templating, such as looping

Get a basic template engine, or just use PHP with includes - it's why the language has the <?php and ?> delimiters.

template_file.php

<html>
<head>
  <title><?php echo $page_title; ?></title>
</head>
<body>
  <?php echo getPageContent(); ?>
</body>

index.php

<?php

$page_title = "This is a simple demo";

function getPageContent() {
    return '<p>Hello World!</p>';
}

include('template_file.php');
Peter Bailey
  • 105,256
  • 31
  • 182
  • 206
  • Unfortunately PHP's method of embedding content makes the resultant HTML very hard to read. It's a shame PHP didn't standardize on short delimiters and provide a shorthand for echo like ASP of old did _(which is one of the very few things ASP did better than PHP IMO.)_ – MikeSchinkel Sep 04 '12 at 20:39
  • 9
    There is a shorthand for echo: `=$valueToEcho;?>` or `<%=$valueToEcho;%>` dependent upon your INI settings. – Peter Bailey Sep 05 '12 at 19:26
  • 4
    Most everything I've read about using those shorthands says using them is a bad practice, and I agree. So unfortunately if you are writing code for distribution you can't depend on those INI settings thus making PHP's "support" for them moot for distributed code. FWIW, I've had to fix bugs in other people's WordPress plugins more than once because they used these shorthands. – MikeSchinkel Sep 05 '12 at 21:27
  • I agree, but this is an odd thing to "complain" about. You're basically saying "It's a shame I have to type 7 extra characters" – Peter Bailey Sep 06 '12 at 18:32
  • 2
    No, I'm not saying it's a shame I have to type 7 characters; you incorrectly attribute my issues. It's not the *typing* I'm concerned with, it's the *reading*. Those characters create lots of visual noise that makes it much harder to scan the source code and understand what the code is doing. For me at least is is _MUCH_ easier to read a HEREDOC. _(And BTW, it's 7 character time how ever many times it's used in a given HTML fragment. But I digress.)_ – MikeSchinkel Sep 09 '12 at 21:32
  • 15
    Short is nicer, cleaner and easier to read. In your views `=$title?>` is **much** nicer than . The downside is, yes, for distribution a lot of ini's have short tags off. **But**, guess what?? As of *PHP 5.4*, short tags are enabled in PHP regardless of ini settings! So if you're coding with a 5.4+ requirement (let's say you're using traits, for example), go ahead and use those awesome short tags!! – Jimbo Mar 14 '13 at 09:57
  • What's so hard about embedding PHP inside HTML with proper delimiters? It doesn't affect readability. In fact, I think most developers who bellyache about readability are writing poorly formed HTML- more so than PHP. Just remember: one of the P's in the a PHP stands for 'preprocessor'. Use PHP for its strengths, and above all, keep it simple! – FredTheWebGuy Jul 24 '13 at 01:59
  • 3
    By the way, = $blah ?> is enabled in 5.4 by default, even if short tags are turned off. – callmetwan May 16 '14 at 16:15
  • So it turns out that the short answer is "No". You really cannot make a function call directly from within HEREDOC string. It took me quite a moment to grasp this! – Ifedi Okonkwo Aug 21 '14 at 15:37
  • @IfediOkonkwo Actually, you can and there are several answers here that show how. It's just not considered good practice to do so. – CJ Dennis Dec 21 '14 at 06:27
  • I couldn't disagree more about wanting to do "more complex templating such as looping" this way. Injecting control flow into an HTML template is the first step to exponentially compounding your complexity. It makes much sense to organize your output data in appropriate data structures first, then use your collection's functions to map/reduce it. It works amazingly well in ReactJS. You should never need a special templating microlanguage to loop. – Josh from Qaribou Sep 14 '15 at 18:28
  • 1
    The question is about inserting function calls into heredoc strings. It is not about a judgement as to whether it is appropriate or not. That's a judgement call for the person who asked the question. Templating engines are a stupid idea because PHP is already a templating language. – JG Estiot Feb 26 '18 at 01:48
  • This seems like the wrong answer to the question. Yes it might not be best practice, but there are situations where you do want to be able to do this. That however isn't the question. – xorinzor Sep 19 '20 at 14:29
46

I would do the following:

$string = <<< heredoc
plain text and now a function: %s
heredoc;
$string = sprintf($string, testfunction());

Not sure if you'd consider this to be more elegant ...

boxxar
  • 11,093
  • 2
  • 20
  • 7
29

For completeness, you can also use the !${''} black magic parser hack:

echo <<<EOT
One month ago was { ${!${''} = date('Y-m-d H:i:s', strtotime('-1 month'))} }.
EOT;

See it live on 3v4l.org.

Note: PHP 8.2 deprecated bare ${} variable variables within strings, in preference to the explicit { ${} } syntax. The example above uses this explicit format to remove the deprecation notice, though it makes this method even more noisy!

bishop
  • 37,830
  • 11
  • 104
  • 139
  • 16
    Did you went to Hogwarts? – Starx Jan 26 '17 at 10:39
  • 2
    This works because `false == ''`. Define a variable with a name of length 0 (`''`). Set it to the value you want (`${''} = date('Y-m-d H:i:s', strtotime('-1 month'))`). Negate it (`!`) and convert it into a variable (`${false}`). `false` needs to get converted to a string, and `(string)false === ''`. If you try to print a falsy value, it will error instead. The following string works on both truthy and falsy values, at the expense of being even more unreadable: `"${(${''}=date('Y-m-d H:i:s', strtotime('-1 month')))!=${''}}"`. – CJ Dennis Feb 25 '18 at 10:22
  • And if you want to print `NAN` as well, use `"${(${''} = date('Y-m-d H:i:s', strtotime('-1 month')) )==NAN}"`. – CJ Dennis Feb 25 '18 at 10:54
  • 1
    This should be the true answer. It works finely. – jacouh Feb 28 '22 at 12:18
  • this continues to run HEREDOC after the EOT; meanining php after is not parased – – DOMDocumentVideoSource Mar 04 '23 at 01:02
  • @DOMDocumentVideoSource Please provide link to 3v4l.org or similar demonstrating that. – bishop Mar 04 '23 at 03:04
17

Try this (either as a global variable, or instantiated when you need it):

<?php
  class Fn {
    public function __call($name, $args) {
      if (function_exists($name)) {
        return call_user_func_array($name, $args);
      }
    }
  }

  $fn = new Fn();
?>

Now any function call goes through the $fn instance. So the existing function testfunction() can be called in a heredoc with {$fn->testfunction()}

Basically we are wrapping all functions into a class instance, and using PHP's __call magic method to map the class method to the actual function needing to be called.

likeitlikeit
  • 5,563
  • 5
  • 42
  • 56
Isofarro
  • 171
  • 1
  • 2
  • 2
    This is a good solution for times when you can't just add a templating engine to an existing project. Thank you, I'm using it now. – Brandon Feb 03 '11 at 19:40
  • should not be used widely when performance is critical: I've read several times performance is worse for `call_user_func_array`, last time in the comments at php.net: http://www.php.net/manual/en/function.call-user-func-array.php#97473 – Markus Oct 18 '11 at 16:43
  • Nice! I love it, why didn't I think of this?!? :-) – MikeSchinkel Sep 04 '12 at 20:37
10

I'm a bit late, but I randomly came across it. For any future readers, here's what I would probably do:

I would just use an output buffer. So basically, you start the buffering using ob_start(), then include your "template file" with any functions, variables, etc. inside of it, get the contents of the buffer and write them to a string, and then close the buffer. Then you've used any variables you need, you can run any function, and you still have the HTML syntax highlighting available in your IDE.

Here's what I mean:

Template File:

<?php echo "plain text and now a function: " . testfunction(); ?>

Script:

<?php
ob_start();
include "template_file.php";
$output_string = ob_get_contents();
ob_end_clean();
echo $output_string;
?>

So the script includes the template_file.php into its buffer, running any functions/methods and assigning any variables along the way. Then you simply record the buffer's contents into a variable and do what you want with it.

That way if you don't want to echo it onto the page right at that second, you don't have to. You can loop and keep adding to the string before outputting it.

I think that's the best way to go if you don't want to use a templating engine.

BraedenP
  • 7,125
  • 4
  • 33
  • 42
8

found nice solution with wrapping function here: http://blog.nazdrave.net/?p=626

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)}";
p.voinov
  • 131
  • 1
  • 2
  • 3
    I suggested exactly the same solution 2.5 years before this. https://stackoverflow.com/a/10713298/1166898 – CJ Dennis Feb 24 '18 at 03:01
7

This snippet will define variables with the name of your defined functions within userscope and bind them to a string which contains the same name. Let me demonstrate.

function add ($int) { return $int + 1; }
$f=get_defined_functions();foreach($f[user]as$v){$$v=$v;}

$string = <<< heredoc
plain text and now a function: {$add(1)}
heredoc;

Will now work.

  • @MichaelMcMillian better not have any variables named the same as any function then, right? – s3c Feb 29 '20 at 16:36
5

I think using heredoc is great for generating HTML code. For example, I find the following almost completely unreadable.

<html>
<head>
  <title><?php echo $page_title; ?></title>
</head>
<body>
  <?php echo getPageContent(); ?>
</body>

However, in order to achieve the simplicity you are forced to evaluate the functions before you start. I don't believe that is such a terrible constraint, since in so doing, you end up separating your computation from display, which is usually a good idea.

I think the following is quite readable:

$page_content = getPageContent();

print <<<END
<html>
<head>
  <title>$page_title</title>
</head>
<body>
$page_content
</body>
END;

Unfortunately, even though it was a good suggestion you made in your question to bind the function to a variable, in the end, it adds a level of complexity to the code, which is not worth, and makes the code less readable, which is the major advantage of heredoc.

MLU
  • 181
  • 1
  • 6
  • 2
    The last 4 years proved this much smarter than most of the other approaches. Using composition in your templates (building a big page composed of smaller pages) and keeping all the control logic separate is the standard approach for anyone who's serious about templating: facebook's ReactJS is excellent for this (as is XHP), as is XSLT (which I don't love, but is academically sound). The only stylistic notes I'd make: I always use {} around my vars, mainly for consistency in readability and to avoid accidents later. Also, don't forget to htmlspecialchars() any data coming from users. – Josh from Qaribou Sep 14 '15 at 18:20
4

you are forgetting about lambda function:

$or=function($c,$t,$f){return$c?$t:$f;};
echo <<<TRUEFALSE
    The best color ever is {$or(rand(0,1),'green','black')}
TRUEFALSE;

You also could use the function create_function

Ismael Miguel
  • 4,185
  • 1
  • 31
  • 42
4

This is a little more elegant today on php 7.x

<?php

$test = function(){
    return 'it works!';
};


echo <<<HEREDOC
this is a test: {$test()}
HEREDOC;
codeasaurus
  • 137
  • 7
3

I'd take a look at Smarty as a template engine - I haven't tried any other ones myself, but it has done me well.

If you wanted to stick with your current approach sans templates, what's so bad about output buffering? It'll give you much more flexibility than having to declare variables which are the string names of the functions you want to call.

HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
nickf
  • 537,072
  • 198
  • 649
  • 721
  • Please add some explanation to your answer such that others can learn from it. How does looking at Smarty resolve the initial question? – Nico Haase Feb 08 '23 at 10:03
2

Here a nice example using @CJDennis proposal:

function double($i)
{ return $i*2; }

function triple($i)
{ return $i*3;}

$tab = 'double';
echo "{$tab(5)} is $tab 5<br>";

$tab = 'triple';
echo "{$tab(5)} is $tab 5<br>";

For instance, a good use for HEREDOC syntax is generate huge forms with master-detail relationship in a Database. One can use HEREDOC feature inside a FOR control, adding a suffix after each field name. It's a typical server side task.

Paulo Buchsbaum
  • 2,471
  • 26
  • 29
2

A bit late but still. This is possible in heredoc!

Have a look in the php manual, section "Complex (curly) syntax"

tftd
  • 16,203
  • 11
  • 62
  • 106
  • I'm already using that syntax in the first example; it has the disadvantage of having the put the function name into a variable before you can call it inside the curly braces in the heredoc section, which is what I was trying to avoid. – Doug Kavendek Apr 13 '11 at 16:09
1

Guys should note that it also works with double-quoted strings.

http://www.php.net/manual/en/language.types.string.php

Interesting tip anyway.

SomeOne
  • 11
  • 1
1
<div><?=<<<heredoc
Use heredoc and functions in ONE statement.
Show lower case ABC="
heredoc
. strtolower('ABC') . <<<heredoc
".  And that is it!
heredoc
?></div>
Ken
  • 2,849
  • 8
  • 24
  • 23
-1
<?php
echo <<<ETO
<h1>Hellow ETO</h1>
ETO;

you should try it . after end the ETO; command you should give an enter.

Rubel Hossain
  • 2,503
  • 2
  • 22
  • 23
  • Please add some explanation to your answer such that others can learn from it. The code you've shared does not call any function within the heredoc – Nico Haase Feb 08 '23 at 10:04