讓 WP2.0 可以正常trackback

An english version of this post can be found here .

把站台從 wordpress 1.5 升上 wordpress 2.0 的人們,可能發現不能 Trackback了。我,也中標了。

Ah Knight’s Blog提供了一個蠻暴力的解法變通的方法:把資料庫中的 to_ping欄位給清光光,並提及可能的原因是:

this problem is because of to_ping field contain some char ( including space, tab which represent by \n\r )

正好在 IRC 上,我跟 ijliaopriv也在討論這個問題,既然問題可能出在 to_ping這個欄位,於是乾脆進source裡翻翻看,看問題到底出在哪裡。

不翻則已,一翻嚇出一聲冷汗。

WordPress 1.5以前發表文章要送出時,使用者得先等 trackback/ping 別人的動作完成。當有許多的URL要trackback時,一個個tackback的結果是換來使用者的漫長等待。因此,在 WordPress 2.0以後,改用了另外一種方式以減少文章按下「發表」之後的等待。其作法是另外用fsockopen呼叫另一個 script來作trackback的動作:wp-admin/execute-pings.php

問題就出在這個 wp-admin/execute-pings.php 裡。

在 wp-admin/execute-pings.php 中,trackback的部份是這樣子實作的:


// Do Trackbacks
while ($trackback = $wpdb->get_row("SELECT ID FROM $wpdb->posts WHERE TRIM(to_ping) != '' AND post_status != 'draft' LIMIT 1")) {
    echo "Trackback : $trackback->ID";
    do_trackbacks($trackback->ID);
} ?>

正常情況下,程式去讀出資料庫中第一筆需要trackback 的post,做完 do_trackbacks,然後以此類推,直到最後一筆做完為止。但是有沒有發現到,萬一第一筆(or第 n筆)沒有trackback成功,仍然留在資料庫中時,會發生什麼事?

是的,用 while這個寫法會使得程式一直嘗試對第一筆符合的資料做 trackback 動作,永遠不會停下來。(這就是我討厭 while的地方)

於是程式永遠在跑第一筆,當然不可能跑到最後一筆(就是要送出 trackback 的那一筆),於是乎,trackback就沒有送出去。

那麼,又是什麼原因為讓之前沒送成功的trackback再次失敗呢?

第一種情況:對方的網頁根本就關掉 trackback了,或是要trackback的網址已經不存在了。那麼trackback當然會失敗。

第二種情況:在 wp-includes/function-post.php 的 do_trackbacks()裡,要先呼叫 wp-includes/function-post.php 的 get_to_ping() 把當要trackback的網址(to_ping)切成array,但是當 to_ping 只剩下一些怪怪的值,如 “\n”(換行)符號,會被下面這一行濾掉:

$to_ping = preg_split('/\s/', $to_ping, -1, PREG_SPLIT_NO_EMPTY);

於是傳回的array變empty array,再碰上 do_trackbacks()裡的這一行:


if ( empty($to_ping) )
return;

噹噹! 就什麼也不做地走掉了,然後回去又碰到 while,就是永無止盡的無窮迴圈了。


解決辦法:在 WP 出新版/新patch解決這個問題之前,可以試試自己改source:

PS. 這只是頭痛醫頭,pingback 也有同樣的問題,不過我不管他了。XD

wp-includes/function-post.php line 700:


if ( empty($to_ping) )
return;

改成


if ( empty($to_ping) ) {
    $wpdb->query("UPDATE $wpdb->posts SET to_ping = '' WHERE ID ='$post_id'");
    return;
}

wp-admin/execute-pings.php line 21:


// Do Trackbacks
while ($trackback = $wpdb->get_row("SELECT ID FROM $wpdb->posts WHERE TRIM(to_ping) != '' AND post_status != 'draft' LIMIT 1")) {
  echo "Trackback : $trackback->ID";
  do_trackbacks($trackback->ID);
}

改成


// Do Trackbacks
// The old code will cause infinite loop when trackback failed.
//

$trackbacks = $wpdb->get_results("SELECT ID FROM $wpdb->posts WHERE TRIM(to_ping) != '' AND post_status != 'draft'");

if (is_array($trackbacks) && count($trackbacks)) {
    foreach ($trackbacks AS $trackback  ) {
        echo "Trackback : $trackback->ID";
        do_trackbacks($trackback->ID);

    } // end foreach

}

或是直接下載 patch檔 解開後,放到要patch的檔案目錄下。在命令列下用 patch 指令(如果有的話):


patch functions-post.php functions-post.php.diff
patch execute-pings.php execute-pings.php.diff

Comments are closed.

%d 位部落客按了讚: