เรียกใช้ฟังก์ชัน PHP ภายในสตริง HEREDOC


92

ใน PHP การประกาศสตริง HEREDOC มีประโยชน์มากสำหรับการส่งออกบล็อกของ html คุณสามารถแยกวิเคราะห์ในตัวแปรได้เพียงแค่นำหน้าด้วย $ แต่สำหรับไวยากรณ์ที่ซับซ้อนมากขึ้น (เช่น $ var [2] [3]) คุณต้องใส่นิพจน์ของคุณไว้ในเครื่องหมายวงเล็บ {}

ใน PHP 5 เป็นไปได้ที่จะเรียกใช้ฟังก์ชันภายใน {} วงเล็บปีกกาภายในสตริง HEREDOC แต่คุณต้องดำเนินการเล็กน้อย ชื่อฟังก์ชันจะต้องถูกเก็บไว้ในตัวแปรและคุณต้องเรียกมันให้เหมือนกับว่ามันเป็นฟังก์ชันที่ตั้งชื่อแบบไดนามิก ตัวอย่างเช่น:

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

อย่างที่คุณเห็นสิ่งนี้ค่อนข้างยุ่งเหยิงมากกว่า:

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

มีวิธีอื่นนอกเหนือจากตัวอย่างโค้ดแรกเช่นการแตกออกจาก HEREDOC เพื่อเรียกใช้ฟังก์ชันหรือย้อนกลับปัญหาและทำสิ่งต่างๆเช่น:

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

อย่างหลังมีข้อเสียตรงที่เอาท์พุทใส่ลงในสตรีมเอาท์พุทโดยตรง (เว้นแต่ฉันจะใช้บัฟเฟอร์เอาท์พุท) ซึ่งอาจไม่ใช่สิ่งที่ฉันต้องการ

ดังนั้นสาระสำคัญของคำถามของฉันคือ: มีวิธีการที่หรูหรากว่านี้หรือไม่?

แก้ไขตามการตอบสนอง:ดูเหมือนว่าเครื่องมือเทมเพลตบางประเภทจะทำให้ชีวิตของฉันง่ายขึ้นมาก แต่โดยพื้นฐานแล้วฉันจะต้องเปลี่ยนสไตล์ PHP ตามปกติของฉัน ไม่ใช่ว่านั่นเป็นสิ่งที่ไม่ดี แต่มันอธิบายถึงความเฉื่อยของฉัน .. ฉันพร้อมที่จะหาวิธีที่จะทำให้ชีวิตง่ายขึ้นดังนั้นตอนนี้ฉันกำลังมองหาเทมเพลต


3
นี่ไม่ใช่คำตอบสำหรับคำถามของคุณอย่างเคร่งครัด แต่ได้รับการสนับสนุนที่ไม่ดีสำหรับการเรียกใช้ฟังก์ชันในคำสั่ง heredoc ฉันมักจะสร้างสตริงที่ฉันต้องการก่อนที่จะพิมพ์ heredoc จากนั้นฉันสามารถใช้บางอย่างเช่นText {$string1} Text {$string2} Textในพันธุกรรม
rinogo

คำตอบ:


51

ฉันจะไม่ใช้ HEREDOC เลยสำหรับเรื่องนี้เป็นการส่วนตัว มันไม่ได้ทำให้เกิดระบบ "การสร้างเทมเพลต" ที่ดี HTML ทั้งหมดของคุณถูกล็อคไว้ในสตริงซึ่งมีข้อเสียหลายประการ

  • ไม่มีตัวเลือกสำหรับ WYSIWYG
  • ไม่มีการเติมโค้ดสำหรับ HTML จาก IDE
  • เอาต์พุต (HTML) ถูกล็อกเป็นไฟล์ลอจิก
  • คุณต้องใช้แฮ็กเหมือนกับสิ่งที่คุณกำลังพยายามทำในตอนนี้เพื่อให้ได้เทมเพลตที่ซับซ้อนมากขึ้นเช่นการวนซ้ำ

