Memanggil fungsi PHP dalam string HEREDOC

91

Dalam PHP, deklarasi string HEREDOC sangat berguna untuk mengeluarkan blok html. Anda bisa membuatnya mengurai dalam variabel hanya dengan memberi awalan $, tetapi untuk sintaks yang lebih rumit (seperti $ var [2] [3]), Anda harus meletakkan ekspresi Anda di dalam {} tanda kurung.

Dalam PHP 5, itu adalah mungkin untuk benar-benar membuat panggilan fungsi dalam {} kurung di dalam string heredoc, tetapi Anda harus melalui sedikit pekerjaan. Nama fungsi itu sendiri harus disimpan dalam variabel, dan Anda harus memanggilnya seperti fungsi yang dinamai secara dinamis. Sebagai contoh:

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

Seperti yang Anda lihat, ini sedikit lebih berantakan daripada hanya:

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

Ada cara lain selain contoh kode pertama, seperti keluar dari HEREDOC untuk memanggil fungsi, atau membalik masalah dan melakukan sesuatu seperti:

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

Yang terakhir memiliki kelemahan bahwa output secara langsung dimasukkan ke dalam aliran output (kecuali saya menggunakan buffering output), yang mungkin bukan yang saya inginkan.

Jadi, inti dari pertanyaan saya adalah: adakah cara yang lebih elegan untuk melakukan pendekatan ini?

Edit berdasarkan tanggapan: Memang terlihat seperti beberapa jenis mesin templat akan membuat hidup saya lebih mudah, tetapi pada dasarnya saya harus membalikkan gaya PHP saya yang biasa. Bukan berarti itu buruk, tapi itu menjelaskan kelembaman saya .. Saya siap untuk mencari cara untuk membuat hidup lebih mudah, jadi saya mencari template sekarang.

Doug Kavendek
sumber
3
Ini bukan hanya jawaban untuk pertanyaan Anda, tetapi karena dukungan yang buruk untuk pemanggilan fungsi dalam pernyataan heredoc, saya biasanya hanya menghasilkan string yang saya perlukan sebelum mencetak heredoc. Kemudian, saya cukup menggunakan sesuatu seperti Text {$string1} Text {$string2} Textdi heredoc.
rinogo

Jawaban:

51

Saya tidak akan menggunakan HEREDOC sama sekali untuk ini, secara pribadi. Itu tidak membuat sistem "pembuatan template" yang baik. Semua HTML Anda dikunci dalam string yang memiliki beberapa kelemahan

  • Tidak ada opsi untuk WYSIWYG
  • Tidak ada penyelesaian kode untuk HTML dari IDE
  • Output (HTML) dikunci ke file logika
  • Anda akhirnya harus menggunakan peretasan seperti yang Anda coba lakukan sekarang untuk mencapai templat yang lebih kompleks, seperti perulangan

Dapatkan mesin templat dasar, atau cukup gunakan PHP dengan penyertaan - itulah alasan bahasa memiliki <?phpdan?> pembatas .

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
sumber
8
Ada singkatan dari echo: <?=$valueToEcho;?>atau <%=$valueToEcho;%>tergantung pada pengaturan INI Anda.
Peter Bailey
3
Hampir semua yang saya baca tentang penggunaan singkatan tersebut mengatakan bahwa menggunakannya adalah praktik yang buruk, dan saya setuju. Jadi sayangnya jika Anda menulis kode untuk distribusi, Anda tidak dapat bergantung pada pengaturan INI sehingga membuat "dukungan" PHP untuk mereka diperdebatkan untuk kode terdistribusi. FWIW, saya harus memperbaiki bug di plugin WordPress orang lain lebih dari sekali karena mereka menggunakan singkatan ini.
MikeSchinkel
1
Tidak, saya tidak mengatakan sayang saya harus mengetik 7 karakter; Anda salah mengaitkan masalah saya. Bukan pengetikan yang saya pedulikan, ini bacaannya . Karakter tersebut menciptakan banyak gangguan visual yang membuatnya lebih sulit untuk memindai kode sumber dan memahami apa yang dilakukan kode tersebut. Bagi saya setidaknya JAUH lebih mudah untuk membaca HEREDOC. (Dan BTW, ini waktu 7 karakter berapa kali itu digunakan dalam fragmen HTML tertentu. Tapi saya ngelantur.)
MikeSchinkel
12
Pendek lebih bagus, lebih bersih dan lebih mudah dibaca. Menurut pandangan Anda, <?=$title?>ini jauh lebih bagus daripada <? Php echo $ title; ?>. Sisi negatifnya adalah, ya, untuk distribusi banyak tag pendek off. Tapi , coba tebak ?? Mulai PHP 5.4 , tag pendek diaktifkan di PHP terlepas dari pengaturan ini! Jadi jika Anda membuat kode dengan persyaratan 5.4+ (katakanlah Anda menggunakan ciri-ciri, misalnya), lanjutkan dan gunakan tag pendek yang mengagumkan itu !!
Jimbo
2
Omong-omong, <? = $ Blah?> Diaktifkan di 5.4 secara default, meskipun tag pendek dinonaktifkan.
callmetwan
72

