Chiamare funzioni PHP all'interno di stringhe HEREDOC


91

In PHP, le dichiarazioni di stringa HEREDOC sono davvero utili per l'output di un blocco di html. Puoi farlo analizzare nelle variabili semplicemente anteponendole $, ma per una sintassi più complicata (come $ var [2] [3]), devi inserire la tua espressione tra parentesi graffe {}.

In PHP 5, è possibile effettuare effettivamente chiamate di funzione tra parentesi graffe {} all'interno di una stringa HEREDOC, ma è necessario eseguire un po 'di lavoro. Il nome della funzione stessa deve essere memorizzato in una variabile e devi chiamarlo come se fosse una funzione con nome dinamico. Per esempio:

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

Come puoi vedere, questo è un po 'più disordinato del semplice:

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

Esistono altri modi oltre al primo esempio di codice, come uscire da HEREDOC per chiamare la funzione o invertire il problema e fare qualcosa come:

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

Quest'ultimo ha lo svantaggio che l'output viene inserito direttamente nel flusso di output (a meno che non stia utilizzando il buffer di output), il che potrebbe non essere quello che voglio.

Quindi, l'essenza della mia domanda è: esiste un modo più elegante per affrontare questo problema?

Modifica in base alle risposte: Certamente sembra che una sorta di motore di template mi renderebbe la vita molto più semplice, ma mi richiederebbe fondamentalmente di invertire il mio solito stile PHP. Non che sia una brutta cosa, ma spiega la mia inerzia .. Tuttavia, sto cercando di trovare modi per rendere la vita più facile, quindi ora sto esaminando i modelli.


3
Questa non è strettamente una risposta alla tua domanda, ma dato lo scarso supporto per le chiamate di funzione nelle istruzioni heredoc, di solito generi solo le stringhe di cui ho bisogno prima di stampare heredoc. Quindi, posso semplicemente usare qualcosa come Text {$string1} Text {$string2} Textin heredoc.
rinogo

Risposte:


51

Personalmente non userei affatto HEREDOC per questo. Semplicemente non è un buon sistema di "creazione di modelli". Tutto il tuo codice HTML è bloccato in una stringa che presenta diversi svantaggi

  • Nessuna opzione per WYSIWYG
  • Nessun completamento del codice per HTML dagli IDE
  • Output (HTML) bloccato su file logici
  • Finisci per dover usare hack come quello che stai cercando di fare ora per ottenere modelli più complessi, come il loop

Ottieni un motore di modelli di base o usa semplicemente PHP con include: è per questo che la lingua ha i delimitatori <?phpe ?>.

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
C'è un'abbreviazione per echo: <?=$valueToEcho;?>o <%=$valueToEcho;%>dipende dalle impostazioni INI.
Peter Bailey

3
Quasi tutto quello che ho letto sull'uso di quelle abbreviazioni dice che usarle è una cattiva pratica e sono d'accordo. Quindi, sfortunatamente, se stai scrivendo codice per la distribuzione non puoi dipendere da quelle impostazioni INI, rendendo così il loro "supporto" PHP discutibile per il codice distribuito. FWIW, ho dovuto correggere i bug nei plug-in di WordPress di altre persone più di una volta perché usavano queste scorciatoie.
MikeSchinkel

1
No, non sto dicendo che è un peccato dover digitare 7 caratteri; attribuisci erroneamente i miei problemi. Non è la digitazione che mi interessa, è la lettura . Quei caratteri creano molto rumore visivo che rende molto più difficile scansionare il codice sorgente e capire cosa sta facendo il codice. Per me almeno è MOLTO più facile leggere un HEREDOC. (E a proposito, è il tempo di 7 caratteri quante volte viene utilizzato in un dato frammento HTML. Ma sto divagando.)
MikeSchinkel

