wppaste
WordPress

get_permalink( int|WP_Post $post = 0, bool $leavename = false ): string|false

Since
1.0.0
Source
wp-includes/link-template.php:170

Builds the front-end URL for a post, page, or attachment based on the site's permalink structure and post type. It accepts a post ID or WP_Post object and falls back to the current global $post when no argument is given, returning false if the post cannot be found. Because the raw URL isn't run through display escaping, wrap it in esc_url() or use the_permalink() when printing it directly in a template.

Retrieves the full permalink for the current post or post ID.

Compatibility

WordPress
since 1.0.0
PHP
7.4–8.6-dev
  • 6.7.7
  • 6.8.8
  • 6.9.7
  • 7.0.4
  • 7.1.0

Present in every tracked release (6.7.7 to 7.1.0), and compiles on PHP 7.4 through 8.6-dev.

Parameters

$postint|WP_Postoptional
Post ID or post object. Default is the global $post.Default: 0
$leavenamebooloptional
Whether to keep post name or page name. Default false.Default: false

Return value

string|false
The permalink URL. False if the post does not exist.

Code examples

Every example is editable and runs in a real WordPress booted in your browser by WordPress Playground. Press Run, then edit the code: clicking away re-runs it. Nothing is sent anywhere until you do.

Print the permalink for each post in a custom loop

Loop over the seeded posts and show the title next to its computed permalink.

$post_ids = array( 1, 2, 3, 4, 5 );

foreach ( $post_ids as $post_id ) {
	$post = get_post( $post_id );

	if ( ! $post ) {
		continue;
	}

	$permalink = get_permalink( $post_id );

	if ( $permalink ) {
		echo esc_html( $post->post_title ) . ': ' . esc_html( $permalink ) . "\n";
	} else {
		echo esc_html( $post->post_title ) . ': no permalink available' . "\n";
	}
}

get_post() is called first so the loop can skip gracefully if an ID has been deleted, since get_permalink() itself would just return false.

Switch on a pretty permalink structure and pass $leavename to see the unreplaced token, a technique similar to what the slug editor on the edit-post screen relies on.

$post = get_post( 2 );

$resolved = get_permalink( $post );
$template = get_permalink( $post, true );

printf( "Resolved: %s\n", esc_html( $resolved ) );
printf( "Template: %s\n", esc_html( $template ) );

With the default plain permalink structure the two calls return an identical ?p= style link, since there is no %postname% token to leave in place.

Common problems and fixes · 4

Why does get_permalink() return false?

The function runs the argument through get_post() and immediately returns false when empty( $post->ID ) is true, which happens for a deleted or nonexistent ID, or when you call it with no argument outside the loop where the global $post isn't set.

Why does get_permalink() keep returning a ?p=123 link instead of a pretty URL?

get_permalink() reads the 'permalink_structure' option directly. If that option is empty (plain permalinks), the function has no template to fill in and falls back to the query-string style link for every post regardless of category or slug.

Why does %category% in my permalink structure only show one term when a post has two categories?

When the structure contains %category%, get_permalink() fetches all categories via get_the_category(), sorts them with wp_list_sort(), and picks a single one to use, it doesn't build a URL with every assigned category.

Do I need to escape the URL get_permalink() returns before echoing it?

Yes. The return value is a plain string built from the permalink structure and post data, with no HTML-attribute escaping applied by the function itself.

Alternatives and related functions

get_the_permalink
When you're already inside The Loop and want the result passed through the 'the_permalink' filter that themes and plugins commonly hook into.
the_permalink
When you just want to echo the current post's permalink directly in a template without capturing it in a variable first.
get_post_permalink
When you specifically need the link for a custom post type and want to bypass the post_type-detection branching that get_permalink() does internally.
get_page_link
When you know in advance you're linking to a page rather than a post, so you can skip the post-type check get_permalink() performs before delegating to it.

Performance profile

How much work a call to get_permalink() does, and what it touches: the algorithmic scaling, the Zend instruction count per call across PHP versions, the hooks it hands control to, and the core code that calls it. Measured from the compiled opcodes, not a stopwatch, so every number is identical on any machine running the same PHP version, and every function in core is ranked by cost.

