【问题标题】:Calling PHP functions within HEREDOC strings在 HEREDOC 字符串中调用 PHP 函数
【发布时间】:2019-04-07 03:25:03
【问题描述】:

在 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 风格。并不是说那是一件坏事,但它解释了我的惰性。不过,我正在想办法让生活更轻松,所以我现在正在研究模板。

【问题讨论】:

  • 严格来说,这不是您问题的答案,但鉴于在 heredoc 语句中对函数调用的支持不佳,我通常只在打印 heredoc 之前生成我需要的字符串。然后,我可以在 heredoc 中简单地使用 Text {$string1} Text {$string2} Text 之类的东西。

标签: php string heredoc


【解决方案1】:

我会看看 Smarty 作为模板引擎 - 我自己没有尝试过任何其他的,但它做得很好。

如果您想坚持当前的方法 sans 模板,那么输出缓冲有什么不好的呢?与必须声明要调用的函数的字符串名称的变量相比,它会给您更大的灵活性。

【讨论】:

    【解决方案2】:

    这在今天的 php 7.x 上稍微优雅了一点

    <?php
    
    $test = function(){
        return 'it works!';
    };
    
    
    echo <<<HEREDOC
    this is a test: {$test()}
    HEREDOC;
    

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

      • 你去霍格沃茨了吗?
      • 之所以有效,是因为false == ''。定义一个名称长度为 0 ('') 的变量。将其设置为您想要的值 (${''} = date('Y-m-d H:i:s', strtotime('-1 month')))。否定它(!)并将其转换为变量(${false})。 false 需要转换为字符串,(string)false === ''。如果您尝试打印虚假值,则会出错。以下字符串适用于真值和假值,但代价是更加不可读:"${(${''}=date('Y-m-d H:i:s', strtotime('-1 month')))!=${''}}".
      • 如果您还想打印NAN,请使用"${(${''} = date('Y-m-d H:i:s', strtotime('-1 month')) )==NAN}"
      【解决方案4】:
      <div><?=<<<heredoc
      Use heredoc and functions in ONE statement.
      Show lower case ABC="
      heredoc
      . strtolower('ABC') . <<<heredoc
      ".  And that is it!
      heredoc
      ?></div>
      

      【讨论】:

        【解决方案5】:
        <?php
        echo <<<ETO
        <h1>Hellow ETO</h1>
        ETO;
        

        你应该试试。 ETO结束后;命令你应该回车。

        【讨论】:

          【解决方案6】:

          就个人而言,我根本不会为此使用 HEREDOC。它只是不构成一个好的“模板构建”系统。你所有的 HTML 都被锁定在一个有几个缺点的字符串中

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

          获取一个基本的模板引擎,或者只使用带有包含的 PHP - 这就是该语言具有 &lt;?php?&gt; 分隔符的原因。

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

          【讨论】:

          • echo 有一个简写形式:&lt;?=$valueToEcho;?&gt;&lt;%=$valueToEcho;%&gt;,具体取决于您的 INI 设置。
          • 我读到的关于使用这些速记的大部分内容都说使用它们是一种不好的做法,我同意。所以不幸的是,如果您正在编写用于分发的代码,则不能依赖这些 INI 设置,从而使 PHP 对它们的“支持”对分发代码没有实际意义。 FWIW,我不得不不止一次修复其他人的 WordPress 插件中的错误,因为他们使用了这些速记。
          • 不,我并不是说我必须输入 7 个字符是一种耻辱;你错误地归因于我的问题。我关心的不是打字,而是阅读。这些字符会产生大量视觉噪音,使​​得扫描源代码和理解代码在做什么变得更加困难。对我来说,至少是 MUCH 更容易阅读 HEREDOC。 (顺便说一句,在给定的 HTML 片段中使用多少次是 7 个字符时间。但我离题了。)
          • Short 更好、更简洁、更易于阅读。在您看来,&lt;?=$title?&gt;很多 ?>。缺点是,是的,很多 ini 的分发都有短标签。 但是,你猜怎么着?从 PHP 5.4 开始,无论 ini 设置如何,在 PHP 中都启用了短标签!因此,如果您的编码要求为 5.4+(例如,假设您正在使用特征),请继续使用那些很棒的短标签!
          • 顺便说一句,= $blah ?> 在 5.4 中默认启用,即使短标签被关闭。
          【解决方案7】:

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

          【讨论】:

          【解决方案8】:

          你忘记了 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

          【讨论】:

            【解决方案9】:

            试试这个(作为全局变量,或者在需要时实例化):

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

            现在任何函数调用都通过$fn 实例。所以现有的函数testfunction()可以用{$fn-&gt;testfunction()}在heredoc中调用

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

            【讨论】:

            • 当您不能只将模板引擎添加到现有项目时,这是一个很好的解决方案。谢谢,我正在使用。
            • 当性能很关键时不应该广泛使用:我已经读过几次call_user_func_array 的性能更差,上次在 php.net 的 cmets 中:php.net/manual/en/function.call-user-func-array.php#97473
            • 不错!我喜欢它,为什么我没有想到这个?!? :-)
            【解决方案10】:

            这是一个使用@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 功能,在每个字段名称后添加一个后缀。这是一个典型的服务器端任务。

            【讨论】:

              【解决方案11】:

              如果你真的想这样做,但比使用类更简单,你可以使用:

              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 位置。
              • 你甚至可以这样做:$my_string = "Half the number of seconds since the Unix Epoch: {$fn(time() / 2 . ' Yes! Really!')}";
              • 更简洁的定义:$fn = function fn($data) { return $data; };
              • @devsmt 你是对的。更短的是:$fn = function ($data) { return $data; };
              • 哦,godegolf?好的,让我进来:$fn=function($data){return $data;}; rhis 应该是最短的
              【解决方案12】:

              我有点晚了,但我偶然发现了它。对于任何未来的读者,我可能会这样做:

              我只会使用输出缓冲区。所以基本上,你使用 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 包含到其缓冲区中,运行任何函数/方法并沿途分配任何变量。然后你只需将缓冲区的内容记录到一个变量中,然后用它做你想做的事。

              这样,如果您不想在那一秒将其回显到页面上,则不必这样做。您可以在输出之前循环并继续添加到字符串。

              如果您不想使用模板引擎,我认为这是最好的方法。

              【讨论】:

                【解决方案13】:

                我认为使用 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的主要优势。

                【讨论】:

                • 过去 4 年证明这比大多数其他方法要聪明得多。在您的模板中使用组合(构建一个由较小页面组成的大页面)并保持所有控制逻辑分开是任何认真对待模板的人的标准方法:facebook 的 ReactJS 非常适合这一点(就像 XHP 一样),XSLT 也是如此(它我不喜欢,但在学术上是合理的)。我要做的唯一风格注释:我总是在我的变量周围使用 {},主要是为了保持可读性的一致性并避免以后发生意外。另外,不要忘记 htmlspecialchars() 来自用户的任何数据。
                【解决方案14】:

                有点晚了,但还是。 这在heredoc中是可能的!

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

                【讨论】:

                • 我已经在第一个示例中使用了该语法;它的缺点是先将函数名放入变量中,然后才能在 heredoc 部分的花括号内调用它,这是我试图避免的。
                【解决方案15】:

                这个 sn-p 将在用户范围内使用您定义的函数的名称定义变量,并将它们绑定到包含相同名称的字符串。让我演示一下。

                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 最好不要将任何变量命名为与任何函数相同,对吧?
                【解决方案16】:

                请注意,它也适用于双引号字符串。

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

                还是很有趣的提示。

                【讨论】:

                  【解决方案17】:

                  我会做以下事情:

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

                  不确定你是否认为这更优雅......

                  【讨论】:

                    猜你喜欢
                    • 2012-02-07
                    • 2019-11-21
                    • 2014-10-04
                    • 2011-11-26
                    • 2021-11-25
                    • 2020-02-24
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多