There are many takes at that. And the most suggested one is to use a ready-made framework. But assuming this code as is:
First of all, I don't see the real need for this router. Why not to request login.php directly and inside it include tpl/login.tpl.php? If you want to hide the .php extension (though not sure why), it's also possible with a simple .htaccess rule.
Speaking of the code itself
Simplifying the code
We can remove the extra nesting and duplication
<?php
$page = $_GET['page'] ?? 'home';
switch ($page) {
case 'login':
require 'inc/login.php';
include 'tpl/login.tpl.php';
break;
case 'logout':
require 'inc/logout.php';
include 'tpl/logout.tpl.php';
break;
case 'home':
require 'inc/home.php';
include 'tpl/home.tpl.php';
break;
default:
http_response_code(404);
}
I must note that checking for empty($_GET) is essentially useless because we are inside a condition that already made sure that $_GET is not empty.
Now we can't help but notice the pattern, and every pattern in the code must be deflated, by means of a loop or similar measure. Here we will use some string/array tricks
$pages = ['login', 'logout', 'home'];
$page = $_GET['page'] ?? 'home';
if (in_array($page, $pages)) {
require "inc/$page.php";
include "tpl/$page.tpl.php";
} else {
http_response_code(404);
}
Making it extensible
What I don't like about this code is that it's static. It can serve only three pages. You'd have to add another branch/array item for the every page. Not very convenient. Why not to make it automatic?
$page = basename($_GET['page'] ?? 'home');
if (file_exists("inc/$page.php")) {
require "inc/$page.php";
include "tpl/$page.tpl.php";
} else {
http_response_code(404);
}
An important note: I was reminded of the necessity to validate the page parameter when it's not whitelisted as in the codes above. basename()
is a good start though one may consider a regexp.
Now you will only have to add a new file to inc folder and it will be served as well
The automation revisited
Through invaluable comments I was also reminded of more subtle details, such as SEO rule of not having two pages with same content. So more extensive validation is a good thing to add.
First of all I would get rid of that page thing. Your web-server already provides the requested resource, through REQUEST_URI variable. No need to pollute the query string with a useless variable that could be easily overwritten. So make your rule just RewriteRule ^([a-z_-]+)$ index.php [L,QSA]
(the original query string is a good thing to keep, so we're adding QSA
)
$page = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$page = trim($page, "/"); // original request may have them
if (in_array($page, ['index.php', 'home'], true)) {
header("Location: /", true, 301);
die; // always die if there is any code after Location:
}
$page = $page ?: 'home'; // make it home if empty
if (preg_match('~^[a-z_-]+$~', $page) && file_exists("inc/$page.php")) {
require "inc/$page.php";
include "tpl/$page.tpl.php";
} else {
http_response_code(404);
}
Here we are making a uniform home page which is just /, and also impose a stricter validation for the page parameter.
Adhering to HTTP standards
HTTP status codes are not for decoration. They must be used in the exact situations they are intended for. When a page is not found, your server should respond with 404 status, not 302. I would advise to create corresponding pages (i.e. 404.php, 500.php) in the inc directory and include them along with setting appropriate HTTP status instead of redirect.