自分自身で小さなショートコードのプラグインを作成していくうちに、ふと疑問に思って調べてみると、ショートコードで使用する JavaScript (スクリプト)や CSS (スタイルシート)は、そのショートコードを使用しない他のページでもロードされていることが判明しました。

このまま行くと、プラグインを追加するたびに新たな CSS と JavaScript でサイトが肥大化してしまいます。

 

過去にショートコードを追加するプラグインを作成する記事を何点か書いたんですが、てっきり、ショートコードに依存している JavaScript や CSS はショートコードを使用しないページではロードされていないと思っていました。

ところが、自作ショートコードを使用していないページでも、全く関係のない JavaScript や CSS がロードされていることがわかってしまいました。思い込みって怖いですね。

それにしても、なんとか対処しなければ・・

 

ショートコードのおさらい

ショートコードを作成して、それに関連した JavaScript や CSS を読み込ませる場合、おおむね次のようなコードになります。

function shortcode_func( $args, $content ) {
    // ショートコードで処理する内容
}

add_shortcode( 'shortcode', 'someshortcode' );

ショートコードタグをハンドラーにして、コールバック関数を登録する形ですね。

また、このショートコードに関連する スクリプト や スタイルシート を読み込ませたい場合は、

function register_func() {
    wp_enqueue_script( $handle, $src, $deps, $ver, $in_footer );
    wp_enqueue_style( $handle, $src, $deps, $ver, $media );
}

add_action( 'wp_enqueue_scripts', 'register_func' );

のように、アクションフックwq_enqueue_scriptsにコールバック関数を登録します。

これらのコールバック関数は、

add_action( 'wp_enqueue_scripts', function() {
    wp_enqueue_script( $handle, $src, $deps, $ver, $in_footer );
    wp_enqueue_style( $handle, $src, $deps, $ver, $media );
});

みたいに、JavaScript でよく使う無名関数を使用することもできる、ということも最近わかってきました。

 

情報を集めるのは以外に難しかった

このままでは、ショートコードと関係のないページにまでスクリプトやスタイルシートのリンクが貼られてしまい、どんどんとページのレスポンスが落ちていくこと必死です。

「ショートコードが呼ばれるページにだけ、スクリプトやスタイルシートを読ませたい。」

なんていうことは、結構簡単にできそうだと思っていたのですが、これが意外と情報の検索に手こずりました。

そしてようやく見つけたのが次のページです。

またしても、Stack Overflow。本当にいつも助けてくれてありがとう。

実際の処理

function save_option_shortcode_post_id_array( $post_id ) {
    if ( wp_is_post_revision( $post_id ) OR 'post' != get_post_type( $post_id )) {
        return;
    }
    $option_name = 'shortcode_tag';
    $id_array = find_shortcode_occurences($option_name);
    $autoload = 'yes';
    if (false == add_option($option_name, $id_array, '', $autoload)) {
        update_option($option_name, $id_array);
    }
}

function find_shortcode_occurences($shortcode, $post_type = 'post') {
    $found_ids = array();
    $args = array(
        'post_type'   => $post_type,
        'post_status' => 'publish',
        'posts_per_page' => -1,
    );
    $query_result = new WP_Query($args);
    foreach ($query_result->posts as $post) {
        if (false !== strpos($post->post_content, $shortcode)) {
            $found_ids[] = $post->ID;
        }
    }
    return $found_ids;
}

add_action('save_post', 'save_option_shortcode_post_id_array' );

上のコードは、save_post アクションで実行されるコールバックの内容です。

このコールバックは、投稿が保存、更新された時に、投稿タイプが POST で、なおかつ投稿がリビジョンでない場合に実行されます。

'shortcode_tag' はショートコードタグです。

実際の処理のメインは、find_shortcode_occurences 関数にあります。