12
Il corto è più bello, più pulito e più facile da leggere. Secondo te <?=$title?>è molto più carino di <? Php echo $ title; ?>. Lo svantaggio è che, sì, per la distribuzione molti ini hanno tag brevi disattivati. Ma indovina cosa ?? A partire da PHP 5.4 , i tag brevi sono abilitati in PHP indipendentemente dalle impostazioni ini! Quindi, se stai codificando con un requisito 5.4+ (supponiamo che tu stia usando tratti, per esempio), vai avanti e usa quei fantastici tag brevi !!
Jimbo

2
A proposito, <? = $ Blah?> È abilitato in 5.4 per impostazione predefinita, anche se i tag brevi sono disattivati.
callmetwan

72

Se vuoi davvero farlo, ma un po 'più semplice rispetto all'utilizzo di una classe, puoi usare:

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

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

Ottimo @CJDennis! Questa è la soluzione migliore e più pulita per l'utilizzo di chiamate di funzioni all'interno di HEREDOC. C'è una bella risorsa in alcune situazioni. Nel mio sito, utilizzo HEREDOC per generare moduli con 22 righe di set di campi (un blocco HEREDOC all'interno di un ciclo FOR), con chiamata di funzione per generare la posizione tabindex.
Paulo Buchsbaum

Puoi anche farlo:$my_string = "Half the number of seconds since the Unix Epoch: {$fn(time() / 2 . ' Yes! Really!')}";
CJ Dennis

2
una definizione più compatta: $fn = function fn($data) { return $data; };
devsmt

1
@devsmt Hai ragione. E ancora più breve è:$fn = function ($data) { return $data; };
CJ Dennis

oh, godegolf? ok, fammi entrare: $fn=function($data){return $data;};dovrebbe essere il più breve
My1

42

Farei quanto segue:

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

Non sono sicuro se lo consideri più elegante ...


17

Prova questo (come variabile globale o istanziato quando ne hai bisogno):

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

  $fn = new Fn();
?>

Ora qualsiasi chiamata di funzione passa attraverso l' $fnistanza. Quindi la funzione esistentetestfunction() può essere chiamata in un heredoc con{$fn->testfunction()}

Fondamentalmente stiamo avvolgendo tutte le funzioni in un'istanza di classe e utilizzando PHP __call magic metodo per mappare il metodo di classe alla funzione effettiva che deve essere chiamata.


2
Questa è una buona soluzione per i momenti in cui non è possibile aggiungere semplicemente un motore di modelli a un progetto esistente. Grazie, lo sto usando ora.
Brandon

non dovrebbe essere usato ampiamente quando le prestazioni sono critiche: ho letto diverse volte le prestazioni sono peggiori call_user_func_array, l'ultima volta nei commenti su php.net: php.net/manual/en/function.call-user-func-array.php # 97473
Markus

Bello! Lo adoro, perché non ci ho pensato?!? :-)
MikeSchinkel

10

Per completezza, puoi anche usare l' hack del parser della !${''} magia nera :

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

7
Sei andato a Hogwarts?
Starx

Questo funziona perché false == ''. Definisci una variabile con un nome di lunghezza 0 ( ''). Impostalo sul valore che desideri ( ${''} = date('Y-m-d H:i:s', strtotime('-1 month'))). Negalo ( !) e convertilo in una variabile ( ${false}). falsedeve essere convertito in una stringa e (string)false === ''. Se provi a stampare un valore falso, verrà invece visualizzato un errore. La seguente stringa funziona sia truthy e valori falsy, a scapito di essere ancora più illeggibile: "${(${''}=date('Y-m-d H:i:s', strtotime('-1 month')))!=${''}}".
CJ Dennis

E se vuoi stampare NANanche tu , usa "${(${''} = date('Y-m-d H:i:s', strtotime('-1 month')) )==NAN}".
CJ Dennis

9

Sono un po 'in ritardo, ma l'ho trovato per caso. Per qualsiasi futuro lettore, ecco cosa farei probabilmente:

