0%

正威網路行銷工作室,專注於基隆、台北與全台客製化網頁設計,提供專業 RWD 響應式、SEO 搜尋引擎優化、品牌形象設計與網站維護託管服務。

搜尋文章
聯絡資訊
地址基隆市仁二路 220 號 5 樓
追蹤我們

WordPress 頁面怎麼執行 PHP?自訂短代碼、頁面範本的安全做法

好用的css語法技巧

用 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 篇文章」來說:

PHP
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 寫的、不想改,可以用輸出緩衝把它接起來:

PHP
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 資料夾,並且只允許英文小寫、數字、底線與減號的檔名。

PHP
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
<?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
<?php echo do_shortcode( '[your_shortcode]' ); ?>

[your_shortcode] 換成外掛提供的短代碼。

舊版文章的例子是在 PHP 裡顯示 Slider Revolution 的輪播,當時是直接呼叫外掛提供的 PHP 函式。比較好的做法是複製輪播設定裡提供的短代碼,用 do_shortcode() 輸出(以外掛實際顯示的短代碼為準):

PHP
<?php echo do_shortcode( '[rev_slider alias="home3"]' ); ?>

home3 換成你的輪播代稱(alias)。用短代碼的好處是,之後就算停用了輪播外掛,頁面只會顯示這段短代碼文字;直接呼叫外掛的函式,外掛一停用就會因為找不到函式而整頁當機。

常見問題

可以直接改主題的 page.php 嗎?

可以,但請改在子主題裡。直接改主題本身的檔案,主題一更新就會被覆蓋。

Code Snippets 外掛也是把 PHP 存在資料庫,不是一樣危險嗎?

差別在「誰能改」。Code Snippets 預設只有管理員能新增、修改程式碼,性質跟改 functions.php 一樣;在文章或小工具裡執行 PHP,則是所有能編輯內容的人都能執行程式。

延伸閱讀

客製功能要做得到,也要做得安全,程式放對地方,網站才不會留下破口。正威網路行銷是 25 年經驗的基隆網頁設計工作室,提供客製化網頁設計與網站維護託管,歡迎來聊聊。

分享這篇文章

返回文章列表更多「Wordpress代碼教學」文章

發表留言

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

讓我們一起打造
您的專屬網站!