I'm building a fairly large website and my .htaccess is starting to feel a bit bloated, is there a way of replacing my current system of - one rule for each of the possibile number of vars that could be passed, to one catch all expression that can account for varying numbers of inputs ?

for example i currently have

RewriteRule ^([a-z]+)/([^/]*)/([^/]*)/([^/]*)/([^/]*)/([^/]*)$ /index.php?mode=$1&id=$2&$3=$4&$5=$6
RewriteRule ^([a-z]+)/([^/]*)/([^/]*)/([^/]*)$ /index.php?mode=$1&id=$2&$3=$4
RewriteRule ^([a-z]+)/([^/]*)$ /index.php?mode=$1&id=$2
RewriteRule ^([a-z]+)$ /index.php?mode=$1

the first backreference is always the mode and (if any more exist) the second is always id, thereafter any further backreferences alternate between the name of the input and its value

i would love to be able to have one expression to gracefully handle all the inputs.


+5  A: 

Do like Drupal:

  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]

And then handle all the stuff in your script.

not exactly what i was looking for but a good alternative nonetheless
How do you handle this in the script later?
+2  A: 

I don't believe that their is a way - but I'd say that your best bet would be to have the script "index.php" process a path instead of having to do so many back references.

So for example, your rewriterule would be

RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]

Or similar... This would then make the $_SERVER['REQUEST_URI'] variable contain the path information, which you can split and parse.

$path = split('/', $_SERVER['REQUEST_URI']);
array_shift($path); // We always have a first null value
$mode = array_shift($path);

This ends up with $mode containing the mode, and $path containing an array of elements that are the rest of your path, so

Would leave you with $mode being 'foo' and $path being an array containing 'bar' and 'baz'

Doesn't work quite as expected because $_SERVER['REQUEST_FILENAME'] should be $_SERVER['REQUEST_URI'] and $mode is always empty. Principle is good though.
Fixed the issues you pointed out

I don't know if it can be done with a single expression, but it can be done with a fixed number of expressions, no matter how long the query string.

Your mod_rewrite rules will be called repeatedly, giving you what is sometimes called mod_rewrite recursion. There are techniques for avoiding it, but I think you want to use it.

Set up a rule that replaces the last pair with name=value&

Keep tacking on the input query string to the output. Every time through, your query string will get longer and your URL will get shorter.

Eventually you have only a single value that matches your last rule.

You have to capture the query string with

RewriteCond %{QUERY_STRING} ^(.*)$

And then you add it back to the output with %1

You'd end up with four lines.

I know four lines is what you started with, but you'd match as many parameters as you want without having to add a fifth line.

RewriteCond %{QUERY_STRING} ^(.*)$
RewriteRule ^(.*/)([^/]+)/([^/]+) $1?$2=$3&%1 [L]
RewriteCond %{QUERY_STRING} ^(.*)$
RewriteRule ^([^/]+)/ $1.php?%1 [L]

This will rewrite the following

/mypage/param1/val1/param2/val2/param3/val3/...     --->

It stops when there is only one parameter remaining. It will take the first "parameter" and call the .php file with that name. There is no limit to the number of param/val pairs.