Aufruf von PHP-Funktionen in HEREDOC-Strings

91

In PHP sind die HEREDOC-Zeichenfolgendeklarationen sehr nützlich für die Ausgabe eines HTML-Blocks. Sie können es in Variablen analysieren lassen, indem Sie ihnen $ voranstellen. Für eine kompliziertere Syntax (wie $ var [2] [3]) müssen Sie Ihren Ausdruck jedoch in {} Klammern setzen.

In PHP 5, es ist möglich , Funktionsaufrufe innerhalb von {} Klammern innerhalb einer HEREDOC Zeichenfolge, aber Sie müssen , um tatsächlich durch ein bisschen Arbeit gehen zu machen. Der Funktionsname selbst muss in einer Variablen gespeichert sein, und Sie müssen ihn so aufrufen, als wäre er eine dynamisch benannte Funktion. Beispielsweise:

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

Wie Sie sehen können, ist dies etwas chaotischer als nur:

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

Neben dem ersten Codebeispiel gibt es noch andere Möglichkeiten, z. B. das Ausbrechen des HEREDOC zum Aufrufen der Funktion oder das Umkehren des Problems und das Ausführen von Aktionen wie:

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

Letzteres hat den Nachteil, dass die Ausgabe direkt in den Ausgabestream gestellt wird (es sei denn, ich verwende die Ausgabepufferung), was möglicherweise nicht das ist, was ich möchte.

Das Wesentliche meiner Frage ist also: Gibt es einen eleganteren Weg, dies zu erreichen?

Bearbeiten basierend auf Antworten: Es scheint sicher, dass eine Art Template-Engine mein Leben viel einfacher machen würde, aber ich müsste im Grunde meinen üblichen PHP-Stil umkehren. Nicht, dass das eine schlechte Sache wäre, aber es erklärt meine Trägheit. Ich bin bereit, Wege zu finden, um das Leben einfacher zu machen, also schaue ich mir jetzt Vorlagen an.

Doug Kavendek
quelle
3
Dies ist keine reine Antwort auf Ihre Frage, aber angesichts der schlechten Unterstützung für Funktionsaufrufe in heredoc-Anweisungen generiere ich normalerweise nur die Zeichenfolgen, die ich vor dem Drucken des heredoc benötige. Dann kann ich einfach so etwas wie Text {$string1} Text {$string2} Textim Heredoc verwenden.
Rinogo

Antworten:

51

Ich persönlich würde HEREDOC dafür überhaupt nicht verwenden. Es ist einfach kein gutes "Template Building" -System. Ihr gesamtes HTML ist in einer Zeichenfolge gesperrt, die mehrere Nachteile hat

  • Keine Option für WYSIWYG
  • Keine Code-Vervollständigung für HTML von IDEs
  • Ausgabe (HTML) an Logikdateien gebunden
  • Am Ende müssen Sie Hacks wie das verwenden, was Sie jetzt versuchen, um komplexere Vorlagen wie Loops zu erzielen

Holen Sie sich eine einfache Template-Engine oder verwenden Sie einfach PHP mit Includes - deshalb hat die Sprache die Trennzeichen <?phpund ?>.

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
quelle
8
Es gibt eine Abkürzung für Echo: <?=$valueToEcho;?>oder <%=$valueToEcho;%>abhängig von Ihren INI-Einstellungen.
Peter Bailey
3
Fast alles, was ich über die Verwendung dieser Kurzschriften gelesen habe, besagt, dass ihre Verwendung eine schlechte Praxis ist, und ich stimme zu. Wenn Sie also Code für die Verteilung schreiben, können Sie sich leider nicht auf diese INI-Einstellungen verlassen, sodass die "Unterstützung" von PHP für verteilten Code in Frage kommt. FWIW, ich musste mehr als einmal Fehler in den WordPress-Plugins anderer Leute beheben, weil sie diese Abkürzungen verwendeten.
MikeSchinkel
1
Nein, ich sage nicht, dass es eine Schande ist, dass ich 7 Zeichen eingeben muss. Sie schreiben meine Probleme falsch zu. Es geht nicht um das Tippen , sondern um das Lesen . Diese Zeichen erzeugen viel visuelles Rauschen, was es viel schwieriger macht, den Quellcode zu scannen und zu verstehen, was der Code tut. Für mich zumindest ist VIEL einfacher , eine HEREDOC zu lesen. (Und übrigens, es ist 7 Zeichen Zeit, wie oft es in einem bestimmten HTML-Fragment verwendet wird. Aber ich schweife ab.)
MikeSchinkel
12
Kurz ist schöner, sauberer und leichter zu lesen. In Ihren Ansichten <?=$title?>ist viel schöner als <? Php echo $ title; ?>. Der Nachteil ist, ja, für die Verteilung haben viele Ini kurze Tags aus. Aber weißt du was? Ab PHP 5.4 sind kurze Tags in PHP unabhängig von den INI-Einstellungen aktiviert! Wenn Sie also mit einer Anforderung von 5.4+ codieren (sagen wir, Sie verwenden zum Beispiel Merkmale), verwenden Sie diese fantastischen kurzen Tags!
Jimbo
2
Übrigens ist <? = $ Blah?> In 5.4 standardmäßig aktiviert, auch wenn kurze Tags deaktiviert sind.
Callmetwan
72

