Forum Moderators: phranque

Message Too Old, No Replies

optional multiple variables

         

willroy

1:11 pm on Jan 14, 2008 (gmt 0)

10+ Year Member



Hi there,

I am trying to get my head around mod rewrite at the moment. I have the following url:

http://www.example.com/index.php?market=$1&product=&2&subcat=$3

All variables are optional so a url could be either of the following:

index.php?market=$1&product=&2
index.php?market=$1
index.php?market=$1&product=&2&subcat=$3

However using the mod reqrite code I have at the moment, it will only redirect when all three variables are present:

RewriteEngine on
RewriteRule ^(.*)/(.*)/(.*)/$ index.php?m=$1&p=$2&s=$3 [L]
RewriteCond %{REQUEST_URI} ^/[^\.]+[^/]$
RewriteRule ^(.*)$ [%{HTTP_HOST}...] [R=301,L]

Could anyone point me in the right direction?

Many thanks, Will.

jdMorgan

2:48 pm on Jan 14, 2008 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



That is the nature of mod_rewrite -- The rule is invoked only if the requested URL exactly matches the RewriteRule pattern and all RewriteCond conditions are met. Any other behaviour would make it rather unusable...

You will either need to use a separate rule for each possible combination of present/not-present path-values, or us a single rewriterule with a specific RewriteCond for each possible combination of present/not-present path-values.

Because your URL format appears to be 'fixed-order,' you should be able to do this using four rules:


RewriteEngine on
#
# Externally redirect to add missing trailing slash if URL path does not contain a period or end with a slash
RewriteCond $1 ^[^\.]+[^/]$
RewriteRule (.*) http://%{HTTP_HOST}/$1/ [R=301,L]
#
# Internally rewrite SEO-friendly URLs to script
RewriteRule ^([^/]+)/([^/]+)/([^/]+)/$ index.php?m=$1&p=$2&s=$3 [L]
RewriteRule ^([^/]+)/([^/]+)/$ index.php?m=$1&p=$2 [L]
RewriteRule ^([^/]+)/$ index.php?m=$1 [L]

Note that I have avoided the use of the easy-but-horribly-inefficient ".*" pattern.

Be aware that the rules above are potentially dangerous: You will not be able to use any 'real' subdirectories whatsoever on this server with these rules in place, because all will be rewritten to index.php. This may be a problem if, for example, your script does not support the Web-standard privacy-policy implementation, which requires a file named /p3p.xml in a subdirectory named /w3c. You may need to add exclusions to these rules if you have any physically-existing subdirectories.

Note as well that external redirects must be done before internal rewrites; Otherwise, a later redirect would expose and earlier rewrite to the client, destroying any advantage of linking to 'SEO-friendly' URLs. In general, place external redirects first in order from most-specific to least-specific, followed by internal rewrites, again from most-specific to least specific.

Jim

willroy

6:28 pm on Jan 14, 2008 (gmt 0)

10+ Year Member



Thanks for the reply.

The code works as described but I can't continue if I can not directly link to folders and files though!

I presume I could always create a sub directory in my root called products and then create a .htaccess with the rewrite rules within that and then I could link to files in folders in the root directory?

Thanks again,
Will.

jdMorgan

7:33 pm on Jan 14, 2008 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



You may need to add exclusions to these rules if you have any physically-existing subdirectories.

Remember that all that matters to mod_rewrite is that a requested URL matches a pattern in a RewriteRule and/or RewriteCond. So, you could add a RewriteCond to each rule that might otherwise cause a "real" directory-index URL to get rewritten to your script. Files are not so much of a problem in your case, because your rules patterns all require a trailing slash, and files don't have trailing slashes.

So that leaves only directory index URLs, whether producing actual directory listings or accessing "index.xyz" in the directory (through the action of the DirectoryIndex directive) that need to be provided for.

If that's not clear, consider your 'home page'. It usually has a URL of example.com/. DirectoryIndex is most often used to rewrite that to example.com/index.html or example.com/index.php, just for example.

Now consider example.com/subdir/. This might produce a directory listing of all files in example.com/subdir, or if DirectoryIndex is applied, and the file exists, then the server would serve example.com/subdir/index.html or example.com/subdir/index.php.

In the second case (with a request for /subdir), the last rule I posted will rewrite this request to index.php?m=subdir unless you take steps to prevent this. You can do so in several ways, each with advantages and disadvantages. The trade-off is almost always a server slow-down versus your convenience.

for example, you could use


RewriteCond $1 !^subdir
RewriteRule ^([^/]+)/$ index.php?m=$1 [L]

This specifically disables the rule if the requested subdirectory path is "/subdir/", and is usable if the number of subdirectories is reasonably small and stable. It's very fast.

On the other hand, you might use


RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/]+)/$ index.php?m=$1 [L]

checks each and every request for a one-level-deep subdirectory URL, and does the rewrite only if that URL does not resolve to a physically-existing subdirectory. The problem is that it incurs a call to the operating system's filesystem handler, and possibly requires accessing the disk itself, to check to see if the directory exists on disk. So when the check is done, you get maybe a 10% slow-down, and if it has to actually check the disk, the response to the user's request might be three times slower. Everything depends on many variables; the user's connection speed, the number of simultaneous requests that the server is handling, the CPU and disk speed, etc. So these are only rough estimates.

The advantage, though, is that you need not maintain a list (in the RewriteCond) of all existing subdirectories; If they are 'real' subdirectories, the rule won't rewrite requests for them. So, it puts a lot more load on the server compared to the first method above, but is much easier on you.

Only you can choose what's right for your site.

Jim

willroy

10:39 am on Jan 15, 2008 (gmt 0)

10+ Year Member



Hello, thanks for the explanation, it was very helpful. I would go with the first of your suggestions whereby I state the names of the actual directories as once all of the folders are set up there will be little need to add more.

How well would this work? :

If I placed an .htaccess file with the same rewrite conditions and rules in a subdirectory /products/ for example and have a script in there to process the variables and then I can always link to ../anotherdirectory/ from there?

Thanks, Will.

jdMorgan

2:52 pm on Jan 15, 2008 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



I'm sorry, I don't understand your question. But in order to allow *any* subdirectory access, the rewrite exclusion code would have to be in the top-level .htaccess file.

There is no need to reproduce the same code in your subdirectories. See "RewriteOptions inherit"

Suggestion: Get the code working in your top-level .htaccess file, then re-assess and move the code to a subdirectory if necessary. Because mod_rewrite changes the URL-to-filename 'map' of your server in rather dramatic ways, it is best to be cautious and proceed in small steps, testing thoroughly at each step, rather than to install a large and/or complex set of rules and then try to test it all at once.

Jim

willroy

5:03 pm on Jan 15, 2008 (gmt 0)

10+ Year Member



Ok, all is working fine now. Thanks again.