Integrating Routing Logic: A Custom PHP Router for Your MVC App
Learn how to build a custom MVC router in PHP. Map URLs to logic, handle 404 errors, and centralize request handling for a cleaner, scalable application.

Previously in this course, we covered Managing State with Superglobals and Mastering Redirects and Header Control. Now that you can handle data and control browser headers, it's time to stop relying on direct file access and build a professional routing system.
Why You Need a Centralized MVC Router
In basic PHP development, your URL usually matches a file on your server (e.g., example.com/about.php maps to about.php). This approach becomes unmanageable as your app grows. A routing system acts as a traffic controller: every request hits one central file, which then decides which piece of code should handle the request based on the URL path.
Implementing an MVC router allows you to keep your application logic out of the public URL structure, improves security, and gives you a single point to handle incoming requests.
First Principles: Mapping Requests to Logic
To build a router, we need to extract the path from the URL and compare it against a list of "routes"—a dictionary of URL patterns paired with specific functions or files.
Here is the basic flow of a request in our current project:
- The browser requests
example.com/contact. - A single entry point (usually
index.php) receives the request. - The router parses the URL to extract the path (
/contact). - The router checks if a handler exists for that path.
- If found, the router executes the handler; if not, it triggers a 404 response.
Worked Example: Building a Simple Router
Let's implement a rudimentary router using an associative array in index.php.
PHP<?php #6A9955">// index.php #6A9955">// 1. Get the path from the URL, removing query parameters $request_path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); #6A9955">// 2. Define our routes $routes = [ '/' => 'controllers/home.php', '/about' => 'controllers/about.php', '/contact' => 'controllers/contact.php', ]; #6A9955">// 3. Centralized Request Handling if (array_key_exists($request_path, $routes)) { require $routes[$request_path]; } else { #6A9955">// 4. Handling 404 scenarios http_response_code(404); require 'views/404.php'; die(); }
By using parse_url, we ensure that example.com/contact?id=1 is correctly identified as /contact, preventing query parameters from breaking our routing logic.
Hands-on Exercise
- Inside your project root, create a
controllers/folder. - Create three files:
home.php,about.php, andcontact.php. Add a simple<h1>tag to each to identify them. - Create a
views/404.phpfile with a "Page Not Found" message. - Implement the
index.phpcode provided above. - Test your router by navigating to your local server URLs and verify that each path loads the correct file.
Common Pitfalls
- Forgetting
http_response_code(404): If you just include the 404 file, the browser still receives a "200 OK" status. Always explicitly set the header so search engines and tools know the page is missing. - Trailing Slashes: Browsers often treat
/aboutand/about/as different paths. In a production environment, you should normalize your paths (e.g., usetrim($path, '/')) so that both variants resolve to the same location. - Case Sensitivity: URLs are often treated as case-sensitive by routing logic. Consider using
strtolower()on the$request_pathif you want your routes to be case-insensitive.
Frequently Asked Questions
Q: Do I need a complex router library? A: Not for this stage of the course. Learning to build a basic router from scratch teaches you the underlying mechanism of request handling.
Q: Can I use this for dynamic URLs like /profile/123?
A: The simple array-based router above only works for static paths. In later lessons, we will look into regex-based routing to capture dynamic segments.
Q: Why use require instead of include?
A: In a routing context, the requested file is usually essential for the page to function. Using require ensures that if the controller file is missing, the script throws a fatal error rather than continuing with broken logic.
Recap
We've moved from simple file-to-URL mapping to a controlled MVC router. By centralizing request handling in index.php, we've made our application easier to manage and established a standard way to handle 404 errors. This foundation is critical for the architectural patterns we will implement as the project evolves.
Up next: We will begin interacting with persistent data by Connecting to MySQL with PDO.
Work with me

Laravel REST API Development
Clean, secure, well-documented Laravel REST APIs — the backend engine for your app, mobile client, or SaaS. Built by an API specialist.

FilamentPHP Admin Panel & Dashboard Development
A powerful admin panel for your Laravel app — built with FilamentPHP so you can manage everything without touching the database.


