
用 WordPress 做網站,遲早會遇到這種需求:想在某個頁面顯示一份自己寫的清單、呼叫某個函式、放一段 PHP 程式。可是在編輯器裡貼上 <?php ... ?>,前台不是原封不動顯示出來,就是整段不見。
這是 WordPress 刻意的設計。這篇說明為什麼,以及正確的做法。(原本另外三篇〈讓 PHP 頁面執行 WordPress 的簡碼〉、〈Revolution 外掛的 PHP 簡碼〉、〈讓 WordPress 小工具中的文字運行 PHP 代碼〉的內容已經整合到這裡。)
為什麼 WordPress 的內容不能執行 PHP?
文章、頁面、小工具的內容都存在資料庫裡,而且很多人都能編輯:編輯、作者、外包寫手。如果內容裡的 PHP 會被執行,等於所有能編輯內容的人,都能在你的主機上執行任何程式,例如讀取 wp-config.php 裡的資料庫密碼、偷偷建立管理員帳號、植入後門。只要其中一個帳號被盜,整個網站就淪陷了。
所以網路上那些「讓文章、小工具可以執行 PHP」的外掛,以及舊教學裡用 eval() 把文字當成程式執行的寫法,都不建議使用。
舊版小工具的 eval 寫法請移除
原本另一篇文章教大家在 functions.php 加一段程式,透過 widget_text 這個 filter,用 eval() 執行文字小工具裡的 PHP。問題是:
eval()會把小工具裡的文字全部當成程式執行,打錯一個字網站就白畫面;小工具內容被人改過,就是一個現成的後門。- 新版 WordPress 的小工具已經改成區塊,這段程式本來就不一定有作用。
當初的需求是想在小工具裡顯示標籤雲,現在直接用內建的「標籤雲」區塊就可以,不需要任何程式。如果你的 functions.php 裡還留著這段,建議刪掉。
正確做法一:自訂短代碼(最常用)
把 PHP 寫在子主題的 functions.php(或 Code Snippets 外掛)裡,包成一個短代碼,再到頁面裡放短代碼。PHP 程式碼只有管理員動得到,編輯頁面的人只能「使用」它,不能改它。
以舊版文章的例子「在頁面顯示某個分類的最新 10 篇文章」來說:
add_shortcode( 'cat_posts', 'ahwei_cat_posts_shortcode' );
function ahwei_cat_posts_shortcode( $atts ) {
$atts = shortcode_atts(
array(
'cat' => 0, // 分類 ID
'number' => 10, // 顯示篇數
),
$atts,
'cat_posts'
);
$query = new WP_Query( array(
'cat' => absint( $atts['cat'] ),
'posts_per_page' => min( 50, max( 1, absint( $atts['number'] ) ) ),
'ignore_sticky_posts' => true,
'no_found_rows' => true,
) );
if ( ! $query->have_posts() ) {
return '';
}
$html = '<ul class="cat-posts">';
while ( $query->have_posts() ) {
$query->the_post();
$html .= sprintf(
'<li><a href="%s">%s</a></li>',
esc_url( get_permalink() ),
esc_html( get_the_title() )
);
}
$html .= '</ul>';
wp_reset_postdata();
return $html;
}使用方式:在頁面加入「短代碼」區塊,填入:
[cat_posts cat="4" number="10"]cat是分類 ID:到後台「文章 → 分類」,點進要用的分類,網址裡tag_ID=後面的數字就是。number是篇數,程式裡限制最多 50 篇,避免有人填太大拖慢網站。
短代碼要「回傳」,不要「輸出」
寫短代碼最常犯的錯,是在函式裡用 echo 直接輸出,結果內容跑到頁面最上面,不在你放短代碼的位置。短代碼的函式一定要把 HTML 組成字串,用 return 回傳。
如果手上的程式碼本來就是用 echo 寫的、不想改,可以用輸出緩衝把它接起來:
add_shortcode( 'my_block', function () {
ob_start();
// 原本用 echo 輸出的程式碼放在這裡
echo '<p>Hello</p>';
return ob_get_clean();
} );舊版文章的查詢寫法也過時了
舊版文章用 query_posts() 抓文章、用 showposts 設定篇數。query_posts() 會把整個頁面的主要查詢換掉,容易讓頁面標題、分頁、側邊欄跟著出錯,官方文件也不建議使用;showposts 則早就改名為 posts_per_page。上面的範例改用 WP_Query,不會影響頁面本身。
其實不一定要寫程式
「顯示某個分類的文章」這個需求,新版 WordPress 內建的「最新文章」區塊或「查詢迴圈」區塊就能做到,可以直接選分類、篇數與顯示方式。先看看內建區塊能不能滿足,再決定要不要寫程式。
正確做法二:用短代碼載入獨立的 PHP 檔
舊版文章的做法,是把 PHP 檔放在 wp-content/uploads/php-content/,再用 [phpcode file="post4"] 載入。程式碼不用全部塞在 functions.php,概念不錯,但有兩個問題:
- 檔案放在
uploads資料夾:那是網站開放上傳檔案的地方,也是駭客最喜歡放 PHP 後門的位置,很多資安外掛與主機會封鎖或掃描那裡的 PHP 檔。自己的程式放在那裡,很難分辨哪個是正常的、哪個是被植入的。 - 沒有檢查檔名:
file參數直接接到路徑上,填入../../這類的值,就能載入網站上其他的 PHP 檔。
修正後的版本:檔案改放在子主題裡的 php-content 資料夾,並且只允許英文小寫、數字、底線與減號的檔名。
add_shortcode( 'phpcode', 'ahwei_phpcode_shortcode' );
function ahwei_phpcode_shortcode( $atts ) {
$atts = shortcode_atts( array( 'file' => '' ), $atts, 'phpcode' );
// 只留下英文小寫、數字、底線與減號,擋掉 ../ 這類路徑
$name = sanitize_key( $atts['file'] );
if ( '' === $name ) {
return '';
}
$path = get_stylesheet_directory() . '/php-content/' . $name . '.php';
if ( ! is_file( $path ) ) {
return '';
}
ob_start();
include $path;
return ob_get_clean();
}使用方式跟以前一樣:在子主題裡建立 php-content/post4.php,頁面裡放 [phpcode file="post4"]。以前放在 uploads/php-content/ 的檔案,搬到子主題的 php-content 資料夾就能繼續用,搬完記得把 uploads 裡的舊檔刪掉。
被載入的 PHP 檔裡用 echo 輸出沒關係(上面的程式會用輸出緩衝接起來),但要輸出網址、標題這類內容時,記得用 esc_url()、esc_html() 跳脫。
正確做法三:頁面範本(傳統主題)
整個頁面都要用自己的 PHP 排版時,做成頁面範本比較適合。在子主題的資料夾裡建立一個檔案,例如 template-cat-list.php:
<?php
/**
* Template Name: 分類文章列表
*/
get_header();
?>
<main id="main" class="site-main">
<?php
while ( have_posts() ) :
the_post();
the_title( '<h1 class="entry-title">', '</h1>' );
the_content();
endwhile;
// 在頁面內容下方,顯示分類 4 的最新 10 篇文章
echo ahwei_cat_posts_shortcode( array( 'cat' => 4, 'number' => 10 ) );
?>
</main>
<?php
get_footer();最上面的 Template Name 註解就是範本名稱。存檔後,在頁面編輯畫面右側的「範本」設定,就能選擇「分類文章列表」。範本裡直接呼叫做法一寫好的函式,不用再寫一次查詢。
區塊主題(例如 Twenty Twenty-Four)的版面是在「外觀 → 編輯器」裡用區塊排的,建議用做法一的短代碼,搭配「短代碼」區塊使用。
進階做法:自訂區塊
如果希望編輯的人可以在區塊編輯器裡直接調整設定(例如用選單挑分類、篇數),可以做成自訂區塊。WordPress 官方提供 @wordpress/create-block 工具產生區塊外掛的骨架,「動態區塊」可以在 PHP 裡產生內容。這需要一些 JavaScript 開發環境的基礎,一般網站用短代碼就很夠了。
反過來:在 PHP 範本裡執行短代碼
外掛給你一段短代碼(例如表單、輪播),但你想放在主題的 PHP 範本裡,直接寫 [xxx] 不會有作用,要用 do_shortcode():
<?php echo do_shortcode( '[your_shortcode]' ); ?>[your_shortcode] 換成外掛提供的短代碼。
舊版文章的例子是在 PHP 裡顯示 Slider Revolution 的輪播,當時是直接呼叫外掛提供的 PHP 函式。比較好的做法是複製輪播設定裡提供的短代碼,用 do_shortcode() 輸出(以外掛實際顯示的短代碼為準):
<?php echo do_shortcode( '[rev_slider alias="home3"]' ); ?>home3 換成你的輪播代稱(alias)。用短代碼的好處是,之後就算停用了輪播外掛,頁面只會顯示這段短代碼文字;直接呼叫外掛的函式,外掛一停用就會因為找不到函式而整頁當機。
常見問題
可以直接改主題的 page.php 嗎?
可以,但請改在子主題裡。直接改主題本身的檔案,主題一更新就會被覆蓋。
Code Snippets 外掛也是把 PHP 存在資料庫,不是一樣危險嗎?
差別在「誰能改」。Code Snippets 預設只有管理員能新增、修改程式碼,性質跟改 functions.php 一樣;在文章或小工具裡執行 PHP,則是所有能編輯內容的人都能執行程式。
延伸閱讀
- WordPress functions.php 實用程式碼整理:10 個常用又安全的小技巧
- WordPress 顯示相關文章:不用外掛,依分類與標籤撈出相關文章
- WordPress 恢復舊版小工具與傳統編輯器(停用古騰堡)的兩種方法
客製功能要做得到,也要做得安全,程式放對地方,網站才不會留下破口。正威網路行銷是 25 年經驗的基隆網頁設計工作室,提供客製化網頁設計與網站維護託管,歡迎來聊聊。







發表留言
還沒有人留言,歡迎第一個分享你的想法或問題。留言經審核後才會顯示,Email 不會公開。