รับเทมเพลตเอ็นจิ้นพื้นฐานหรือใช้ PHP กับ include - นั่นเป็นเหตุผลว่าทำไมภาษาจึงมี<?phpและ?>ตัวคั่น

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
มีชวเลขสำหรับ echo: <?=$valueToEcho;?>หรือ<%=$valueToEcho;%>ขึ้นอยู่กับการตั้งค่า INI ของคุณ
Peter Bailey

3
ทุกสิ่งที่ฉันเคยอ่านเกี่ยวกับการใช้ shorthands เหล่านั้นบอกว่าการใช้มันเป็นการปฏิบัติที่ไม่ดีและฉันก็เห็นด้วย ดังนั้นหากคุณกำลังเขียนโค้ดสำหรับการแจกจ่ายคุณไม่สามารถพึ่งพาการตั้งค่า INI เหล่านั้นได้จึงทำให้ "การสนับสนุน" ของ PHP สำหรับพวกเขา moot สำหรับโค้ดแบบกระจาย FWIW ฉันต้องแก้ไขข้อบกพร่องในปลั๊กอิน WordPress ของคนอื่นมากกว่าหนึ่งครั้งเพราะพวกเขาใช้ shorthands เหล่านี้
MikeSchinkel

1
ไม่ฉันไม่ได้บอกว่ามันน่าเสียดายที่ฉันต้องพิมพ์ 7 ตัวอักษร; คุณระบุปัญหาของฉันไม่ถูกต้อง มันไม่ได้เป็นพิมพ์ฉันกังวลกับมันเป็นอ่าน อักขระเหล่านี้สร้างสัญญาณรบกวนภาพจำนวนมากซึ่งทำให้ยากต่อการสแกนซอร์สโค้ดและทำความเข้าใจว่าโค้ดกำลังทำอะไรอยู่ สำหรับฉันอย่างน้อยจะเป็นมากง่ายต่อการอ่าน HEREDOC (และ BTW เป็นเวลา 7 อักขระเท่าที่เคยใช้ในส่วน HTML ที่กำหนด แต่ฉันพูดนอกเรื่อง)
MikeSchinkel

12
แบบสั้นดีกว่าสะอาดกว่าและอ่านง่ายกว่า ในมุมมองของคุณ<?=$title?>เป็นมาก nicer กว่า <? php echo $ ชื่อ; ?>. ข้อเสียคือใช่สำหรับการแจกจ่าย ini จำนวนมากจะมีการปิดแท็กสั้น ๆ แต่เดาอะไร ?? ตั้งแต่PHP 5.4แท็กสั้น ๆ จะเปิดใช้งานใน PHP โดยไม่คำนึงถึงการตั้งค่า ini! ดังนั้นหากคุณกำลังเขียนโค้ดด้วยข้อกำหนด 5.4+ (สมมติว่าคุณกำลังใช้ลักษณะ) ให้ใช้แท็กสั้น ๆ ที่ยอดเยี่ยมเหล่านั้นเลย !!
Jimbo

2
อย่างไรก็ตาม <? = $ blah?> ถูกเปิดใช้งานใน 5.4 โดยค่าเริ่มต้นแม้ว่าแท็กแบบสั้นจะปิดอยู่ก็ตาม
callmetwan

72

หากคุณต้องการทำสิ่งนี้จริง ๆ แต่ง่ายกว่าการใช้คลาสคุณสามารถใช้ได้:

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

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

เยี่ยมมาก @CJDennis! นั่นคือทางออกที่ดีที่สุดและสะอาดที่สุดสำหรับการใช้ฟังก์ชันโทรภายใน HEREDOC มีทรัพยากรที่ดีในบางสถานการณ์ ในไซต์ของฉันฉันใช้ HEREDOC เพื่อสร้างแบบฟอร์มที่มีชุดฟิลด์ 22 บรรทัด (บล็อก HEREDOC ภายใน FOR loop) พร้อมกับการเรียกใช้ฟังก์ชันเพื่อสร้างตำแหน่ง tabindex
Paulo Buchsbaum

