Make WordPress Core

Opened 6 years ago

Closed 6 years ago

Last modified 6 years ago

#12240 closed enhancement (fixed)

Let plugins filter post permalink before core replacements

Reported by: shidouhikari Owned by: ryan
Milestone: 3.0 Priority: normal
Severity: normal Version: 2.9.1
Component: Permalinks Keywords: has-patch reporter-feedback
Focuses: Cc:


In the function get_permalink(), there is the filter 'post_link' that lets plugins filter a post permalink.

But this filter is run after all rewrite tags are replaced.

I suggest give plugins more control over posts permalink generation, adding a filter before rewrite tags start being parsed, so that they can replace it as needed.

Attachments (1)

link-template.php.diff (550 bytes) - added by shidouhikari 6 years ago.
This patch adds a new filter, allowing plugins to work over permalink before rewrite tags start being parsed and replaced

Download all attachments as: .zip

Change History (18)

6 years ago

This patch adds a new filter, allowing plugins to work over permalink before rewrite tags start being parsed and replaced

#1 follow-up: @filosofo
6 years ago

Couldn't this be done with the 'option_permalink_structure' filter or in the 'post_link' filter by getting the 'permalink_structure' option value directly?

#2 in reply to: ↑ 1 @shidouhikari
6 years ago

Replying to filosofo

I couldn't find the 'option_permalink_structure' filter, could you point me where it is?

The idea is not just have the 'permalink_structure' option value, it is to be able to convert a rewrite tag into a value *before* core is able to do it.

For example, let's say I wanna use %category% in the permalink, but a specific one, not the one with lowest ID.

If there was a filter after 'permalink_structure' option value is loaded and before WordPress starts parsing it for rewrite tags, I could use the filter and replace %category% for the category I want, instead of letting WordPress do it on its way.

Since there is no such filter, when the available filter is called it's too late, because in the final permalink I don't know exactly where %category% was, to do what I wanted. I'd need to find out what's "category" in the final permalink (which would be harder and may break with newer WordPress versions) and do a str_replace over it.

A note about this exemple: since, during rewrite rules parsing, any category is accepted for %category%, it would require no rewrite rule changes for it to work.

Another example of its use is to achieve better performance when custom permalinks are used. Instead of letting WordPress to all the parsing and replacement, to later just throw away its work and return the actual custom permalink, we could just use the first filter to add it, and Wordpress would be saved from doing its work without need.

#3 @nacin
6 years ago

  • Milestone changed from 2.9.3 to 3.0

#4 @shidouhikari
6 years ago

  • Keywords has-patch tested added; permalink filter hook get_permalink post_link removed

I've been running this patch on my 2.9.2 sites and it works fine.

The only thing the patch changes is adding the filter. If the new filter isn't used by any plugin, nothing will change as usual for unused filters.

Any harm a plugin may do with the new filter, it can do with the old 'post_link' filter, so we should be OK on that too.

#5 @nacin
6 years ago

The option_permalink_structure filter is triggered when get_option('permalink_structure') is called, which has an option_$option filter.

If the filters in get_option() aren't enough to do what you want to do, a short sample plugin may help us understand it better.

#6 @nacin
6 years ago

  • Keywords reporter-feedback added; tested removed

#7 @shidouhikari
6 years ago

  • Cc shidouhikari added

Ok, I think I'm better talking ih PHP than in english :P

This code is only didactic, a simplified version of a plugin, I didn't test it but it's enough to explain my idea. The function would be hooked to this new filter.