Jika Anda benar-benar ingin melakukan ini tetapi sedikit lebih sederhana daripada menggunakan kelas, Anda dapat menggunakan:

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

$my_string = <<<EOT
Number of seconds since the Unix Epoch: {$fn(time())}
EOT;
CJ Dennis
sumber
Hebat @CJDennis! Itu adalah solusi terbaik dan terbersih untuk menggunakan panggilan fungsi di dalam HEREDOC. Ada sumber daya yang bagus dalam beberapa situasi. Di situs saya, saya menggunakan HEREDOC untuk menghasilkan formulir dengan 22 baris set bidang (blok HEREDOC di dalam loop FOR), dengan pemanggilan fungsi untuk menghasilkan posisi tabindex.
Paulo Buchsbaum
Anda bahkan dapat melakukan ini:$my_string = "Half the number of seconds since the Unix Epoch: {$fn(time() / 2 . ' Yes! Really!')}";
CJ Dennis
2
definisi yang lebih ringkas: $fn = function fn($data) { return $data; };
devsmt
1
@ Dev. Anda benar. Dan bahkan lebih singkatnya adalah:$fn = function ($data) { return $data; };
CJ Dennis
oh, dewa serigala? oke, biarkan saya masuk: ini $fn=function($data){return $data;};harus menjadi yang terpendek
My1
42

Saya akan melakukan hal berikut:

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

Tidak yakin apakah Anda akan menganggap ini lebih elegan ...

boxxar
sumber
17

Coba ini (baik sebagai variabel global, atau dibuat instance-nya saat Anda membutuhkannya):

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

  $fn = new Fn();
?>

Sekarang pemanggilan fungsi apa pun melewati $fninstance. Jadi fungsinya sudah adatestfunction() bisa dipanggil secara heredoc dengan{$fn->testfunction()}

Pada dasarnya kita membungkus semua fungsi menjadi instance kelas, dan menggunakan __call magicmetode PHP untuk memetakan metode kelas ke fungsi sebenarnya yang perlu dipanggil.

Isofarro
sumber
2
Ini adalah solusi yang baik untuk saat-saat Anda tidak bisa begitu saja menambahkan mesin template ke proyek yang ada. Terima kasih, saya menggunakannya sekarang.
Brandon
sebaiknya tidak digunakan secara luas ketika kinerja sangat penting: Saya telah membaca beberapa kali kinerja menjadi lebih buruk call_user_func_array, terakhir kali di komentar di php.net: php.net/manual/en/function.call-user-func-array.php # 97473
Markus
Bagus! Saya menyukainya, mengapa saya tidak memikirkan ini?!? :-)
MikeSchinkel
11

Untuk kelengkapannya, Anda juga bisa menggunakan hack !${''} black magic parser :

echo <<<EOT
One month ago was ${!${''} = date('Y-m-d H:i:s', strtotime('-1 month'))}.
EOT;
uskup
sumber
8
Apakah Anda pergi ke Hogwarts?
Starx
Ini berhasil karena false == ''. Tentukan variabel dengan nama panjang 0 ( ''). Setel ke nilai yang Anda inginkan ( ${''} = date('Y-m-d H:i:s', strtotime('-1 month'))). Negasikan itu ( !) dan ubah menjadi variabel ( ${false}). falseperlu diubah menjadi string, dan (string)false === ''. Jika Anda mencoba mencetak nilai yang salah, itu akan menjadi kesalahan. String berikut bekerja pada kedua truthy dan nilai-nilai falsy, dengan mengorbankan menjadi lebih terbaca: "${(${''}=date('Y-m-d H:i:s', strtotime('-1 month')))!=${''}}".
CJ Dennis
Dan jika Anda ingin mencetak NANjuga, gunakan "${(${''} = date('Y-m-d H:i:s', strtotime('-1 month')) )==NAN}".
CJ Dennis
9

Saya agak terlambat, tapi saya kebetulan menemukannya. Untuk pembaca yang akan datang, inilah yang mungkin akan saya lakukan:

Saya hanya akan menggunakan buffer keluaran. Jadi pada dasarnya, Anda memulai buffering menggunakan ob_start (), lalu memasukkan "file template" Anda dengan fungsi, variabel, dll. Di dalamnya, dapatkan konten buffer dan menuliskannya ke string, lalu tutup buffer. Kemudian Anda telah menggunakan variabel apa pun yang Anda butuhkan, Anda dapat menjalankan fungsi apa pun, dan Anda masih memiliki penyorotan sintaks HTML di IDE Anda.

Inilah yang saya maksud:

File Template:

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

Naskah:

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

Jadi skrip menyertakan template_file.php ke dalam buffernya, menjalankan fungsi / metode apa pun dan menugaskan variabel apa pun di sepanjang jalan. Kemudian Anda cukup merekam konten buffer ke dalam variabel dan melakukan apa yang Anda inginkan dengannya.