Cost class
Heavy

Reaches the database via get_post().

Scaling
Constant

No loop in the body: the same number of instructions runs whatever you pass in.

Instructions
28–182

Executed per call on PHP 8.5, depending on the branch taken. The body compiles to 210.

Plugin surface
3 hooks

Third-party callbacks on 'pre_post_link', 'post_link_category', 'post_link' run inside this call, and their cost is not bounded by anything here.

Called by
50

50 places in core call this, so the cost is paid more often than your own code shows.

What it touches

  • querycontent queryget_post()called directly
  • optionoption read or writeget_option()called directly
  • hookthird-party callbacksapply_filters()called directly
  • cacheobject cachewp_cache_get()one call below get_permalink()
  • serializeserialisationmaybe_unserialize()one call below get_permalink()

Further down the call graph this can also reach transient. That is the worst case, several calls deep and usually down an error path, not what a normal call pays.

What one call costs · 52 distinct outcomes

One number would be a lie: the work depends on which branch runs. These are every distinct cost get_permalink() can have, taken from its control-flow graph on PHP 8.5.

WhenInstructionsCalls it makes
empty($post)28–35get_post()
is_object($post) && isset($post) && empty($post)30–32none
!empty($post)36–43get_post(), get_page_link()
is_object($post) && isset($post) && !empty($post)38–40get_page_link()
!empty($post)38–45get_post(), get_attachment_link()
is_object($post) && isset($post) && !empty($post)40–42get_attachment_link()
!empty($post)46–53get_post(), get_post_types(), get_post_permalink()
is_object($post) && isset($post) && !empty($post)48–50get_post_types(), get_post_permalink()
!empty($post)65–72get_post(), get_post_types(), get_option(), apply_filters(), home_url(), apply_filters()
is_object($post) && isset($post) && !empty($post)67–69get_post_types(), get_option(), apply_filters(), home_url(), apply_filters()
!empty($post) && wp_force_plain_post_permalink()69–76get_post(), get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), home_url(), apply_filters()
is_object($post) && isset($post) && !empty($post) && wp_force_plain_post_permalink()71–73get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), home_url(), apply_filters()
40 further outcomes, up to 179 instructions
!empty($post) && !wp_force_plain_post_permalink() && !$permalink110–117get_post(), get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), explode(), home_url(), user_trailingslashit(), apply_filters()
is_object($post) && isset($post) && !empty($post) && !wp_force_plain_post_permalink() && !$permalink112–114get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), explode(), home_url(), user_trailingslashit(), apply_filters()
!empty($post) && !wp_force_plain_post_permalink()118–125get_post(), get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_userdata(), explode(), home_url(), user_trailingslashit(), apply_filters()
!empty($post) && !wp_force_plain_post_permalink() && !empty($category)119–126get_post(), get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), explode(), home_url(), user_trailingslashit(), apply_filters()
is_object($post) && isset($post) && !empty($post) && !wp_force_plain_post_permalink()120–122get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_userdata(), explode(), home_url(), user_trailingslashit(), apply_filters()
is_object($post) && isset($post) && !empty($post) && !wp_force_plain_post_permalink() && !empty($category)121–123get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), explode(), home_url(), user_trailingslashit(), apply_filters()
!empty($post) && !wp_force_plain_post_permalink() && $permalink && !empty($category)127–134get_post(), get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), get_userdata(), explode(), home_url(), user_trailingslashit(), apply_filters()
!empty($post) && !wp_force_plain_post_permalink() && empty($category)128–135get_post(), get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), get_option(), get_term(), explode(), home_url(), user_trailingslashit(), apply_filters()
is_object($post) && isset($post) && !empty($post) && !wp_force_plain_post_permalink() && $permalink && !empty($category)129–131get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), get_userdata(), explode(), home_url(), user_trailingslashit(), apply_filters()
is_object($post) && isset($post) && !empty($post) && !wp_force_plain_post_permalink() && empty($category)130–132get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), get_option(), get_term(), explode(), home_url(), user_trailingslashit(), apply_filters()
!empty($post) && !wp_force_plain_post_permalink() && empty($category)132–141get_post(), get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), get_option(), get_term(), is_wp_error(), explode(), home_url(), user_trailingslashit(), apply_filters()
is_object($post) && isset($post) && !empty($post) && !wp_force_plain_post_permalink() && empty($category)134–138get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), get_option(), get_term(), is_wp_error(), explode(), home_url(), user_trailingslashit(), apply_filters()
!empty($post) && !wp_force_plain_post_permalink() && $permalink && empty($category)136–143get_post(), get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), get_option(), get_term(), get_userdata(), explode(), home_url(), user_trailingslashit(), apply_filters()
is_object($post) && isset($post) && !empty($post) && !wp_force_plain_post_permalink() && $permalink && empty($category)138–140get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), get_option(), get_term(), get_userdata(), explode(), home_url(), user_trailingslashit(), apply_filters()
!empty($post) && !wp_force_plain_post_permalink() && $permalink && empty($category)140–149get_post(), get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), get_option(), get_term(), is_wp_error(), get_userdata(), explode(), home_url(), user_trailingslashit(), apply_filters()
!empty($post) && !wp_force_plain_post_permalink() && !$category_object && !empty($category)142–149get_post(), get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), wp_list_sort(), apply_filters(), get_term(), explode(), home_url(), user_trailingslashit(), apply_filters()
is_object($post) && isset($post) && !empty($post) && !wp_force_plain_post_permalink() && $permalink && empty($category)142–146get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), get_option(), get_term(), is_wp_error(), get_userdata(), explode(), home_url(), user_trailingslashit(), apply_filters()
is_object($post) && isset($post) && !empty($post) && !wp_force_plain_post_permalink() && !$category_object && !empty($category)144–146get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), wp_list_sort(), apply_filters(), get_term(), explode(), home_url(), user_trailingslashit(), apply_filters()
!empty($post) && !wp_force_plain_post_permalink() && $permalink && !$category_object && !empty($category)150–157get_post(), get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), wp_list_sort(), apply_filters(), get_term(), get_userdata(), explode(), home_url(), user_trailingslashit(), apply_filters()
!empty($post) && !wp_force_plain_post_permalink() && !$category_object && empty($category)151–158get_post(), get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), wp_list_sort(), apply_filters(), get_term(), get_option(), get_term(), explode(), home_url(), user_trailingslashit(), apply_filters()
is_object($post) && isset($post) && !empty($post) && !wp_force_plain_post_permalink() && $permalink && !$category_object && !empty($category)152–154get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), wp_list_sort(), apply_filters(), get_term(), get_userdata(), explode(), home_url(), user_trailingslashit(), apply_filters()
!empty($post) && !wp_force_plain_post_permalink() && $category_object && !empty($category)152–159get_post(), get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), wp_list_sort(), apply_filters(), get_term(), get_category_parents(), explode(), home_url(), user_trailingslashit(), apply_filters()
is_object($post) && isset($post) && !empty($post) && !wp_force_plain_post_permalink() && !$category_object && empty($category)153–155get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), wp_list_sort(), apply_filters(), get_term(), get_option(), get_term(), explode(), home_url(), user_trailingslashit(), apply_filters()
is_object($post) && isset($post) && !empty($post) && !wp_force_plain_post_permalink() && $category_object && !empty($category)154–156get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), wp_list_sort(), apply_filters(), get_term(), get_category_parents(), explode(), home_url(), user_trailingslashit(), apply_filters()
!empty($post) && !wp_force_plain_post_permalink() && !$category_object && empty($category)155–164get_post(), get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), wp_list_sort(), apply_filters(), get_term(), get_option(), get_term(), is_wp_error(), explode(), home_url(), user_trailingslashit(), apply_filters()
is_object($post) && isset($post) && !empty($post) && !wp_force_plain_post_permalink() && !$category_object && empty($category)157–161get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), wp_list_sort(), apply_filters(), get_term(), get_option(), get_term(), is_wp_error(), explode(), home_url(), user_trailingslashit(), apply_filters()
!empty($post) && !wp_force_plain_post_permalink() && $permalink && !$category_object && empty($category)159–166get_post(), get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), wp_list_sort(), apply_filters(), get_term(), get_option(), get_term(), get_userdata(), explode(), home_url(), user_trailingslashit(), apply_filters()
!empty($post) && !wp_force_plain_post_permalink() && $permalink && $category_object && !empty($category)160–167get_post(), get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), wp_list_sort(), apply_filters(), get_term(), get_category_parents(), get_userdata(), explode(), home_url(), user_trailingslashit(), apply_filters()
is_object($post) && isset($post) && !empty($post) && !wp_force_plain_post_permalink() && $permalink && !$category_object && empty($category)161–163get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), wp_list_sort(), apply_filters(), get_term(), get_option(), get_term(), get_userdata(), explode(), home_url(), user_trailingslashit(), apply_filters()
!empty($post) && !wp_force_plain_post_permalink() && $category_object && empty($category)161–168get_post(), get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), wp_list_sort(), apply_filters(), get_term(), get_category_parents(), get_option(), get_term(), explode(), home_url(), user_trailingslashit(), apply_filters()
is_object($post) && isset($post) && !empty($post) && !wp_force_plain_post_permalink() && $permalink && $category_object && !empty($category)162–164get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), wp_list_sort(), apply_filters(), get_term(), get_category_parents(), get_userdata(), explode(), home_url(), user_trailingslashit(), apply_filters()
is_object($post) && isset($post) && !empty($post) && !wp_force_plain_post_permalink() && $category_object && empty($category)163–165get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), wp_list_sort(), apply_filters(), get_term(), get_category_parents(), get_option(), get_term(), explode(), home_url(), user_trailingslashit(), apply_filters()
!empty($post) && !wp_force_plain_post_permalink() && $permalink && !$category_object && empty($category)163–172get_post(), get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), wp_list_sort(), apply_filters(), get_term(), get_option(), get_term(), is_wp_error(), get_userdata(), explode(), home_url(), user_trailingslashit(), apply_filters()
is_object($post) && isset($post) && !empty($post) && !wp_force_plain_post_permalink() && $permalink && !$category_object && empty($category)165–169get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), wp_list_sort(), apply_filters(), get_term(), get_option(), get_term(), is_wp_error(), get_userdata(), explode(), home_url(), user_trailingslashit(), apply_filters()
!empty($post) && !wp_force_plain_post_permalink() && $category_object && empty($category)165–174get_post(), get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), wp_list_sort(), apply_filters(), get_term(), get_category_parents(), get_option(), get_term(), is_wp_error(), explode(), home_url(), user_trailingslashit(), apply_filters()
is_object($post) && isset($post) && !empty($post) && !wp_force_plain_post_permalink() && $category_object && empty($category)167–171get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), wp_list_sort(), apply_filters(), get_term(), get_category_parents(), get_option(), get_term(), is_wp_error(), explode(), home_url(), user_trailingslashit(), apply_filters()
!empty($post) && !wp_force_plain_post_permalink() && $permalink && $category_object && empty($category)169–176get_post(), get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), wp_list_sort(), apply_filters(), get_term(), get_category_parents(), get_option(), get_term(), get_userdata(), explode(), home_url(), user_trailingslashit(), apply_filters()
is_object($post) && isset($post) && !empty($post) && !wp_force_plain_post_permalink() && $permalink && $category_object && empty($category)171–173get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), wp_list_sort(), apply_filters(), get_term(), get_category_parents(), get_option(), get_term(), get_userdata(), explode(), home_url(), user_trailingslashit(), apply_filters()
!empty($post) && !wp_force_plain_post_permalink() && $permalink && $category_object && empty($category)173–182get_post(), get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), wp_list_sort(), apply_filters(), get_term(), get_category_parents(), get_option(), get_term(), is_wp_error(), get_userdata(), explode(), home_url(), user_trailingslashit(), apply_filters()
is_object($post) && isset($post) && !empty($post) && !wp_force_plain_post_permalink() && $permalink && $category_object && empty($category)175–179get_post_types(), get_option(), apply_filters(), wp_force_plain_post_permalink(), get_the_category(), wp_list_sort(), apply_filters(), get_term(), get_category_parents(), get_option(), get_term(), is_wp_error(), get_userdata(), explode(), home_url(), user_trailingslashit(), apply_filters()

