情報科学屋さんを目指す人のメモ

方法・手順・解説を書き残すブログ。私と同じことを繰り返さずに済むように。

【WordPress】「WP_Filesystem($credentials)」で落ちる原因と対策メモ(WordPress Filesystem API関連)

PHP (25) WordPress (80) WordPress-Filesystem API (2) WordPressプラグイン (26)

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を追加しなおしたところ、うまく動作しました

コメント(0)

新しいコメントを投稿