Làm thế nào để cấu trúc hệ thống mẫu bằng PHP đơn giản?


15

Tôi đã đọc câu hỏi này trên stackoverflow:

/programming/104516/calling-php-fifts-within-heredoc-strings

và câu trả lời được chấp nhận nói để làm các mẫu PHP đơn giản như thế này:

mẫu.php:

<html>
    <đầu>
        <title> <? = $ title?> </ title>
    </ đầu>
    <cơ thể>
        <? = getContent ()?>
    </ cơ thể>
</ html>

chỉ mục.php:

<?

$ title = 'Tiêu đề demo';

hàm getContent () {
    trở lại '<p> Xin chào thế giới! </ p>';
}

bao gồm ('template.php');

?>

Đối với tôi ở trên không có cấu trúc tốt theo nghĩa là template.php phụ thuộc vào các biến được định nghĩa trong các tập lệnh khác. Và bạn đang sử dụng một tệp bao gồm () để thực thi mã khi bạn thực hiện include('template.php')(trái ngược với việc sử dụng hàm bao gồm () để bao gồm một lớp hoặc một hàm không được thực thi ngay lập tức).

Tôi cảm thấy như một cách tiếp cận tốt hơn là bọc mẫu của bạn bên trong một hàm:

mẫu.php:

<?

mẫu hàm ($ title, $ content) {
    ob_start (); ?>

<html>
    <đầu>
        <title> <? = $ title?> </ title>
    </ đầu>
    <cơ thể>
        <? = $ nội dung?>
    </ cơ thể>
</ html>

    <? php trả về ob_get_clean ();
}

?>

chỉ mục.php:

<?

allow_once ('template.php');

mẫu in ('Tiêu đề demo', '<p> Xin chào thế giới! </ p>');

?>

Là cách tiếp cận thứ hai tốt hơn? Có cách nào tốt hơn để làm điều đó?

Câu trả lời:


12

Tôi sẽ không làm cách tiếp cận thứ hai vì các tham số không được đặt tên.

Một bài luận được viết tốt về mô tả cách hệ thống mẫu nên hoạt động đến từ Parr, nó thường được trích dẫn bởi những người viết hệ thống mẫu và / hoặc khung web-mvc.

Cá nhân, điều tôi thường thích là triển khai lớp ArrayObject với một mảng thuộc tính và mẫu sẽ tham chiếu $this->propertyName, trong thực tế sẽ là đối tượng mẫu $this->property['name']. Điều này cũng có thể đạt được chỉ bằng cách sử dụng __set__get, vì vậy:

class Template {
  private $_scriptPath=TEMPLATE_PATH;//comes from config.php
  public $properties;
  public function setScriptPath($scriptPath){
    $this->_scriptPath=$scriptPath;
  }
  public function __construct(){
      $this->properties = array();
  }
  public function render($filename){

   ob_start();
   if(file_exists($this->_scriptPath.$filename)){
     include($this->_scriptPath.$filename);
    } else throw new TemplateNotFoundException();
    return ob_get_clean();
  }
  public function __set($k, $v){
      $this->properties[$k] = $v;
  }
  public function __get($k){
      return $this->properties[$k];
  }
}

và một mẫu sẽ trông như sau:

<html>
      <head>
         <title><?=$this->title?></title>
      </head>
      <body>Hey <?=$this->name?></body>
</html>

và gọi nó sẽ giống như:

$view = new Template();
$view->title="Hello World app";
$view->properties['name'] = "Jude";
echo $view->render('hello.inc');

Theo như tôi nhớ, đây là cách mà các công cụ mẫu Symfony 1.x và Zend_View cũ trông như thế nào, và đối với tôi nó vẫn ổn.


TinyButStrong cũng làm một cái gì đó tương tự
Izkata

Làm thế nào để bạn xử lý vòng lặp trên một số điểm nói? Bạn có sử dụng một mẫu "phụ" riêng biệt không?
Ryan

Tôi tự hỏi liệu có cách nào để loại bỏ việc sử dụng $ this trong mẫu không, ví dụ chỉ là $ title hoặc $ name.
Ryan

1
@Ryan Bạn có thể sử dụng extract($this->properties);để trích xuất các thuộc tính thành các biến, ngay trước câu lệnh bao gồm.
Joyce Babu

1

Một khuôn mẫu sẽ luôn phụ thuộc vào các biến bên ngoài và với PHP là một ngôn ngữ động, không có cách nào để thực thi tại thời điểm biên dịch mà chúng sẽ ở đó. Vì vậy, cách tiếp cận đầu tiên có lẽ là tốt; vẫn còn một vài vấn đề

  • Ví dụ này không quan tâm đến mã hóa HTML đầu ra, điều đó có nghĩa là bạn có các lỗ hổng XSS tiềm năng.
  • Nếu một trong những biến đó không được đặt, mã sẽ đưa ra cảnh báo; bạn sẽ không thấy cảnh báo có ý nghĩa hay không (có thể biến là tùy chọn).

