WordPressでページネーションを実装する際に、URLに「#038;」が入ってしまうことはありませんか?これは、URLが二重にエスケープ処理されてしまうことが原因で発生します。今回は、この問題の具体的な原因と、その解決方法について紹介します。
URLに「#038;」が入る原因とは?
「&」はHTMLエンティティの一つで、「&」を表す記号です。WordPressで使用するget_pagenum_link()やesc_url()関数は、このエンティティをエスケープすることで、URLに「#038;」が含まれることがあります。
特に、カスタムプラグインやテーマを作成している際、ページネーション機能を実装していると、URLに「#038;」が含まれ、ページ番号をクリックしても正しく動作しないことがあります。
例えば、以下のようなコードでページネーションを実装している場合、URLに「#038;」が混入する可能性があります。
$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_pagenum_link()とesc_url()によってURLが二重にエスケープされ、&が#038;となってしまうことがあります。get_pagenum_link()の第2引数$escapeは既定でtrueのため、外側で改めてesc_url()を呼ぶと二重にエスケープされてしまうのが根本原因です。
解決方法
この問題を解決するためには、いくつかの方法があります。以下に3つの方法を紹介します。
方法1:get_pagenum_link()でesc_url()を使わない
最も簡単な解決策は、get_pagenum_link()関数の第2引数にfalseを渡し、内部のesc_url()処理を無効にすることです。これにより、二重エスケープが防がれます。
$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);
方法2:html_entity_decode()でデコードする
もう一つの方法は、get_pagenum_link()が返す値をhtml_entity_decode()でデコードして、エスケープされた文字を元に戻すことです。
$args_paginate = array(
'base' => str_replace($big, '%#%', html_entity_decode(get_pagenum_link($big))),
'current' => max(1, $paged),
'total' => $my_query->max_num_pages,
);
echo paginate_links($args_paginate);
方法3:str_replace()でエンティティを置換する
もう一つのアプローチとして、str_replace()を使って「&」を「&」に置き換える方法もあります。
エンティティの置換だけに気を取られて
$big(999999999)を'%#%'に置き換える処理を忘れると、paginate_links()がページ番号を差し込む位置を見つけられず、生成される全ページのリンクが同一URLになってしまいます。次のようなコードは一見動きそうに見えて、この落とし穴にはまっています。$args_paginate = array(
'base' => str_replace(['&', '%#%'], ['&', '%#%'], get_pagenum_link($big)),
'current' => max(1, $paged),
'total' => $my_query->max_num_pages,
);
echo paginate_links($args_paginate);
// $big -> '%#%' の置換が抜けているため、全ページのリンクが同一URLになってしまう
エンティティの置換と%#%への置換の両方を行う必要があります。
$args_paginate = array(
'base' => str_replace($big, '%#%', str_replace('&', '&', get_pagenum_link($big))),
'current' => max(1, $paged),
'total' => $my_query->max_num_pages,
);
echo paginate_links($args_paginate);
関連記事
よくある質問(FAQ)
get_pagenum_link()は第2引数$escapeが既定でtrueのため、内部でesc_url()相当の処理が実行され&が&に変換されます。この戻り値をさらに外側でesc_url()にかけると二重にエスケープされ、URLに「#038;」が現れます。get_pagenum_link($big, false)のように第2引数にfalseを渡し内部のエスケープを無効にするか、html_entity_decode()で戻り値をデコードします。自前で文字列連結する場合はstr_replace('&', '&', $url)で変換することもできます。add_query_arg()の結果をesc_url()でエスケープすると解決します。HTML属性(href等)に出力する場合は必ずesc_url()を通すのが正しい実装です。すでにエスケープ済みの値を再度esc_url()に通さないよう注意してください。まとめ
WordPressでURLに「#038;」が含まれる問題は、get_pagenum_link()とesc_url()による二重エスケープが原因です。この問題を解決するためには、esc_url()相当の処理を無効にするか、エンティティをデコードまたは置換する方法があります。最も簡単で効果的な方法は、get_pagenum_link()の第2引数にfalseを渡すことです。str_replace()でエンティティを置換する方法を使う場合は、ページ番号のプレースホルダー%#%への置換を忘れないようにしてください。