WP_Query 関数で、投稿タイプが POST で、なおかつ公開されている投稿の一覧を取得し、その投稿の記事(post_content)の内容を検索して、ショートコードタグ(= shortcode_tag) が含まれているか調べて、含まれている場合はその投稿IDを配列に保存。

その後その配列を、add_option 関数でテーブル(例: wp_options) にレコードを追加、あるいは(すでに存在している場合は)、update_option 関数でレコードを更新します。

function yourshortcode_add_scripts_and_styles() {
    $page_id = get_the_ID();
    $option_id_array = get_option('shortcode_tag');
    if (in_array($page_id, $option_id_array)) {
        wp_enqueue_script( $handle, $src, $deps, $ver, $footer = true );
        wp_enqueue_style( $handle, $src , $deps);
    }
}

add_action('wp_enqueue_scripts', 'yourshortcode_add_scripts_and_styles');

次は、投稿のページが開かれる際の処理になります。オプションテーブル(wp_options)から、ショートコードタグで該当するレコードをフェッチして、そのレコードの値と、投稿ページのIDを比較して該当している場合にのみ、スタイルシートやスクリプトを読み込むようにしています。

フェッチしたレコードは次のような感じになります。

> select * from wp_options where option_name = 'kanren';
+-----------+-------------+--------------------------------------------+----------+
| option_id | option_name | option_value                               | autoload |
+-----------+-------------+--------------------------------------------+----------+
|      1013 | kanren      | a:4:{i:0;i:73;i:1;i:68;i:2;i:58;i:3;i:47;} | yes      |
+-----------+-------------+--------------------------------------------+----------+
1 row in set (0.006 sec)

option_name はショートコードタグ、option_value にIDの配列が入っています。

option_value はなんとなくですが、要素が4つ入った配列と、その内容が登録されているみたいですね。

 

まとめ

最初に思っていたよりは、結構手間のかかるコードだったのには驚きです。

でも、先のコードを自身のプラグイン似合うように書き直すと、ショートコードの出現するページと、出現しないページでは、きちんとスクリプトやスタイルシートが適切に設定されるようになりました。

ただ、問題が・・

一つは、投稿を作成・更新するたびに、全投稿がフェッチされるのかと思うと、本当にコレでいいのかどうか迷います。

また、もう一つは、このコードを適用したとしても、また別のプラグインを作成するときには、関数名を別にしないことにはエラーが発生します。他のプラグインや、functions.php などに含まれていない関数名をつけるとなるとやたらと長い関数名をつけなくてはならないので面倒です。しかも、たいがいはでたらめな英語だし・・

で、結局この問題を解決するにはプラグインを class でかく必要が出てくるみたいですね。

ですが、それはまた別のお話になります。

 

Appendix: 付録

今回の記事中にでてきた関数などのリファレンスです。

add_shortcode 関数

ショートコードタグのフックを追加します。

<?php add_shortcode( $tag, $func ); ?>
パラメータ
$tag
(文字列)(必須) ショートコードタグ
$func
(関数名)(必須) ショートコードが見つかったときに実行するコールバック関数
戻り値

なし

 

wp_enqueue_style 関数

WordPress が生成するページに、スタイルシート(CSS)をリンクします。

<? wp_enqueue_style( $handle, $src, $deps, $ver, $media ) ?>
パラメータ
$handle
(必須)ハンドル名
$src
(オプション)スタイルシートのURL
$deps
(オプション)このスタイルシートが読み込まれる前に読まれて置く必要のあるスタイルシートがあれば、そのハンドル名を配列として渡す。
$ver
(オプション)バージョン文字列。指定しないと、Wordpressのバージョン番号がクエリストリングとして付与されます。
$media
(オプション)スタイルシートが定義されているメディアを指定。
戻り値

なし

 

wp_enqueue_script 関数

WordPress が生成するページに、JavaScript をリンクします。

