「&」はHTMLエンティティ( & と ; で囲まれた文字列)の1つで、HTMLでは「&」(アンパサンド)を示すものになりますね。( 「&」も「&」に同じ)
ワードプレスの自作プラグインとかでWP_Queryを使って記事の一覧表示させてページネーションを付けた場合、ページネーションの数字(2ページ目とか)をクリックすると、この「&」の「&」を除いた「#038;」という文字列がURLに入ってしまう場合があります。
原因を色々調べた結果、URLにページネーションとは関係のないキーが入る場合(今回ご紹介するケースでは「&settings-updated=true」)、ページネーションを表示する関数の中で「&」を「&」にエスケープして、結果ページネーションリンク(URL)に「#038;」が入ってしまうようです。
その具体的症状や原因、解決する方法を参考までに。
実際のコードと症状
自作プラグインを作っていたらURLになぜか「#038;」が入り、
ページネーションがうまくできない。
(ページ番号をクリックしてもそのページが表示されない)
その時のコードは以下のような感じです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
<?php // ここにSettings APIを使った、設定を保存するのための // コードがここにいろいろある // // でその後に以下のように一覧表示を付けている //ページ番号を取得:最初は1ページ目 $paged = ( $_GET['paged'] ) ? $_GET['paged'] : 1; //WP_Queryの検索条件 $args_query = array( 'post_type' => 'publish', //投稿 'post_status' => 'publish', //公開済 'paged' => $paged, //表示するページ番号 ); //一覧の取得と表示 $my_query = new WP_Query($args_query); if ( $my_query->have_posts() ) : echo '<ul>'; while ( $my_query->have_posts() ) : $my_query->the_post(); //表示したいHTMLコードを書く endwhile; echo '</ul>'; wp_reset_postdata(); //WP_Query のリセット else: echo '<p>no data</p>'; endif; //ページペーション $big = 999999999; $args_paginate = array( 'base' => str_replace($big, '%#%', esc_url(get_pagenum_link($big))), 'current' => max(1, $paged ), 'total' => $my_query->max_num_pages, ); echo paginate_links( $args_paginate ); ?> |
ページネーションのコードは、ワードプレスの公式リファレンス(基本的な例の箇所)を基本にして若干変えているだけ。
(ワードプレスの管理画面だと get_query_var()がうまく動作しないので$_GET[‘paged’]でページ番号($paged)を取得しているところが異なる。詳しくは 管理画面や設定ページではget_query_varが使えない?!を参照 ))
これで実際に一覧表示させてみるとうまく動作しているようですが、
例えば以下のような操作をするとURLに「#038;settings-updated=true」が入り、ページネーションがうまく動作しなくなってしまいます。
(ページ番号をクリックしてもそのページが表示されない)
- 1)ワードプレス管理画面の「設定」メニューからプラグインの設定画面を表示
(この時のURLは以下)
http://xxxx/options-general.php?page=jin-more-tag-auto - 2)同じページにあるSettings APIを使った設定を保存
(保存後のURLは以下)
http://xxxx/options-general.php?page=jin-more-tag-auto - 3)その後、記事一覧のページネーションで「2ページ目」をクリックすると以下のURLになる
http://xxxx/options-general.php?page=jin-more-tag-auto#038;settings-updated=true&paged=2
※)補足
「More Tag Auto」というプラグインを作り、その設定画面のURLを「jin-more-tag-auto」としているので、URLは http://xxxxx/options-general.php?page=jin-more-tag-auto となっている
原因と解決法
なぜ「#038;~」 といった文字列がURLに入るのか、
その原因はページネーションのコード中にあるesc_url()関数とget_pagenum_link()関数にあるようです。(35行目)
31 32 33 34 35 36 37 38 39 40 |
//ページペーション $big = 999999999; $args_paginate = array( 'base' => str_replace($big, '%#%', esc_url(get_pagenum_link($big))), 'current' => max(1, $paged ), 'total' => $my_query->max_num_pages, ); echo paginate_links( $args_paginate ); |
詳しくは Paginate Link generate additional #038(英語)で分かりましたが、
そもそもget_pagenum_link() 関数は、初期状態(第二引数で false を指定しない場合)では esc_url()を使ってURLを返すようになってます。(つまり esc_url(get_pagenum_link($big) ) は2重でエスケープしている)
esc_url() は「&」をHTMLエンティティの「&」に変換し、
(上の例では「&settings-updated=true」となり)
ページネーションを表示する paginate_links()(39行目)がこの「&」以降「#038;settings-updated」をキー(「true」を値)と解釈してしまい、結果URLにそれが表示される、となるようです。
※)今回の例では、記事一覧を表示する同じ画面上に別の設定があり、その設定を保存したことでページネーションのリンクURLに余計な「&settings-updated=true」が付き、その「&」がget_pagenum_link() の中で変にエスケープされて「&」になった、となりそうです)
また、get_pagenum_link() の値を元にページネーションを表示する paginate_links()(39行目)は、関数リファレンスにあるソースコードを見ると、内部的に一旦デコード(html_entity_decode)してesc_url()を使ってURLを返すようになってます。
以上からURLに出てくる「#038;」の表示をなくすには以下の3つの対応が考えられる、ってことになりますね。
- その1)そもそも get_pagenum_link() では esc_url() を使用しない
1'base' => str_replace($big, '%#%', esc_url(get_pagenum_link( $big , false ) ) ) - その2)get_pagenum_link()の返り値をHTMLエンティティデコードしておく
(「#038」を「&」に戻しておく)
1'base' => str_replace($big, '%#%', html_entity_decode( get_pagenum_link( $big ) ) ) - その3)get_pagenum_link()の返り値に含まれる「&」を「&」に置き換える
1'base' => str_replace( [ $big, '&' ], [ '%#%', '&' ], get_pagenum_link( $big ) )
この3つの方法を各々試してみると、
いずれも「#038;」がなくなり、正しいURLでページネーションも動作するようです。
私の場合、一番お手軽な「その1」を採用しました。
31 32 33 34 35 36 37 38 39 40 |
//ページペーション $big = 999999999; $args_paginate = array( 'base' => str_replace($big, '%#%', get_pagenum_link( $big , false ) ), 'current' => max(1, $paged ), 'total' => $my_query->max_num_pages, ); echo paginate_links( $args_paginate ); |
以上、ご参考までに。