Across PHP versions

PHPCompiledExecutedBranchesNotes
8.6-dev21028–18218
8.521028–18218
8.421028–1821815 fewer instructions than PHP 8.3
8.322528–19718
8.222528–19718
8.122528–19718
7.422528–19718

An instruction is not a fixed amount of time, so a matching count is not necessarily the same speed; what it rules out is a difference in the work itself.

Hooks and filters fired · 3

3 hooks fire while get_permalink() runs, in this order:

  1. apply_filters( pre_post_link )filterline 217 (+47 into the body)

    Filters the permalink structure for a post before token replacement occurs.

  2. apply_filters( post_link_category )filterline 244 (+74 into the body)

    Filters the category that gets used in the %category% permalink token.

  3. apply_filters( post_link )filterline 308 (+138 into the body)

    Filters the permalink for a post.

Uses · 17

Show all 17

Used by · 50

Show all 50

Source code

function get_permalink( $post = 0, $leavename = false ) {	$rewritecode = array(		'%year%',		'%monthnum%',		'%day%',		'%hour%',		'%minute%',		'%second%',		$leavename ? '' : '%postname%',		'%post_id%',		'%category%',		'%author%',		$leavename ? '' : '%pagename%',	); 	if ( is_object( $post ) && isset( $post->filter ) && 'sample' === $post->filter ) {		$sample = true;	} else {		$post   = get_post( $post );		$sample = false;	} 	if ( empty( $post->ID ) ) {		return false;	} 	if ( 'page' === $post->post_type ) {		return get_page_link( $post, $leavename, $sample );	} elseif ( 'attachment' === $post->post_type ) {		return get_attachment_link( $post, $leavename );	} elseif ( in_array( $post->post_type, get_post_types( array( '_builtin' => false ) ), true ) ) {		return get_post_permalink( $post, $leavename, $sample );	} 	$permalink = get_option( 'permalink_structure' ); 	/**	 * Filters the permalink structure for a post before token replacement occurs.	 *	 * Only applies to posts with post_type of 'post'.	 *	 * @since 3.0.0	 *	 * @param string  $permalink The site's permalink structure.	 * @param WP_Post $post      The post in question.	 * @param bool    $leavename Whether to keep the post name.	 */	$permalink = apply_filters( 'pre_post_link', $permalink, $post, $leavename ); 	if (		$permalink &&		! wp_force_plain_post_permalink( $post )	) { 		$category = '';		if ( str_contains( $permalink, '%category%' ) ) {			$cats = get_the_category( $post->ID );			if ( $cats ) {				$cats = wp_list_sort(					$cats,					array(						'term_id' => 'ASC',					)				); 				/**				 * Filters the category that gets used in the %category% permalink token.				 *				 * @since 3.5.0				 *				 * @param WP_Term  $cat  The category to use in the permalink.				 * @param array    $cats Array of all categories (WP_Term objects) associated with the post.				 * @param WP_Post  $post The post in question.				 */				$category_object = apply_filters( 'post_link_category', $cats[0], $cats, $post ); 				$category_object = get_term( $category_object, 'category' );				$category        = $category_object->slug;				if ( $category_object->parent ) {					$category = get_category_parents( $category_object->parent, false, '/', true ) . $category;

Changelog

Introduced in 1.0.0. Unchanged from 6.7.7 through 7.1.0.

  1. 6.7.7
  2. 6.8.8
  3. 6.9.7
  4. 7.0.4
  5. 7.1.0

Signature, return type and hooks compared across 5 parsed releases.

About this page

Parsed data
Generated from the wordpress-develop 7.1.0 tag, from src/wp-includes/link-template.php, and regenerated for each WordPress release so it tracks the code rather than a snapshot of it.
Corrections
Something wrong on this page? Report it and it gets fixed in the next regeneration.