Một cách tiếp cận rất đơn giản là viết một hàm, tốt nhất là với một tên rất ngắn, chăm sóc cả hai thứ này; gọi nó _()dường như là một quy ước chung. Một phiên bản đơn giản có thể trông như thế này:

function _($raw) {
    if (isset($raw)) {
        return htmlspecialchars($raw);
    }
}

Và sau đó trong mẫu của bạn, bạn sẽ làm:

<title><?= _($title) ?></title>

Nhiều thứ để làm với _()chức năng:

  • Cung cấp cho nó một đối số thứ hai để cho phép ghi đè htmlspecialcharscuộc gọi, trong trường hợp bạn muốn truyền HTML vào nó thay vì chuỗi thô.
  • Thêm loại kiểm tra để các mảng và các đối tượng sẽ không mang lại ArrayObject, mà là một cái gì đó có ý nghĩa (hoặc chuỗi rỗng).
  • Thêm hỗ trợ quốc tế hóa (nhưng một đối số khác).
  • Khi ở chế độ gỡ lỗi, hãy in tên của biến với một số định dạng đặc biệt nếu nó không được xác định. Bằng cách này, bạn có thể thấy trong nháy mắt nếu có bất cứ điều gì sai với mẫu.

1

Cái đầu tiên template.phpdễ tải hơn là vào dreamweaver / bluegriffon / bất cứ thứ gì và được chỉnh sửa bởi một nhà thiết kế web không hiểu biết về phía máy chủ, thứ hai ... nó vẫn có thể, nhưng nó có nhiều khả năng bị hỏng trong quá trình chỉnh sửa .

Tôi sẽ đi với người đầu tiên để giữ cho nó trông giống như HTML với các lợi ích , cách mà các nhà thiết kế bên thứ 3 thường thích.


theo nghĩa nào? bạn có nghĩa là nó không thể được thay đổi bằng cách kéo và thả? về mặt văn bản, cái thứ hai giống như cái thứ nhất nhưng có một vài dòng PHP được bao bọc. Vì vậy, giả sử nó sẽ luôn có cấu trúc đó, việc chỉnh sửa bằng tay sẽ không khó hơn nữa.
Ryan

1
Một mẫu tốt có ý nghĩa với các nhà phát triển, một mẫu tuyệt vời có ý nghĩa với các nhà thiết kế.
Citricguy

1

Vì Codeigniter là khung yêu thích của tôi, tôi đề xuất cho bạn một giải pháp Codeigniter-ish:

class Template {
    protected $path, $data;

    public function __construct($path, $data = array()) {
        $this->path = $path;
        $this->data = $data;
    }

    public function render() {
        if(file_exists($this->path)){
            //Extracts vars to current view scope
            extract($this->data);

            //Starts output buffering
            ob_start();

            //Includes contents
            include $this->path;
            $buffer = ob_get_contents();
            @ob_end_clean();

            //Returns output buffer
            return $buffer;
        } else {
            //Throws exception
        }
    }       
}

Mẫu của bạn sẽ trông như sau:

<html>
    <head>
        <title><?=$title?></title>
    </head>
    <body>
        <?=$content?>
    </body>
</html>

Và bạn sẽ kết xuất nó bằng cách khởi tạo lớp, truyền dữ liệu trong một mảng dưới dạng tham số của hàm tạo và gọi phương thức 'render':

$data = array('title' => 'My title', 'content' => 'My content');
$tmpl = new Template('template.php', $data);
echo $tmpl->render();

Bằng cách này, bạn không trực tiếp thực thi mã được bao gồm và bạn cũng đang giữ mẫu của bạn có thể đọc được, vì vậy các nhà thiết kế sẽ rất vui.


-3

Php không phải là ngôn ngữ lập trình. Vì vậy, việc viết mạnh mẽ là không thể nếu ngôn ngữ của bạn không có hệ thống loại thực. Nhưng bạn có thể giúp IDE của mình như PHP-Storm thực hiện thủ thuật bạn muốn ...

Định nghĩa quan điểm của bạn:

interface mySimpleView { 
  public function getTitle(); 
} 

Tệp mẫu của bạn:

<?php
/**
* @var $this mySimpleView  <== Your IDE will try to use this type.
*/
?>

<h1><?= $this->getTitle() ?></h1>
<p><?= $this->getContent() ?></p> <!-- <= IDE will warn you !  -->

Tôi thích câu trả lời này (mặc dù rõ ràng "Php không phải là ngôn ngữ lập trình" sẽ luôn gây tranh cãi.
Ronnie
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.