Appel de fonctions PHP dans des chaînes HEREDOC


91

En PHP, les déclarations de chaînes HEREDOC sont vraiment utiles pour sortir un bloc de html. Vous pouvez le faire analyser dans les variables simplement en les préfixant avec $, mais pour une syntaxe plus compliquée (comme $ var [2] [3]), vous devez mettre votre expression entre {} accolades.

En PHP 5, il est possible de faire des appels de fonction entre {} accolades dans une chaîne HEREDOC, mais vous devez faire un peu de travail. Le nom de la fonction lui-même doit être stocké dans une variable et vous devez l'appeler comme s'il s'agissait d'une fonction nommée dynamiquement. Par exemple:

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

Comme vous pouvez le voir, c'est un peu plus compliqué que simplement:

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

Il existe d'autres moyens en plus du premier exemple de code, comme sortir du HEREDOC pour appeler la fonction, ou inverser le problème et faire quelque chose comme:

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

Ce dernier a l'inconvénient que la sortie est directement placée dans le flux de sortie (sauf si j'utilise la mise en mémoire tampon de sortie), ce qui n'est peut-être pas ce que je veux.

Donc, l'essence de ma question est: y a-t-il une manière plus élégante d'aborder cela?

Modifier en fonction des réponses: Il semble certainement qu'une sorte de moteur de modèle me simplifierait la vie, mais il me faudrait essentiellement inverser mon style PHP habituel. Ce n'est pas que ce soit une mauvaise chose, mais cela explique mon inertie. Je suis prêt à trouver des moyens de rendre la vie plus facile, alors je cherche des modèles maintenant.


3
Ce n'est pas strictement une réponse à votre question, mais étant donné la mauvaise prise en charge des appels de fonction dans les instructions heredoc, je génère généralement juste les chaînes dont j'ai besoin avant d'imprimer l'heredoc. Ensuite, je peux simplement utiliser quelque chose comme Text {$string1} Text {$string2} Textdans l'hérédoc.
rinogo

Réponses:


51

Je n'utiliserais pas du tout HEREDOC pour cela, personnellement. Cela ne fait tout simplement pas un bon système de "création de modèles". Tout votre HTML est verrouillé dans une chaîne qui présente plusieurs inconvénients

  • Aucune option pour WYSIWYG
  • Pas de complétion de code pour HTML à partir des IDE
  • Sortie (HTML) verrouillée sur les fichiers logiques
  • Vous finissez par devoir utiliser des hacks comme ce que vous essayez de faire maintenant pour obtenir des modèles plus complexes, tels que la boucle

Obtenez un moteur de modèle de base, ou utilisez simplement PHP avec des inclusions - c'est pourquoi le langage a les délimiteurs <?phpet ?>.

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
Il existe un raccourci pour l'écho: <?=$valueToEcho;?>ou en <%=$valueToEcho;%>fonction de vos paramètres INI.
Peter Bailey

3
Presque tout ce que j'ai lu sur l'utilisation de ces raccourcis indique que leur utilisation est une mauvaise pratique, et je suis d'accord. Donc, malheureusement, si vous écrivez du code pour la distribution, vous ne pouvez pas dépendre de ces paramètres INI, ce qui rend le "support" de PHP pour eux sans intérêt pour le code distribué. FWIW, j'ai dû corriger des bogues dans les plugins WordPress d'autres personnes plus d'une fois parce qu'ils utilisaient ces raccourcis.
MikeSchinkel

1
Non, je ne dis pas que c'est dommage que je doive taper 7 caractères; vous attribuez incorrectement mes problèmes. Ce n'est pas la frappe qui me préoccupe, c'est la lecture . Ces caractères créent beaucoup de bruit visuel qui rend beaucoup plus difficile l'analyse du code source et la compréhension de ce que fait le code. Pour moi au moins, il est BEAUCOUP plus facile de lire un HEREDOC. (Et BTW, c'est le temps de 7 caractères combien de fois il est utilisé dans un fragment HTML donné. Mais je m'éloigne du sujet.)
MikeSchinkel

