Wywoływanie funkcji PHP w łańcuchach HEREDOC

91

W PHP deklaracje ciągów HEREDOC są bardzo przydatne do wyświetlania bloku html. Możesz go przeanalizować w zmiennych, po prostu poprzedzając je przedrostkiem $, ale dla bardziej skomplikowanej składni (np. $ Var [2] [3]), musisz umieścić swoje wyrażenie w nawiasach {}.

W PHP 5, to jest możliwe, aby właściwie wykonywać połączenia funkcjonować w {} szelki wewnątrz łańcucha heredoc, ale trzeba przejść przez trochę pracy. Sama nazwa funkcji musi być przechowywana w zmiennej i należy ją nazywać tak, jakby była funkcją o nazwie dynamicznej. Na przykład:

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

Jak widać, jest to trochę bardziej bałaganiarskie niż tylko:

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

Oprócz pierwszego przykładu kodu istnieją inne sposoby, takie jak wyjście z HEREDOC w celu wywołania funkcji lub odwrócenie problemu i wykonanie czegoś takiego:

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

Ta ostatnia ma tę wadę, że dane wyjściowe są bezpośrednio umieszczane w strumieniu wyjściowym (chyba że używam buforowania wyjściowego), co może nie być tym, czego chcę.

A więc sedno mojego pytania brzmi: czy istnieje bardziej elegancki sposób podejścia do tego?

Edycja na podstawie odpowiedzi: Z pewnością wydaje się, że jakiś rodzaj silnika szablonów znacznie ułatwiłby mi życie, ale wymagałby ode mnie zasadniczo odwrócenia mojego zwykłego stylu PHP. Nie żeby to było złe, ale wyjaśnia moją bezwładność. Jestem gotów na wymyślanie sposobów na ułatwienie życia, więc teraz zajmuję się szablonami.

Doug Kavendek
źródło
3
Nie jest to ściśle odpowiedź na twoje pytanie, ale biorąc pod uwagę słabe wsparcie dla wywołań funkcji w instrukcjach heredoc, zwykle po prostu generuję ciągi znaków, których będę potrzebować przed wydrukowaniem heredoc. Wtedy mogę po prostu użyć czegoś takiego jak Text {$string1} Text {$string2} Textw heredoc.
rinogo

Odpowiedzi:

51

Osobiście nie użyłbym do tego w ogóle HEREDOC-a. To po prostu nie jest dobrym systemem do „budowania szablonów”. Cały twój HTML jest zamknięty w ciągu, który ma kilka wad

  • Brak opcji dla WYSIWYG
  • Brak uzupełniania kodu dla HTML z IDE
  • Wyjście (HTML) zablokowane w plikach logicznych
  • W końcu musisz używać hacków, takich jak to, co próbujesz teraz zrobić, aby osiągnąć bardziej złożone szablony, takie jak zapętlanie