Wenn Sie dies wirklich tun möchten, aber etwas einfacher als die Verwendung einer Klasse, können Sie Folgendes verwenden:

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

$my_string = <<<EOT
Number of seconds since the Unix Epoch: {$fn(time())}
EOT;
CJ Dennis
quelle
Tolles @CJDennis! Dies ist die beste und sauberste Lösung für die Verwendung von Funktionen, die in HEREDOC aufgerufen werden. In einigen Situationen gibt es eine nette Ressource. Auf meiner Site verwende ich HEREDOC zum Generieren von Formularen mit 22 Zeilen Feldsätzen (ein HEREDOC-Block innerhalb einer FOR-Schleife) mit Funktionsaufruf, um die Tabindex-Position zu generieren.
Paulo Buchsbaum
Sie können dies sogar tun:$my_string = "Half the number of seconds since the Unix Epoch: {$fn(time() / 2 . ' Yes! Really!')}";
CJ Dennis
2
eine kompaktere Definition: $fn = function fn($data) { return $data; };
devsmt
1
@devsmt Du hast recht. Und noch kürzer ist:$fn = function ($data) { return $data; };
CJ Dennis
Oh, Godegolf? Okay, lass mich $fn=function($data){return $data;};
rein
42

Ich würde folgendes tun:

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

Ich bin mir nicht sicher, ob Sie dies für eleganter halten würden ...

boxxar
quelle
17

Versuchen Sie dies (entweder als globale Variable oder instanziiert, wenn Sie es benötigen):

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

  $fn = new Fn();
?>

Jetzt geht jeder Funktionsaufruf durch die $fnInstanz. So kann die vorhandene Funktion testfunction()in einem Heredoc mit aufgerufen werden{$fn->testfunction()}

Grundsätzlich packen wir alle Funktionen in eine Klasseninstanz und verwenden die PHP- __call magicMethode, um die Klassenmethode der eigentlichen Funktion zuzuordnen, die aufgerufen werden muss.

Isofarro
quelle
2
Dies ist eine gute Lösung für Zeiten, in denen Sie einem vorhandenen Projekt nicht einfach eine Vorlagen-Engine hinzufügen können. Danke, ich benutze es jetzt.
Brandon
sollte nicht häufig verwendet werden, wenn die Leistung kritisch ist: Ich habe mehrmals gelesen, dass die Leistung beim call_user_func_arrayletzten Mal in den Kommentaren auf php.net schlechter ist: php.net/manual/en/function.call-user-func-array.php # 97473
Markus
Nett! Ich liebe es, warum habe ich nicht daran gedacht?!? :-)
MikeSchinkel
10

Der Vollständigkeit halber können Sie auch den Parser-Hack für !${''} schwarze Magie verwenden :

echo <<<EOT
One month ago was ${!${''} = date('Y-m-d H:i:s', strtotime('-1 month'))}.
EOT;
Bischof
quelle
7
Bist du nach Hogwarts gegangen?
Starx
Das funktioniert weil false == ''. Definieren Sie eine Variable mit einem Namen der Länge 0 ( ''). Stellen Sie den gewünschten Wert ein ( ${''} = date('Y-m-d H:i:s', strtotime('-1 month'))). Negiere es ( !) und konvertiere es in eine Variable ( ${false}). falsemuss in einen String konvertiert werden, und (string)false === ''. Wenn Sie versuchen, einen falschen Wert zu drucken, tritt stattdessen ein Fehler auf. Die folgende Zeichenfolge arbeitet sowohl mit wahrheitsgemäßen als auch mit fälschlichen Werten, auf Kosten der Unlesbarkeit : "${(${''}=date('Y-m-d H:i:s', strtotime('-1 month')))!=${''}}".
CJ Dennis
Und wenn Sie auch drucken möchten NAN, verwenden Sie "${(${''} = date('Y-m-d H:i:s', strtotime('-1 month')) )==NAN}".
CJ Dennis
9

Ich bin etwas spät dran, aber ich bin zufällig darauf gestoßen. Für zukünftige Leser würde ich wahrscheinlich Folgendes tun:

Ich würde nur einen Ausgabepuffer verwenden. Im Grunde genommen starten Sie die Pufferung mit ob_start (), fügen dann Ihre "Vorlagendatei" mit allen Funktionen, Variablen usw. hinzu, rufen den Inhalt des Puffers ab, schreiben ihn in eine Zeichenfolge und schließen dann den Puffer. Dann haben Sie alle Variablen verwendet, die Sie benötigen, Sie können jede Funktion ausführen und Sie haben immer noch die HTML-Syntaxhervorhebung in Ihrer IDE verfügbar.