<? wp_enqueue_script( $handle, $src, $deps, $ver, $in_footer ); ?>
パラメータ
$handle
(文字列)(必須)ハンドル名
$src
(文字列)(オプション)スクリプトのURL
$deps
(配列)(オプション) このスクリプトが依存するスクリプトのハンドル名。
$ver
(オプション)バージョン文字列。指定しないと、Wordpressのバージョン番号がクエリストリングとして付与されます。
$in_footer
(オプション) 値が true の場合、スクリプトは </body> タグの前に配置される。
戻り値

なし

 

add_action 関数

特定のアクションに関数をフックします。

<?php add_action( $hook, $function_to_add, $priority, $accepted_args ); ?>
パラメータ
$hook
(文字列)(必須) 関数をフックしたいアクション名
$function_to_add
(コールバック)(必須) コールバック関数。もしくはコールバック関数名
$priority
(整数)(オプション) 関数の実行順序
$accepted_args
(整数)(オプション) 関数の引数の数
戻り値

常にtrue。

 

add_option 関数

データベースの wp_options テーブルに、オプション/値の対を安全に追加するための方法です。

<?php add_option( $option, $value, $deprecated, $autoload ); ?>
パラメータ
$option
(文字列)(必須) 追加されるオプションの名前。空白の代わりにアンダースコアを使用。大文字は使用しない。
$value
(mixed)(オプション) このオプション名の値
$deprecated
(文字列)(オプション) WordPress 2.3 で非推奨になりました

$autoload
:(文字列)(オプション) wp_lodad_alloptions() で自動的にこのオプションを読み出すのかどうか(オブジェクトキャッシュにこのオプションがロードされる)。yes か no で指定する。

戻り値

オプションが追加されなかったら false 追加されたら true。

 

update_option 関数

データベースの wp_options テーブルに、オプション/値の対を更新するために使用します。この関数は add_option の代わりに使用できます。update_option は、オプションがすでに存在しているかどうかを確認し、存在しない場合は、add_option( $option, $value) で追加されます。add_option 関数でしか使用できない引数をしないかぎりは、update_option で追加と更新の両方に使用できます。

<?php update_option( $option, $new_value, $autoload ); ?>
パラメータ
$option
(文字列)(必須) オプション名。小文字のみ。
$newvalue
(混合)(必須) このオプションの新しい値
$autoload
(混合)(オプション) WordPress の起動時にオプションを読み込むかどうか。既存のオプションについては、$value も変更された場合に限り更新できます。 yes または true で有効、no または false で無効。存在しないオプションの場合は、デフォルトで yes
戻り値

更新に成功すると true 、失敗したら false を返します。

 

get_option 関数

データベースの wp_options テーブルから、名前を指定してオプションの値を取得する安全な方法です。

<?php get_option( $option, $default ); ?>
パラメータ
$option
(文字列)(必須) 取得するオプションの名前。小文字のみ。
$default
(mixed)(オプション) 値が返されない場合のデフォルト値。
戻り値

(mixed) オプションが存在していた場合は、現在の値。存在せず、$default が指定されていたらその値。そうでなければ、false 。

 

wp_enqueue_scripts アクションフック

wp_enqueue_scripts はフロントエンドに表れることを意図されるアイテムを、キューに追加する際に使われるのに適切なフックです。名前にも関わらず、このフックはスクリプトとスタイルシートの両方をキューするために使用されます。

 

save_post アクションフック

投稿や固定ページが作成または更新された時に実行されるアクション。

// 投稿IDのみを使う場合
<?php add_action( 'save_post', 'function_name' ); ?>

// 3つのパラメーターを使う場合(13は優先度)
<?php add_action( 'save_post', 'function_name', 13, 3 ); ?>
パラメータ
$post_ID
(整数)(必須) 投稿ID
$post
(WP_Post オブジェクト)(必須) 投稿データ
$update
(真偽値)(必須) 既存投稿の更新なら true、新規投稿なら false

0件のコメント

コメントを残す

メールアドレスが公開されることはありません。