paginate_links()
概述
paginate_links() 函数用于生成归档文章页面的分页链接,技术上可应用于任何需要分页的区域。它通过参数控制链接的格式、显示方式和返回类型,默认用于处理 WP_Query 的分页。
关键要点
- 核心功能:生成分页链接,支持自定义 URL 基础(base)和格式(format),其中 base 需包含 %_% 占位符,format 需包含 %#% 占位符以替换页码。
- 返回类型:通过 'type' 参数控制,可选 'plain'(默认,返回字符串)、'array'(返回数组)或 'list'(返回无序 HTML 列表)。
- 关键参数:包括 'total'(总页数)、'current'(当前页码)、'show_all'(是否显示所有页面)、'end_size' 和 'mid_size'(控制页面显示范围)、'prev_next'(是否包含上一页/下一页链接)。
- 辅助功能:支持通过 'add_args' 添加查询参数,'before_page_number' 和 'after_page_number' 增强链接可访问性,'aria_current' 设置 ARIA 属性。
- 默认行为:基于当前查询自动设置 total 和 current,使用 WP_Query 的 max_num_pages 和 'paged' 查询变量。
代码示例
echo paginate_links( array(
'base' => str_replace( $big, '%#%', esc_url( get_pagenum_link( $big ) ) ),
'format' => '?paged=%#%',
'current' => max( 1, get_query_var('paged') ),
'total' => $wp_query->max_num_pages
) );注意事项
- base 参数应使用完整 URL(如 https://example.com/page/%_%),否则可能导致链接错误。
- 在 PHP 8 及以上版本中,注意类型转换,例如使用 $big = '999999999'; 以避免字符串替换失败。
- 为提升可访问性,建议使用 'before_page_number' 和 'after_page_number' 为屏幕阅读器添加上下文。
- 当 total 小于 2 时,函数返回 void,即无输出。
Retrieves paginated links for archive post pages.
Description
Technically, the function can be used to create paginated link list for any area. The ‘base’ argument is used to reference the url, which will be used to create the paginated links. The ‘format’ argument is then used for replacing the page number. It is however, most likely and by default, to be used on the archive post pages.
The ‘type’ argument controls format of the returned value. The default is ‘plain’, which is just a string with the links separated by a newline character. The other possible values are either ‘array’ or ‘list’. The ‘array’ value will return an array of the paginated link list to offer full control of display. The ‘list’ value will place all of the paginated links in an unordered HTML list.
The ‘total’ argument is the total amount of pages and is an integer. The ‘current’ argument is the current page number and is also an integer.
An example of the ‘base’ argument is “http://example.com/all_posts.php%_%” and the ‘%_%’ is required. The ‘%_%’ will be replaced by the contents of in the ‘format’ argument. An example for the ‘format’ argument is “?page=%#%” and the ‘%#%’ is also required. The ‘%#%’ will be replaced with the page number.
You can include the previous and next links in the list by setting the ‘prev_next’ argument to true, which it is by default. You can set the previous text, by using the ‘prev_text’ argument. You can set the next text by setting the ‘next_text’ argument.
If the ‘show_all’ argument is set to true, then it will show all of the pages instead of a short list of the pages near the current page. By default, the ‘show_all’ is set to false and controlled by the ‘end_size’ and ‘mid_size’ arguments. The ‘end_size’ argument is how many numbers on either the start and the end list edges, by default is 1. The ‘mid_size’ argument is how many numbers to either side of current page, but not including current page.
It is possible to add query vars to the link by using the ‘add_args’ argument and see add_query_arg() for more information.
The ‘before_page_number’ and ‘after_page_number’ arguments allow users to augment the links themselves. Typically this might be to add context to the numbered links so that screen reader users understand what the links are for.
The text strings are added before and after the page number – within the anchor tag.
Parameters
$argsstring|arrayoptional-
Array or string of arguments for generating paginated links for archives.
basestringBase of the paginated url. Default empty.formatstringFormat for the pagination structure. Default empty.totalintThe total amount of pages. Default is the value WP_Query‘smax_num_pagesor 1.currentintThe current page number. Default is'paged'query var or 1.aria_currentstringThe value for the aria-current attribute. Possible values are'page','step','location','date','time','true','false'. Default is'page'.show_allboolWhether to show all pages. Default false.end_sizeintHow many numbers on either the start and the end list edges.
Default 1.mid_sizeintHow many numbers to either side of the current pages. Default 2.prev_nextboolWhether to include the previous and next links in the list. Default true.prev_textstringThe previous page text. Default ‘« Previous’.next_textstringThe next page text. Default ‘Next »’.typestringControls format of the returned value. Possible values are'plain','array'and'list'. Default is'plain'.add_argsarrayAn array of query args to add. Default false.add_fragmentstringA string to append to each link. Default empty.before_page_numberstringA string to appear before the page number. Default empty.after_page_numberstringA string to append after the page number. Default empty.
Source
function paginate_links( $args = '' ) {
global $wp_query, $wp_rewrite;
// Setting up default values based on the current URL.
$pagenum_link = html_entity_decode( get_pagenum_link() );
$url_parts = explode( '?', $pagenum_link );
// Get max pages and current page out of the current query, if available.
$total = isset( $wp_query->max_num_pages ) ? $wp_query->max_num_pages : 1;
$current = get_query_var( 'paged' ) ? (int) get_query_var( 'paged' ) : 1;
// Append the format placeholder to the base URL.
$pagenum_link = trailingslashit( $url_parts[0] ) . '%_%';
// URL base depends on permalink settings.
$format = $wp_rewrite->using_index_permalinks() && ! strpos( $pagenum_link, 'index.php' ) ? 'index.php/' : '';
$format .= $wp_rewrite->using_permalinks() ? user_trailingslashit( $wp_rewrite->pagination_base . '/%#%', 'paged' ) : '?paged=%#%';
$defaults = array(
'base' => $pagenum_link, // http://example.com/all_posts.php%_% : %_% is replaced by format (below).
'format' => $format, // ?page=%#% : %#% is replaced by the page number.
'total' => $total,
'current' => $current,
'aria_current' => 'page',
'show_all' => false,
'prev_next' => true,
'prev_text' => __( '« Previous' ),
'next_text' => __( 'Next »' ),
'end_size' => 1,
'mid_size' => 2,
'type' => 'plain',
'add_args' => array(), // Array of query args to add.
'add_fragment' => '',
'before_page_number' => '',
'after_page_number' => '',
);
$args = wp_parse_args( $args, $defaults );
if ( ! is_array( $args['add_args'] ) ) {
$args['add_args'] = array();
}
// Merge additional query vars found in the original URL into 'add_args' array.
if ( isset( $url_parts[1] ) ) {
// Find the format argument.
$format = explode( '?', str_replace( '%_%', $args['format'], $args['base'] ) );
$format_query = isset( $format[1] ) ? $format[1] : '';
wp_parse_str( $format_query, $format_args );
// Find the query args of the requested URL.
wp_parse_str( $url_parts[1], $url_query_args );
// Remove the format argument from the array of query arguments, to avoid overwriting custom format.
foreach ( $format_args as $format_arg => $format_arg_value ) {
unset( $url_query_args[ $format_arg ] );
}
$args['add_args'] = array_merge( $args['add_args'], urlencode_deep( $url_query_args ) );
}
// Who knows what else people pass in $args.
$total = (int) $args['total'];
if ( $total < 2 ) {
return;
}
$current = (int) $args['current'];
$end_size = (int) $args['end_size']; // Out of bounds? Make it the default.
if ( $end_size < 1 ) {
$end_size = 1;
}
$mid_size = (int) $args['mid_size'];
if ( $mid_size < 0 ) {
$mid_size = 2;
}
$add_args = $args['add_args'];
$r = '';
$page_links = array();
$dots = false;
if ( $args['prev_next'] && $current && 1 < $current ) :
$link = str_replace( '%_%', 2 === $current ? '' : $args['format'], $args['base'] );
$link = str_replace( '%#%', $current - 1, $link );
if ( $add_args ) {
$link = add_query_arg( $add_args, $link );
}
$link .= $args['add_fragment'];
$page_links[] = sprintf(
'<a class="prev page-numbers" href="%s">%s</a>',
/**
* Filters the paginated links for the given archive pages.
*
* @since 3.0.0
*
* @param string $link The paginated link URL.
*/
esc_url( apply_filters( 'paginate_links', $link ) ),
$args['prev_text']
);
endif;
for ( $n = 1; $n <= $total; $n++ ) :
if ( $n === $current ) :
$page_links[] = sprintf(
'<span aria-current="%s" class="page-numbers current">%s</span>',
esc_attr( $args['aria_current'] ),
$args['before_page_number'] . number_format_i18n( $n ) . $args['after_page_number']
);
$dots = true;
else :
if ( $args['show_all'] || ( $n <= $end_size || ( $current && $n >= $current - $mid_size && $n <= $current + $mid_size ) || $n > $total - $end_size ) ) :
$link = str_replace( '%_%', 1 === $n ? '' : $args['format'], $args['base'] );
$link = str_replace( '%#%', $n, $link );
if ( $add_args ) {
$link = add_query_arg( $add_args, $link );
}
$link .= $args['add_fragment'];
$page_links[] = sprintf(
'<a class="page-numbers" href="%s">%s</a>',
/** This filter is documented in wp-includes/general-template.php */
esc_url( apply_filters( 'paginate_links', $link ) ),
$args['before_page_number'] . number_format_i18n( $n ) . $args['after_page_number']
);
$dots = true;
elseif ( $dots && ! $args['show_all'] ) :
$page_links[] = '<span class="page-numbers dots">' . __( '…' ) . '</span>';
$dots = false;
endif;
endif;
endfor;
if ( $args['prev_next'] && $current && $current < $total ) :
$link = str_replace( '%_%', $args['format'], $args['base'] );
$link = str_replace( '%#%', $current + 1, $link );
if ( $add_args ) {
$link = add_query_arg( $add_args, $link );
}
$link .= $args['add_fragment'];
$page_links[] = sprintf(
'<a class="next page-numbers" href="%s">%s</a>',
/** This filter is documented in wp-includes/general-template.php */
esc_url( apply_filters( 'paginate_links', $link ) ),
$args['next_text']
);
endif;
switch ( $args['type'] ) {
case 'array':
return $page_links;
case 'list':
$r .= "<ul class='page-numbers'>nt<li>";
$r .= implode( "</li>nt<li>", $page_links );
$r .= "</li>n</ul>n";
break;
default:
$r = implode( "n", $page_links );
break;
}
/**
* Filters the HTML output of paginated links for archives.
*
* @since 5.7.0
*
* @param string $r HTML output.
* @param array $args An array of arguments. See paginate_links()
* for information on accepted arguments.
*/
$r = apply_filters( 'paginate_links_output', $r, $args );
return $r;
}
Hooks
- apply_filters( ‘paginate_links’, string $link )
-
Filters the paginated links for the given archive pages.
- apply_filters( ‘paginate_links_output’, string $r, array $args )
-
Filters the HTML output of paginated links for archives.
Skip to note 8 content
Codex
Example With a Custom Query
When querying a loop with new WP_Query set the
totalparameter to themax_num_pagesproperty of theWP_Queryobject.Example of a custom query:
5, 'category_name' => 'gallery', 'paged' => $paged, ); $the_query = new WP_Query( $args ); ?> <!-- the loop etc.. -->Example of
paginate_linksparameters adapted to the custom query above:str_replace( $big, '%#%', esc_url( get_pagenum_link( $big ) ) ), 'format' => '?paged=%#%', 'current' => max( 1, get_query_var('paged') ), 'total' => $the_query->max_num_pages ) ); ?>Skip to note 9 content
Codex
Improving Accessibility
To add context to the numbered links to ensure that screen reader users understand what the links are for:
str_replace( $big, '%#%', esc_url( get_pagenum_link( $big ) ) ), 'format' => '?paged=%#%', 'current' => max( 1, get_query_var('paged') ), 'total' => $wp_query->max_num_pages, 'before_page_number' => '<span class="screen-reader-text">'.$translated.' </span>' ) ); ?>'prev_text' => is_rtl() ? 'Previous Page →' : 'Previous Page ←', 'next_text' => is_rtl() ? 'Next Page ←' : 'Next Page →',$big = '999999999';, otherwise the first string replacement will fail.Skip to note 10 content
Hay
You might notice that
paginate_linksalways gives back HTML, even if you’re settingtypeto array, which makes it a bit hard if you want to completely customise the HTML structure of your pagination. I wrote a little function that gives you back an array of objects instead of HTML that might make this a bit easier.function wpdocs_get_paginated_links( $query ) { // When we're on page 1, 'paged' is 0, but we're counting from 1, // so we're using max() to get 1 instead of 0 $currentPage = max( 1, get_query_var( 'paged', 1 ) ); // This creates an array with all available page numbers, if there // is only *one* page, max_num_pages will return 0, so here we also // use the max() function to make sure we'll always get 1 $pages = range( 1, max( 1, $query->max_num_pages ) ); // Now, map over $pages and return the page number, the url to that // page and a boolean indicating whether that number is the current page return array_map( function( $page ) use ( $currentPage ) { return ( object ) array( "isCurrent" => $page == $currentPage, "page" => $page, "url" => get_pagenum_link( $page ) ); }, $pages ); }And use it like this (given that
$queryis the name of your custom query):<ul> <li> isCurrent ): ?> <strong>page ) ?></strong> <a href="<?php esc_attr_e( $link->url ) ?>"> page ) ?> </a> </li> </ul>Skip to note 11 content
Binsaifullah
// first create a function function pagainate_link_function(){ global $wp_query; echo paginate_links(array( 'current'=>max(1,get_query_var('paged')), 'total'=>$wp_query->max_num_pages, 'type'=>'list', //default it will return anchor )); } // Now call this function in your desired placeSkip to note 12 content
Sean Leavey
Note that the
baseargument should be fully qualified, e.g.https://example.com/my-post/%_%#some-id, not just%_%#some-id.If the
baseargument doesn’t contain the absolute URL, then when you are on the second page the link to the first page will not work; instead it will link to the current page. This is because the “Previous” link is set to thebaseargument with the%_%part removed, and no extra?paged=%#%part added. Your browser then interprets this to mean the current page, so the “Previous” link points to the current page.Skip to note 13 content
Codex
Basic Example
To add pagination to your search results and archives, you can use the following example:
str_replace( $big, '%#%', esc_url( get_pagenum_link( $big ) ) ), 'format' => '?paged=%#%', 'current' => max( 1, get_query_var('paged') ), 'total' => $wp_query->max_num_pages ) ); ?>Skip to note 14 content
Chetan Prajapati
str_replace( $big, '%#%', esc_url( get_pagenum_link( $big ) ) ), 'format' => '?paged=%#%', 'current' => max( 1, get_query_var('paged') ), 'total' => $wp_query->max_num_pages ) ); ?>Aligned array items properly based on WPCS.