Make WordPress Core

Opened 13 years ago

Closed 11 years ago

Last modified 11 years ago

#16303 closed defect (bug) (fixed)

Improve documentation and usability of WP_Rewrite Endpoint support

Reported by: westi's profile westi Owned by: westi's profile westi
Milestone: 3.7 Priority: lowest
Severity: normal Version: 3.1
Component: Rewrite Rules Keywords: westi-likes has-patch commit dev-feedback needs-docs
Focuses: Cc:

Description

When you know how it works the WP_Rewrite Endpoint support is really simple and cool.

When you don't it looks really difficult to use.

We should document it better and add an endpoint mask for Custom Post Types.

Attachments (4)

16303.diff (2.0 KB) - added by westi 13 years ago.
16303.patch (1.8 KB) - added by hakre 13 years ago.
16303.ep-docs.diff (2.5 KB) - added by duck_ 12 years ago.
16303.2.diff (807 bytes) - added by duck_ 12 years ago.

Download all attachments as: .zip

Change History (32)

@westi
13 years ago

#1 follow-up: @westi
13 years ago

A json endpoint example:

function json_init() {
	add_rewrite_endpoint( 'json', EP_ROOT ^ EP_PERMALINK ^ EP_ALL_ARCHIVES );
}

add_action( 'init', 'json_init' );

function json_request( $query_vars ) {
	if ( isset( $query_vars['json'] ) )
		$query_vars['json'] = true;

	return $query_vars;
}

add_filter( 'request', 'json_request' );

function json_template_redirect() {
	global $wp_query, $post, $comment;

	if ( ! get_query_var( 'json' ) ) 
		return true;

	$data = array();
	$post_keys = array( 'ID', 'post_author', 'post_title', 'post_content', 'post_date_gmt', 'post_status', 'post_name', 'post_type', 'comment_count' );
	$comment_keys = array( 'comment_ID', 'comment_author', 'comment_content', 'comment_date_gmt', 'comment_type' );
	
	while ( have_posts() ) {
		the_post();
		
		foreach ( $post_keys as $_key )
			$_post[$_key] = $post->$_key;

		if ( post_type_supports( $_post['post_type'], 'comments' ) ) {
			$wp_query->comments = get_comments( array('post_id' => $post->ID, 'status' => 'approve', 'order' => 'ASC') );
			$wp_query->comment_count = count($wp_query->comments);
			update_comment_cache($wp_query->comments);
			
			$_post['comments'] = array();
	
			while ( have_comments() ) {
				the_comment();
				foreach ( $comment_keys as $_key )
					$_comment[$_key] = $comment->$_key;
	
				$_post['comments'][ $_comment['comment_type'] . '-' . $_comment['comment_ID'] ] = $_comment;
			}
		}

		$data[ $_post['post_type'] . '-' . $_post['ID'] ] = $_post;
	}

	die( json_encode( $data ) );
}
add_action( 'template_redirect', 'json_template_redirect' );

#2 @scribu
13 years ago

  • Cc scribu added

#3 @westi
13 years ago

  • Keywords westi-likes added; 3.2-early removed
  • Milestone changed from Future Release to 3.2
  • Priority changed from normal to lowest

Although not a roadmapped 3.2 feature this is a really simple patch which makes the existing functionality much easier to understand and proves how useable it actually is.

So I really want to put this in 3.2 - low priority though

#4 @mikeschinkel
13 years ago

  • Cc mikeschinkel@… added

@westi - I'd really like to understand the API endpoint functionality but thus far can't grok it. Has it been documented yet? Google fails me. Any chance of a short-term Cliff Notes version?

#5 follow-up: @dd32
13 years ago

mikeschinkel: It's actually very simple.

You register a "Endpoint" with a Endpoint Mask.

add_rewrite_endpoint( 'json', EP_ROOT ^ EP_PERMALINK ^ EP_ALL_ARCHIVES );

If you understand Binary mathematics, it's a lot easier, as it's using Bitwise comparisons ( this site seems to give a reasonable overview of it)

Each set of rewrite rules that is generated by core is given a Endpoint Mask(a bitmask), for example, posts get EP_PERMALINK, If the bits set in EP_PERMALINK is "equal to" the bits in the specified endpoint mask, then that endpoint is added to the rules generated.