Folgendes meine ich:

Vorlagendatei:

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

Skript:

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

Das Skript nimmt also die Datei template_file.php in seinen Puffer auf, führt alle Funktionen / Methoden aus und weist dabei Variablen zu. Dann zeichnen Sie einfach den Inhalt des Puffers in eine Variable auf und machen damit, was Sie wollen.

Auf diese Weise müssen Sie dies nicht tun, wenn Sie es nicht gleich in dieser Sekunde auf der Seite wiedergeben möchten. Sie können die Zeichenfolge wiederholen und weiter ergänzen, bevor Sie sie ausgeben.

Ich denke, das ist der beste Weg, wenn Sie keine Template-Engine verwenden möchten.

BraedenP
quelle
6

Dieses Snippet definiert Variablen mit dem Namen Ihrer definierten Funktionen in userscope und bindet sie an eine Zeichenfolge, die denselben Namen enthält. Lassen Sie mich demonstrieren.

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;

Wird jetzt funktionieren.

Michael McMillan
quelle
@MichaelMcMillian haben besser keine Variablen, die den gleichen Namen wie eine Funktion haben, oder?
s3c
6

Ich habe hier eine gute Lösung mit Wrapping-Funktion gefunden: 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
quelle
2
Ich habe genau die gleiche Lösung 2,5 Jahre zuvor vorgeschlagen. stackoverflow.com/a/10713298/1166898
CJ Dennis
5

Ich denke, die Verwendung von Heredoc ist großartig, um HTML-Code zu generieren. Zum Beispiel finde ich das Folgende fast völlig unlesbar.

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

Um die Einfachheit zu erreichen, müssen Sie jedoch die Funktionen bewerten, bevor Sie beginnen. Ich glaube nicht, dass dies eine so schreckliche Einschränkung ist, da Sie dabei Ihre Berechnung von der Anzeige trennen, was normalerweise eine gute Idee ist.

Ich denke, das Folgende ist gut lesbar:

$page_content = getPageContent();

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

Obwohl es ein guter Vorschlag war, die Sie in Ihrer Frage gemacht haben, die Funktion an eine Variable zu binden, fügt dies dem Code leider eine Komplexität hinzu, die sich nicht lohnt, und macht den Code weniger lesbar, was bedeutet der große Vorteil von Heredoc.

MLU
quelle
2
Die letzten 4 Jahre haben sich als viel intelligenter erwiesen als die meisten anderen Ansätze. Die Verwendung von Kompositionen in Ihren Vorlagen (Erstellen einer großen Seite aus kleineren Seiten) und das Trennen der gesamten Steuerlogik ist der Standardansatz für alle, die es ernst meinen mit Vorlagen: Facebooks ReactJS eignet sich hervorragend dafür (wie auch XHP), ebenso wie XSLT (welche) Ich liebe nicht, aber es ist akademisch fundiert. Die einzigen stilistischen Anmerkungen, die ich machen würde: Ich verwende immer {} um meine Vars, hauptsächlich aus Gründen der Konsistenz der Lesbarkeit und um spätere Unfälle zu vermeiden. Vergessen Sie auch nicht, alle Daten von Benutzern in htmlspecialchars () zu speichern.
Josh von Qaribou
4

Ich würde mir Smarty als Template-Engine ansehen - ich habe selbst keine anderen ausprobiert, aber es hat mir gut getan.

Wenn Sie sich an Ihren aktuellen Ansatz ohne Vorlagen halten möchten , was ist dann so schlecht an der Ausgabepufferung? Sie haben viel mehr Flexibilität, als Variablen deklarieren zu müssen, die die Zeichenfolgennamen der Funktionen sind, die Sie aufrufen möchten.

nickf
quelle
2

Hier ein schönes Beispiel mit dem Vorschlag von @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>";

Eine gute Verwendung der HEREDOC-Syntax besteht beispielsweise darin, große Formulare mit Master-Detail-Beziehung in einer Datenbank zu generieren. Sie können die HEREDOC-Funktion in einem FOR-Steuerelement verwenden und nach jedem Feldnamen ein Suffix hinzufügen. Es ist eine typische serverseitige Aufgabe.

Paulo Buchsbaum
quelle
2

Sie vergessen die Lambda-Funktion:

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

Sie können auch die Funktion create_function verwenden

Ismael Miguel
quelle
1
<div><?=<<<heredoc
Use heredoc and functions in ONE statement.
Show lower case ABC="
heredoc
. strtolower('ABC') . <<<heredoc
".  And that is it!
heredoc
?></div>
Ken
quelle
0
<?php
echo <<<ETO
<h1>Hellow ETO</h1>
ETO;

Du solltest es versuchen . nach dem Ende der ETO; Befehl sollten Sie eine Eingabe geben.

Rubel Hossain
quelle
0

Dies ist heute auf PHP 7.x etwas eleganter

<?php

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


echo <<<HEREDOC
this is a test: {$test()}
HEREDOC;
Codeasaurus
quelle