在HEREDOC字符串中调用PHP函数


91

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

在PHP 5中,这可能实际上使{}一个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(); ?>

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

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

根据响应进行编辑:当然,某种模板引擎会让我的生活更轻松,但是这基本上需要我反转惯用的PHP样式。并不是说那是一件坏事,但这可以解释我的惯性。尽管我想尽办法使生活变得更轻松,所以我现在正在研究模板。


3
从严格意义上讲,这并不是您的问题的答案,但是鉴于Heredoc语句中对函数调用的支持不佳,因此我通常只生成在打印Heredoc之前需要的字符串。然后,我可以简单地Text {$string1} Text {$string2} Text在Heredoc中使用类似的内容。
rinogo'3

Answers:


51

我个人不会使用HEREDOC。它只是不能构成一个好的“模板构建”系统。您所有的HTML都被锁定在一个字符串中,这有几个缺点

  • 所见即所得没有选择
  • IDE中的HTML没有代码完成
  • 输出(HTML)锁定到逻辑文件
  • 您最终不得不像现在尝试那样使用hack来实现更复杂的模板,例如循环

获得基本的模板引擎,或者仅将PHP与include一起使用-这就是该语言具有<?php?>分隔符的原因。

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

8
回声有一个简写:<?=$valueToEcho;?><%=$valueToEcho;%>取决于您的INI设置。
彼得·贝利

3
我所读过的关于使用这些速记的大多数内容都说,使用它们是一种不良做法,我同意。因此,不幸的是,如果您要编写用于分发的代码,则不能依赖于这些INI设置,从而使PHP对它们的“支持”无法解决分布式代码的问题。FWIW,我不得不多次修复其他人的WordPress插件中的错误,因为他们使用了这些简写形式。
MikeSchinkel 2012年

1
不,我并不是说我必须输入7个字符,这是很遗憾的。您错误地归因于我的问题。我所关心的不是打字,而是阅读。这些字符会产生大量视觉干扰,从而使扫描源代码和理解代码的作用变得更加困难。至少对我来说实在是MUCH更容易阅读HEREDOC。(顺便说一句,在给定的HTML片段中使用了7次字符时间。但是我离题了。)
MikeSchinkel 2012年

12
简短更好,更干净,更易于阅读。在你的意见<?=$title?>漂亮比<PHP的echo $标题?; ?>。不利的一面是,对于发行而言,很多ini都缺少短标签。但是,你猜怎么着?从PHP 5.4开始,无论ini设置如何,短标记都在PHP中启用!因此,如果您的编码要求为5.4+(例如,您正在使用traits),请继续使用那些很棒的短标记!!
Jimbo,2013年

2
顺便说一下,即使关闭了短标签,默认情况下在5.4中也启用了<?= $ blah?>。
callmetwan 2014年

72

如果您确实想这样做,但是比使用类简单一些,则可以使用:

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

$my_string = <<<EOT
Number of seconds since the Unix Epoch: {$fn(time())}
EOT;

太好了@CJDennis!这是在HEREDOC中使用函数调用的最佳,最简洁的解决方案。在某些情况下有很好的资源。在我的站点中,我使用HEREDOC生成具有22行字段集(FOR循环内的HEREDOC块)的表单,并带有函数调用以生成tabindex位置。
Paulo Buchsbaum 2013年

您甚至可以这样做:$my_string = "Half the number of seconds since the Unix Epoch: {$fn(time() / 2 . ' Yes! Really!')}";
CJ Dennis

2
一个更紧凑的定义: $fn = function fn($data) { return $data; };
devsmt

1
@devsmt你是对的。甚至更短的是:$fn = function ($data) { return $data; };
CJ Dennis

哦,哥德福?好吧,让我进去:$fn=function($data){return $data;};rhis应该是最短的
My1

42

我将执行以下操作:

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

不知道您是否会认为这更优雅...


17

尝试以下操作(作为全局变量,或在需要时实例化):

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

  $fn = new Fn();
?>

现在,任何函数调用都将通过$fn实例。因此,现有功能testfunction()可以在Heredoc中使用{$fn->testfunction()}

基本上,我们将所有函数包装到一个类实例中,并使用PHP的__call magic方法将类方法映射到需要调用的实际函数。


2
当您不能仅仅向现有项目中添加模板引擎时,这是一个很好的解决方案。谢谢,我现在正在使用它。
布兰登