Zdobądź podstawowy silnik szablonów lub po prostu użyj PHP z dołączeniami - dlatego język ma ograniczniki <?phpi ?>.

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');
Peter Bailey
źródło
8
Istnieje skrót dla echa: <?=$valueToEcho;?>lub <%=$valueToEcho;%>zależny od ustawień INI.
Peter Bailey,
3
Prawie wszystko, co przeczytałem o używaniu tych skrótów, mówi, że używanie ich jest złą praktyką i zgadzam się. Więc niestety, jeśli piszesz kod do dystrybucji, nie możesz polegać na tych ustawieniach INI, co powoduje, że "wsparcie" PHP dla nich nie działa dla kodu rozproszonego. FWIW, musiałem naprawiać błędy we wtyczkach WordPress innych ludzi więcej niż raz, ponieważ używali tych skrótów.
MikeSchinkel,
1
Nie, nie mówię, że szkoda, że ​​muszę wpisać 7 znaków; nieprawidłowo przypisujesz moje problemy. Nie chodzi o pisanie , ale o czytanie . Te znaki tworzą dużo wizualnego szumu, który znacznie utrudnia skanowanie kodu źródłowego i zrozumienie, co robi kod. Dla mnie jest DUŻO łatwiejsze do odczytania HEREDOC. (A tak przy okazji, to czas 7 znaków, ile razy jest używany w danym fragmencie HTML. Ale
dygresję
12
Krótki jest ładniejszy, bardziej przejrzysty i czytelniejszy. W twoich poglądach <?=$title?>jest o wiele ładniejszy niż <? Php echo $ title; ?>. Wadą jest to, że w przypadku dystrybucji wiele ini ma krótkie znaczniki. Ale zgadnij co? Od wersji PHP 5.4 krótkie tagi są włączone w PHP niezależnie od ustawień ini! Więc jeśli kodujesz z wymaganiem 5.4+ (powiedzmy, że używasz na przykład cech), śmiało i używaj tych niesamowitych krótkich tagów !!
Jimbo,
2
Nawiasem mówiąc, <? = $ Blah?> Jest domyślnie włączone w wersji 5.4, nawet jeśli krótkie tagi są wyłączone.
callmetwan
72

Jeśli naprawdę chcesz to zrobić, ale jest to nieco prostsze niż użycie klasy, możesz użyć:

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

$my_string = <<<EOT
Number of seconds since the Unix Epoch: {$fn(time())}
EOT;
CJ Dennis
źródło
Świetnie @CJDennis! To najlepsze i najczystsze rozwiązanie do korzystania z funkcji wywoływanych w HEREDOC. W niektórych sytuacjach jest niezły zasób. W mojej witrynie używam HEREDOC do generowania formularzy z 22 wierszami zestawów pól (blok HEREDOC w pętli FOR), z wywołaniem funkcji w celu wygenerowania pozycji tabindeksu.
Paulo Buchsbaum
Możesz nawet to zrobić:$my_string = "Half the number of seconds since the Unix Epoch: {$fn(time() / 2 . ' Yes! Really!')}";
CJ Dennis
2
bardziej zwarta definicja: $fn = function fn($data) { return $data; };
devsmt
1
@devsmt Masz rację. A jeszcze krótszy jest:$fn = function ($data) { return $data; };
CJ Dennis
och, godegolf? okej, wpuść mnie:$fn=function($data){return $data;}; rhis powinno być najkrótsze
My1
42

Zrobiłbym co następuje:

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

Nie jestem pewien, czy uważasz to za bardziej eleganckie ...

boxxar
źródło
17

Spróbuj tego (jako zmienna globalna lub utworzona, gdy jej potrzebujesz):

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

  $fn = new Fn();
?>

Teraz każde wywołanie funkcji przechodzi przez $fninstancję. Tak więc istniejącą funkcję testfunction()można wywołać w heredoc z{$fn->testfunction()}

Zasadniczo pakujemy wszystkie funkcje w instancję klasy i używamy __call magicmetody PHP do mapowania metody klasy na rzeczywistą funkcję, która ma zostać wywołana.

Isofarro
źródło
2
To dobre rozwiązanie w sytuacjach, gdy nie można po prostu dodać silnika szablonów do istniejącego projektu. Dziękuję, teraz go używam.
Brandon
nie powinno być szeroko stosowane, gdy wydajność jest krytyczna: kilka razy czytałem, że wydajność jest gorsza call_user_func_array, ostatnio w komentarzach na php.net
Markus
Ładny! Uwielbiam to, dlaczego o tym nie pomyślałem?!? :-)
MikeSchinkel
11

Aby uzyskać kompletność, możesz również użyć !${''} czarnej magii parsera :

echo <<<EOT
One month ago was ${!${''} = date('Y-m-d H:i:s', strtotime('-1 month'))}.
EOT;
biskup
źródło
8
Czy poszedłeś do Hogwartu?
Starx
To działa, ponieważ false == ''. Zdefiniuj zmienną o nazwie o długości 0 ( ''). Ustaw żądaną wartość ( ${''} = date('Y-m-d H:i:s', strtotime('-1 month'))). Neguj to ( !) i zamień na zmienną ( ${false}). falsemusi zostać przekonwertowany na ciąg, a (string)false === ''. Jeśli spróbujesz wydrukować fałszywą wartość, zamiast tego wystąpi błąd. Następujący ciąg działa zarówno truthy i wartości falsy, kosztem bycia jeszcze bardziej nieczytelne: "${(${''}=date('Y-m-d H:i:s', strtotime('-1 month')))!=${''}}".
CJ Dennis,
A jeśli chcesz również drukować NAN, użyj "${(${''} = date('Y-m-d H:i:s', strtotime('-1 month')) )==NAN}".
CJ Dennis,
9

Trochę się spóźniłem, ale przypadkowo trafiłem na to. Oto, co prawdopodobnie bym zrobił dla przyszłych czytelników:

Po prostu użyłbym bufora wyjściowego. Więc w zasadzie zaczynasz buforowanie za pomocą ob_start (), następnie umieszczasz w nim swój "plik szablonu" z dowolnymi funkcjami, zmiennymi itp., Uzyskujesz zawartość bufora i zapisujesz ją w łańcuchu, a następnie zamykasz bufor. Następnie użyłeś dowolnych potrzebnych zmiennych, możesz uruchomić dowolną funkcję i nadal masz podświetlanie składni HTML dostępne w swoim IDE.

Oto o co mi chodzi:

Plik szablonu:

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

Scenariusz:

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

Tak więc skrypt włącza plik template_file.php do swojego bufora, uruchamiając wszystkie funkcje / metody i przypisując po drodze wszelkie zmienne. Następnie po prostu rejestrujesz zawartość bufora w zmiennej i robisz z nią, co chcesz.

