-
-
Save carlalexander/ea15aea001f5cfc611d8 to your computer and use it in GitHub Desktop.
WordPress routing system
This file contains 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 | |
/** | |
* The Processor is in charge of the interaction between the routing system and | |
* the rest of WordPress. | |
* | |
* @author Carl Alexander <contact@carlalexander.ca> | |
*/ | |
class Processor | |
{ | |
/** | |
* The matched route found by the router. | |
* | |
* @var Route | |
*/ | |
private $matched_route; | |
/** | |
* The router. | |
* | |
* @var Router | |
*/ | |
private $router; | |
/** | |
* The routes we want to register with WordPress. | |
* | |
* @var Route[] | |
*/ | |
private $routes; | |
/** | |
* Constructor. | |
* | |
* @param Router $router | |
* @param Route[] $routes | |
*/ | |
public function __construct(Router $router, array $routes = array()) | |
{ | |
$this->router = $router; | |
$this->routes = $routes; | |
} | |
/** | |
* Initialize processor with WordPress. | |
* | |
* @param Router $router | |
* @param Route[] $routes | |
*/ | |
public static function init(Router $router, array $routes = array()) | |
{ | |
$self = new self($router, $routes); | |
add_action('init', array($self, 'register_routes')); | |
add_action('parse_request', array($self, 'match_request')); | |
add_action('template_include', array($self, 'load_route_template')); | |
add_action('template_redirect', array($self, 'call_route_hook')); | |
} | |
/** | |
* Checks to see if a route was found. If there's one, it calls the route hook. | |
*/ | |
public function call_route_hook() | |
{ | |
if (!$this->matched_route instanceof Route || !$this->matched_route->has_hook()) { | |
return; | |
} | |
do_action($this->matched_route->get_hook()); | |
} | |
/** | |
* Checks to see if a route was found. If there's one, it loads the route template. | |
* | |
* @param string $template | |
* | |
* @return string | |
*/ | |
public function load_route_template($template) | |
{ | |
if (!$this->matched_route instanceof Route || !$this->matched_route->has_template()) { | |
return $template; | |
} | |
$route_template = get_query_template($this->matched_route->get_template()); | |
if (!empty($route_template)) { | |
$template = $route_template; | |
} | |
return $template; | |
} | |
/** | |
* Attempts to match the current request to a route. | |
* | |
* @param WP $environment | |
*/ | |
public function match_request(WP $environment) | |
{ | |
$matched_route = $this->router->match($environment->query_vars); | |
if ($matched_route instanceof Route) { | |
$this->matched_route = $matched_route; | |
} | |
if ($matched_route instanceof \WP_Error && in_array('route_not_found', $matched_route->get_error_codes())) { | |
wp_die($matched_route, 'Route Not Found', array('response' => 404)); | |
} | |
} | |
/** | |
* Register all our routes into WordPress. | |
*/ | |
public function register_routes() | |
{ | |
$routes = apply_filters('my_plugin_routes', $this->routes); | |
foreach ($routes as $name => $route) { | |
$this->router->add_route($name, $route); | |
} | |
$this->router->compile(); | |
$routes_hash = md5(serialize($routes)); | |
if ($routes_hash != get_option('my_plugin_routes_hash')) { | |
flush_rewrite_rules(); | |
update_option('my_plugin_routes_hash', $routes_hash); | |
} | |
} | |
} |
This file contains 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 | |
/** | |
* A Route describes a route and its parameters. | |
* | |
* @author Carl Alexander <contact@carlalexander.ca> | |
*/ | |
class Route | |
{ | |
/** | |
* The hook called when this route is matched. | |
* | |
* @var string | |
*/ | |
private $hook; | |
/** | |
* The URL path that the route needs to match. | |
* | |
* @var string | |
*/ | |
private $path; | |
/** | |
* The template that the route wants to load. | |
* | |
* @var string | |
*/ | |
private $template; | |
/** | |
* Constructor. | |
* | |
* @param string $path | |
* @param string $hook | |
* @param string $template | |
*/ | |
public function __construct($path, $hook = '', $template = '') | |
{ | |
$this->hook = $hook; | |
$this->path = $path; | |
$this->template = $template; | |
} | |
/** | |
* Get the hook called when this route is matched. | |
* | |
* @return string | |
*/ | |
public function get_hook() | |
{ | |
return $this->hook; | |
} | |
/** | |
* Get the URL path that the route needs to match. | |
* | |
* @return string | |
*/ | |
public function get_path() | |
{ | |
return $this->path; | |
} | |
/** | |
* Get the template that the route wants to load. | |
* | |
* @return string | |
*/ | |
public function get_template() | |
{ | |
return $this->template; | |
} | |
/** | |
* Checks if this route want to call a hook when matched. | |
* | |
* @return bool | |
*/ | |
public function has_hook() | |
{ | |
return !empty($this->hook); | |
} | |
/** | |
* Checks if this route want to load a template when matched. | |
* | |
* @return bool | |
*/ | |
public function has_template() | |
{ | |
return !empty($this->template); | |
} | |
} |
This file contains 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 | |
/** | |
* The Router manages routes using the WordPress rewrite API. | |
* | |
* @author Carl Alexander <contact@carlalexander.ca> | |
*/ | |
class Router | |
{ | |
/** | |
* All registered routes. | |
* | |
* @var Route[] | |
*/ | |
private $routes; | |
/** | |
* Query variable used to identify routes. | |
* | |
* @var string | |
*/ | |
private $route_variable; | |
/** | |
* Constructor. | |
* | |
* @param string $route_variable | |
* @param Route[] $routes | |
*/ | |
public function __construct($route_variable = 'route_name', array $routes = array()) | |
{ | |
$this->routes = array(); | |
$this->route_variable = $route_variable; | |
foreach ($routes as $name => $route) { | |
$this->add_route($name, $route); | |
} | |
} | |
/** | |
* Add a route to the router. Overwrites a route if it shares the same name as an already registered one. | |
* | |
* @param string $name | |
* @param Route $route | |
*/ | |
public function add_route($name, Route $route) | |
{ | |
$this->routes[$name] = $route; | |
} | |
/** | |
* Compiles the router into WordPress rewrite rules. | |
*/ | |
public function compile() | |
{ | |
add_rewrite_tag('%'.$this->route_variable.'%', '(.+)'); | |
foreach ($this->routes as $name => $route) { | |
$this->add_rule($name, $route); | |
} | |
} | |
/** | |
* Flushes all WordPress routes. | |
* | |
* @uses flush_rewrite_rules() | |
*/ | |
public function flush() | |
{ | |
flush_rewrite_rules(); | |
} | |
/** | |
* Tries to find a matching route using the given query variables. Returns the matching route | |
* or a WP_Error. | |
* | |
* @param array $query_variables | |
* | |
* @return Route|WP_Error | |
*/ | |
public function match(array $query_variables) | |
{ | |
if (empty($query_variables[$this->route_variable])) { | |
return new WP_Error('missing_route_variable'); | |
} | |
$route_name = $query_variables[$this->route_variable]; | |
if (!isset($this->routes[$route_name])) { | |
return new WP_Error('route_not_found'); | |
} | |
return $this->routes[$route_name]; | |
} | |
/** | |
* Adds a new WordPress rewrite rule for the given Route. | |
* | |
* @param string $name | |
* @param Route $route | |
* @param string $position | |
*/ | |
private function add_rule($name, Route $route, $position = 'top') | |
{ | |
add_rewrite_rule($this->generate_route_regex($route), 'index.php?'.$this->route_variable.'='.$name, $position); | |
} | |
/** | |
* Generates the regex for the WordPress rewrite API for the given route. | |
* | |
* @param Route $route | |
* | |
* @return string | |
*/ | |
private function generate_route_regex(Route $route) | |
{ | |
return '^'.ltrim(trim($route->get_path()), '/').'$'; | |
} | |
} |
This file contains 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 | |
require 'class-processor.php'; | |
require 'class-route.php'; | |
require 'class-router.php'; | |
$router = new Router('my_plugin_route_name'); | |
$routes = array( | |
'my_plugin_index' => new Route('/my-plugin', '', 'my-plugin-index'), | |
'my_plugin_redirect' => new Route('/my-plugin/redirect', 'my_plugin_redirect'), | |
); | |
Processor::init($router, $routes); | |
function my_plugin_redirect() | |
{ | |
$location = '/'; | |
if (!empty($_GET['location'])) { | |
$location = $_GET['location']; | |
} | |
wp_redirect($location); | |
} | |
add_action('my_plugin_redirect', 'my_plugin_redirect'); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi, could you tell me under which license this code is?