当性能至关重要时,不应被广泛使用:我已经读过好几次了call_user_func_array,对于,性能是差的,最后一次是在php.net的注释中:php.net/manual/en/function.call-user-func-array.php #97473
马库斯

真好!我喜欢它,为什么我没想到呢?!:-)
MikeSchinkel 2012年

10

为了完整起见,您还可以使用!${''} black magic parser hack

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

7
你去过霍格沃茨吗?
Starx

这是因为false == ''。定义一个名称长度为0('')的变量。将其设置为所需的值(${''} = date('Y-m-d H:i:s', strtotime('-1 month')))。取反(!)并将其转换为变量(${false})。false需要转换为字符串,然后(string)false === ''。如果您尝试打印一个伪造的值,它将出现错误。下面的字符串适用于既truthy和falsy值,在被更不可读的费用:"${(${''}=date('Y-m-d H:i:s', strtotime('-1 month')))!=${''}}"
CJ丹尼斯

并且如果您也想打印NAN,请使用"${(${''} = date('Y-m-d H:i:s', strtotime('-1 month')) )==NAN}"
CJ丹尼斯

9

我来晚了,但我随机遇到了它。对于以后的读者,我可能会这样做:

我只会使用输出缓冲区。因此,基本上,您可以使用ob_start()开始缓冲,然后在其中包含任何函数,变量等的“模板文件”,获取缓冲区的内容并将其写入字符串,然后关闭缓冲区。然后,您使用了所需的任何变量,可以运行任何函数,并且您的IDE中仍然可以使用HTML语法突出显示。

这就是我的意思:

模板文件:

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

脚本:

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

因此,脚本将template_file.php包含在其缓冲区中,并在运行过程中运行任何函数/方法并分配任何变量。然后,您只需将缓冲区的内容记录到一个变量中,然后对它进行所需的操作即可。

这样,如果您不想在当下立即将其回显到页面上,则不必这样做。您可以循环并继续添加到字符串,然后再输出它。

如果您不想使用模板引擎,那是最好的方法。


6

此代码片段将使用用户范围内定义的函数的名称定义变量,并将其绑定到包含相同名称的字符串。让我示范一下。

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;

现在可以使用。


@MichaelMcMillian最好不要将任何变量命名为与任何函数相同的名称,对吗?
s3c

6

在这里找到了带有包装功能的不错的解决方案: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)}";

2
在此之前的2.5年,我提出了完全相同的解决方案。stackoverflow.com/a/10713298/1166898
CJ丹尼斯

5

我认为使用Heredoc非常适合生成HTML代码。例如,我发现以下几乎完全不可读的内容。

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

但是,为了实现简单性,您必须在开始之前评估功能。我不认为这是一个可怕的约束,因为这样做会使您最终将计算与显示分开,这通常是个好主意。

我认为以下内容可读性强:

$page_content = getPageContent();

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

不幸的是,尽管这是您在问题中提出的将函数绑定到变量的好建议,但最终,它给代码增加了一定程度的复杂性,这是不值得的,并且使代码的可读性较差,即Heredoc的主要优势。


2
最近的4年证明,它比其他大多数方法都聪明得多。对于认真研究模板的人,在模板中使用合成(构建由较小页面组成的大页面)并使所有控制逻辑保持分离是标准方法:facebook的ReactJS在这方面(与XHP一样)非常出色,XSLT也是如此(我不爱,但是学术上不错。我要写的唯一的风格注释:我总是在变量周围使用{},主要是为了保持可读性并避免以后发生意外。另外,不要忘记对来自用户的数据进行htmlspecialchars()。
来自Qaribou的Josh,2015年

4

我将Smarty用作模板引擎-我本人还没有尝试过其他引擎,但是它做得很好。

如果您想坚持使用当前的方法而没有模板,那么输出缓冲有什么不好呢?它比必须声明变量(要声明的函数的字符串名称)要灵活得多。


3

您忘记了lambda函数:

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

您也可以使用函数create_function



2

这是一个使用@CJDennis建议的好例子:

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

例如,HEREDOC语法的一个很好的用途是在数据库中生成具有主从关系的大型表格。可以在FOR控件中使用HEREDOC功能,在每个字段名称后添加后缀。这是典型的服务器端任务。



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

0
<?php
echo <<<ETO
<h1>Hellow ETO</h1>
ETO;

你应该试试看 。ETO结束后;命令,您应该输入一个。


0

今天在PHP 7.x上更加优雅

<?php

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


echo <<<HEREDOC
this is a test: {$test()}
HEREDOC;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.