Vorrei solo usare un buffer di output. Quindi fondamentalmente, inizi il buffering usando ob_start (), quindi includi il tuo "file modello" con qualsiasi funzione, variabile, ecc. Al suo interno, ottieni il contenuto del buffer e lo scrivi su una stringa, quindi chiudi il buffer. Quindi hai usato tutte le variabili di cui hai bisogno, puoi eseguire qualsiasi funzione e hai ancora l'evidenziazione della sintassi HTML disponibile nel tuo IDE.

Ecco cosa intendo:

File modello:

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

Script:

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

Quindi lo script include template_file.php nel suo buffer, eseguendo qualsiasi funzione / metodo e assegnando qualsiasi variabile lungo il percorso. Quindi registri semplicemente il contenuto del buffer in una variabile e fai quello che vuoi con esso.

In questo modo, se non vuoi farlo eco sulla pagina proprio in quel secondo, non devi. Puoi ripetere e continuare ad aggiungere alla stringa prima di emetterla.

Penso che sia il modo migliore se non vuoi usare un motore di modelli.


6

Questo frammento definirà le variabili con il nome delle funzioni definite all'interno di userscope e le legherà a una stringa che contiene lo stesso nome. Fammi dimostrare.

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;

Ora funzionerà.


@MichaelMcMillian meglio non avere variabili con lo stesso nome di qualsiasi funzione, giusto?
s3c

6

ho trovato una bella soluzione con la funzione di wrapping qui: 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
Ho suggerito esattamente la stessa soluzione 2,5 anni prima. stackoverflow.com/a/10713298/1166898
CJ Dennis

5

Penso che usare heredoc sia ottimo per generare codice HTML. Ad esempio, trovo quanto segue quasi completamente illeggibile.

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

Tuttavia, per ottenere la semplicità sei costretto a valutare le funzioni prima di iniziare. Non credo che sia un vincolo così terribile, poiché così facendo, finisci per separare il tuo calcolo dal display, che di solito è una buona idea.

Penso che quanto segue sia abbastanza leggibile:

$page_content = getPageContent();

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

Sfortunatamente, anche se è stato un buon suggerimento che hai dato nella tua domanda per legare la funzione a una variabile, alla fine, aggiunge un livello di complessità al codice, che non vale, e rende il codice meno leggibile, il che è il principale vantaggio di heredoc.


2
Gli ultimi 4 anni si sono dimostrati molto più intelligenti della maggior parte degli altri approcci. Usare la composizione nei tuoi modelli (costruire una grande pagina composta da pagine più piccole) e tenere separata tutta la logica di controllo è l'approccio standard per chiunque sia seriamente interessato alla creazione di modelli: ReactJS di Facebook è eccellente per questo (come XHP), così come XSLT (che Non amo, ma è accademicamente valido). Le uniche note stilistiche che farei: uso sempre {} intorno ai miei vars, principalmente per coerenza nella leggibilità e per evitare incidenti in seguito. Inoltre, non dimenticare di htmlspecialchars () tutti i dati provenienti dagli utenti.
Josh da Qaribou

4

Darei un'occhiata a Smarty come motore di modelli: non ne ho provati altri da solo, ma mi ha fatto bene.

Se vuoi restare fedele al tuo approccio attuale senza i modelli, cosa c'è di male nel buffering dell'output? Ti darà molta più flessibilità rispetto a dover dichiarare variabili che sono i nomi delle stringhe delle funzioni che vuoi chiamare.


3

ti stai dimenticando della funzione lambda:

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

Puoi anche usare la funzione create_function



2

Ecco un bell'esempio che utilizza la proposta di @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>";

Ad esempio, un buon uso della sintassi HEREDOC è generare enormi moduli con relazione master-dettaglio in un database. È possibile utilizzare la funzionalità HEREDOC all'interno di un controllo FOR, aggiungendo un suffisso dopo ogni nome di campo. È una tipica attività lato server.



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;

dovresti provarlo . dopo la fine l'ETO; comando dovresti dare un invio.


0

Questo è un po 'più elegante oggi su php 7.x

<?php

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


echo <<<HEREDOC
this is a test: {$test()}
HEREDOC;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.