W ten sposób, jeśli nie chcesz odbijać tego na stronie w tej chwili, nie musisz tego robić. Możesz zapętlić i kontynuować dodawanie do ciągu przed jego wyprowadzeniem.

Myślę, że to najlepszy sposób, jeśli nie chcesz używać silnika szablonów.

BraedenP
źródło
6

Ten fragment kodu zdefiniuje zmienne z nazwami zdefiniowanych funkcji w ramach usercope i powiąże je z łańcuchem o takiej samej nazwie. Pokażę.

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;

Teraz zadziała.

Michael McMillan
źródło
@MichaelMcMillian lepiej nie mieć żadnych zmiennych nazwanych tak samo jak każda funkcja, prawda?
s3c
6

znalazłem fajne rozwiązanie z funkcją zawijania tutaj: 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)}";
p.voinov
źródło
2
Dokładnie to samo rozwiązanie zasugerowałem 2,5 roku wcześniej. stackoverflow.com/a/10713298/1166898
CJ Dennis
5

Myślę, że użycie heredoc jest świetne do generowania kodu HTML. Na przykład uważam, że następujące informacje są prawie całkowicie nieczytelne.

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

Jednak, aby osiągnąć prostotę, jesteś zmuszony ocenić funkcje przed rozpoczęciem. Nie wierzę, że jest to tak straszne ograniczenie, ponieważ robiąc to, w końcu oddzielasz swoje obliczenia od wyświetlania, co zwykle jest dobrym pomysłem.

Myślę, że to jest całkiem czytelne:

$page_content = getPageContent();

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

Niestety, mimo że była to dobra sugestia, którą poczyniłeś w swoim pytaniu, aby powiązać funkcję ze zmienną, w końcu dodaje to poziom złożoności do kodu, co nie jest warte, i sprawia, że ​​kod jest mniej czytelny, co jest główna zaleta heredoc.

MLU
źródło
2
Ostatnie 4 lata dowiodły, że jest to znacznie mądrzejsze niż większość innych podejść. Używanie kompozycji w szablonach (budowanie dużej strony składającej się z mniejszych stron) i oddzielanie całej logiki sterowania jest standardowym podejściem dla każdego, kto poważnie myśli o szablonach: ReactJS na Facebooku jest do tego doskonały (podobnie jak XHP), podobnie jak XSLT (który Nie kocham, ale jest akademicki). Jedyne uwagi stylistyczne, jakie poczyniłem: zawsze używam {} wokół moich vars, głównie dla zachowania spójności i późniejszego uniknięcia wypadków. Nie zapomnij również o htmlspecialchars () żadnych danych pochodzących od użytkowników.
Josh z Qaribou
4

Spojrzałbym na Smarty'ego jako silnik szablonu - Nie próbowałem pozostałe nie ja, ale to zrobiła mi dobrze.

Jeśli chcesz trzymać się obecnego podejścia bez szablonów, co jest takiego złego w buforowaniu danych wyjściowych? Zapewnia znacznie większą elastyczność niż konieczność deklarowania zmiennych, które są nazwami ciągów funkcji, które chcesz wywołać.

nickf
źródło
3

zapominasz o funkcji lambda:

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

Możesz również użyć funkcji create_function

Ismael Miguel
źródło
2

Trochę późno, ale nadal. W heredoc jest to możliwe!

Zajrzyj do podręcznika PHP, w sekcji „Złożona (kędzierzawa) składnia”

tftd
źródło
Używam już tej składni w pierwszym przykładzie; ma tę wadę, że umieszczenie nazwy funkcji w zmiennej, zanim będzie można ją wywołać w nawiasach klamrowych w sekcji heredoc, jest tym, czego starałem się uniknąć.
Doug Kavendek
2

Oto ładny przykład z wykorzystaniem propozycji @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>";

Na przykład dobrym zastosowaniem składni HEREDOC jest generowanie w bazie danych ogromnych formularzy z relacją wzorzec-szczegół. Można użyć funkcji HEREDOC wewnątrz kontrolki FOR, dodając sufiks po każdej nazwie pola. To typowe zadanie po stronie serwera.

Paulo Buchsbaum
źródło
1
<div><?=<<<heredoc
Use heredoc and functions in ONE statement.
Show lower case ABC="
heredoc
. strtolower('ABC') . <<<heredoc
".  And that is it!
heredoc
?></div>
Rozpoznać
źródło
1

To jest trochę bardziej eleganckie dzisiaj na php 7.x.

<?php

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


echo <<<HEREDOC
this is a test: {$test()}
HEREDOC;
codeasaurus
źródło
0
<?php
echo <<<ETO
<h1>Hellow ETO</h1>
ETO;

powinieneś spróbować . po zakończeniu ETO; polecenie należy wprowadzić.

Rubel Hossain
źródło