Вызов функций PHP в строках HEREDOC

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} Textheredoc.
rinogo 06

Ответы:

51

Лично я бы вообще не использовал HEREDOC для этого. Это просто не подходит для хорошей системы «построения шаблонов». Весь ваш HTML заключен в строку, которая имеет несколько недостатков.

  • Нет возможности для WYSIWYG
  • Нет автозавершения кода для HTML из IDE
  • Вывод (HTML) заблокирован для файлов логики
  • В конечном итоге вам придется использовать хаки, подобные тому, что вы пытаетесь сделать сейчас, для достижения более сложных шаблонов, таких как цикл

Получите базовый двигатель шаблона, или просто использовать PHP с включает в себя - именно поэтому язык имеет <?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
Существует сокращение от echo: <?=$valueToEcho;?>или <%=$valueToEcho;%>зависит от ваших настроек INI.
Питер Бейли
3
Почти все, что я читал об использовании этих сокращений, говорит, что их использование - плохая практика, и я согласен. Так , к сожалению , если вы пишете код для распространения не может зависеть от этих параметров INI таким образом делая «поддержку» РНР для них тоо для распределенного кода. FWIW, мне приходилось исправлять ошибки в плагинах WordPress других людей более одного раза, потому что они использовали эти сокращения.
MikeSchinkel 05
1
Нет, я не говорю, что жаль, что мне приходится набирать 7 символов; вы неправильно приписываете мои проблемы. Меня волнует не набор текста , а чтение . Эти символы создают много визуального шума, что значительно затрудняет сканирование исходного кода и понимание того, что он делает. Для меня, по крайней мере, НАМНОГО легче читать HEREDOC. (И, кстати, это 7 символов, сколько бы раз оно не использовалось в данном фрагменте HTML. Но я отвлекся.)
MikeSchinkel
12
Короткие лучше, понятнее и легче читаются. На ваш взгляд, <?=$title?>это намного лучше, чем <? Php echo $ title; ?>. Обратной стороной является то, что для распространения многие ini имеют короткие теги. Но знаете что ?? Начиная с PHP 5.4 , короткие теги включены в PHP независимо от настроек ini! Так что, если вы кодируете с требованием 5.4+ (допустим, вы используете черты характера, например), продолжайте и используйте эти замечательные короткие теги !!
Jimbo
2
Кстати, <? = $ Blah?> По умолчанию включен в 5.4, даже если короткие теги отключены.
callmetwan
72

Если вы действительно хотите сделать это, но немного проще, чем использование класса, вы можете использовать:

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

$my_string = <<<EOT
Number of seconds since the Unix Epoch: {$fn(time())}
EOT;
CJ Деннис
источник
Отлично @CJDennis! Это лучшее и самое чистое решение для использования вызова функций внутри HEREDOC. В некоторых ситуациях есть хороший ресурс. На своем сайте я использую HEREDOC для создания форм с 22 строками наборов полей (блок HEREDOC внутри цикла FOR) с вызовом функции для генерации позиции tabindex.
Пауло Бухсбаум
Вы даже можете сделать это:$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;};
впусти
42

Я бы сделал следующее:

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

Не уверен, что вы считаете это более элегантным ...

Boxxar
источник
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()}

В основном мы оборачиваем все функции в экземпляр класса и используем __call magicметод PHP для сопоставления метода класса с фактической функцией, которую необходимо вызвать.

Изофарро
источник
2
Это хорошее решение для тех случаев, когда вы не можете просто добавить шаблонизатор в существующий проект. Спасибо, сейчас пользуюсь.
Брэндон
не следует широко использовать, когда производительность критична: я несколько раз читал, что производительность хуже call_user_func_array, последний раз в комментариях на php.net: php.net/manual/en/function.call-user-func-array.php # 97473
Маркус
Ницца! Мне это нравится, почему я не подумал об этом?!? :-)
MikeSchinkel
11

Для полноты картины можно также использовать взлом парсера !${''} черной магии :

