Created
June 2, 2012 00:32
-
-
Save joehoyle/2855923 to your computer and use it in GitHub Desktop.
HM_Rewrite
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /** | |
| * HM_Rewrite and HM_Rewrite_Rule are wrappers for the WordPress rewrite / wp query system | |
| * | |
| * The goal of HM_Rewrite and associated fuctions / classes is to make it very easy to add new routing points | |
| * with new pages (as in dynamic pages, post_type_archive etc). It basically wrapps a few tasks into a nice API. | |
| * | |
| * Everything (almost) you need for setting up a new routing page can be done all at once, relying heavily on PHP | |
| * Closures. It essentially wrapps adding to the rewrite_rules, adding your template file to template_redirect, | |
| * wp_title hook, body_class hook, parse_query hook etc. Also also provides some callbacks for conveniance. | |
| * | |
| * Each rewrite rule is an instance of HM_Rewrite_Rule. Here you add the regex / wp_query vars and any other | |
| * options for the "page". For example a callback function to parse_request to add additional query vars, or a callback | |
| * body_class. | |
| * | |
| * There is also a wrapper function for all of this in one call hm_rewrite_rule(). hm_rewrite_rule() is generally the | |
| * recommended interface, you can interact with the underlying objects for more advanced stuff (and also tacking onto | |
| * other rewrite rules) | |
| * | |
| * Simple use case example: | |
| * | |
| * hm_rewrite_rule( array( | |
| * | |
| * 'regex' => '^users/([^/]+)/?', | |
| * 'query' => 'author_name=$matches[1]&', | |
| * 'template' => 'user-archive.php', | |
| * 'body_class_callback' => function( $classes ) { | |
| * $classes[] = 'user-archive'; | |
| * $classes[] = 'user-' . get_query_var( 'author_name' ); | |
| * return $classes; | |
| * }, | |
| * 'title_callback' => function( $title, $seperator ) { | |
| * return get_query_var( 'author_name' ) . ' ' . $seperator . ' ' . $title; | |
| * } | |
| * ) ); | |
| * | |
| * | |
| * | |
| * A more advanced example using more callbacks | |
| * | |
| * * hm_rewrite_rule( array( | |
| * | |
| * 'regex' => '^reviews/([^/]+)/?', // a review category page | |
| * 'query' => 'review_category=$matches[1]&', | |
| * 'template' => 'review-category.php', | |
| * 'request_callback' => function( WP $wp ) { | |
| * | |
| * // if the review category is "laptops" then only show items in draft | |
| * if ( $wp->query_vars['review_category'] == 'laptops' ) | |
| * $wp->query_vars['post_status'] = 'draft'; | |
| * }, | |
| * 'query_callback' => function( WP_Query $query ) { | |
| * | |
| * //overwrite is_home because WordPress gets it wrong here | |
| * $query->is_home = false; | |
| * }, | |
| * 'body_class_callback' => function( $classes ) { | |
| * $classes[] = get_query_var( 'review_category' ); | |
| * return $classes; | |
| * }, | |
| * 'title_callback' => function( $title, $seperator ) { | |
| * return review_category . ' ' . $seperator . ' ' . $title; | |
| * } | |
| * ) ); | |
| * | |
| * | |
| */ | |
| class HM_Rewrite { | |
| static private $instance; | |
| private $rules = array(); | |
| /** | |
| * Add a rewrite rule, this should be called on `init` | |
| * | |
| * @param HM_Rewrite_Rule $rule [description] | |
| */ | |
| public static function add_rule( HM_Rewrite_Rule $rule ) { | |
| self::$rules[] = $rule; | |
| } | |
| /** | |
| * Get all the rewrite rules added | |
| * | |
| * @return HM_Rewrite_Rule[] | |
| */ | |
| public static function get_rules() { | |
| return self::$rules; | |
| } | |
| /** | |
| * Remove a rule | |
| * | |
| * @param string $id the identifier for the rule (regex or id if specific in the rule) | |
| */ | |
| public static function remove_rule( $id ) { | |
| $rules = &self::$rules; | |
| array_walk( self::$rules, function( $rule, $key ) use ( $id, &$rules ) { | |
| if ( $rule->id == $id ) | |
| unset( $rules[$key] ); | |
| } ); | |
| } | |
| /** | |
| * Get the rewrite rule for a given id (regex or id if specificed in the rule) | |
| * | |
| * @param string $id | |
| * @return HM_Rewrite_Rule | |
| */ | |
| public static function get_rule( $id ) { | |
| $rule = array_filter( self::$rules, function( HM_Rewrite_Rule $rule ) use ( $id ) { | |
| return $rule->id == $id; | |
| } ); | |
| return reset( $rule ); | |
| } | |
| /** | |
| * Called when a regex is matched on page load, will call the rewrite rule responsible for it (if any) | |
| * | |
| * @param string $regex | |
| */ | |
| public static function matched_regex( $regex ) { | |
| array_walk( self::$rules, function( HM_Rewrite_Rule $rule ) use ( $regex ) { | |
| if ( $rule->get_regex() == $regex ) | |
| $rule->matched_rule(); | |
| } ); | |
| } | |
| } | |
| class HM_Rewrite_Rule { | |
| public $id = ''; | |
| public $regex = ''; | |
| public $query_args = ''; | |
| public $request_callbacks = array(); | |
| public $query_callbacks = array(); | |
| public $title_callbacks = array(); | |
| public $parse_query_callbacks = array(); | |
| public $body_class_callbacks = array(); | |
| public $template = ''; | |
| public $body_classes = array(); | |
| public $access_rule = ''; | |
| public function __construct( $regex, $id = null ) { | |
| $this->regex = $regex; | |
| $this->id = $id ? $id : $regex; | |
| } | |
| /** | |
| * Get the regex for the rewrite rule | |
| * | |
| * @return string | |
| */ | |
| public function get_regex() { | |
| if ( substr( $this->regex, 0, 1 ) !== substr( $this->regex, strlen( $this->regex ) - 1 ) ) | |
| return '~' . $this->regex . '~'; | |
| return $this->regex; | |
| } | |
| public function set_wp_query_args( $vars ) { | |
| $this->query_args = wp_parse_args( $vars ); | |
| } | |
| /** | |
| * Get the WP_Query args for this rule | |
| * @return array | |
| */ | |
| public function get_wp_query_args() { | |
| return $this->query_args; | |
| } | |
| /** | |
| * Called when this rule is matched for the page load | |
| * | |
| */ | |
| public function matched_rule() { | |
| global $wp; | |
| $wp->query = $this->get_wp_query_args(); | |
| foreach ( $this->request_callbacks as $callback ) | |
| call_user_func_array( $callback, array( $wp ) ); | |
| $t = $this; | |
| // set up the hooks for everything | |
| add_action( 'template_redirect', function( $template ) use ( $t ) { | |
| global $wp_query; | |
| // check permissions | |
| $permission = $t->access_rule; | |
| $redirect = ''; | |
| switch ( $permission ) { | |
| case 'logged_out_only' : | |
| $redirect = is_user_logged_in(); | |
| break; | |
| case 'logged_in_only' : | |
| $redirect = ! is_user_logged_in(); | |
| break; | |
| case 'displayed_user_only' : | |
| $redirect = ! is_user_logged_in() || get_query_var( 'author' ) != get_current_user_id(); | |
| break; | |
| } | |
| if ( $redirect ) { | |
| $redirect = home_url( '/' ); | |
| // If there is a "redirect_to" redirect there | |
| if ( ! empty( $_REQUEST['redirect_to'] ) ) | |
| $redirect = hm_parse_redirect( urldecode( esc_url( $_REQUEST['redirect_to'] ) ) ); | |
| wp_redirect( $redirect ); | |
| exit; | |
| } | |
| foreach ( $t->query_callbacks as $callback ) | |
| call_user_func_array( $callback, array( $wp_query ) ); | |
| if ( $t->template ) { | |
| include( $t->template ); | |
| exit; | |
| } | |
| }); | |
| add_filter( 'parse_query', $closure = function( WP_Query $query ) use ( $t, &$closure ) { | |
| // only run this hook once | |
| remove_filter( 'parse_query', $closure ); | |
| foreach ( $t->parse_query_callbacks as $callback ) | |
| call_user_func_array( $callback, array( $query ) ); | |
| } ); | |
| add_filter( 'body_class', function( $classes ) use ( $t ) { | |
| $classes = array_merge( $t->body_classes, $classes ); | |
| foreach ( $t->body_class_callbacks as $callback ) | |
| $classes = call_user_func_array( $callback, array( $classes ) ); | |
| return $classes; | |
| } ); | |
| add_filter( 'wp_title', function( $title, $sep = '' ) use ( $t ) { | |
| foreach ( $t->title_callbacks as $callback ) | |
| $title = call_user_func_array( $callback, array( $title ) ); | |
| return $title; | |
| }, 10, 2 ); | |
| } | |
| /** | |
| * Set the template file to render this request | |
| * | |
| * @param string $template | |
| */ | |
| public function set_template( $template ) { | |
| if ( strpos( $template, ABSPATH ) !== 0 ) | |
| $template = get_template_directory() . '/' . $template; | |
| $this->template = $template; | |
| } | |
| public function set_access_rule( $rule ) { | |
| $this->access_rule = $rule; | |
| } | |
| /** | |
| * Add a callback for when the request is mached | |
| * | |
| * The callback will be called with the WP object | |
| * | |
| * @param function $callback | |
| */ | |
| public function add_request_callback( $callback ) { | |
| $this->request_callbacks[] = $callback; | |
| } | |
| /** | |
| * Add a callback for when the WP_Query's parse_query is fired | |
| * | |
| * The callback will be called with the WP_Query object, before the query is done, after parse_args | |
| * | |
| * @param function $callback | |
| */ | |
| public function add_parse_query_callback( $callback ) { | |
| $this->parse_query_callbacks[] = $callback; | |
| } | |
| /** | |
| * Add a callback for when the WP_Query is being set up | |
| * | |
| * The callback will be called with the WP_Query object, after the query is done, after parse_args | |
| * | |
| * @param function $callback | |
| */ | |
| public function add_query_callback( $callback ) { | |
| $this->query_callbacks[] = $callback; | |
| } | |
| /** | |
| * Add a callback for when the wp_title hook is fired | |
| * | |
| * The callback will be called with the title of the page | |
| * | |
| * @param function $callback | |
| */ | |
| public function add_title_callback( $callback ) { | |
| $this->title_callbacks[] = $callback; | |
| } | |
| /** | |
| * Add a callback for when the body_class hook is fired | |
| * | |
| * The callback will be called with the classes added so far | |
| * | |
| * @param function $callback | |
| */ | |
| public function add_body_class_callback( $callback ) { | |
| $this->body_class_callbacks[] = $callback; | |
| } | |
| /** | |
| * Add a class to be added via body_class() | |
| * @param string $class | |
| */ | |
| public function add_body_class( $class ) { | |
| $classes = explode( ' ', $class ); | |
| $this->body_classes = array_merge( $this->body_classes, $classes ); | |
| } | |
| } | |
| /** | |
| * The main wrapper function for the HM_Rewrite API. Use this to add new rewrite rules | |
| * | |
| * Create a new rewrite with the arguments listed below. The only required argument is 'regex' | |
| * | |
| * @param string $regex The rewrite regex, start / end delimter not required. Eg: '^people/([^/]+)/?' | |
| * @param mixed $query The WP_Query args to be used on this "page" | |
| * @param string $template The template file used to render the request. Not required, will use | |
| * the template file for the WP_Query if not set. Relative to template_directory() or absolute. | |
| * @param string $body_class A class to be added to body_class in the rendered template | |
| * @param function $body_class_callback A callback that will be hooked into body_class | |
| * @param function $request_callback A callback that will be called as soon as the request matching teh regex is hit. | |
| * called with the WP object | |
| * @param function $parse_query_callback A callback taht will be hooked into 'parse_query'. Use this to modify query vars | |
| * in the main WP_Query | |
| * @param function $title_callback A callback that will be hooked into wp_title | |
| * @param function $query_callback A callback taht will be called once the WP_Query has finished. Use to overrite any | |
| * annoying is_404, is_home etc that you custom query may not match to. | |
| * @param function $access_rule An access rule for restriciton to logged-in-users only for example. | |
| */ | |
| function hm_rewrite_rule( $args = array() ) { | |
| $regex = $args['regex']; | |
| $rule = new HM_Rewrite_Rule( $regex, isset( $args['id'] ) ? $args['id'] : null ); | |
| if ( ! empty( $args['template'] ) ) | |
| $rule->set_template( $args['template'] ); | |
| if ( ! empty( $args['body_class'] ) ) | |
| $rule->add_body_class( $args['body_class'] ); | |
| if ( ! empty( $args['body_class_callback'] ) ) | |
| $rule->add_body_class_callback( $args['body_class_callback'] ); | |
| if ( ! empty( $args['request_callback'] ) ) | |
| $rule->add_request_callback( $args['request_callback'] ); | |
| if ( ! empty( $args['parse_query_callback'] ) ) | |
| $rule->add_parse_query_callback( $args['parse_query_callback'] ); | |
| if ( ! empty( $args['title_callback'] ) ) | |
| $rule->add_title_callback( $args['title_callback'] ); | |
| if ( ! empty( $args['query_callback'] ) ) | |
| $rule->add_query_callback( $args['query_callback'] ); | |
| if ( ! empty( $args['access_rule'] ) ) | |
| $rule->set_access_rule( $args['access_rule'] ); | |
| if ( ! empty( $args['query'] ) ) | |
| $rule->set_wp_query_args( $args['query'] ); | |
| HM_Rewrite::add_rule( $rule ); | |
| } | |
| /** | |
| * Add the custom rewrite rules to the main | |
| * rewrite rules array | |
| * | |
| * @param array $rules | |
| * @return array $rules | |
| */ | |
| add_filter( 'rewrite_rules_array', function( $rules ) { | |
| $new_rules = array(); | |
| foreach ( HM_Rewrite::get_rules() as $rule ) | |
| $new_rules[$rule->get_regex()] = ''; | |
| return array_merge( $new_rules, $rules ); | |
| } ); | |
| /** | |
| * Set the current rewrite rule | |
| * | |
| * @param object $request | |
| * @return null | |
| */ | |
| add_filter( 'parse_request', function( WP $request ) { | |
| $matched_regex = $request->matched_rule; | |
| HM_Rewrite::matched_regex( $matched_regex ); | |
| } ); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment