├── LICENSE.txt ├── composer.json ├── readme.md └── src └── WP_Route.php /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Anthony Budd 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "anthonybudd/wp_route", 3 | "type": "library", 4 | "authors": [ 5 | { 6 | "name": "Anthony Budd", 7 | "email": "anthonybudd94@gmail.com" 8 | } 9 | ], 10 | "autoload": { 11 | "files": ["src/WP_Route.php"] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # WP_Route 2 | 3 | ### A simple way to make custom routes in WordPress. 4 | WP_Route is a simple way to create custom routes in WordPress for listening for webhooks, oAuth callbacks and basic routing. WP_Route is a single class solution that does not require any set-up and supports route parameters and redirects. 5 | 6 | ## Introduction: **[Medium Post](https://medium.com/@AnthonyBudd/wp-route-a-simple-way-to-make-custom-routes-in-wordpress-5ab1b3063115)** 7 | 8 | ```php 9 | 10 | WP_Route::get('flights', 'listFlights'); 11 | WP_Route::post('flights/{flight}', 'singleFlight'); 12 | WP_Route::put('flights/{flight}/book/{date}', 'bookFlight'); 13 | WP_Route::delete('flights/{flight}/delete', 'deleteFlight'); 14 | 15 | WP_Route::any('flights/{flight}', array('Class', 'staticMethod')); 16 | WP_Route::patch('flights/{flight}', array($object, 'method')); 17 | WP_Route::match(['get', 'post'], 'flights/{flight}/confirm', 'confirmFlight'); 18 | WP_Route::redirect('from/here', '/to/here', 301); 19 | 20 | 21 | ``` 22 | 23 | # Installation 24 | 25 | Require WP_Route with composer 26 | 27 | ``` 28 | $ composer require anthonybudd/WP_Route 29 | ``` 30 | 31 | **Or** 32 | 33 | Download the WP_Route class and require it at the top of your functions.php file. This is not recommended. 34 | 35 | ```php 36 | require 'WP_Route/src/WP_Route.php'; 37 | ``` 38 | 39 | 40 | # GET Started 41 | Simply define a route by calling any of the static methods get(), post(), put(), patch(), delete() or any(). This will bind the specified URL to a callable. When a HTTP request bound for that URL is detected, WP_Route will call the callable. 42 | 43 | ```php 44 | WP_Route::get('flights', 'listFlights'); 45 | 46 | // http://example.com/flights 47 | function listFlights(){ 48 | 49 | // Your Code Here! 50 | 51 | } 52 | ``` 53 | 54 | # Parameters 55 | If you need to extract route parameters from the request URI you can do this by wrapping the value to be extracted in curly brackets. The extracted values will be provided to the callable as function arguments as shown below. 56 | ```php 57 | WP_Route::get('flights/{flight}', 'singleFlight'); 58 | 59 | function singleFlight($flight){ 60 | echo $flight; // 1 61 | } 62 | ``` 63 | 64 | # Methods 65 | ### get($route, $callable) 66 | ### any(), post(), put(), patch(), delete() 67 | All of these methods are used for binding a specific route and HTTP request method to a callable. The method any() will bind a route to a callable but will be HTTP method agnostic. 68 | ```php 69 | 70 | WP_Route::get('flights', 'listFlights'); 71 | WP_Route::post('flights/{flight}', array('FlightController', 'singleFlight')); 72 | 73 | function listFlights(){ 74 | // Your Code Here 75 | } 76 | 77 | Class FlightController{ 78 | public static function singleFlight($flight){ 79 | // Your Code Here 80 | } 81 | } 82 | ``` 83 | 84 | ### match($methods, $route, $callable) 85 | If you want to bind a callable to multiple HTTP methods but you do not want to use any(), you can use match(). The first parameter must be an array of HTTP request methods. The arguments $route and $callable work the same as get(). 86 | ```php 87 | WP_Route::match(['get', 'post'], 'flights/{flight}/confirm', 'confirmFlight'); 88 | 89 | function confirmFlight($flight){ 90 | // Your Code Here 91 | } 92 | ``` 93 | 94 | ### redirect($route, $redirect, $code = 301) 95 | The redirect() method will redirect the user to the argument $redirect when they navigate to the route. To set a custom HTTP response code use the 3rd argument $code. 96 | ```php 97 | WP_Route::redirect('open-google', 'https://google.com', 301); 98 | ``` 99 | 100 | 101 | -------------------------------------------------------------------------------- /src/WP_Route.php: -------------------------------------------------------------------------------- 1 | array(), 18 | 'GET' => array(), 19 | 'POST' => array(), 20 | 'HEAD' => array(), 21 | 'PUT' => array(), 22 | 'DELETE' => array(), 23 | ); 24 | 25 | private function __construct(){} 26 | 27 | public static function instance(){ 28 | static $instance = NULL; 29 | 30 | if($instance === NULL){ 31 | $instance = new Self(); 32 | $instance->hook(); 33 | } 34 | 35 | return $instance; 36 | } 37 | 38 | 39 | // ----------------------------------------------------- 40 | // CREATE ROUTE METHODS 41 | // ----------------------------------------------------- 42 | public static function any($route, $callable){ 43 | $r = Self::instance(); 44 | $r->addRoute('ANY', $route, $callable); 45 | } 46 | 47 | public static function get($route, $callable){ 48 | $r = Self::instance(); 49 | $r->addRoute('GET', $route, $callable); 50 | } 51 | 52 | public static function post($route, $callable){ 53 | $r = Self::instance(); 54 | $r->addRoute('POST', $route, $callable); 55 | } 56 | 57 | public static function head($route, $callable){ 58 | $r = Self::instance(); 59 | $r->addRoute('HEAD', $route, $callable); 60 | } 61 | 62 | public static function put($route, $callable){ 63 | $r = Self::instance(); 64 | $r->addRoute('PUT', $route, $callable); 65 | } 66 | 67 | public static function delete($route, $callable){ 68 | $r = Self::instance(); 69 | $r->addRoute('DELETE', $route, $callable); 70 | } 71 | 72 | public static function match($methods, $route, $callable){ 73 | if(!is_array($methods)){ 74 | throw new Exception("\$methods must be an array"); 75 | } 76 | 77 | $r = Self::instance(); 78 | foreach($methods as $method){ 79 | if(!in_array(strtoupper($method), array_keys($this->routes))){ 80 | throw new Exception("Unknown method {$method}"); 81 | } 82 | 83 | $r->addRoute(strtoupper($method), $route, $callable); 84 | } 85 | } 86 | 87 | public static function redirect($route, $redirect, $code = 301){ 88 | $r = Self::instance(); 89 | $r->addRoute('ANY', $route, $redirect, array( 90 | 'code' => $code, 91 | 'redirect' => $redirect, 92 | )); 93 | } 94 | 95 | 96 | // ----------------------------------------------------- 97 | // INTERNAL UTILITY METHODS 98 | // ----------------------------------------------------- 99 | private function addRoute($method, $route, $callable, $options = array()){ 100 | $this->routes[$method][] = (object) array_merge(array( 101 | 'route' => ltrim($route, '/'), 102 | 'callable' => $callable, 103 | ), $options); 104 | } 105 | 106 | private function hook(){ 107 | if(!$this->hooked){ 108 | add_filter('init', array('WP_Route', 'onInit'), 1, 0); 109 | $this->hooked = TRUE; 110 | } 111 | } 112 | 113 | public static function onInit(){ 114 | $r = Self::instance(); 115 | $r->handle(); 116 | } 117 | 118 | private function getRouteParams($route){ 119 | $tokenizedRoute = $this->tokenize($route); 120 | $tokenizedRequestURI = $this->tokenize($this->requestURI()); 121 | preg_match_all('/\{\s*.+?\s*\}/', $route, $matches); 122 | 123 | $return = array(); 124 | foreach($matches[0] as $key => $match){ 125 | $search = array_search($match, $tokenizedRoute); 126 | if($search !== FALSE){ 127 | $return[] = $tokenizedRequestURI[$search]; 128 | } 129 | } 130 | 131 | return $return; 132 | } 133 | 134 | 135 | // ----------------------------------------------------- 136 | // GENERAL UTILITY METHODS 137 | // ----------------------------------------------------- 138 | public static function routes(){ 139 | $r = Self::instance(); 140 | return $r->routes; 141 | } 142 | 143 | public function tokenize($url){ 144 | return array_filter(explode('/', ltrim($url, '/'))); 145 | } 146 | 147 | public function requestURI(){ 148 | return ltrim($_SERVER['REQUEST_URI'], '/'); 149 | } 150 | 151 | // ----------------------------------------------------- 152 | // handle() 153 | // ----------------------------------------------------- 154 | public function handle(){ 155 | $method = strtoupper($_SERVER['REQUEST_METHOD']); 156 | $routes = array_merge($this->routes[$method], $this->routes['ANY']); 157 | $requestURI = $this->requestURI(); 158 | $tokenizedRequestURI = $this->tokenize($requestURI); 159 | 160 | foreach($routes as $key => $route){ 161 | // First, filter routes that do not have equal tokenized lengths 162 | if(count($this->tokenize($route->route)) !== count($tokenizedRequestURI)){ 163 | unset($routes[$key]); 164 | continue; 165 | } 166 | 167 | // Add more filtering here as routing gets more complex. 168 | } 169 | 170 | $routes = array_values($routes); 171 | if(isset($routes[0])){ 172 | $route = $routes[0]; 173 | 174 | if(is_string($route->callable) && 175 | class_exists($route->callable) && 176 | is_subclass_of($route->callable, 'WP_AJAX')){ 177 | 178 | $callable = $route->callable; 179 | $controller = new $callable; 180 | call_user_func_array(array($controller, 'boot'), $this->getRouteParams($route->route)); 181 | 182 | }elseif(isset($routes[0]->redirect)){ 183 | 184 | $redirect = $routes[0]->redirect; 185 | header("Location: {$redirect}", TRUE, $routes[0]->code); 186 | die; 187 | 188 | }else{ 189 | 190 | call_user_func_array($route->callable, $this->getRouteParams($route->route)); 191 | 192 | } 193 | } 194 | } 195 | } --------------------------------------------------------------------------------