echo <<<EOT
One month ago was ${!${''} = date('Y-m-d H:i:s', strtotime('-1 month'))}.
EOT;
епископ
источник
8
Вы ходили в Хогвартс?
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 Dennis
А если вы хотите еще и напечатать NAN, используйте "${(${''} = date('Y-m-d H:i:s', strtotime('-1 month')) )==NAN}".
CJ Dennis
9

Я немного опоздал, но случайно наткнулся на это. Для будущих читателей вот что я, вероятно, сделал бы:

Я бы просто использовал выходной буфер. Таким образом, вы начинаете буферизацию с помощью ob_start (), затем включаете в него свой «файл шаблона» с любыми функциями, переменными и т. Д., Получаете содержимое буфера и записываете их в строку, а затем закрываете буфер. Затем вы использовали любые необходимые переменные, вы можете запускать любую функцию, и у вас по-прежнему есть подсветка синтаксиса HTML, доступная в вашей IDE.

Вот что я имею в виду:

Файл шаблона:

<?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 в свой буфер, выполняя любые функции / методы и по ходу назначая любые переменные. Затем вы просто записываете содержимое буфера в переменную и делаете с ней все, что хотите.

Таким образом, если вы не хотите отображать его на странице прямо в эту секунду, вам не нужно. Вы можете зацикливаться и продолжать добавлять к строке перед ее выводом.

Я думаю, что это лучший вариант, если вы не хотите использовать движок шаблонов.

BraedenP
источник
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 Dennis
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.

MLU
источник
2
Последние 4 года показали, что это намного умнее, чем большинство других подходов. Использование композиции в ваших шаблонах (создание большой страницы, состоящей из меньших страниц) и разделение всей логики управления - стандартный подход для всех, кто серьезно относится к шаблонам: Facebook ReactJS отлично подходит для этого (как и XHP), как и XSLT (который Не люблю, но академически здоров). Единственные стилистические примечания, которые я сделаю: я всегда использую {} вокруг своих варов, в основном для единообразия в удобочитаемости и во избежание случайностей в дальнейшем. Также не забывайте использовать htmlspecialchars () любые данные, поступающие от пользователей.
Джош из Карибу,
4

Я бы посмотрел на Smarty как на шаблонизатор - я сам не пробовал никаких других, но он мне хорошо помог.

Если вы хотели придерживаться своего текущего подхода без шаблонов, что плохого в буферизации вывода? Это даст вам гораздо больше гибкости, чем необходимость объявления переменных, которые являются строковыми именами функций, которые вы хотите вызвать.

Nickf
источник
3

вы забываете о лямбда-функции:

$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

Немного поздно, но все же. Это возможно в heredoc!

Загляните в руководство по php, раздел «Сложный (фигурный) синтаксис»

tftd
источник
Я уже использую этот синтаксис в первом примере; у него есть недостаток, заключающийся в том, что имя функции помещается в переменную до того, как вы можете вызвать ее внутри фигурных скобок в разделе heredoc, чего я пытался избежать.
Дуг Кавендек
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 можно использовать для создания огромных форм с отношениями "главный-подробный" в базе данных. Можно использовать функцию HEREDOC внутри элемента управления FOR, добавляя суффикс после имени каждого поля. Это типичная задача на стороне сервера.

Пауло Бухсбаум
источник
1
<div><?=<<<heredoc
Use heredoc and functions in ONE statement.
Show lower case ABC="
heredoc
. strtolower('ABC') . <<<heredoc
".  And that is it!
heredoc
?></div>
Кен
источник
1

Сегодня это немного более элегантно на php 7.x

<?php

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


echo <<<HEREDOC
this is a test: {$test()}
HEREDOC;
кодазавр
источник
0
<?php
echo <<<ETO
<h1>Hellow ETO</h1>
ETO;

тебе стоит попробовать это . после окончания ВЭТО; команда вы должны дать ввод.

Рубель Хоссейн
источник