I used MVC architecture pattern to do that. here are some starter resources that can get you where you need to be. There might be better ways to that but this is one way.
Starter Code: panique/mini
main routing Algorithm
To give the gist of the algorithm here is the .htaccess file inside the Public folder.
# If the following conditions are true, then rewrite the URL:
# If the requested filename is not a directory,
RewriteCond %{REQUEST_FILENAME} !-d
# and if the requested filename is not a regular file that exists,
RewriteCond %{REQUEST_FILENAME} !-f
# and if the requested filename is not a symbolic link,
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule ^(.+)$ index.php?url=$1 [QSA,L]
The routes were processed in Application.php using the following algorithm
<?php
class Application
{
/** @var null The controller */
private $url_controller = null;
/** @var null The method (of the above controller), often also named "action" */
private $url_action = null;
/** @var array URL parameters */
private $url_params = array();
/**
* "Start" the application:
* Analyze the URL elements and calls the according controller/method or the fallback
*/
public function __construct()
{
// create array with URL parts in $url
$this->splitUrl();
// check for controller: no controller given ? then load start-page
if (!$this->url_controller) {
require APP . 'controller/home.php';
$page = new Home();
$page->index();
}
elseif (file_exists(APP . 'controller/' . $this->url_controller . '.php')) {
// here we did check for controller: does such a controller exist ?
// if so, then load this file and create this controller
// example: if controller would be "city", then this line would translate into: $this->city= new City();
require APP . 'controller/' . $this->url_controller . '.php';
$this->url_controller = new $this->url_controller();
// check for method: does such a method exist in the controller ?
if (method_exists($this->url_controller, $this->url_action)) {
if (!empty($this->url_params)) {
// Call the method and pass arguments to it
call_user_func_array(array($this->url_controller, $this->url_action), $this->url_params);
} else {
// If no parameters are given, just call the method without parameters, like $this->city->method();
$this->url_controller->{$this->url_action}();
}
} else {
if (strlen($this->url_action) == 0) {
// no action defined: call the default index() method of a selected controller
$this->url_controller->index();
}
else {
header('location: ' . URL . 'problem');
}
}
} else {
header('location: ' . URL . 'problem');
}
}
private function splitUrl()
{
if (isset($_GET['url'])) {
// split URL
$url = trim($_GET['url'], '/');
$url = filter_var($url, FILTER_SANITIZE_URL);
$url = explode('/', $url);
// Put URL parts into according properties
$this->url_controller = isset($url[0]) ? $url[0] : null;
$this->url_action = isset($url[1]) ? $url[1] : null;
// Remove controller and action from the split URL
unset($url[0], $url[1]);
// Rebase array keys and store the URL params
$this->url_params = array_values($url);
}
}
if your Urls are like below, then they be would be broken down as
http://siteaddress.com/cities/city/london
Controller: Cities
method: City
params: London
http://siteaddress.com/cities/city/birmingham
Controller: Cities
method: City
params: Birmingham
Here is the snapshot of the project structure

I used this playlist for better understanding. YOUTUBE: Build a PHP MVC Application: Introduction (Part 1/9)