[WordPressコアを読む]プラグインのバージョンチェック

はじめに

WordPressのコアを読んで勉強しています。

一覧はWordPress Core Readingにありますので、こちらもどうぞ。

この投稿はWordPress4.3のコアを読んで作成されています。

極力バージョンアップの際には記事を更新するようにしますが、投稿作成時のコアバージョンにはご注意下さい。


 

WordPressのプラグインバージョンのチェック

WordPressのプラグインは、機能追加や修正などがあった際にはアップデートをする必要があります。

新しいバージョンのプラグインがリリースされると、管理画面のインストールされているプラグインの一覧で該当するプラグインの下に次のようなリンクが表示されます。

「新しいバージョンの [プラグイン名] が利用可能です。バージョン [新しいバージョン番号] の詳細を見るか、今すぐ更新して下さい。 」

スクリーンショット 2015-08-23 23.12.35


どのような仕組みでプラグインのバージョンをチェックしているのか?

WordPress本体がバージョンチェックを行うのは公式ディレクトリに掲載されているプラグインのみとなります。

wordpress.org より掲載申請を行い、専用のSubversionにコミットすることで公開されます。

では、どのようにしてバージョンのチェックを行っているのか見ていくことにします。


 

まずは、更新メッセージを表示している箇所を探す

スクリーンショット 2015-08-23 23.12.35

一覧用のテーブル

インストールされているプラグインの一覧は

wp-admin/includes/class-wp-plugins-list-table.php

に定義されているclass WP_Plugins_List_Tableで出力されています。

更新メッセージ部のHTML

 <tr class="plugin-update-tr active" id="hello-dolly-update" data-slug="hello-dolly" data-plugin="hello.php">...</tr>

プラグインの情報を表示するテーブル行(tr)が出力された後に、更新の必要があるプラグインに関してはplugin-update-trというcssクラスがついたテーブル行が足されていることが分かりました。

更新メッセージを出力しているのはどこか?

単純に、plugin-update-trというcssクラスで検索してもヒットしませんでした。

そこで先ほどの、class WP_Plugins_List_Tableを読み進めていくと、各行(各プラグイン)を出力するためのsingle_rowメソッドが以下のように定義されていることが分かりました。