Note, that Custom Post Types by default get given _no_ endpoint mask, you can set it when you register the post type, but for best results, it should be a unique number (well, power of 2) global to the install, this is a bit hard to track and I've personally just ended up using stupidly high numbers (240 for example) as masks.

westi: That patch looks like it's missing a file? You've defined the bitmask, but never actually used it?

#6 follow-up: @hakre
13 years ago

Just reviewd the patch and refreshed against trunk, my 2 cents:

  • As written in comments, the Bitmask is OR'ed. That's what common as well (PHP Example)
  • There is no vartype "bitmask" (isn't it int?, see notes)
  • Adding an endpoint does always mean a new endpoint (tautological).
  • It's 3.2.0 now.

Additional notes:

  • EP_ALL can be safely defined as 16 383, that spares the calculation.
  • Same for EP_ALL_ARCHIVES, can be pre-calculated as well (documentation could show what value stands for)
  • About the missing use of the define dd32 was pointing westi to I have no clue.

westi: WP_Rewrite::add_endpoint() had documented the $places as an array, you changed that to bitmask (and I to int because I think bitmasks are ints). What PHP vartype is that $places parameter of the said function?

@hakre
13 years ago

#7 in reply to: ↑ 6 @westi
13 years ago

Replying to hakre:

Additional notes:

  • EP_ALL can be safely defined as 16 383, that spares the calculation.

This was changed on purpose to ease maintenance and to hilight to people reading the code that it wasn't a static value and could change.

  • Same for EP_ALL_ARCHIVES, can be pre-calculated as well (documentation could show what value stands for)

All "Archive" type pages

  • About the missing use of the define dd32 was pointing westi to I have no clue.

The define is added but not used as it's really useful for plugins :)

westi: WP_Rewrite::add_endpoint() had documented the $places as an array, you changed that to bitmask (and I to int because I think bitmasks are ints). What PHP vartype is that $places parameter of the said function?

bitmasks are ints yes - I was just being more specific.

Take note all the code in my original patch was written on a transatlantic flight :)

#8 in reply to: ↑ 5 @mikeschinkel
13 years ago

Replying to dd32:

Thanks for attempting to explain. I'm familiar with bitwise math (learned it well in my assembler class in college) it's just that I don't understand what happens when each of the following masks are used. What does EP_ROOT imply? Or EP_PERMALINK? Or EP_ALL_ARCHIVES?

Thanks in advance.

#9 @ryan
13 years ago

  • Milestone changed from 3.2 to Future Release

#10 @westi
13 years ago

  • Keywords 3.3-early commit added

#11 @ryan
13 years ago

  • Milestone changed from Future Release to 3.3

#12 @nacin
13 years ago

Shouldn't CPTs add their own endpoint masks? Or is this for the CPT archive only?

#13 @jane
13 years ago

  • Keywords dev-feedback added

I don't see any clear resolution here, and we're at freeze for 3.3. Leads/committers, please weigh in on the commit vs punt decision.

#14 @ryan
13 years ago

  • Milestone changed from 3.3 to Future Release

@duck_
12 years ago

#15 @duck_
12 years ago

I was looking at the documentation for rewrite.php in general and I remembered this ticket. 16303.ep-docs.diff was my attempt at some better inline documentation for endpoints.

In documenting this I noticed some strange behaviour with the endpoint code given in my example, see #19876.

#16 @duck_
12 years ago

In [19753]:

Improve the inline documentation of rewrite endpoints. See #16303.

@duck_
12 years ago

#17 follow-up: @duck_
12 years ago

  • Keywords 3.3-early removed

I attached a new version of the improved functionality patch.

However, there are a couple of issues:

  • Should EP_ROOT be included in EP_ALL_ARCHIVES? It depends on the page_on_front option as to whether or not it's an archive.
  • Is EP_CPT_ARCHIVE usable? This was alluded to in previous comments. register_post_type() uses WP_Rewrite::add_rule() when creating rewrites for CPT archives, and this means no endpoints.

#18 in reply to: ↑ 17 @westi
12 years ago

Replying to duck_:

I attached a new version of the improved functionality patch.

However, there are a couple of issues:

  • Should EP_ROOT be included in EP_ALL_ARCHIVES? It depends on the page_on_front option as to whether or not it's an archive.
  • Is EP_CPT_ARCHIVE usable? This was alluded to in previous comments. register_post_type() uses WP_Rewrite::add_rule() when creating rewrites for CPT archives, and this means no endpoints.