12
Short est plus agréable, plus propre et plus facile à lire. Selon vous, <?=$title?>c'est beaucoup plus agréable que <? Php echo $ title; ?>. L'inconvénient est, oui, pour la distribution, de nombreux ini ont des balises courtes. Mais devinez quoi ?? Depuis PHP 5.4 , les balises courtes sont activées dans PHP indépendamment des paramètres ini! Donc, si vous codez avec une exigence 5.4+ (disons que vous utilisez des traits, par exemple), allez-y et utilisez ces superbes balises courtes !!
Jimbo

2
Au fait, <? = $ Blah?> Est activé par défaut dans la version 5.4, même si les balises courtes sont désactivées.
callmetwan

72

Si vous voulez vraiment faire cela mais un peu plus simple que d'utiliser une classe, vous pouvez utiliser:

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

$my_string = <<<EOT
Number of seconds since the Unix Epoch: {$fn(time())}
EOT;

Super @CJDennis! C'est la meilleure et la plus propre solution pour utiliser les fonctions appelées dans HEREDOC. Il y a une belle ressource dans certaines situations. Dans mon site, j'utilise HEREDOC pour générer des formulaires avec 22 lignes d'ensembles de champs (un bloc HEREDOC dans une boucle FOR), avec appel de fonction afin de générer la position tabindex.
Paulo Buchsbaum

Vous pouvez même faire ceci:$my_string = "Half the number of seconds since the Unix Epoch: {$fn(time() / 2 . ' Yes! Really!')}";
CJ Dennis

2
une définition plus compacte: $fn = function fn($data) { return $data; };
devsmt

1
@devsmt Vous avez raison. Et encore plus court est:$fn = function ($data) { return $data; };
CJ Dennis

oh, godegolf? ok, laissez-moi entrer: $fn=function($data){return $data;};rhis devrait être le plus court
My1

42

Je ferais ce qui suit:

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

Je ne sais pas si vous considérez cela comme plus élégant ...


17

Essayez ceci (soit en tant que variable globale, soit instanciée lorsque vous en avez besoin):

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

  $fn = new Fn();
?>

Maintenant, tout appel de fonction passe par l' $fninstance. Ainsi, la fonction existante testfunction()peut être appelée dans un heredoc avec{$fn->testfunction()}

Fondamentalement, nous enveloppons toutes les fonctions dans une instance de classe et utilisons la __call magicméthode PHP pour mapper la méthode de classe à la fonction réelle à appeler.


2
C'est une bonne solution pour les moments où vous ne pouvez pas simplement ajouter un moteur de création de modèles à un projet existant. Merci, je l'utilise maintenant.
Brandon

ne doit pas être largement utilisé lorsque les performances sont critiques: j'ai lu plusieurs fois que les performances sont pires pour la call_user_func_arraydernière fois dans les commentaires sur php.net: php.net/manual/en/function.call-user-func-array.php # 97473
Markus

Agréable! J'adore, pourquoi n'y ai-je pas pensé?!? :-)
MikeSchinkel

10

Pour être complet, vous pouvez également utiliser le hack parseur de !${''} magie noire :

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

7
Êtes-vous allé à Poudlard?
Starx le

Cela fonctionne parce que false == ''. Définissez une variable avec un nom de longueur 0 ( ''). Réglez-le sur la valeur souhaitée ( ${''} = date('Y-m-d H:i:s', strtotime('-1 month'))). Niez-le ( !) et convertissez-le en une variable ( ${false}). falsedoit être converti en une chaîne, et (string)false === ''. Si vous essayez d'imprimer une valeur erronée, une erreur se produira à la place. La chaîne suivante fonctionne aussi bien sur truthy et les valeurs falsy, au détriment d'être encore plus illisible: "${(${''}=date('Y-m-d H:i:s', strtotime('-1 month')))!=${''}}".
CJ Dennis

Et si vous souhaitez également imprimer NAN, utilisez "${(${''} = date('Y-m-d H:i:s', strtotime('-1 month')) )==NAN}".
CJ Dennis

9

Je suis un peu en retard, mais je suis tombé dessus au hasard. Pour tous les futurs lecteurs, voici ce que je ferais probablement:

J'utiliserais simplement un tampon de sortie. Donc, fondamentalement, vous démarrez la mise en mémoire tampon en utilisant ob_start (), puis incluez votre "fichier modèle" avec toutes les fonctions, variables, etc. à l'intérieur, récupérez le contenu du tampon et écrivez-le dans une chaîne, puis fermez le tampon. Ensuite, vous avez utilisé toutes les variables dont vous avez besoin, vous pouvez exécuter n'importe quelle fonction et vous avez toujours la coloration syntaxique HTML disponible dans votre IDE.

