Виклик функцій 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} Textгередоку.
rinogo 06.03.18

Відповіді:


51

Я б взагалі не використовував HEREDOC для цього, особисто. Це просто не робить хорошої системи "побудови шаблонів". Весь ваш HTML заблокований у рядку, що має кілька недоліків

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

Отримайте основний механізм шаблонів або просто використовуйте PHP з включеннями - ось чому мова має <?phpі ?>роздільники.

шаблон_файлу.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

1
Ні, я не кажу, що це ганьба, що мені доводиться вводити 7 символів; Ви неправильно приписуєте мої проблеми. Мене стосується не набір тексту , це читання . Ці символи створюють безліч візуальних шумів, що значно ускладнює сканування вихідного коду та розуміння того, що робить код. Для мене принаймні набагато простіше читати HEREDOC. (І до речі, це 7 символів, скільки разів він використовується у даному фрагменті HTML. Але я відступаю.)
MikeSchinkel

12
Короткий приємніший, чистіший і легший для читання. У ваших поглядах <?=$title?>є набагато краще , ніж <PHP відлуння $ заголовка; ?>. Недоліком є, так, для розповсюдження у багатьох ini є вимкнені короткі теги. Але , вгадайте, що ?? Починаючи з PHP 5.4 , короткі теги включаються в PHP незалежно від налаштувань ini! Отже, якщо ви кодуєте з вимогою 5.4+ (скажімо, ви використовуєте риси, наприклад), продовжуйте і використовуйте ці приголомшливі короткі теги !!
Джимбо

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;

Чудово @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());

Не впевнений, чи вважаєте ви це більш елегантним ...


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

10

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

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 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 у свій буфер, запускаючи будь-які функції / методи та призначаючи будь-які змінні на цьому шляху. Потім ви просто записуєте вміст буфера у змінну і робите з нею те, що хочете.

Таким чином, якщо ви не хочете повторити це на сторінці прямо в ту секунду, вам не потрібно. Ви можете циклічно продовжувати додавати до рядка, перш ніж виводити його.

Я думаю, що це найкращий шлях, якщо ви не хочете використовувати шаблонний механізм.


6

Цей фрагмент визначатиме змінні з іменем визначених вами функцій в usercope та прив’язуватиме їх до рядка, що містить те саме ім’я. Дозвольте мені продемонструвати.

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;

На жаль, навіть незважаючи на хорошу пропозицію, яку ви зробили у своєму питанні, пов’язати функцію зі змінною, врешті-решт вона додає коду рівень складності, що не варто, і робить код менш читабельним, тобто головна перевага гередоку.


2
Останні 4 роки довели це набагато розумніше, ніж більшість інших підходів. Використання композиції у ваших шаблонах (створення великої сторінки, складеної з менших сторінок) та збереження всієї логіки управління окремо - це стандартний підхід для тих, хто серйозно ставиться до шаблонування: ReactJS з facebook чудово для цього (як і XHP), як і XSLT (який Я не люблю, але академічно обгрунтований). Єдині стилістичні нотатки, які я б зробив: я завжди використовую {} навколо своїх варіантів, головним чином для постійності читабельності та для уникнення випадків пізніше. Крім того, не забувайте htmlspecialchars () будь-які дані, що надходять від користувачів.
Джош із Карібу

4

Я б подивився на Smarty як на шаблонний механізм - я ще нічого не пробував, але це зробило мене добре.

Якщо ви хотіли дотримуватися вашого поточного шаблону без шаблонів, що поганого в буферизації виводу? Це дасть вам набагато більшу гнучкість, ніж необхідність оголошувати змінні, які є іменами рядків функцій, які ви хочете викликати.


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, розділ "Складний (фігурний) синтаксис"


Я вже використовую цей синтаксис у першому прикладі; він має недолік, коли ім’я функції поміщається у змінну, перш ніж ви зможете викликати її всередині фігурних дужок у розділі 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>

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

ви повинні спробувати. після закінчення ETO; команда, яку ви повинні дати enter.


0

Сьогодні це трохи елегантніше на php 7.x

<?php

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


echo <<<HEREDOC
this is a test: {$test()}
HEREDOC;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.