Calling PHP functions within HEREDOC strings
Asked Answered
T

18

106

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.

Teran answered 19/9, 2008 at 18:54 Comment(1)
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.Rockyrococo
A
57

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');
Athal answered 19/9, 2008 at 19:10 Comment(13)
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.)Peddling
There is a shorthand for echo: <?=$valueToEcho;?> or <%=$valueToEcho;%> dependent upon your INI settings.Athal
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.Peddling
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"Athal
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.)Peddling
Short is nicer, cleaner and easier to read. In your views <?=$title?> is much nicer than <?php echo $title; ?>. 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!!Manicure
What's so hard about embedding PHP inside HTML with proper <?php ?> 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!Snips
By the way, <?= $blah ?> is enabled in 5.4 by default, even if short tags are turned off.Gutbucket
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!Triviality
@IfediOkonkwo Actually, you can and there are several answers here that show how. It's just not considered good practice to do so.Decathlon
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.Clinical
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.Crumby
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.Primp
D
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;
Decathlon answered 23/5, 2012 at 4:6 Comment(12)
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.Cepheus
You can even do this: $my_string = "Half the number of seconds since the Unix Epoch: {$fn(time() / 2 . ' Yes! Really!')}";Decathlon
a more compact definition: $fn = function fn($data) { return $data; };Volution
@Volution You're right. And even shorter is: $fn = function ($data) { return $data; };Decathlon
oh, godegolf? okay, let me in: $fn=function($data){return $data;}; rhis should be the shortestNadianadine
@Nadianadine No, $f=function($d){return$d;}; is shorter, but this is not Code Golf.Decathlon
I didnt even know you could return without spaceNadianadine
@Nadianadine Golf? $fn='strval';Atabrine
@Olivier "{${'fn'}(time())}" and "{$fn(time())}" work exactly the same way. Both need a variable defined.Decathlon
@CJDennis After understanding your answer, I'm sharing my thoughts- It means If we want to call functions inside heredoc then we have to first initialize variables 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 ?Seline
@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()}.Decathlon
Since PHP 7.4, fn is a reserved word. php.net/manual/en/migration74.incompatible.phpDecathlon
A
57

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');
Athal answered 19/9, 2008 at 19:10 Comment(13)
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.)Peddling
There is a shorthand for echo: <?=$valueToEcho;?> or <%=$valueToEcho;%> dependent upon your INI settings.Athal
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.Peddling
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"Athal
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.)Peddling
Short is nicer, cleaner and easier to read. In your views <?=$title?> is much nicer than <?php echo $title; ?>. 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!!Manicure
What's so hard about embedding PHP inside HTML with proper <?php ?> 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!Snips
By the way, <?= $blah ?> is enabled in 5.4 by default, even if short tags are turned off.Gutbucket
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!Triviality
@IfediOkonkwo Actually, you can and there are several answers here that show how. It's just not considered good practice to do so.Decathlon
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.Clinical
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.Crumby
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.Primp
H
48

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 ...

Haemoglobin answered 19/9, 2008 at 18:59 Comment(0)
E
32

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!

Extricate answered 24/3, 2016 at 14:31 Comment(6)
Did you went to Hogwarts?Couteau
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')))!=${''}}".Decathlon
And if you want to print NAN as well, use "${(${''} = date('Y-m-d H:i:s', strtotime('-1 month')) )==NAN}".Decathlon
This should be the true answer. It works finely.Decommission
this continues to run HEREDOC after the EOT; meanining php after is not parased –Britanybritches
@Britanybritches Please provide link to 3v4l.org or similar demonstrating that.Extricate
E
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.

Eckard answered 22/12, 2009 at 18:11 Comment(3)
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.Furtive
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: php.net/manual/en/function.call-user-func-array.php#97473Maynardmayne
Nice! I love it, why didn't I think of this?!? :-)Peddling
A
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.

Assuming answered 6/9, 2009 at 20:49 Comment(0)
F
9

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)}";
Fortepiano answered 20/11, 2014 at 20:53 Comment(1)
I suggested exactly the same solution 2.5 years before this. https://mcmap.net/q/203526/-calling-php-functions-within-heredoc-stringsDecathlon
A
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.

Andiron answered 8/12, 2010 at 13:2 Comment(1)
@MichaelMcMillian better not have any variables named the same as any function then, right?Sankhya
O
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.

Oxidase answered 17/6, 2011 at 10:54 Comment(1)
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.Clinical
L
5

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;
Laverty answered 20/11, 2019 at 23:9 Comment(0)
C
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

Crapshooter answered 23/9, 2013 at 9:2 Comment(0)
R
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.

Rik answered 19/9, 2008 at 23:42 Comment(1)
Please add some explanation to your answer such that others can learn from it. How does looking at Smarty resolve the initial question?Hardbitten
E
3

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

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

Egyptian answered 8/4, 2011 at 22:58 Comment(1)
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.Teran
C
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.

Cepheus answered 18/1, 2013 at 4:24 Comment(0)
F
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.

Flashlight answered 10/5, 2010 at 11:20 Comment(0)
T
1
<div><?=<<<heredoc
Use heredoc and functions in ONE statement.
Show lower case ABC="
heredoc
. strtolower('ABC') . <<<heredoc
".  And that is it!
heredoc
?></div>
Turgite answered 17/9, 2016 at 2:13 Comment(0)
J
0

After trying all methods that I found, here are my conclusions.

Here you have two methods:

function fna($data) {
                    return $data;
                }
                echo <<<EOT
                function within heredoc method 1 - 
                ${!${''} = fna("return of function 1")}<br>
                
                function within heredoc method 2 - 
                EOT.fna("return of function 2").<<<EOT
                <br>continue...
                EOT;

The first one is the same that you will create a variable out of heredoc and will call the function into that variable, after calling the variable within heredoc. That method is very inefficient because it opens space in the memory and you put more instructions to execute than with method 2, which is similar to concatenation with simple or doubles quotes.

The second one is the better to call functions in general, but if you create your own function and use it with frequency heredoc, I think it would be a good practice to assign it into a variable:

    $test = function($data){
    return $data;
};
echo <<<HEREDOC
this is a test: {$test("with HEREDOC")} <br>
HEREDOC;
Jenness answered 13/1, 2024 at 6:41 Comment(0)
A
-1
<?php
echo <<<ETO
<h1>Hellow ETO</h1>
ETO;

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

Aspirant answered 3/9, 2016 at 5:36 Comment(1)
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 heredocHardbitten

© 2022 - 2025 — McMap. All rights reserved.