I don't think that the ROOT of the site is ever an archive view really (and it people want to include it they can OR it in) I think have different behaviour for / not for page on front could be confusing and would mean we would need to flush and regen rules if the option changed.

I'm not sure if EP_CPT_ARCHIVE is currently usable but it is something that I feel should be usable - use case something like the example JSON endpoint above would automatically get a JSON version of any CPT archive view if it works ( I can't remember how I tested at the time but I can probably dig the code out - I was working around the concepts of a Person CPT with the CPT Archives be "People")

#19 follow-up: @lgedeon
12 years ago

  • Cc luke.gedeon@… added

Here is another example that might help with documenting end-points:

This code adds an endpoint of the form /embed/xxxx/ to the end of any post. We then load a separate template to render the article in one of several embeddable formats.

class plugin_embeddable_post {

	public function __construct() {
		add_action( 'admin_init', array( $this, 'admin_init' ) );
		add_filter( 'query_vars', array( $this, 'query_vars' ) );
		add_filter( 'template_include', array( $this, 'template_include' ) );
	}

	/**
	 * add_rewrite_endpoint just adds an endpoint to the pool of endpoints that
	 * will be used when generate_rewrite_rules is run. It does nothing on it's
	 * own but generate_rewrite_rules fires too late, so we use admin_init to
	 * make sure it is there when rules are flushed.
	 */
	public function admin_init() {
		add_rewrite_endpoint( 'embed', EP_PERMALINK  );
	}

	public function query_vars( $public_query_vars ) {
		$public_query_vars[] = 'embed';
		return $public_query_vars;
	}

	public function template_include( $template ) {

		if ( 'format1' == get_query_var( 'embed' ) )
			$template = locate_template( 'embed-format1.php' );

		// add additional embeddable formats here or use one template for all

		return $template;
	}
}
$plugin_embeddable_post = new plugin_embeddable_post;
Last edited 12 years ago by lgedeon (previous) (diff)

#20 in reply to: ↑ 19 @duck_
12 years ago

Replying to lgedeon:

Here is another example that might help with documenting end-points:

This code adds an endpoint of the form /embed/xxxx/ to the end of any post. We then load a separate template to render the article in one of several embeddable formats.

Just a couple of notes:

  • You shouldn't pass an array as $places to add_rewrite_endpoint(). AFAIK the binary representation of an array is "1" so it works for EP_PERMALINK, but try the same code but with EP_PAGES and it will still add the endpoint to post permalinks. This was a documentation error previously.
  • WP_Rewrite::add_endpoint() will add the query var for you. I guess that since you're hooking into admin_init to add_rewrite_endpoint() it's not going to work unless you hook into init instead.

#21 @lgedeon
12 years ago

Hi duck_

Thanks for the feedback. I wrote this before the documentation update and missed that when I copy/pasted. BTW, thank you so much for updating the docs. This is a really cool feature and the doc helps make it available to more devs.

I did see that add_rewrite_endpoint() adds the query_var but since I only need to call add_rewrite_endpoint() when rewrite rules are flushed and need the query var all the time, it almost makes sense to split them. The other option as you mentioned is to call add_rewrite_endpoint() all the time, on init. The function is light enough that it probably wouldn't hurt. I just had not decided yet... so I did half of both. :)

#22 in reply to: ↑ 1 @WraithKenny
12 years ago

Replying to westi:

A ticket pointing to your example code: #19493

#23 @ethitter
12 years ago

  • Cc ehitter@… added

#24 @tomas.k.7
11 years ago

  • Keywords needs-docs added
  • Severity changed from normal to trivial

you need to flush the new rule: $wp_rewrite->flush_rules();

otherwise you will get error 404...

#25 @SergeyBiryukov
11 years ago

  • Severity changed from trivial to normal

#26 @duck_
11 years ago

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

In 24812:

Introduce a new endpoint mask for all archives, fixes #16303.

#27 @helen
11 years ago

  • Milestone changed from Future Release to 3.7

#28 @duck_
11 years ago

A new ticket has been opened to deal with CPT archive endpoint support: #24853.

Note: See TracTickets for help on using tickets.