คุณสามารถทำได้:$my_string = "Half the number of seconds since the Unix Epoch: {$fn(time() / 2 . ' Yes! Really!')}";
CJ Dennis

2
คำจำกัดความที่กะทัดรัดมากขึ้น: $fn = function fn($data) { return $data; };
devsmt

1
@devsmt คุณพูดถูก และที่สั้นกว่านั้นคือ:$fn = function ($data) { return $data; };
CJ Dennis

โอ้โกเดกอลฟ์? โอเคให้ฉันใน: $fn=function($data){return $data;};rhis ควรจะสั้นที่สุด
My1

42

ฉันจะทำสิ่งต่อไปนี้:

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

ไม่แน่ใจว่าคุณคิดว่าสิ่งนี้จะดูหรูหรากว่านี้ ...


17

ลองใช้สิ่งนี้ (ไม่ว่าจะเป็นตัวแปรส่วนกลางหรือสร้างอินสแตนซ์เมื่อคุณต้องการ):

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

  $fn = new Fn();
?>

ตอนนี้การเรียกใช้ฟังก์ชันใด ๆ จะต้องผ่าน$fnอินสแตนซ์ ดังนั้นจึงtestfunction()สามารถเรียกฟังก์ชันที่มีอยู่ในพันธุกรรมด้วย{$fn->testfunction()}

โดยพื้นฐานแล้วเรากำลังรวมฟังก์ชันทั้งหมดไว้ในอินสแตนซ์คลาสและใช้__call magicเมธอดของ PHP เพื่อแมปเมธอดคลาสกับฟังก์ชันจริงที่จำเป็นต้องเรียกใช้


2
นี่เป็นทางออกที่ดีสำหรับบางครั้งที่คุณไม่สามารถเพิ่มเครื่องมือสร้างเทมเพลตลงในโครงการที่มีอยู่ได้ ขอบคุณตอนนี้ฉันกำลังใช้งานอยู่
Brandon

ไม่ควรใช้กันอย่างแพร่หลายเมื่อประสิทธิภาพเป็นสิ่งสำคัญ: ฉันอ่านหลายครั้งแล้วประสิทธิภาพแย่ลงสำหรับcall_user_func_arrayครั้งสุดท้ายในความคิดเห็นที่ php.net: php.net/manual/en/function.call-user-func-array.php # 97473
Markus

ดี! ชอบจังทำไมไม่คิดแบบนี้!? :-)
MikeSchinkel

11

เพื่อความสมบูรณ์คุณยังสามารถใช้แฮ็คตัวแยกวิเคราะห์!${''} มนต์ดำ :

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

8
คุณไปฮอกวอตส์หรือเปล่า?
Starx

สิ่งนี้ได้ผลเพราะfalse == ''. กำหนดตัวแปรด้วยชื่อความยาว 0 ( '') ตั้งเป็นค่าที่คุณต้องการ ( ${''} = date('Y-m-d H:i:s', strtotime('-1 month'))) ลบมัน ( !) และแปลงเป็นตัวแปร ( ${false}) ความต้องการที่จะได้รับการแปลงเป็นสตริงและfalse (string)false === ''หากคุณพยายามพิมพ์ค่าที่ไม่ถูกต้องจะมีข้อผิดพลาดแทน สตริงต่อไปนี้ทำงานได้ทั้งบน truthy และค่า falsy "${(${''}=date('Y-m-d H:i:s', strtotime('-1 month')))!=${''}}"ที่ค่าใช้จ่ายของการเป็นแม้กระทั่งไม่สามารถอ่านได้มากขึ้น:
CJ Dennis

และถ้าคุณต้องการพิมพ์NANด้วยให้ใช้"${(${''} = date('Y-m-d H:i:s', strtotime('-1 month')) )==NAN}".
CJ Dennis

9

ฉันช้าไปหน่อย แต่ฉันบังเอิญเจอมัน สำหรับผู้อ่านในอนาคตนี่คือสิ่งที่ฉันอาจจะทำ:

ฉันจะใช้บัฟเฟอร์เอาต์พุต โดยพื้นฐานแล้วคุณเริ่มการบัฟเฟอร์โดยใช้ ob_start () จากนั้นรวม "ไฟล์เทมเพลต" ของคุณพร้อมกับฟังก์ชันตัวแปรและอื่น ๆ ภายในไฟล์รับเนื้อหาของบัฟเฟอร์และเขียนลงในสตริงจากนั้นปิดบัฟเฟอร์ จากนั้นคุณใช้ตัวแปรที่คุณต้องการคุณสามารถเรียกใช้ฟังก์ชันใดก็ได้และคุณยังมีการเน้นไวยากรณ์ HTML ที่มีอยู่ใน IDE ของคุณ

นี่คือสิ่งที่ฉันหมายถึง:

ไฟล์เทมเพลต:

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

สคริปต์:

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

ดังนั้นสคริปต์จะรวม template_file.php ไว้ในบัฟเฟอร์เรียกใช้ฟังก์ชัน / วิธีการใด ๆ และกำหนดตัวแปรใด ๆ ไปพร้อมกัน จากนั้นคุณเพียงแค่บันทึกเนื้อหาของบัฟเฟอร์ลงในตัวแปรและทำตามที่คุณต้องการ

ด้วยวิธีนี้หากคุณไม่ต้องการสะท้อนไปที่หน้าในวินาทีนั้นคุณก็ไม่จำเป็นต้องทำ คุณสามารถวนซ้ำและเพิ่มไปยังสตริงก่อนที่จะส่งออก

ฉันคิดว่านั่นเป็นวิธีที่ดีที่สุดหากคุณไม่ต้องการใช้เครื่องมือสร้างเทมเพลต


6

ตัวอย่างข้อมูลนี้จะกำหนดตัวแปรด้วยชื่อของฟังก์ชันที่คุณกำหนดไว้ภายใน userscope และผูกเข้ากับสตริงที่มีชื่อเดียวกัน ให้ฉันสาธิต

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;

ตอนนี้จะทำงาน


@MichaelMcMillian ดีกว่าไม่มีตัวแปรที่ชื่อเหมือนกับฟังก์ชันใด ๆ ใช่ไหม?
s3c

6

พบทางออกที่ดีพร้อมฟังก์ชั่นการตัดที่นี่: 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
ฉันแนะนำวิธีแก้ปัญหาเดียวกันเมื่อ 2.5 ปีก่อนหน้านี้ stackoverflow.com/a/10713298/1166898
CJ Dennis

5

ฉันคิดว่าการใช้ heredoc นั้นยอดเยี่ยมสำหรับการสร้างโค้ด HTML ตัวอย่างเช่นฉันพบว่าสิ่งต่อไปนี้อ่านไม่ได้เกือบทั้งหมด

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

อย่างไรก็ตามเพื่อให้บรรลุความเรียบง่ายคุณจำเป็นต้องประเมินฟังก์ชันก่อนที่จะเริ่ม ฉันไม่เชื่อว่านั่นเป็นข้อ จำกัด ที่แย่มากเนื่องจากในการทำเช่นนั้นคุณต้องแยกการคำนวณของคุณออกจากการแสดงผลซึ่งโดยปกติแล้วเป็นความคิดที่ดี

ฉันคิดว่าสิ่งต่อไปนี้ค่อนข้างอ่านได้:

$page_content = getPageContent();

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

น่าเสียดายที่แม้ว่าจะเป็นข้อเสนอแนะที่ดีที่คุณตั้งคำถามในการผูกฟังก์ชันเข้ากับตัวแปรในท้ายที่สุดมันก็เพิ่มระดับความซับซ้อนให้กับโค้ดซึ่งไม่คุ้มค่าและทำให้โค้ดอ่านได้น้อยลงซึ่งก็คือ ข้อได้เปรียบที่สำคัญของกรรมพันธุ์


2
4 ปีที่ผ่านมาพิสูจน์แล้วว่าฉลาดกว่าแนวทางอื่น ๆ มาก การใช้องค์ประกอบในเทมเพลตของคุณ (การสร้างเพจขนาดใหญ่ที่ประกอบด้วยเพจขนาดเล็ก) และการแยกตรรกะการควบคุมทั้งหมดออกจากกันเป็นแนวทางมาตรฐานสำหรับทุกคนที่จริงจังกับเทมเพลต: ReactJS ของ facebook นั้นยอดเยี่ยมสำหรับสิ่งนี้ (เช่นเดียวกับ XHP) เช่นเดียวกับ XSLT (ซึ่ง ฉันไม่ได้รัก แต่ฟังดูวิชาการ) หมายเหตุเกี่ยวกับโวหารเดียวที่ฉันทำ: ฉันมักจะใช้ {} กับตัวแทนของฉันโดยส่วนใหญ่เพื่อความสม่ำเสมอในการอ่านและเพื่อหลีกเลี่ยงอุบัติเหตุในภายหลัง นอกจากนี้อย่าลืม htmlspecialchars () ข้อมูลใด ๆ ที่มาจากผู้ใช้
Josh จาก Qaribou

4

ฉันจะดูSmartyเป็นเครื่องมือแม่แบบ - ฉันไม่ได้ลองใช้ตัวอื่นด้วยตัวเอง แต่ก็ทำได้ดี

หากคุณต้องการยึดติดกับแนวทางปัจจุบันของคุณแซนเท็เพลตสิ่งที่เลวร้ายเกี่ยวกับการบัฟเฟอร์เอาต์พุต? จะให้ความยืดหยุ่นมากกว่าการประกาศตัวแปรซึ่งเป็นชื่อสตริงของฟังก์ชันที่คุณต้องการเรียกใช้


3

คุณกำลังลืมเกี่ยวกับฟังก์ชันแลมด้า:

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

คุณยังสามารถใช้ฟังก์ชัน create_function


2

สายไปหน่อย แต่ก็ยัง สิ่งนี้เป็นไปได้ในกรรมพันธุ์!

ดูในคู่มือ php ส่วน "ไวยากรณ์ที่ซับซ้อน (หยิก)"


ฉันใช้ไวยากรณ์นั้นในตัวอย่างแรกอยู่แล้ว มันมีข้อเสียของการใส่ชื่อฟังก์ชันลงในตัวแปรก่อนที่คุณจะสามารถเรียกมันภายในวงเล็บปีกกาในส่วน heredoc ซึ่งเป็นสิ่งที่ฉันพยายามหลีกเลี่ยง
Doug Kavendek

2

นี่เป็นตัวอย่างที่ดีโดยใช้ข้อเสนอ @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>";

ตัวอย่างเช่นการใช้ไวยากรณ์ HEREDOC ที่ดีคือการสร้างรูปแบบขนาดใหญ่ที่มีความสัมพันธ์หลักกับรายละเอียดในฐานข้อมูล หนึ่งสามารถใช้คุณลักษณะ HEREDOC ภายในตัวควบคุม FOR โดยเพิ่มคำต่อท้ายหลังชื่อเขตข้อมูลแต่ละรายการ เป็นงานฝั่งเซิร์ฟเวอร์ทั่วไป



1
<div><?=<<<heredoc
Use heredoc and functions in ONE statement.
Show lower case ABC="
heredoc
. strtolower('ABC') . <<<heredoc
".  And that is it!
heredoc
?></div>

1

วันนี้ดูหรูหรากว่าเล็กน้อยใน php 7.x

<?php

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


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

0
<?php
echo <<<ETO
<h1>Hellow ETO</h1>
ETO;

คุณควรลอง หลังจากสิ้นสุด ETO; คำสั่งที่คุณควรป้อน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.