* WordPress Post Template Functions.
* Gets content for the current post in the loop.
* Display the ID of the current item in the WordPress Loop.
function the_ID() { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid
* Retrieve the ID of the current item in the WordPress Loop.
* @return int|false The ID of the current item in the WordPress Loop. False if $post is not set.
function get_the_ID() { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid
return ! empty( $post ) ? $post->ID : false;
* Display or retrieve the current post title with optional markup.
* @param string $before Optional. Markup to prepend to the title. Default empty.
* @param string $after Optional. Markup to append to the title. Default empty.
* @param bool $echo Optional. Whether to echo or return the title. Default true for echo.
* @return void|string Void if `$echo` argument is true, current post title if `$echo` is false.
function the_title( $before = '', $after = '', $echo = true ) {
$title = get_the_title();
if ( strlen( $title ) == 0 ) {
$title = $before . $title . $after;
* Sanitize the current title when retrieving or displaying.
* Works like the_title(), except the parameters can be in a string or
* an array. See the function for what can be override in the $args parameter.
* The title before it is displayed will have the tags stripped and esc_attr()
* before it is passed to the user or displayed. The default as with the_title(),
* is to display the title.
* @param string|array $args {
* Title attribute arguments. Optional.
* @type string $before Markup to prepend to the title. Default empty.
* @type string $after Markup to append to the title. Default empty.
* @type bool $echo Whether to echo or return the title. Default true for echo.
* @type WP_Post $post Current post object to retrieve the title for.
* @return void|string Void if 'echo' argument is true, the title attribute if 'echo' is false.
function the_title_attribute( $args = '' ) {
$parsed_args = wp_parse_args( $args, $defaults );
$title = get_the_title( $parsed_args['post'] );
if ( strlen( $title ) == 0 ) {
$title = $parsed_args['before'] . $title . $parsed_args['after'];
$title = esc_attr( strip_tags( $title ) );
if ( $parsed_args['echo'] ) {
* If the post is protected and the visitor is not an admin, then "Protected"
* will be displayed before the post title. If the post is private, then
* "Private" will be located before the post title.
* @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post.
function get_the_title( $post = 0 ) {
$post = get_post( $post );
$title = isset( $post->post_title ) ? $post->post_title : '';
$id = isset( $post->ID ) ? $post->ID : 0;
if ( ! empty( $post->post_password ) ) {
/* translators: %s: Protected post title. */
$prepend = __( 'Protected: %s' );
* Filters the text prepended to the post title for protected posts.
* The filter is only applied on the front end.
* @param string $prepend Text displayed before the post title.
* Default 'Protected: %s'.
* @param WP_Post $post Current post object.
$protected_title_format = apply_filters( 'protected_title_format', $prepend, $post );
$title = sprintf( $protected_title_format, $title );
} elseif ( isset( $post->post_status ) && 'private' === $post->post_status ) {
/* translators: %s: Private post title. */
$prepend = __( 'Private: %s' );
* Filters the text prepended to the post title of private posts.
* The filter is only applied on the front end.
* @param string $prepend Text displayed before the post title.
* @param WP_Post $post Current post object.
$private_title_format = apply_filters( 'private_title_format', $prepend, $post );
$title = sprintf( $private_title_format, $title );
* Filters the post title.
* @param string $title The post title.
* @param int $id The post ID.
return apply_filters( 'the_title', $title, $id );
* Display the Post Global Unique Identifier (guid).
* The guid will appear to be a link, but should not be used as a link to the
* post. The reason you should not use it as a link, is because of moving the
* URL is escaped to make it XML-safe.
* @param int|WP_Post $post Optional. Post ID or post object. Default is global $post.
function the_guid( $post = 0 ) {
$post = get_post( $post );
$guid = isset( $post->guid ) ? get_the_guid( $post ) : '';
$id = isset( $post->ID ) ? $post->ID : 0;
* Filters the escaped Global Unique Identifier (guid) of the post.
* @param string $guid Escaped Global Unique Identifier (guid) of the post.
* @param int $id The post ID.
echo apply_filters( 'the_guid', $guid, $id );
* Retrieve the Post Global Unique Identifier (guid).
* The guid will appear to be a link, but should not be used as an link to the
* post. The reason you should not use it as a link, is because of moving the
* @param int|WP_Post $post Optional. Post ID or post object. Default is global $post.
function get_the_guid( $post = 0 ) {
$post = get_post( $post );
$guid = isset( $post->guid ) ? $post->guid : '';
$id = isset( $post->ID ) ? $post->ID : 0;
* Filters the Global Unique Identifier (guid) of the post.
* @param string $guid Global Unique Identifier (guid) of the post.
* @param int $id The post ID.
return apply_filters( 'get_the_guid', $guid, $id );
* Display the post content.
* @param string $more_link_text Optional. Content for when there is more text.
* @param bool $strip_teaser Optional. Strip teaser content before the more text. Default false.
function the_content( $more_link_text = null, $strip_teaser = false ) {
$content = get_the_content( $more_link_text, $strip_teaser );
* Filters the post content.
* @param string $content Content of the current post.
$content = apply_filters( 'the_content', $content );
$content = str_replace( ']]>', ']]>', $content );
* Retrieve the post content.
* @since 5.2.0 Added the `$post` parameter.
* @global int $page Page number of a single post/page.
* @global int $more Boolean indicator for whether single post/page is being viewed.
* @global bool $preview Whether post/page is in preview mode.
* @global array $pages Array of all pages in post/page. Each array element contains
* part of the content separated by the `<!--nextpage-->` tag.
* @global int $multipage Boolean indicator for whether multiple pages are in play.
* @param string $more_link_text Optional. Content for when there is more text.
* @param bool $strip_teaser Optional. Strip teaser content before the more text. Default false.
* @param WP_Post|object|int $post Optional. WP_Post instance or Post ID/object. Default null.
function get_the_content( $more_link_text = null, $strip_teaser = false, $post = null ) {
global $page, $more, $preview, $pages, $multipage;
$_post = get_post( $post );
if ( ! ( $_post instanceof WP_Post ) ) {
// Use the globals if the $post parameter was not specified,
// but only after they have been set up in setup_postdata().
if ( null === $post && did_action( 'the_post' ) ) {
$elements = compact( 'page', 'more', 'preview', 'pages', 'multipage' );
$elements = generate_postdata( $_post );
if ( null === $more_link_text ) {
$more_link_text = sprintf(
'<span aria-label="%1$s">%2$s</span>',
/* translators: %s: Post title. */
__( 'Continue reading %s' ),
// If post password required and it doesn't match the cookie.
if ( post_password_required( $_post ) ) {
return get_the_password_form( $_post );
// If the requested page doesn't exist.
if ( $elements['page'] > count( $elements['pages'] ) ) {
// Give them the highest numbered page that DOES exist.
$elements['page'] = count( $elements['pages'] );
$page_no = $elements['page'];
$content = $elements['pages'][ $page_no - 1 ];
if ( preg_match( '/<!--more(.*?)?-->/', $content, $matches ) ) {
if ( has_block( 'more', $content ) ) {
// Remove the core/more block delimiters. They will be left over after $content is split up.
$content = preg_replace( '/<!-- \/?wp:more(.*?) -->/', '', $content );
$content = explode( $matches[0], $content, 2 );
if ( ! empty( $matches[1] ) && ! empty( $more_link_text ) ) {
$more_link_text = strip_tags( wp_kses_no_null( trim( $matches[1] ) ) );
$content = array( $content );
if ( false !== strpos( $_post->post_content, '<!--noteaser-->' ) && ( ! $elements['multipage'] || 1 == $elements['page'] ) ) {
if ( $elements['more'] && $strip_teaser && $has_teaser ) {
if ( count( $content ) > 1 ) {
if ( $elements['more'] ) {
$output .= '<span id="more-' . $_post->ID . '"></span>' . $content[1];
if ( ! empty( $more_link_text ) ) {
* Filters the Read More link text.
* @param string $more_link_element Read More link element.
* @param string $more_link_text Read More text.
$output .= apply_filters( 'the_content_more_link', ' <a href="' . get_permalink( $_post ) . "#more-{$_post->ID}\" class=\"more-link\">$more_link_text</a>", $more_link_text );
$output = force_balance_tags( $output );
* Display the post excerpt.
* Filters the displayed post excerpt.
* @param string $post_excerpt The post excerpt.
echo apply_filters( 'the_excerpt', get_the_excerpt() );
* Retrieves the post excerpt.
* @since 4.5.0 Introduced the `$post` parameter.
* @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post.
* @return string Post excerpt.
function get_the_excerpt( $post = null ) {
if ( is_bool( $post ) ) {
_deprecated_argument( __FUNCTION__, '2.3.0' );
$post = get_post( $post );
if ( post_password_required( $post ) ) {
return __( 'There is no excerpt because this is a protected post.' );
* Filters the retrieved post excerpt.
* @since 4.5.0 Introduced the `$post` parameter.
* @param string $post_excerpt The post excerpt.
* @param WP_Post $post Post object.
return apply_filters( 'get_the_excerpt', $post->post_excerpt, $post );
* Determines whether the post has a custom excerpt.
* For more information on this and similar theme functions, check out
* the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
* Conditional Tags} article in the Theme Developer Handbook.
* @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post.
* @return bool True if the post has a custom excerpt, false otherwise.
function has_excerpt( $post = 0 ) {
$post = get_post( $post );
return ( ! empty( $post->post_excerpt ) );
* Displays the classes for the post container element.
* @param string|string[] $class One or more classes to add to the class list.
* @param int|WP_Post $post_id Optional. Post ID or post object. Defaults to the global `$post`.
function post_class( $class = '', $post_id = null ) {
// Separates classes with a single space, collates classes for post DIV.
echo 'class="' . esc_attr( implode( ' ', get_post_class( $class, $post_id ) ) ) . '"';
* Retrieves an array of the class names for the post container element.
* The class names are many. If the post is a sticky, then the 'sticky'
* class name. The class 'hentry' is always added to each post. If the post has a
* post thumbnail, 'has-post-thumbnail' is added as a class. For each taxonomy that
* the post belongs to, a class will be added of the format '{$taxonomy}-{$slug}' -
* eg 'category-foo' or 'my_custom_taxonomy-bar'.
* The 'post_tag' taxonomy is a special
* case; the class has the 'tag-' prefix instead of 'post_tag-'. All class names are
* passed through the filter, {@see 'post_class'}, with the list of class names, followed by
* $class parameter value, with the post ID as the last parameter.
* @since 4.2.0 Custom taxonomy class names were added.
* @param string|string[] $class Space-separated string or array of class names to add to the class list.
* @param int|WP_Post $post_id Optional. Post ID or post object.
* @return string[] Array of class names.
function get_post_class( $class = '', $post_id = null ) {
$post = get_post( $post_id );
if ( ! is_array( $class ) ) {
$class = preg_split( '#\s+#', $class );
$classes = array_map( 'esc_attr', $class );
// Ensure that we always coerce class to being an array.