在 HEREDOC 字符串中调用 PHP 函数

在 PHP 中,HEREDOC 字符串声明对于输出 html 块非常有用。您可以通过在变量前面加上 $来解析它,但是对于更复杂的语法(比如 $var [2][3]) ,您必须将表达式放在{}括号中。

在 PHP5中,实际上可以在 HEREDOC 字符串中的{}括号内进行函数调用,但是您必须完成一些工作。函数名本身必须存储在一个变量中,而且必须像调用动态命名的函数那样调用它。例如:

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

正如你所看到的,这比仅仅:

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

除了第一个代码示例之外,还有其他方法,比如从 HEREDOC 中分离出来调用函数,或者反转问题并执行以下操作:

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

后者的缺点是输出直接放入输出流(除非我使用输出缓冲) ,这可能不是我想要的。

因此,我的问题的实质是: 有没有一种更优雅的方式来解决这个问题?

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.

87519 次浏览

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

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');

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.

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.

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.

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

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

Interesting tip anyway.

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.

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

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

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.

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;

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.

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

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)}";

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;
<?php
echo <<<ETO
<h1>Hellow ETO</h1>
ETO;

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

<div><?=<<<heredoc
Use heredoc and functions in ONE statement.
Show lower case ABC="
heredoc
. strtolower('ABC') . <<<heredoc
".  And that is it!
heredoc
?></div>

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;