1

I'm building a simple PHP router and I understand the most things except for the things in the ".htaccess" file. If I comment out the things in the ".htaccess" file, nothing changes, or I don't notice anything changing(the contents of the ".htaccess" file is from a tutorial). The ".htaccess" file contains the following things

RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.+)$ index.php [QSA,L]

and this is the content from the index.php

$request = $_SERVER['REQUEST_URI'];

switch ($request) {
    case '/ROUTER/index.php':
    case '/ROUTER/index.php/home':
         require __DIR__ . '/controller/homeController.php';
         break;
    case '/ROUTER/index.php/about' :
         require __DIR__ . '/controller/shopController.php';
         break;
    default:
         http_response_code(404);
         require __DIR__ . '/controller/404Controller.php';
         break;
}

the contents in the homeController.php etc files are just a simple echo with a text. Maybe someone can explain to me what happens in the ".htaccess" file.

MrWhite
  • 43,179
  • 8
  • 60
  • 84
anyone
  • 39
  • 4
  • `!-d` and `!-f` mean "if the directory and file don't exist". If you comment them out, all requests go to your router. But if you leave them alone, regular files, such as images, can be accessed without going through your router. – Chris Haas Jan 26 '22 at 19:06
  • Note also that use of `.htaccess` is a feature of the Apache web server. If you're using something else like nginx or the PHP built-in server, then this file does nothing. – Alex Howansky Jan 26 '22 at 19:09
  • and what's about the RewriteBase and RewriteRule – anyone Jan 26 '22 at 19:12
  • This is the standard Apache .htaccess file to enable the Front Controller pattern in a PHP app. It basically says, "take any request for a file that doesn't exist and send it to index.php instead of throwing a 404 error". (And then index.php handles whether or not a 404 gets thrown.) – Alex Howansky Jan 26 '22 at 19:18
  • You got 2 excellent answers already, both to explain mod_rewrite (which often uses regular expressions aka "regex". I recommend you implement the changes in the answer from @MrWhite. What you want is to show and accept the url's of either http://www.yoursite.com/router/home or http://www.yoursite.com/home. In order to accomplish this your own code must present those url's in links and any internal navigation. – gview Jan 26 '22 at 23:40

2 Answers2

2

Most web servers have logic like this for determining which file to serve:

  • If a file exists with a file name that matches the URL path, use that file
  • If a directory exists with a name that matches the URL path:
    • If a default file (usually index.html or index.php) exists in that directory, use that file
    • If configured to show a file listing, do so

Otherwise the web server returns an error status.


Many web applications want to implement their own logic for what to show for each URL and implement their own routing rules. They typically use .htaccess to create a Front controller that passes nearly every request into a single central file that handles its own routing for the web application. Your .htaccess has fairly standard front controller rules that pass most URLs into index.php

Breaking it down line by line:

  • RewriteEngine On -- Enables mod rewrite which is an Apache module that uses rules to change which file handles which URLs
  • RewriteBase / -- Specifies which directory the rules are relative to. This can usually be omitted because / is the default value in .htaccess in the document root. See What does RewriteBase do and how to use it?
  • RewriteCond %{REQUEST_FILENAME} !-d -- If the requested URL is not an existing directory
  • RewriteCond %{REQUEST_FILENAME} !-f -- If the requested URL is not an existing file
  • RewriteRule ^(.+)$ index.php [QSA,L] -- Handle all URLs with index.php. A rewrite rule has three sections:
    • The pattern to match against the URL path (^(.+)$)
      • ^ is starts with
      • .+ one or more of any character
      • $ is ends with
      • () parenthesis capture the matched characters as a group that is available in a variable ($1). The parenthesis in this rule could be removed because the variable is never used.
    • The target file that handles the URLs (index.php)
    • Flags that modify how the rule works ([QSA,L])
      • QSA is query string append to make sure index.php has access to anything after the ? in the URL
      • L is last so that any later rewrite rules wouldn't also get executed.

In effect, it uses the default web server logic for files or directories that exist, but pass all other requests to index.php.

Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
2

@StephenOstermiller's answer has done a great job of describing the .htaccess file / front-controller process, but I thought I'd address the other issue you raised...

If I comment out the things in the ".htaccess" file, nothing changes

That's because the format of the URLs you are using (eg. /ROUTER/index.php/home) completely negates the need for the .htaccess file to begin with. You are calling index.php (the "front-controller") directly in the URL itself and passing /home as additional pathname information (aka. path-info).

The .htaccess file is still processed, but the 2nd condition (RewriteCond directive), which checks that the request does not map to a physical file, fails (index.php is a physical file) so the rule is not triggered (it does nothing).

The additional path-info on the URL is available to PHP in the $_SERVER['PATH_INFO'] superglobal. So, if you are using URLs of the form /ROUTER/index.php/home then you could write your front-controller (index.php) like this instead (simplified):

// URLs of the form "/ROUTER/index.php/home"
$request = $_SERVER['PATH_INFO'];

switch ($request) {
    case '':
    case '/home':
         :
    case '/about' :
         :
}

(As noted above, this is not making use of .htaccess)


On the other hand, your .htaccess file allows you to have URLs of the form /ROUTER/home (or simply /home), avoiding you having to include index.php in the URL, which is then internally rewritten to index.php (by the rule in .htaccess). You then use $_SERVER['REQUEST_URI'] (as in your original script) to access the requested URL in PHP.

For example (simplified):

// URLs of the form "/ROUTER/home"
$request = $_SERVER['REQUEST_URI'];

switch ($request) {
    case '/ROUTER/':
    case '/ROUTER/home':
         :
    case '/ROUTER/about' :
         :
}

However, your existing .htaccess file is not configured correctly for this. The .htaccess file is assuming index.php is located in the document root, but your URLs suggest you have a /ROUTER subdirectory, in which index.php (the "front-controller") is located.

If your .htaccess file is in the /ROUTER subdirectory at /ROUTER/.htaccess then remove the RewriteBase directive entirely.

If, however, your .htaccess file is located in the document root then you will need to change your RewriteBase directive to read:

RewriteBase /ROUTER

(Setting RewriteBase /ROUTER in both cases will also work.)

MrWhite
  • 43,179
  • 8
  • 60
  • 84