Url Rewriting like WordPress Php by Rajesh Kumar Sahanee - June 2, 2019June 2, 20190 Post Views: 6,468 Hello Friends, Today I am going to share a code for Url rewriting like WordPress do. I have taken help to develop this code from WordPress codex link mentioned below:- https://core.trac.wordpress.org/browser/tags/5.2/src/wp-includes/class-wp-post-type.php#L532 https://core.trac.wordpress.org/browser/tags/5.2/src/wp-includes/class-wp-matchesmapregex.php Actually, these days I am developing CMS in which I was required to do Url rewriting code. So to do Url Rewriting like WordPress I have googled and researched on WordPress codex. Now Here is the final code.. config.php config.php PHP <?php include_once 'matches.php'; define("SITE_URL", "http://localhost/urlrewriting"); $rewrite = array(); function updateSlugForRewrite(&$items, $parentId = "0", $parentItem = array()) { // Parent items control $isParentItem = false; foreach ($items as $item) { if ($item['parent'] == $parentId) { $isParentItem = true; break; } } // Prepare items if ($isParentItem) { foreach ($items as &$item) { if ($item['parent'] == $parentId) { $item['slug'] = isset($parentItem['slug']) ? ($parentItem['slug'] . "/" . $item['slug']) : $item['slug']; updateSlugForRewrite($items, $item['id'], $item); } } } } function updateRewriteRules($additional_rules = array(), $location = "bottom") { $rewrite_rules = array(); //adding additional rules in array at the top if (!empty($additional_rules) && $location == 'top') { foreach ($additional_rules as $key => $value) { $rewrite_rules[$key] = $value; } } /* login, logout & register rewrites */ $rewrite_rules['login$'] = 'index.php?login=1'; $rewrite_rules['logout$'] = 'index.php?logout=1'; $rewrite_rules['register$'] = 'index.php?register=1'; /* * categories rewrites * categories will be fetched from database dynamically but for testing I am using fixed data */ $categories = array( array('id' => "1", 'name' => "Java", 'slug' => "java", 'description' => "", 'parent' => "0"), array('id' => "2", 'name' => "JavaFX", 'slug' => "javafx", 'description' => "", 'parent' => "0"), array('id' => "3", 'name' => "Projects", 'slug' => "projects", 'description' => "", 'parent' => "0"), array('id' => "4", 'name' => "Java Projects", 'slug' => "java-projects", 'description' => "", 'parent' => "3"), array('id' => "5", 'name' => "PHP Projects", 'slug' => "php-projects", 'description' => "", 'parent' => "3"), array('id' => "6", 'name' => "Uncategorized", 'slug' => "uncategorized", 'description' => "", 'parent' => "0") ); updateSlugForRewrite($categories, 0); foreach ($categories as $c) { $rewrite_rules['(' . $c['slug'] . ')/page/?([0-9]{1,})/?$'] = 'index.php?category_name=$matches[1]&paged=$matches[2]'; $rewrite_rules['(' . $c['slug'] . ')/?$'] = 'index.php?category_name=$matches[1]'; } /* tag rewrites */ $rewrite_rules['tag/([^/]+)/page/?([0-9]{1,})/?$'] = 'index.php?tag=$matches[1]&paged=$matches[2]'; $rewrite_rules['tag/([^/]+)/?$'] = 'index.php?tag=$matches[1]'; /* blogs paging */ $rewrite_rules['page/?([0-9]{1,})/?$'] = 'index.php?&paged=$matches[1]'; /* search rewrites */ $rewrite_rules['search/(.+)/page/?([0-9]{1,})/?$'] = 'index.php?s=$matches[1]&paged=$matches[2]'; $rewrite_rules['search/(.+)/?$'] = 'index.php?s=$matches[1]'; /* author rewrites */ $rewrite_rules['author/([^/]+)/page/?([0-9]{1,})/?$'] = 'index.php?author_name=$matches[1]&paged=$matches[2]'; $rewrite_rules['author/([^/]+)/?$'] = 'index.php?author_name=$matches[1]'; /* year month day wise posts */ $rewrite_rules['([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/page/?([0-9]{1,})/?$'] = 'index.php?year=$matches[1]&month=$matches[2]&day=$matches[3]&paged=$matches[4]'; $rewrite_rules['([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/?$'] = 'index.php?year=$matches[1]&month=$matches[2]&day=$matches[3]'; $rewrite_rules['([0-9]{4})/([0-9]{1,2})/page/?([0-9]{1,})/?$'] = 'index.php?year=$matches[1]&month=$matches[2]&paged=$matches[3]'; $rewrite_rules['([0-9]{4})/([0-9]{1,2})/?$'] = 'index.php?year=$matches[1]&month=$matches[2]'; $rewrite_rules['([0-9]{4})/page/?([0-9]{1,})/?$'] = 'index.php?year=$matches[1]&paged=$matches[2]'; $rewrite_rules['([0-9]{4})/?$'] = 'index.php?year=$matches[1]'; /**/ $rewrite_rules['(.?.+?)/page/?([0-9]{1,})/?$'] = 'index.php?pagename=$matches[1]&paged=$matches[2]'; $rewrite_rules['(.?.+?)(?:/([0-9]+))?/?$'] = 'index.php?pagename=$matches[1]&page=$matches[2]'; $rewrite_rules['([^/]+)/page/?([0-9]{1,})/?$'] = 'index.php?name=$matches[1]&paged=$matches[2]'; $rewrite_rules['([^/]+)(?:/([0-9]+))?/?$'] = 'index.php?name=$matches[1]&page=$matches[2]'; //adding additional rules in array in the bottom if (!empty($additional_rules) && $location == 'bottom') { foreach ($additional_rules as $key => $value) { $rewrite_rules[$key] = $value; } } /* * Here rewrite rules I am storing in global variable named rewrite but * this can be serialized and stored to database so that it can be accessed wherever needed just like * I have called saveConfig in comment for real project to save rewrite rules in Database */ $GLOBALS['rewrite'] = $rewrite_rules; //saveConfig('rewrite_rules', serialize($rewrite_rules)); } function parse_request($extra_query_vars = '') { $rewrite_index = "index.php"; // Process PATH_INFO, REQUEST_URI, and 404 for permalinks. // Fetch the rewrite rules. /* * Here rewrite rules I am fetching from global variable named rewrite but * this can be unserialize after fetching from database just like * I have called getConfig function to fetch from database and unserialized in real project */ $rewrite = $GLOBALS['rewrite']; //$rewrite = unserialize(getConfig("rewrite_rules")); if (!empty($rewrite)) { // If we match a rewrite rule, this will be cleared. $error = '404'; $pathinfo = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : ''; list( $pathinfo ) = explode('?', $pathinfo); $pathinfo = str_replace('%', '%25', $pathinfo); list( $req_uri ) = explode('?', $_SERVER['REQUEST_URI']); $self = $_SERVER['PHP_SELF']; $home_path = trim(parse_url(SITE_URL, PHP_URL_PATH), '/'); $home_path_regex = sprintf('|^%s|i', preg_quote($home_path, '|')); // Trim path info from the end and the leading home path from the // front. For path info requests, this leaves us with the requesting // filename, if any. For 404 requests, this leaves us with the // requested permalink. $req_uri = str_replace($pathinfo, '', $req_uri); $req_uri = trim($req_uri, '/'); $req_uri = preg_replace($home_path_regex, '', $req_uri); $req_uri = trim($req_uri, '/'); $pathinfo = trim($pathinfo, '/'); $pathinfo = preg_replace($home_path_regex, '', $pathinfo); $pathinfo = trim($pathinfo, '/'); $self = trim($self, '/'); $self = preg_replace($home_path_regex, '', $self); $self = trim($self, '/'); // The requested permalink is in $pathinfo for path info requests and // $req_uri for other requests. if (!empty($pathinfo) && !preg_match('|^.*' . $rewrite_index . '$|', $pathinfo)) { $requested_path = $pathinfo; } else { // If the request uri is the index, blank it out so that we don't try to match it against a rule. if ($req_uri == $rewrite_index) { $req_uri = ''; } $requested_path = $req_uri; } $requested_file = $req_uri; // Look for matches. $request_match = $requested_path; if (empty($request_match)) { // An empty request could only match against ^$ regex if (isset($rewrite['$'])) { $query = $rewrite['$']; $matches = array(''); } } else { foreach ((array) $rewrite as $match => $query) { // If the requested file is the anchor of the match, prepend it to the path info. if (!empty($requested_file) && strpos($match, $requested_file) === 0 && $requested_file != $requested_path) { $request_match = $requested_file . '/' . $requested_path; } if (preg_match("#^$match#", $request_match, $matches) || preg_match("#^$match#", urldecode($request_match), $matches)) { // Got a match. $matched_rule = $match; break; } } } if (isset($matched_rule)) { // Trim the query of everything up to the '?'. $query = preg_replace('!^.+\?!', '', $query); // Substitute the substring matches into the query. $matched_query = addslashes(MatchesMapRegex::apply($query, $matches)); // Parse the query. parse_str($matched_query, $perma_query_vars); // If we're processing a 404 request, clear the error var since we found something. if ('404' == $error) { unset($error, $_GET['error']); } } // If req_uri is empty or if it is a request for ourself, unset error. if (empty($requested_path) || $requested_file == $self) { unset($error, $_GET['error']); if (isset($perma_query_vars)) { unset($perma_query_vars); } } } if (isset($perma_query_vars)) { return $perma_query_vars; } return null; } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 <?phpinclude_once 'matches.php'; define("SITE_URL", "http://localhost/urlrewriting");$rewrite = array(); function updateSlugForRewrite(&$items, $parentId = "0", $parentItem = array()) { // Parent items control $isParentItem = false; foreach ($items as $item) { if ($item['parent'] == $parentId) { $isParentItem = true; break; } } // Prepare items if ($isParentItem) { foreach ($items as &$item) { if ($item['parent'] == $parentId) { $item['slug'] = isset($parentItem['slug']) ? ($parentItem['slug'] . "/" . $item['slug']) : $item['slug']; updateSlugForRewrite($items, $item['id'], $item); } } }} function updateRewriteRules($additional_rules = array(), $location = "bottom") { $rewrite_rules = array(); //adding additional rules in array at the top if (!empty($additional_rules) && $location == 'top') { foreach ($additional_rules as $key => $value) { $rewrite_rules[$key] = $value; } } /* login, logout & register rewrites */ $rewrite_rules['login$'] = 'index.php?login=1'; $rewrite_rules['logout$'] = 'index.php?logout=1'; $rewrite_rules['register$'] = 'index.php?register=1'; /* * categories rewrites * categories will be fetched from database dynamically but for testing I am using fixed data */ $categories = array( array('id' => "1", 'name' => "Java", 'slug' => "java", 'description' => "", 'parent' => "0"), array('id' => "2", 'name' => "JavaFX", 'slug' => "javafx", 'description' => "", 'parent' => "0"), array('id' => "3", 'name' => "Projects", 'slug' => "projects", 'description' => "", 'parent' => "0"), array('id' => "4", 'name' => "Java Projects", 'slug' => "java-projects", 'description' => "", 'parent' => "3"), array('id' => "5", 'name' => "PHP Projects", 'slug' => "php-projects", 'description' => "", 'parent' => "3"), array('id' => "6", 'name' => "Uncategorized", 'slug' => "uncategorized", 'description' => "", 'parent' => "0") ); updateSlugForRewrite($categories, 0); foreach ($categories as $c) { $rewrite_rules['(' . $c['slug'] . ')/page/?([0-9]{1,})/?$'] = 'index.php?category_name=$matches[1]&paged=$matches[2]'; $rewrite_rules['(' . $c['slug'] . ')/?$'] = 'index.php?category_name=$matches[1]'; } /* tag rewrites */ $rewrite_rules['tag/([^/]+)/page/?([0-9]{1,})/?$'] = 'index.php?tag=$matches[1]&paged=$matches[2]'; $rewrite_rules['tag/([^/]+)/?$'] = 'index.php?tag=$matches[1]'; /* blogs paging */ $rewrite_rules['page/?([0-9]{1,})/?$'] = 'index.php?&paged=$matches[1]'; /* search rewrites */ $rewrite_rules['search/(.+)/page/?([0-9]{1,})/?$'] = 'index.php?s=$matches[1]&paged=$matches[2]'; $rewrite_rules['search/(.+)/?$'] = 'index.php?s=$matches[1]'; /* author rewrites */ $rewrite_rules['author/([^/]+)/page/?([0-9]{1,})/?$'] = 'index.php?author_name=$matches[1]&paged=$matches[2]'; $rewrite_rules['author/([^/]+)/?$'] = 'index.php?author_name=$matches[1]'; /* year month day wise posts */ $rewrite_rules['([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/page/?([0-9]{1,})/?$'] = 'index.php?year=$matches[1]&month=$matches[2]&day=$matches[3]&paged=$matches[4]'; $rewrite_rules['([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/?$'] = 'index.php?year=$matches[1]&month=$matches[2]&day=$matches[3]'; $rewrite_rules['([0-9]{4})/([0-9]{1,2})/page/?([0-9]{1,})/?$'] = 'index.php?year=$matches[1]&month=$matches[2]&paged=$matches[3]'; $rewrite_rules['([0-9]{4})/([0-9]{1,2})/?$'] = 'index.php?year=$matches[1]&month=$matches[2]'; $rewrite_rules['([0-9]{4})/page/?([0-9]{1,})/?$'] = 'index.php?year=$matches[1]&paged=$matches[2]'; $rewrite_rules['([0-9]{4})/?$'] = 'index.php?year=$matches[1]'; /**/ $rewrite_rules['(.?.+?)/page/?([0-9]{1,})/?$'] = 'index.php?pagename=$matches[1]&paged=$matches[2]'; $rewrite_rules['(.?.+?)(?:/([0-9]+))?/?$'] = 'index.php?pagename=$matches[1]&page=$matches[2]'; $rewrite_rules['([^/]+)/page/?([0-9]{1,})/?$'] = 'index.php?name=$matches[1]&paged=$matches[2]'; $rewrite_rules['([^/]+)(?:/([0-9]+))?/?$'] = 'index.php?name=$matches[1]&page=$matches[2]'; //adding additional rules in array in the bottom if (!empty($additional_rules) && $location == 'bottom') { foreach ($additional_rules as $key => $value) { $rewrite_rules[$key] = $value; } } /* * Here rewrite rules I am storing in global variable named rewrite but * this can be serialized and stored to database so that it can be accessed wherever needed just like * I have called saveConfig in comment for real project to save rewrite rules in Database */ $GLOBALS['rewrite'] = $rewrite_rules; //saveConfig('rewrite_rules', serialize($rewrite_rules));} function parse_request($extra_query_vars = '') { $rewrite_index = "index.php"; // Process PATH_INFO, REQUEST_URI, and 404 for permalinks. // Fetch the rewrite rules. /* * Here rewrite rules I am fetching from global variable named rewrite but * this can be unserialize after fetching from database just like * I have called getConfig function to fetch from database and unserialized in real project */ $rewrite = $GLOBALS['rewrite']; //$rewrite = unserialize(getConfig("rewrite_rules")); if (!empty($rewrite)) { // If we match a rewrite rule, this will be cleared. $error = '404'; $pathinfo = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : ''; list( $pathinfo ) = explode('?', $pathinfo); $pathinfo = str_replace('%', '%25', $pathinfo); list( $req_uri ) = explode('?', $_SERVER['REQUEST_URI']); $self = $_SERVER['PHP_SELF']; $home_path = trim(parse_url(SITE_URL, PHP_URL_PATH), '/'); $home_path_regex = sprintf('|^%s|i', preg_quote($home_path, '|')); // Trim path info from the end and the leading home path from the // front. For path info requests, this leaves us with the requesting // filename, if any. For 404 requests, this leaves us with the // requested permalink. $req_uri = str_replace($pathinfo, '', $req_uri); $req_uri = trim($req_uri, '/'); $req_uri = preg_replace($home_path_regex, '', $req_uri); $req_uri = trim($req_uri, '/'); $pathinfo = trim($pathinfo, '/'); $pathinfo = preg_replace($home_path_regex, '', $pathinfo); $pathinfo = trim($pathinfo, '/'); $self = trim($self, '/'); $self = preg_replace($home_path_regex, '', $self); $self = trim($self, '/'); // The requested permalink is in $pathinfo for path info requests and // $req_uri for other requests. if (!empty($pathinfo) && !preg_match('|^.*' . $rewrite_index . '$|', $pathinfo)) { $requested_path = $pathinfo; } else { // If the request uri is the index, blank it out so that we don't try to match it against a rule. if ($req_uri == $rewrite_index) { $req_uri = ''; } $requested_path = $req_uri; } $requested_file = $req_uri; // Look for matches. $request_match = $requested_path; if (empty($request_match)) { // An empty request could only match against ^$ regex if (isset($rewrite['$'])) { $query = $rewrite['$']; $matches = array(''); } } else { foreach ((array) $rewrite as $match => $query) { // If the requested file is the anchor of the match, prepend it to the path info. if (!empty($requested_file) && strpos($match, $requested_file) === 0 && $requested_file != $requested_path) { $request_match = $requested_file . '/' . $requested_path; } if (preg_match("#^$match#", $request_match, $matches) || preg_match("#^$match#", urldecode($request_match), $matches)) { // Got a match. $matched_rule = $match; break; } } } if (isset($matched_rule)) { // Trim the query of everything up to the '?'. $query = preg_replace('!^.+\?!', '', $query); // Substitute the substring matches into the query. $matched_query = addslashes(MatchesMapRegex::apply($query, $matches)); // Parse the query. parse_str($matched_query, $perma_query_vars); // If we're processing a 404 request, clear the error var since we found something. if ('404' == $error) { unset($error, $_GET['error']); } } // If req_uri is empty or if it is a request for ourself, unset error. if (empty($requested_path) || $requested_file == $self) { unset($error, $_GET['error']); if (isset($perma_query_vars)) { unset($perma_query_vars); } } } if (isset($perma_query_vars)) { return $perma_query_vars; } return null;} matches.php matches.php PHP <?php /** * Helper class to remove the need to use eval to replace $matches[] in query strings. * Taken From Wordpress */ class MatchesMapRegex { private $_matches; public $output; private $_subject; public $_pattern = '(\$matches\[[1-9]+[0-9]*\])'; // magic number public function __construct($subject, $matches) { $this->_subject = $subject; $this->_matches = $matches; $this->output = $this->_map(); } public static function apply($subject, $matches) { $oSelf = new MatchesMapRegex($subject, $matches); return $oSelf->output; } private function _map() { $callback = array($this, 'callback'); return preg_replace_callback($this->_pattern, $callback, $this->_subject); } public function callback($matches) { $index = intval(substr($matches[0], 9, -1)); return ( isset($this->_matches[$index]) ? urlencode($this->_matches[$index]) : '' ); } } 1234567891011121314151617181920212223242526272829303132333435 <?php /** * Helper class to remove the need to use eval to replace $matches[] in query strings. * Taken From Wordpress */class MatchesMapRegex { private $_matches; public $output; private $_subject; public $_pattern = '(\$matches\[[1-9]+[0-9]*\])'; // magic number public function __construct($subject, $matches) { $this->_subject = $subject; $this->_matches = $matches; $this->output = $this->_map(); } public static function apply($subject, $matches) { $oSelf = new MatchesMapRegex($subject, $matches); return $oSelf->output; } private function _map() { $callback = array($this, 'callback'); return preg_replace_callback($this->_pattern, $callback, $this->_subject); } public function callback($matches) { $index = intval(substr($matches[0], 9, -1)); return ( isset($this->_matches[$index]) ? urlencode($this->_matches[$index]) : '' ); } } index.php index.php PHP <?php include_once 'config.php'; /* * This function can be called where updatation of rewrite rules required * and can be updated to database i.e while creating new Category or updating permalink, etc */ updateRewriteRules(); $request = parse_request(); ?> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> <?php if ($request == null || empty($request)) { //Process home page echo "include home page here"; //include('home.php'); } else if (isset($request['category_name'])) { //Process category page echo "inclue category page here"; //include('category.php'); } else if (isset($request['tag'])) { //Process tag page echo "include tag page here"; //include('tag.php'); } else if (isset($request['s'])) { //Process search page echo "include search page here"; //include('search.php'); } else if (isset($request['author_name'])) { //Process author page echo "include author page here"; //include('author.php'); } else if (isset($request['pagename'])) { //Process page/post page echo "page/post page here and show page or post according to post_type"; //include('post.php'); } else if (isset($request['year']) || isset($request['month']) || isset($request['day'])) { //Process page/post page echo "include posts page here"; //include('post.php'); } else if (isset($request['login'])) { //Process product page echo "include login page here"; //include('login.php'); } else if (isset($request['logout'])) { //Process logout page echo "logout code or logout page here"; //include('logout.php'); } else if (isset($request['register'])) { //Process register page echo "include registration page here"; //include('register.php'); } else { //Process 404 page echo "include Page not found template here"; //include('404.php'); } ?> </body> </html> 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556 <?phpinclude_once 'config.php'; /* * This function can be called where updatation of rewrite rules required * and can be updated to database i.e while creating new Category or updating permalink, etc */updateRewriteRules(); $request = parse_request();?><!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title></title> </head> <body> <?php if ($request == null || empty($request)) { //Process home page echo "include home page here"; //include('home.php'); } else if (isset($request['category_name'])) { //Process category page echo "inclue category page here"; //include('category.php'); } else if (isset($request['tag'])) { //Process tag page echo "include tag page here"; //include('tag.php'); } else if (isset($request['s'])) { //Process search page echo "include search page here"; //include('search.php'); } else if (isset($request['author_name'])) { //Process author page echo "include author page here"; //include('author.php'); } else if (isset($request['pagename'])) { //Process page/post page echo "page/post page here and show page or post according to post_type"; //include('post.php'); } else if (isset($request['year']) || isset($request['month']) || isset($request['day'])) { //Process page/post page echo "include posts page here"; //include('post.php'); } else if (isset($request['login'])) { //Process product page echo "include login page here"; //include('login.php'); } else if (isset($request['logout'])) { //Process logout page echo "logout code or logout page here"; //include('logout.php'); } else if (isset($request['register'])) { //Process register page echo "include registration page here"; //include('register.php'); } else { //Process 404 page echo "include Page not found template here"; //include('404.php'); } ?> </body></html> .htaccess .htaccess Apache # BEGIN <IfModule mod_rewrite.c> RewriteEngine On RewriteBase /urlrewriting/ RewriteRule ^index\.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /urlrewriting/index.php [L] </IfModule> # END 12345678910 # BEGIN<IfModule mod_rewrite.c>RewriteEngine OnRewriteBase /urlrewriting/RewriteRule ^index\.php$ - [L]RewriteCond %{REQUEST_FILENAME} !-fRewriteCond %{REQUEST_FILENAME} !-dRewriteRule . /urlrewriting/index.php [L]</IfModule># END Note: Here RewriteBase and RewriteRule starts with “/urlrewrting/“, because the all code is in urlrewriting directory but you can change it to “/“, if you put your code in www directory in case of LAMP, WAMP or MAMP htdocs in case of XAMPP or public_html in case of hosted on server. Output Download Url Rewriting Like WordPress 1 file(s) 4.47 KB Download Thanks for Visiting Please do share if you like it