Voici ce que je veux dire:

Fichier modèle:

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

Scénario:

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

Ainsi, le script inclut le template_file.php dans sa mémoire tampon, exécutant toutes les fonctions / méthodes et affectant toutes les variables en cours de route. Ensuite, vous enregistrez simplement le contenu du tampon dans une variable et faites ce que vous voulez avec.

De cette façon, si vous ne voulez pas faire écho sur la page à cette seconde, vous n'êtes pas obligé de le faire. Vous pouvez boucler et continuer à ajouter à la chaîne avant de la sortir.

Je pense que c'est la meilleure façon de procéder si vous ne voulez pas utiliser un moteur de création de modèles.


6

Cet extrait de code définira des variables avec le nom de vos fonctions définies dans userscope et les liera à une chaîne qui contient le même nom. Laissez-moi vous démontrer.

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;

Va maintenant fonctionner.


@MichaelMcMillian ferait mieux de ne pas avoir de variables nommées de la même manière que n'importe quelle fonction, non?
s3c le

6

trouvé une solution intéressante avec la fonction d'emballage ici: 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
J'ai suggéré exactement la même solution 2,5 ans avant cela. stackoverflow.com/a/10713298/1166898
CJ Dennis

5

Je pense que l'utilisation d'heredoc est idéale pour générer du code HTML. Par exemple, je trouve ce qui suit presque complètement illisible.

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

Cependant, afin d'obtenir la simplicité, vous êtes obligé d'évaluer les fonctions avant de commencer. Je ne pense pas que ce soit une contrainte aussi terrible, car ce faisant, vous finissez par séparer votre calcul de l'affichage, ce qui est généralement une bonne idée.

Je pense que ce qui suit est assez lisible:

$page_content = getPageContent();

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

Malheureusement, même si c'était une bonne suggestion que vous avez faite dans votre question de lier la fonction à une variable, au final, cela ajoute un niveau de complexité au code, ce qui ne vaut pas la peine, et le rend moins lisible, ce qui est l'avantage majeur d'heredoc.


2
Les 4 dernières années se sont avérées beaucoup plus intelligentes que la plupart des autres approches. Utiliser la composition dans vos modèles (créer une grande page composée de pages plus petites) et garder toute la logique de contrôle séparée est l'approche standard pour tous ceux qui s'intéressent sérieusement à la création de modèles: le ReactJS de Facebook est excellent pour cela (tout comme XHP), tout comme XSLT (qui Je n'aime pas, mais c'est académique). Les seules notes stylistiques que je ferais: j'utilise toujours {} autour de mes variables, principalement pour la cohérence de la lisibilité et pour éviter les accidents plus tard. N'oubliez pas non plus de htmlspecialchars () toutes les données provenant des utilisateurs.
Josh de Qaribou le

4

Je jetterais un œil à Smarty en tant que moteur de modèle - je n'en ai pas essayé d'autres moi-même, mais cela m'a bien fonctionné.

Si vous vouliez vous en tenir à votre approche actuelle sans modèles, qu'est-ce qui est si mauvais dans la mise en mémoire tampon de sortie? Cela vous donnera beaucoup plus de flexibilité que d'avoir à déclarer des variables qui sont les noms de chaîne des fonctions que vous souhaitez appeler.


3

vous oubliez la fonction lambda:

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

Vous pouvez également utiliser la fonction create_function



2

Voici un bel exemple utilisant la proposition @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>";

Par exemple, une bonne utilisation de la syntaxe HEREDOC est de générer des formulaires volumineux avec une relation maître-détails dans une base de données. On peut utiliser la fonction HEREDOC dans un contrôle FOR, en ajoutant un suffixe après chaque nom de champ. C'est une tâche typique du côté serveur.



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;

tu devrais l'essayer . après la fin de l'ETO; commande, vous devez donner une entrée.


0

C'est un peu plus élégant aujourd'hui sur php 7.x

<?php

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


echo <<<HEREDOC
this is a test: {$test()}
HEREDOC;
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.