スポンサーリンク
WordPressプラグイン開発時に、WordPress Filesystem APIを利用しました。POSTリクエストを処理する場所を変更したところ、ディレクトリのコピーに失敗するようになってしまい、原因をたどった結果、WP_Filesystem($credentials)を呼び出す際に、落ちてしまっていることが分かりました。そのときに施した対策を紹介します。
目次
スポンサーリンク
実装しようとしていたこと
プラグインの設定画面のフォームからPOSTリクエストを受け取り、WordPress Filesystem APIを使用してディレクトリをコピーして、結果を反映するためにwp_redirectを使用して再読込する、ということをしようとしていました。このファイル操作後にwp_redirectでページを読み込み直す、という仕組みについては、themes.phpのdelete actionの部分を参考にしました(/wp-admin/themes.php source code)。
wp_redirectが動かない
POSTの受け取り処理は、add_options_pageで指定したcallback(つまり、設定画面を表示する関数)の冒頭に書きました(参考:管理メニューの追加 - WordPress Codex 日本語版)。
そして、Filesystem APIを使用してファイル操作を実装したところ、wp_redirectが動作しないことがわかりました。
この理由は、add_options_pageでしていした callbackが呼び出される前に、options-general.php実行時点ですでにページ出力が始まってしまっており、wp_redirectが行うヘッダの変更ができなくなっているからでした。
wp_redirect will send an header, so printing/echoing something before it, will have result in failure.
So check and see if before this: (引用元)
つまり、ページの出力(echo)が始まる前の時点で、POSTの処理およびファイルの操作を行い、wp_redirectまで実行する必要がありました。
POST処理の場所を移動
ここで、POST処理の場所を手前に移動することにしました。
まず最初にプラグインのphp(プラグイン読み込み時に実行されるphp)に直書きしてみましたが、ここではうまく動作せず(原因を分離していない)、admin_post_(action)に追加してみても、うまく動きませんでした。
wp_redirectが動くようにするとFilesystem APIがコケる
この過程で気がついたのですが、wp_redirectを動かそうと、処理を手前に持ってくると、今度はFilesystem APIのほうが動かなくなっていました。
Filesystem APIの作法では、まず書き込み権限を得るために、request_filesystem_credentialsを呼び出すのが決まりになっています。
ここで、権限を得るのに失敗しているのかと思ったのですが、どうやら戻り値はちゃんとTrueになっていて、その後のチェックも通過していました。
そして、そこから先へ進むと、「WP_Filesystem($credentials)」というこれまたFilesystem APIの作法(権限を登録して、global $wp_filesystemを有効にする)で落ちていることが分かりました。
原因:file.phpの読み込みタイミング
そこで、似たような問題に遭遇している人を探してみたところ、「wordpress - Use WP_Filesystem outside WP_admin - Stack Overflow」が参考になりました。
この対策は、WP_filesystemを利用するより前に「require_once(ABSPATH . 'wp-admin/includes/file.php');」を呼び出すというものでした。そして、実際にこれを追加してみたところ、うまく動作しました。なので、これはこれで対策として機能しているのですが、結局この方法は採用せず、別の方法で対策しています(後述)。
つまり、WP_Filesystemを呼び出す前にfile.phpの読み込みが必要なのに、それより早いところまで処理を移動してしまったため、落ちてしまっていた、ということのようです。
(この時点では、WordPressのどのページでも呼び出される感の強いinit actionにPOSTの処理を実装していました(Plugin API/Action Reference/init « WordPress Codex)。)
対策:呼び出しタイミングの変更
というわけなので、WP_filesystem APIの呼び出しタイミングが、WordPressのデザインに逆らう位置に来てしまっているから、それ自体を直す必要がある、と考えるほうが適切だと思います。
そこで、「find /path/to/wp-admin -type f -print | xargs grep "/file\.php" /dev/null」こんな感じでfile.phpの読み込み場所を探してみたところ、wp-admin/includes/admin.phpだけがHITしました(wp-admin/admin.phpではないので注意)。
ただ、ここからたどるより、とりあえず「admin_init」というactionがちょうど良さそうだったので、ここにPOSTを処理するcallbackを追加しなおしたところ、うまく動作しました。
スポンサーリンク
スポンサーリンク