There’s no need to escape the URL twice – get_pagenum_link()
by default returns an escaped URL — when the second parameter is true
, esc_url()
is used; else, esc_url_raw()
is used:
-
With
esc_url()
:http://localhost/wordpress/blog/?filter=23&orderby=oldest
-
With
esc_url_raw()
:http://localhost/wordpress/blog/?filter=23&orderby=oldest
And the problem occurs when the base
contains ?
, or a query string, and that the URL is escaped (e.g. using esc_url()
).
Because esc_url()
converts &
(ampersand) to its HTML entity, which is &
, and when the base
contains a query string, paginate_links()
will parse the base
value/URL, and when the &
is escaped, anything after each &
and the nearest =
is treated as a query string name/key, and added to the base
‘s query string:
http://localhost/wordpress/blog/?filter=23&orderby=oldest#038;orderby=oldest
In the above example, the URL is like that because paginate_links()
(or more precisely, wp_parse_str()
used in paginate_links()
) interpreted #038;orderby
as the key of oldest
; i.e. #038;orderby=oldest
— it should be &orderby=oldest
where the key is orderby
.
(But of course, the browser sees anything after the #
as a URL fragment. And if you follow the link, on the next page, $_GET
will not have an entry for #038;orderby
; i.e. $_GET['#038;orderby']
doesn’t exist.)
So here are several ways to fix/avoid the problem:
-
Use
html_entity_decode()
just as WordPress does viapaginate_links()
:'base' => str_replace( $big, '%#%', html_entity_decode( get_pagenum_link( $big ) ) )
-
When you call
get_pagenum_link()
, set the second parameter set tofalse
:'base' => str_replace( $big, '%#%', get_pagenum_link( $big, false ) )
-
Use
str_replace()
to replace the&
with&
:'base' => str_replace( [ $big, '&' ], [ '%#%', '&' ], get_pagenum_link( $big ) )
Don’t worry about not escaping the base
because paginate_links()
actually escapes all the links/URLs in the returned result.
Friendly Warning: Some of the links in this answer point you to a huge HTML file / web page..