2010-07-24 13 views
5

Tổng quanPHP - tạo khuôn mẫu với các thẻ tùy chỉnh - đây có phải là cách sử dụng hợp pháp của eval không?

Khoảng cuối năm 2009, tôi đã viết một hệ thống khuôn mẫu đơn giản cho PHP/HTML để được sử dụng trong nhà bởi nhà thiết kế của chúng tôi cho các trang web loại tài liệu-ware. Mục tiêu của hệ thống là cho phép tạo khuôn mẫu trong HTML thuần túy khác thông qua các thẻ tùy chỉnh được xử lý bởi PHP. Ví dụ, một trang templated có thể trông như thế này:

<tt:Page template="templates/main.html"> 
    <tt:Content name="leftColumn"> 
    <p> blah blah </p> 
    ... 
    </tt:Content> 
    <tt:Content name="rightColumn"> 
    <p> blah blah </p> 
    ... 
    </tt:Content> 
</tt:Page> 

Mẫu bản thân có thể trông như thế này:

<html> 
    <head>...</head> 
    <body> 
    <div style="float:left; width:45%"> 
     <tt:Container name="leftColumn" /> 
    </div> 
    <div style="width:45%"> 
     <tt:Container name="rightColumn" /> 
    </div> 
    </body> 
</html> 

Bên cạnh những trang và nội dung/thẻ container, có một vài thẻ khác bao gồm trong cốt lõi cho các công cụ như kiểm soát luồng, lặp qua bộ sưu tập, xuất các giá trị động, v.v. Khung được thiết kế để dễ dàng thêm bộ thẻ của riêng bạn được đăng ký theo tiền tố và không gian tên khác.

Tuỳ Thẻ để PHP

Làm thế nào để chúng ta phân tích các thẻ tùy chỉnh? Vì không bảo đảm rằng tệp HTML là XML được định dạng tốt nên các giải pháp như XSLT/XPATH sẽ không đáng tin cậy. Thay vào đó, chúng tôi sử dụng regex để tìm các thẻ có tiền tố đã đăng ký và thay thế bằng các mã PHP. Các mã PHP là một thiết kế dựa trên stack ... khi gặp phải một thẻ mở, một đối tượng đại diện cho thẻ được tạo ra đẩy vào stack, và "chức năng khởi tạo" của nó (nếu có) chạy. Bất cứ khi nào một thẻ đóng đã đăng ký được gặp phải, đối tượng gần đây nhất được bật ra khỏi ngăn xếp và "hàm kết xuất" của nó chạy.

Vì vậy, sau khi khuôn khổ thay thế các thẻ khuôn mẫu với PHP, trang ví dụ của chúng ta có thể trông giống như thế này (trong bất động sản đó là một chút xấu hơn):

<?php $tags->push('tt', 'Page', array('template'=>'templates/main.html')); ?> 
    <?php $tags->push('tt', 'Content', array('name'=>'leftColumn')); ?> 
    <p> blah blah </p> 
    ... 
    <?php $tags->pop(); ?> 
    <?php $tags->push('tt', 'Content', array('name'=>'rightColumn')); ?> 
    <p> blah blah </p> 
    ... 
    <?php $tags->pop(); ?> 
<?php $tags->pop(); ?> 

Các tốt, xấu, và eval

Bây giờ, làm cách nào để thực thi mã PHP mới được tạo của chúng tôi? Tôi có thể nghĩ ra một vài lựa chọn ở đây. Cách đơn giản nhất là chỉ đơn giản là eval chuỗi và hoạt động đủ tốt. Tuy nhiên, bất kỳ lập trình viên sẽ cho bạn biết "eval là ác, không sử dụng nó ..." vì vậy câu hỏi là, liệu có bất cứ điều gì thích hợp hơn eval rằng chúng ta có thể sử dụng ở đây?

Tôi đã xem xét sử dụng tệp tạm thời hoặc được lưu trong bộ nhớ cache, sử dụng các luồng đầu ra, v.v., nhưng theo như tôi thấy, chúng không cung cấp bất kỳ lợi thế thực nào trên eval. Caching có thể tăng tốc mọi thứ, nhưng trong thực tế tất cả các trang web chúng tôi có trên điều này đã nhanh chóng nhanh chóng, vì vậy tôi thấy không cần phải thực hiện tối ưu hóa tốc độ vào thời điểm này.

Câu hỏi

Đối với mỗi trong những điều trong danh sách này: nó là một ý tưởng tốt? Bạn có thể nghĩ ra một giải pháp thay thế tốt hơn không?

  • toàn bộ ý tưởng nói chung (tags tùy chỉnh cho html/php)
  • chuyển đổi thẻ mã php thay vì xử lý trực tiếp
  • cách tiếp cận dựa trên stack
  • việc sử dụng eval (hoặc tương tự)

Cảm ơn bạn đã đọc và TIA để được tư vấn. :)

+0

+1 cho câu hỏi được định dạng tốt, được viết tốt và được suy nghĩ kỹ lưỡng. – gpmcadam

+0

Cảm ơn. OT: Tôi thích avatar của bạn. Gigantor từ Evol Intent đang mặc một chiếc áo thun với thiết kế y hệt như vậy khi anh đến đây vài năm trước. Nó có phải là logo cho một hãng thu âm hay một thứ gì đó, hay chỉ là một thiết kế ngẫu nhiên? Đã tự hỏi rằng trong nhiều năm. –

Trả lời

2