/**
 * @global string $status
 * @global int $page
 * @global string $s
 * @global array $totals
 *
 * @param array $item
 */
 public function single_row( $item ) {

そして、当該メソッドでプラグイン情報を出力後にafter_plugin_row_[プラグインファイル]アクションフックが実行されていることを確認しました。

 /**
 * Fires after each specific row in the Plugins list table.
 *
 * The dynamic portion of the hook name, `$plugin_file`, refers to the path
 * to the plugin file, relative to the plugins directory.
 *
 * @since 2.7.0
 *
 * @param string $plugin_file Path to the plugin file, relative to the plugins directory.
 * @param array $plugin_data An array of plugin data.
 * @param string $status Status of the plugin. Defaults are 'All', 'Active',
 * 'Inactive', 'Recently Activated', 'Upgrade', 'Must-Use',
 * 'Drop-ins', 'Search'.
 */
 do_action( "after_plugin_row_$plugin_file", $plugin_file, $plugin_data, $status );

after_plugin_row_[プラグインファイル]アクションフックはどこからフックされているか?

インストールされているプラグインの一覧で、各プラグインの情報出力後に実行されるafter_plugin_row_[プラグインファイル]アクションをフックしている箇所は

wp-admin/includes/update.php

に定義されているwp_plugin_update_rowsメソッドでした。

当該メソッド内でのみadd_actionが登録されています。

 /**
 * @since 2.9.0
 */
 function wp_plugin_update_rows() {
 if ( !current_user_can('update_plugins' ) )
 return;

$plugins = get_site_transient( 'update_plugins' );
 if ( isset($plugins->response) && is_array($plugins->response) ) {
 $plugins = array_keys( $plugins->response );
 foreach( $plugins as $plugin_file ) {
 add_action( "after_plugin_row_$plugin_file", 'wp_plugin_update_row', 10, 2 );
 }
 }
 }

まずはログインしているユーザーがプラグインを更新する権限を持っているかをチェックしていますね。

次にTransients APIより、update_pluginsというキーで値を取得しています。

この値を表示すると次のような感じになりました。

※Akismet、Hello Dolly がインストールされていて、Hello Dollyに更新がある場合

 stdClass Object
 (
 [last_checked] => 1440341492
 [checked] => Array
 (
 [akismet/akismet.php] => 3.1.3
 [hello.php] => 1.5
 )

[response] => Array
 (
 [hello.php] => stdClass Object
 (
 [id] => 3564
 [slug] => hello-dolly
 [plugin] => hello.php
 [new_version] => 1.6
 [url] => https://wordpress.org/plugins/hello-dolly/
 [package] => https://downloads.wordpress.org/plugin/hello-dolly.1.6.zip
 )

)

[translations] => Array
 (
 )

[no_update] => Array
 (
 [akismet/akismet.php] => stdClass Object
 (
 [id] => 15
 [slug] => akismet
 [plugin] => akismet/akismet.php
 [new_version] => 3.1.3
 [url] => https://wordpress.org/plugins/akismet/
 [package] => https://downloads.wordpress.org/plugin/akismet.3.1.3.zip
 )

)

)

[response]プロパティに更新対象のプラグインが配列でセットされているようです。

どこで、Transientsに、update_pluginsというキーで情報をセットしているのか?

 

set_site_transient( 'update_plugins', [値] );

 

のようにして保存いるはずなので探してみると

 

/wp-includes/update.php

に定義されているwp_update_pluginsメソッドの中であることが分かりました。

ここでインストールされている各WordPressプラグインのバージョンを何らかの方法で公式ディレクトリと比較しているはず。

読み進めていくと以下のような箇所を見つけました。

 $url = $http_url = 'http://api.wordpress.org/plugins/update-check/1.1/';
 if ( $ssl = wp_http_supports( array( 'ssl' ) ) )
 $url = set_url_scheme( $url, 'https' );

$raw_response = wp_remote_post( $url, $options );

 

api.wordpress.orgに、プラグインアップデートチェック用のAPIがあった

ということが分かりました。

パラメータは以下のように渡していました。

 $options = array(
 'timeout' => $timeout,
 'body' => array(
 'plugins' => wp_json_encode( $to_send ),
 'translations' => wp_json_encode( $translations ),
 'locale' => wp_json_encode( $locales ),
 'all' => wp_json_encode( true ),
 ),
 'user-agent' => 'WordPress/' . $wp_version . '; ' . get_bloginfo( 'url' )
 );

 

APIにPOSTしてみる

次のようなパラメータを先ほどのAPIに渡してみました。

 $timeout = 30;
 $to_send = array(
 "plugins"=> array(
 "akismet/akismet.php" => array(
 "Name" => "Akismet",
 "PluginURI" => "http://akismet.com/",
 "Version" => "3.1.3",
 "Description" => 'Used by millions, Akismet is quite possibly the best way in the world to <strong>protect your blog from comment and trackback spam</strong>. It keeps your site protected from spam even while you sleep. To get started: 1) Click the "Activate" link to the left of this description, 2) <a href="http://akismet.com/get/">Sign up for an Akismet API key</a>, and 3) Go to your Akismet configuration page, and save your API key.',
 "Author" => "Automattic",
 "AuthorURI" => "http://automattic.com/wordpress-plugins/",
 "TextDomain" => "akismet",
 "DomainPath" => "",
 "Network" => false,
 "Title" =>"Akismet",
 "AuthorName" =>"Automattic",
 ),
 "hello.php" => array(
 "Name" => "Hello Dolly",
 "PluginURI" => "http://wordpress.org/plugins/hello-dolly/",
 "Version" => "1.5",
 "Description" => 'This is not just a plugin, it symbolizes the hope and enthusiasm of an entire generation summed up in two words sung most famously by Louis Armstrong: Hello, Dolly. When activated you will randomly see a lyric from <cite>Hello, Dolly</cite> in the upper right of your admin screen on every page.',
 "Author" => "Matt Mullenweg",
 "AuthorURI" => "http://ma.tt/",
 "TextDomain" => "",
 "DomainPath" => "",
 "Network" => false,
 "Title" => "Hello Dolly",
 "AuthorName" => "Matt Mullenweg",
 ),
 ),
 "active" => array(
 "hello.php"
 ),
 );

$translations = array(
 "akismet" => array (
 "ja" => array (
 "POT-Creation-Date" => "",
 "PO-Revision-Date" => "2015-08-01 15:50:07+0000",
 "Project-Id-Version" => "Stable (latest release)",
 "X-Generator" => "GlotPress/1.0-alpha-1100",
 )
 )
 );

$locales = array( "ja" );

$options = array(
 'timeout' => $timeout,
 'body' => array(
 'plugins' => wp_json_encode( $to_send ),
 'translations' => wp_json_encode( $translations ),
 'locale' => wp_json_encode( $locales ),
 'all' => wp_json_encode( true ),
 ),
 'user-agent' => 'WordPress/4.3; ' . get_bloginfo( 'url' )
 );

次のように結果をパースしてみました。※エラーチェックしていません

 $url = $http_url = 'http://api.wordpress.org/plugins/update-check/1.1/';
 $raw_response = wp_remote_post( $url, $options );
 var_dump(json_decode(wp_remote_retrieve_body($raw_response)));

結果は、更新の必要があるものはpluginsに、更新の必要がないものはno_updateにセットされていることが分かりました。
そして、この結果をTransients APIで記憶しています。

 object(stdClass)#202 (3) {
 ["plugins"]=> object(stdClass)#203 (1) {
 ["hello.php"]=> object(stdClass)#204 (6) {
 ["id"]=> string(4) "3564"
 ["slug"]=> string(11) "hello-dolly"
 ["plugin"]=> string(9) "hello.php"
 ["new_version"]=> string(3) "1.6"
 ["url"]=> string(42) "https://wordpress.org/plugins/hello-dolly/"
 ["package"]=> string(58) "https://downloads.wordpress.org/plugin/hello-dolly.1.6.zip" } }
 ["translations"]=> array(0) { }
 ["no_update"]=> object(stdClass)#205 (1) {
 ["akismet/akismet.php"]=> object(stdClass)#206 (6) {
 ["id"]=> string(2) "15"
 ["slug"]=> string(7) "akismet"
 ["plugin"]=> string(19) "akismet/akismet.php"
 ["new_version"]=> string(5) "3.1.3"
 ["url"]=> string(38) "https://wordpress.org/plugins/akismet/"
 ["package"]=> string(56) "https://downloads.wordpress.org/plugin/akismet.3.1.3.zip" } } }


まとめ

  • api.wordpress.orgに現在インストールされているプラグイン情報を通知して更新の必要があるかを問い合わせる
  • API問い合わせ結果に基づいて、プラグイン一覧で更新の必要があるかを表示する