// this filter only works if it is called before Wordpress core starts converting posts permastruct into a permalink
// it searchs for the rewrite tag '%category%' and replaces it with a custom category slug
function parseLink($permalink, $post) {
$category = null;

	if (strpos($permalink, '%category%') !== false) {
		$cat = null;

		// retrieves a post metadata, which has a category ID
		$category_permalink = get_post_meta($post->ID, '_category_permalink', true);

		// if metadata is found, get that permalink from the retrieved ID
		if ($category_permalink) $cat = get_category($category_permalink);

		// if the category is found, get its slug and do the replacement
		if ($cat) {
			$category = $cat->slug;
			if ($parent = $cat->parent){
				$category = get_category_parents($parent, false, '/', true) . $category;
			$permalink = str_replace('%category%', $category, $permalink);


	// if '%category%' is not part of posts permastruct, nothing had to be customly changed
	//if this post doesn't have the metadata, return the $permalink without removing the '%category%' rewrite tag, and let default Wordpress core deal with it
	return $permalink;

Without the filter hook I'm suggesting, it would require a new rewrite tag to be done.

#8 follow-up: @Denis-de-Bernardy
6 years ago

I'm not understanding why any of this can't be done with the existing filters.

#9 in reply to: ↑ 8 @shidouhikari
6 years ago

Replying to Denis-de-Bernardy:

Take a look at link-template.php. What get_permalink() does is get the post permastruct and search for each rewrite tag in it, and when found the tag is replaced by that post's correspondent value. When it's done with all tags, we have the final permalink to that post, and then 'post_link' filter is run.

It passes the final pernalink, after the permastruct is processed, and doing so we can't customly replace default tags, which forces us to create new ones that Wordpress won't process and will leave for 'post_link' filter.

Since Wordpress's rewrite rule doesn't verify if some tags value is really related to a post (I mean, you can use %tag% in permalink, get a post's permalink generated by it and change that default tag to another one that's not assigned to that post, and you'll still receive that post), we could write a plugin to change the default value Wordpress chooses for those tags, and generate custom permalinks that would be recognized by rewrite rules parser without needing to hack it.

It's just an exemple of what direct access to permastruct before permalink is generated can be done. In another exemple, a plugin could add a metabox to the post form, where the user could add keywords to be used for %tag%. These keywords would be searched for existing tags, and if not found new ones would be created, and these tags IDs stored in a metadata. When the permalink is asked, the plugin replaces %tag% for these custom tags, giving a huge variety of possibilities to be used in permalinks, outside the post slug. And without needing to create new rewrite tags for it, since any tag is accepted by rewrite rule parser.

Today this can't be done, because we don't have access to the permalink before its tags are processed, we only have access to it after the processing is finished. So we must create new tags, so that get_permalink() leave then alone, and we can replace them for what we want there. And doing so we must also add the custom tag to rewrite parser.

Try to use the exemple I provided and you can see it can't be done with 'post_link'. Because when the filter is called, %category% is already replaced by a category and it's not available anymore.

But if a filter is used before get_permalink() starts parsing the permastruct, any tag can be customly parsed by a plugin, enhancing Wordpress's default behavior.

#10 @shidouhikari
6 years ago

'option_permalink_structure' could be used if there was a way to assure get_option() is called from get_permalink().

Because if get_option('permalink_structure') is called elsewhere without the objective of converting the permastruct to a permalink, the permastruct would get messed up and other codes be broken by the plugin that uses it this filter.

#11 @dd32
6 years ago

  • Resolution set to fixed
  • Status changed from new to closed

(In [13549]) Add a 'pre_post_link' filter to get_permalink() before token replacement occurs. Fixes #12240

#12 @dd32
6 years ago

The way i see it, Is that this allows for internal tokens to be replaced by plugins with custom data, For example, the Ordering of categories, or changing the slug displayed for a author.

It would also allow for a Rewrite plugin to offer a different permastruct for posts on a per-post basis, without duplicating WordPress's code and/or parsing the link twice.

#13 @Denis-de-Bernardy
6 years ago

fair enough. might we need an extra handler during permalink parsing to play with it?

#14 follow-up: @shidouhikari
6 years ago

Thx dd32 :)

If nothing goes wrong I'll have a plugin being released together with 3.0 that will benefit for it!

Denis, what you mean by handler?
That filter is enough for what I'm thinking.

#15 in reply to: ↑ 14 @Denis-de-Bernardy
6 years ago

Replying to shidouhikari:

Thx dd32 :)

If nothing goes wrong I'll have a plugin being released together with 3.0 that will benefit for it!

Denis, what you mean by handler?
That filter is enough for what I'm thinking.

Indeed, but I'm not 100% is sufficient for what DD32 was thinking. If, for instance, a custom post type decides to add a custom stub, a custom permalink tag, etc... the rewrite rules filter comes to mind, but there might needs to be a filter when it gets interpreted or something like that in order to take full advantage of the new hook.

#16 @dd32
6 years ago

You're correct, This filter would be used as a small part of a much larger implementation.

Rewrite rules are hookable enough for it, and this filter only applies to posts, no other post type, they've got their own specific filters i believe.

#17 @shidouhikari
6 years ago

This is only for post indeed, I didn't have custom post types in mind.

The whole idea is to have access to existing rewrite tags during permalink generation. Other resources like date and category that don't use rewrite tags don't need it at all. For custom post types I'd need to see how their permalink is generated and parsed, I never worked with them.

If somebody wanna have control over these resources he may need other filters for sure, but that's beyond the scope of my suggestion idea :D

If anything, post a mail to wp-hackers with this subject and we can extend it.

Note: See TracTickets for help on using tickets.