Hãy để tôi ủng hộ một cách tiếp cận khác nhau. Thay vì tạo mã PHP động và sau đó cố gắng tìm ra cách thực thi nó một cách an toàn, hãy thực thi nó trực tiếp khi bạn gặp phải các thẻ. Bạn có thể xử lý toàn bộ khối HTML trong một lần chuyển và xử lý từng thẻ khi bạn gặp phải thẻ đó ngay lập tức.

Viết vòng lặp tìm kiếm thẻ. Cấu trúc cơ bản của nó sẽ trông giống như sau:

  1. Tìm thẻ tùy chỉnh mà bạn tìm thấy tại vị trí n.
  2. Mọi thứ trước vị trí n phải là HTML đơn giản, vì vậy hãy lưu nó ra để xử lý hoặc xuất nó ngay lập tức (nếu bạn không có thẻ trên ngăn xếp $tags có thể bạn không cần phải lưu nó ở bất kỳ đâu).
  3. Thực thi mã thích hợp cho thẻ. Thay vì tạo mã gọi $tags->push, chỉ cần gọi trực tiếp $tags->push.
  4. Quay trở lại bước 1.

Với phương pháp này bạn chỉ gọi các hàm PHP trực tiếp, bạn không bao giờ xây dựng mã PHP một cách nhanh chóng và sau đó thực hiện nó sau này. Sự cần thiết cho eval đã biến mất.

Về cơ bản, bạn sẽ có hai trường hợp cho bướC# 3. Khi bạn gặp một thẻ mở, bạn sẽ làm ngay lập tức push. Sau đó, khi bạn nhấn thẻ đóng, bạn có thể thực hiện pop và sau đó xử lý thẻ theo cách thích hợp, bây giờ bạn đã xử lý toàn bộ nội dung của phần tử tùy chỉnh.

Việc xử lý HTML theo cách này cũng hiệu quả hơn. Thực hiện nhiều tìm kiếm và thay thế trên một chuỗi HTML dài không hiệu quả như mỗi tìm kiếm và mỗi thay thế là O ( n) trên chiều dài của chuỗi.Có nghĩa là bạn liên tục quét chuỗi lặp đi lặp lại, và mỗi lần bạn thay thế, bạn phải tạo ra các chuỗi hoàn toàn mới có độ dài tương tự. Nếu bạn có 20KB HTML thì mỗi thay thế liên quan đến việc tìm kiếm thông qua 20KB đó và sau đó tạo một chuỗi 20KB mới sau đó.

+0

John, điều này nghe giống như cách thích hợp để làm điều đó cho HTML thuần túy hoàn toàn (không có PHP), và tôi sẽ chụp một bức ảnh. Tuy nhiên khi tôi thiết kế hệ thống templating, tôi nghĩ nó sẽ rất tuyệt nếu bạn có thể trộn PHP vào, và tôi nghĩ một số trang web đã bị trộn lẫn. Có lẽ đó là một ý tưởng thiết kế tồi, nhưng tôi nghĩ nó bây giờ cần phải sử dụng 'eval' hoặc tương tự. Ngoài ra, tại thời điểm thiết kế, tôi nghĩ rằng việc lưu vào bộ nhớ cache PHP được tạo ra sẽ là một ý tưởng hay. Bạn có nghĩ rằng có thể so sánh hoặc nhanh hơn cách tiếp cận của bạn ở đây không? –

+0

Caching sẽ giảm thiểu các mối quan tâm về hiệu năng, vì vậy bạn chỉ cần phải lo lắng về khía cạnh bảo mật. –

+0

Nếu bạn cho phép người dùng kết hợp trong mã PHP thì đó là toàn bộ 'quả bóng sáp'. Một khi bạn đang thực hiện mã tùy ý sau đó đáng lo ngại về một cuộc gọi đến eval là khá vô nghĩa. Mối quan tâm bảo mật của bạn sau đó sẽ thay đổi để sandboxing mã PHP, sử dụng safe_mode của PHP, khóa quyền người dùng để họ không thể thực hiện các cuộc gọi hệ thống ngẫu nhiên(), v.v. –

0

Vâng, thay vì eval, bạn có thể làm những gì zend và khung lớn khác làm, sử dụng đầu ra đệm:

ob_start(); 
include($template_file); //has some HTML and output generating PHP 
$result = ob_get_contents(); 
ob_end_clean(); 
+0

Điều này có nghĩa là lưu mã PHP trung gian (được tạo từ phân tích cú pháp đánh dấu) vào một tệp và sau đó bao gồm nó. Điều này liên quan đến rất nhiều truy cập đĩa mà chỉ đơn giản bằng cách sử dụng 'eval' thì không. Lợi thế của việc làm theo cách này là gì? –

+0

Tôi hiểu ý của bạn là gì. Câu trả lời này không sai, nhưng không thực tế ở đây. Tôi sẽ đăng một câu trả lời khác, nhiều câu trả lời lành mạnh hơn –

+0

@no, câu trả lời mới của tôi đã được đăng. –

2

tôi đăng câu trả lời khác bởi vì nó hoàn toàn khác so với lần đầu tiên, mà cũng có thể là quý giá.

Về cơ bản, câu hỏi này yêu cầu cách thực thi mã PHP bằng regex. Nó có vẻ không rõ ràng, nhưng đây là những gì eval đang có ý định thực hiện.

Với điều đó nói rằng, thay vì thực hiện một pass preg_replace và sau đó làm một eval, bạn chỉ có thể sử dụng hàm preg_replace_callback của PHP để thực thi một đoạn mã khi được so khớp.

Xem ở đây bao hàm hoạt động: http://us.php.net/manual/en/function.preg-replace-callback.php

+0

Mike, xem phản ứng của tôi với John (các bạn đang nói khá nhiều điều tương tự tôi nghĩ). –