Linkback Plugin を導入する

ものすごく苦労しました…… php 界隈の知識が乏しいのが災いしました。

環境はこんな感じです。

# nginx -v
nginx version: nginx/1.10.3

# php -v
PHP 7.0.15 (cli) (built: Mar 20 2017 17:47:24) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2017 Zend Technologies
    with Zend OPcache v7.0.15, Copyright (c) 1999-2017, by Zend Technologies

トラックバック url を表示させた時に、ヘンな url だなぁと思っていたんですよね。

/lib/plugin/linkback/exe/trackback.php/wiki:dokuwiki:plugin_linkback

これ、単純に考えたら web サーバーは trackback.php っていうディレクトリを参照しに行きますよね。

2017/04/14 21:07:33 [error] 2303#0: *224 open() "/path/to/dokuwiki/lib/plugins/linkback/exe/trackback.php/blog:2017:install_plugin_linkback" failed (20: Not a directory), client: ip.addr.of.client, server: webserver, request: "POST /dokuwiki/lib/plugins/linkback/exe/trackback.php/blog:2017:install_plugin_linkback HTTP/1.0", host: "host"

やっぱり nginx はディレクトリとして参照しに行って error_log を出力していました……そりゃそうですよね。

実は答えに辿り着くまでにものすごく苦労したんですが、結果的には nginx:PHP FastCGI Example を参考に、nginx.conf の php 処理の部分を見直しました。

location ~ [^/]\.php(/|$) {
    fastcgi_split_path_info         ^(.+\.php)(/.*)$;
    if (!-f $document_root$fastcgi_script_name) {
        return 404;
    }

    fastcgi_pass                    unix:/run/php-fpm.socket;

    include                         /etc/nginx/fastcgi.conf;
    fastcgi_param SCRIPT_FILENAME   $document_root$fastcgi_script_name;
    fastcgi_param REDIRECT_STATUS   200;
    fastcgi_param PATH_INFO         $fastcgi_path_info;
    fastcgi_param PATH_TRANSLATED   $document_root$fastcgi_path_info;
}

php-fpm に処理を投げるための location の正規表現が \.php$ だったんですが、後ろの / があってもフックできるようになっています。

そしてキモは fastcgi_split_path_info ^(.+\.php)(/.*)$; ですね。これで trackback.php/wiki:dokuwiki... みたいな妙な url を分割しているようです。

ついでに、参照した nginx のドキュメントでは fastcgi_params1) に環境変数 PATH_INFO PATH_TRANSLATED を設定していますが、今回の場合は fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info; として location 内に設定しています。

ちなみにこの設定だと、もし trackback.php とかそういう名前のディレクトリが存在した場合にちゃんと動作しないという言及も見掛けました。それに対応するために php.ini で cgi.fix_pathinfo=0 として、nginx.conf にあといくつか location を追加して……という対応もあるようですが、今回はうまく動かなかったので見送っています。

単純にブラウザからトラックバック url を叩いてみればよいですね。

<?xml version="1.0" encoding="iso-8859-1"?>
<response>
<error>1</error>
<message>Trackback was not received via HTTP POST.</message>
</response>

ブラウザから直叩きでは GET メソッドになってしまうので結果はエラーですが、少なくとも trackback.php は動いていることが確認できます。

[2017.04.19 追記] ここに書いてある error と warning は、どちらも GitHub に Pull Request 投げて修正してもらいました

trackback.php が動くようになったので、じゃあ pingback.php も確認しようということで、ブラウザから叩いてみたところ……

Deprecated: Methods with the same name as their class will not be constructors in a future version of PHP; PingbackServer has a deprecated constructor in /path/to/dokuwiki/lib/plugins/linkback/exe/pingback.php on line 35
Fatal error: Uncaught Error: Call to undefined method PingbackServer::IXR_Server() in /path/to/dokuwiki/lib/plugins/linkback/exe/pingback.php:45 Stack trace: #0 /path/to/dokuwiki/lib/plugins/linkback/exe/pingback.php(193): PingbackServer->PingbackServer() #1 {main} thrown in /path/to/dokuwiki/lib/plugins/linkback/exe/pingback.php on line 45

……落ちました。orz

実は前半の warning は、一番最初に trackback.php を叩いた時にも表示されていたんですよね……php-fpm の起動後、最初の1回だけ表示されるとかそんな感じなんだと思います。まぁ warning だし、「クラス名と同名のメソッドは、そのうちコンストラクターとして扱われなくなるよ」と、要はそういうことを言っているんだと思いますが、当面は問題なし! として無視していたんですが。

今回 pingback.php が落ちた箇所を見てみると……

class PingbackServer extends IXR_Server {
 
    // helper instance
    var $tools;
 
    /**
     * Register service and construct helper
     */
    function PingbackServer() {
        $this->tools =& plugin_load('tools', 'linkback');
        $this->IXR_Server(array (                          # ←ここで落ちた
            'pingback.ping' => 'this:ping',
 
        ));
    }

……うわぁぁぁ思いっきりコンストラクターだ。

warning は class PingbackServer のコンストラクターとして、クラス名と同名のメソッド function PingbackServer が存在していることに対するものですね。ここは将来的に使えなくなる書き方だけれど、現状は問題がないはずです。 そして落ちた箇所は $this->IXR_Server( で、この PingbackServer クラスは extends IXR_Server で IXR_Server クラスを継承していると。やりたいことは親クラスのコンストラクターの呼び出しっぽいですね。

さて IXR_Server クラスは require_once (DOKU_INC . 'inc/IXR_Library.php'); らしいので、これを覗いてみることにします。

class IXR_Server {
    var $data;
    /** @var array */
    var $callbacks = array();
    var $message;
    /** @var array */
    var $capabilities;
 
    /**
     * @param array|bool $callbacks
     * @param bool $data
     * @param bool $wait
     */
    function __construct($callbacks = false, $data = false, $wait = false) {
        $this->setCapabilities();
        if($callbacks) {
            $this->callbacks = $callbacks;
        }
        $this->setCallbacks();
 
        if(!$wait) {
            $this->serve($data);
        }
    }

あ~なるほど……こちらはクラス名と同名のコンストラクターではなく、たぶん新しいコンストラクターの定義方法に対応しているんですね。だから Call to undefined method PingbackServer::IXR_Server() になるんですね。

子クラスから親クラスのコンストラクターを呼ぶとか、あまりにも普通に行うことだし、__construct() を呼ぶ方法なんて調べりゃわかるでしょ。ということで php マニュアル > 言語リファレンス > クラスとオブジェクト > コンストラクタとデストラクタ です。parent::__construct() で呼べるらしいのですね。よし、pingback.php を修正します。

--- pingback.php.orig   2017-04-15 15:22:42.326965674 +0900
+++ pingback.php        2017-04-15 16:28:24.718717123 +0900
@@ -42,7 +42,7 @@ class PingbackServer extends IXR_Server
      */
     function PingbackServer() {
         $this->tools =& plugin_load('tools', 'linkback');
-        $this->IXR_Server(array (
+        parent::__construct(array (
             'pingback.ping' => 'this:ping',
 
         ));

例によってブラウザから直接叩いてみた結果。

XML-RPC server accepts POST requests only.

おっけーですね。trackback.php と同じように「GET じゃダメ」エラーが出てます。

この wiki の中の記事で相互に trackback やら pingback やら飛ばしてみた感じは、うまく動いているようですね。

そして今回得た教訓は warning でももう少し気に掛けましょう ですかね……


1)
うちの環境では fastcgi.conf
  • wiki/dokuwiki/plugin_linkback
  • 最終更新: 2019/02/17 15:59