Dengan begitu, jika Anda tidak ingin menampilkannya langsung ke halaman pada saat itu juga, Anda tidak perlu melakukannya. Anda dapat melakukan loop dan terus menambahkan string sebelum mengeluarkannya.

Saya pikir itulah cara terbaik untuk pergi jika Anda tidak ingin menggunakan mesin templating.

BraedenP
sumber
6

Cuplikan ini akan menentukan variabel dengan nama fungsi yang Anda tetapkan dalam userscope dan mengikatnya ke string yang berisi nama yang sama. Biar saya tunjukkan.

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;

Sekarang akan bekerja.

Michael McMillan
sumber
@MichaelMcMillian sebaiknya tidak memiliki variabel yang dinamai sama dengan fungsi apa pun, bukan?
s3c
6

menemukan solusi yang bagus dengan fungsi pembungkus di sini: 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
sumber
2
Saya menyarankan solusi yang persis sama 2,5 tahun sebelumnya. stackoverflow.com/a/10713298/1166898
CJ Dennis
5

Saya pikir menggunakan heredoc bagus untuk menghasilkan kode HTML. Misalnya, saya menemukan yang berikut ini hampir tidak terbaca sama sekali.

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

Namun, untuk mencapai kesederhanaan, Anda dipaksa untuk mengevaluasi fungsi sebelum Anda mulai. Saya tidak percaya itu kendala yang mengerikan, karena dengan melakukan itu, Anda akhirnya memisahkan perhitungan Anda dari tampilan, yang biasanya merupakan ide yang bagus.

Menurut saya yang berikut ini cukup mudah dibaca:

$page_content = getPageContent();

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

Sayangnya, meskipun itu adalah saran bagus yang Anda buat dalam pertanyaan Anda untuk mengikat fungsi ke variabel, pada akhirnya, itu menambah tingkat kerumitan pada kode, yang tidak layak, dan membuat kode kurang dapat dibaca, yaitu keuntungan utama dari heredoc.

MLU
sumber
2
Empat tahun terakhir membuktikan ini jauh lebih pintar daripada kebanyakan pendekatan lainnya. Menggunakan komposisi dalam templat Anda (membangun halaman besar yang terdiri dari halaman-halaman yang lebih kecil) dan menjaga semua logika kontrol terpisah adalah pendekatan standar bagi siapa saja yang serius tentang pembuatan template: ReactJS facebook sangat baik untuk ini (seperti halnya XHP), seperti halnya XSLT (yang mana Saya tidak suka, tetapi sehat secara akademis). Satu-satunya catatan gaya yang akan saya buat: Saya selalu menggunakan {} di sekitar vars saya, terutama untuk konsistensi dalam keterbacaan dan untuk menghindari kecelakaan di kemudian hari. Selain itu, jangan lupa untuk htmlspecialchars () data apa pun yang berasal dari pengguna.
Josh dari Qaribou
4

Saya akan melihat Smarty sebagai mesin templat - Saya belum pernah mencoba yang lain, tetapi itu telah membantu saya dengan baik.

Jika Anda ingin tetap menggunakan template tanpa pendekatan Anda saat ini , apa buruknya buffering keluaran? Ini akan memberi Anda lebih banyak fleksibilitas daripada harus mendeklarasikan variabel yang merupakan nama string dari fungsi yang ingin Anda panggil.

nickf
sumber
3

Anda lupa tentang fungsi lambda:

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

Anda juga bisa menggunakan fungsi create_function

Ismael Miguel
sumber
2

Agak terlambat tapi tetap saja. Ini mungkin dalam heredoc!

Lihat di manual php, bagian "Sintaks kompleks (keriting)"

tftd
sumber
Saya sudah menggunakan sintaks itu di contoh pertama; itu memiliki kerugian memiliki menempatkan nama fungsi ke dalam variabel sebelum Anda dapat memanggilnya di dalam kurung kurawal di bagian heredoc, yang saya coba hindari.
Doug Kavendek
2

Berikut contoh yang bagus menggunakan proposal @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>";

Misalnya, penggunaan sintaks HEREDOC yang baik adalah menghasilkan formulir besar dengan hubungan detail master dalam Database. Seseorang dapat menggunakan fitur HEREDOC di dalam kontrol FOR, menambahkan sufiks setelah setiap nama bidang. Ini adalah tugas sisi server yang khas.

Paulo Buchsbaum
sumber
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
sumber
1

Ini sedikit lebih elegan hari ini di php 7.x

<?php

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


echo <<<HEREDOC
this is a test: {$test()}
HEREDOC;
codeasaurus
sumber
0
<?php
echo <<<ETO
<h1>Hellow ETO</h1>
ETO;

kamu harus mencobanya . setelah mengakhiri ETO; perintah Anda harus memasukkan.

Rubel Hossain
sumber