Pretty URL via htaccess using any number of parameters - php

Actually i have this URL:
http://www.example.com/index.php?site=contact&param1=value1&param2=value2&param3=value3
But i want to have this URL format:
http://www.example.com/contact/param1:value1/param2:value2/param3:value3
So the "contact" goes to variable $_GET["site"] and rest of parameters should be able to access via $_GET["param1"], $_GET["param2"] etc. The problem is, it has to work with any number of parameters (there could be param4 or even param50 or any other name of parameter). Is it possible via htaccess to cover all these cases?

Mod_rewrite has a maximum of 10 variables it can send:
RewriteRule backreferences:
These are backreferences of the form $N (0 <= N <= 9), which provide access to the grouped parts (in parentheses) of the pattern, from the RewriteRule which is subject to the current set of RewriteCond conditions.
mod_rewrite manual
so what you desire is NOT possible with htaccess only. a common way is to rewrite everything to one file and let that file determine what to do in a way like:
.htaccess
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteRule ^(.*)$ index.php [L,NC]
index.php
$aUrlArray = explode('/',str_ireplace(',','/',$_SERVER['REQUEST_URI'])); // explode every part of url
foreach($aUrlArray as $sUrlPart){
$aUrlPart = explode(':',$sUrlPart); //explode on :
if (count($aUrlPart) == 2){ //if not 2 records, then it's not param:value
echo '<br/>paramname:' .$aUrlPart[0];
echo '<br/>paramvalue' .$aUrlPArt[1];
} else {
echo '<br/>'.$sUrlPart;
}
}

Garytje's answer is almost correct.
Actually, you can achieve what you want with htaccess only, even if this is not something commonly used for that purpose.
Indeed, it would be more natural to delegate the logic to a script. But if you really want to do it with mod_rewrite, there are a lot of techniques to simulate the same behaviour. For instance, here is an example of workaround:
# Extract a pair "key:value" and append it to the query string
RewriteRule ^contact/([^:]+):([^/]+)/?(.*)$ /contact/$3?$1=$2 [L,QSA]
# We're done: rewrite to index.php
RewriteCond %{QUERY_STRING} !^$
RewriteRule ^contact/$ /index.php?site=contact [L,QSA]
From your initial example, /contact/param1:value1/param2:value2/param3:value3 will first be rewritten to /contact/param2:value2/param3:value3?param1=value1. Then, mod_rewrite will match it again and rewrite it to /contact/param3:value3?param1=value1&param2=value2. And so on, until no pair key:value is found after /contact/. Finally, it is rewritten to /index.php?site=contact&param1=value1&param2=value2&param3=value3.
This technique allows you to have a number of parameters greater than 9 without being limited by mod_rewrite. You can see it as a loop reading the url step by step. But, again, this is maybe not the best idea to use htaccess only for that purpose.

This is entirely doable using some creative htaccess and PHP. Effectively what you are doing here is telling Apache to direct all page requests to index.php if they are not for a real file or directory on the server...
## No directory listings
IndexIgnore *
## Can be commented out if causes errors, see notes above.
Options +FollowSymlinks
Options -Indexes
## Mod_rewrite in use.
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteCond %{REQUEST_URI} !^/index\.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule .* index.php [L]
After this all you need to do is go into PHP and access the full user requested URL structure using the $_SERVER['REQUEST_URI'] superglobal and then break it down into an array using explode("/", $_SERVER['REQUEST_URI']).
I currently use this on a number of my sites with all of the sites being served by index.php but with url structures such as...
http://www.domain.com/forums/11824-some-topic-name/reply
which is then processed by the explode command to appear in an array as...
0=>"forums", 1=>"11824-some-topic-name",2=>"reply"

Try this..
.htaccesss
RewriteEngine on
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule .* /index.php [L,QSA]
index.php
$uri = $_SERVER['REQUEST_URI'];
$uri_array = explode( "/", $uri );
switch ( $uri_array[0] ) {
case '':
/* serve index page */
break;
case 'contact':
// Code
break;
}

This is doable using only htaccess with something along the lines of...
([a-zA-Z0-9]+):{1}([a-zA-Z0-9]+)
([a-zA-Z0-9]+) will match alpha-numeric strings.
:{1} will match 1 colon.
Expanding from there will probably be required based on weird URLs that turn up.

Related

Manage slugs with PHP and .htaccess (mod_rewrite)

Let's call my site:
www.example.com
and I have a PHP file like this:
www.example.com/product.php?id=50
I would like to access it by using
www.example.com/product/50
but ALSO, very important, I have several subdirectories like
www.example.com/subsite/product.php?id=50
www.example.com/subsubsite/product.php?id=50
That must become
www.example.com/subsite/product/50
www.example.com/subsubsite/product/50
How can I solve it at best with PHP and .htaccess using mod_rewrite?
I banged my head with other questions like this one but to no avail.
I can't seem to find a solution that works flawlessly, taking care of all imported files like CSS, JS and PHP classes.
Ok so this might not be the complete answer but should help you find your way.
You can use regex to match your desired path pattern. So for example your htaccess might look something like...
# Check if module is installed
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
# Check query for matching pattern and pass id, but also append additional query strings
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^\/]+\/)?product\/([0-9]+)$ /$1product.php?id=$2 [L,QSA]
# If not file or directory on server, send to 404.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /404.php [L]
</IfModule>
And what this does is...
1. Match the uri with a regex pattern
Regex: ^([^\/]+\/)?product\/([0-9]+)$
^ - Start of string.
([^\/]+\/)? - matches any directory (if exists) and stores it for reuse.
product\/([0-9]+) - Your desired path e.g. product/50 and stores the number "id" for reuse.
$ - End of string.
2. Pass captured directory and id to our file
Like so: /$1product.php?id=$2 [L,QSA]
$1 is our directory name including the trailing slash e.g. subsubsite/
$2 is our product id e.g. 50
[L,QSA] The QSA flag means we can access additional query string parameters e.g. /product/50?show=all&updated=1. More about flags can be found here http://httpd.apache.org/docs/current/rewrite/flags.html#flag_qsa
3. 404 anything not matching
Like so:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /404.php [L]
!-f If request is not a file
!-d If request is not a directory
/404.php The file used for presenting a 404 error.
Getting the id...
With the above, you can get the ID within your product.php file like so:
$id = (int)$_GET[ 'id' ];

htaccess rewrite a multi-query string into a path

How would i go about changing the query string
file.php?id=number&string=some-words
into this
file/number/some-words/
I know this has been asked a million times before, but I've looked at a number of solutions on here and they were all single query based (as in just ?something and not ?something&something-else).
Also once rewritten, does the php still read the original page query-string when using $_GET or $_REQUEST etc... even though it now appears as a path?
Any help is appreciated.
Thanks.
RewriteRule takes a regular expression which can be as complicated as you want it followed by the real URL that will load your file. You put parenthesis around the parts you want to capture in the regex and can reference those with $1 for first group, $2 for second group and so on in the URL part. For example:
RewriteRule ^(\w+)/(\d+)/(.*)$ index.php?file=$1&id=$2&words=$3
This would match 3 groups:
letters/numbers up to the first slash
some numbers up to the second slash
anything after that including additional slashes
And those can be referenced by $1, $2, $3 as seen in the second part with index.php.
The only issue with this though is that if you are missing any one part, the pattern in the rule won't match. So you either need to have a separate rule for each variant:
#matches all 3 parts
RewriteRule ^(\w+)/(\d+)/(.*)$ index.php?file=$1&id=$2&words=$3
#matches the first 2 parts
RewriteRule ^(\w+)/(\d+)$ index.php?file=$1&id=$2
#matches just the first part
RewriteRule ^(\w+)$ index.php?file=$1
#matches everything else
RewriteRule ^.*$ index.php
Or you can do what is usually called bootstrapping which is where you use a single RewriteRule to redirect everything to a single php file like so:
RewriteRule ^(.*)$ index.php
And then you can just use php to determine what the different parts are. Inside php there is a built in server variable $_SERVER['REQUEST_URI'] that will give you the URI part of the url which is everything after the domain and first slash including any query string parameters. This is based on the URL that the user requested and not the one rewritten by apache. You can explode('/', $_SERVER['REQUEST_URI']) to get the individual parts and do whatever you want with them.
You can place your code in Apache .htaccess files. This could look something like this:
Options +FollowSymLinks
RewriteEngine On
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteRule ^users/(\d+)*$ ./profile.php?id=$1
RewriteRule ^threads/(\d+)*$ ./thread.php?id=$1
RewriteRule ^search/(.*)$ ./search.php?query=$1
Or you can use only htaccess and php:
htaccess
Options +FollowSymLinks
RewriteEngine On
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteRule ^.*$ ./index.php
PHP
<?php
#remove the directory path we don't want
$request = str_replace("/envato/pretty/php/", "", $_SERVER['REQUEST_URI']);
#split the path by '/'
$params = split("/", $request);
?>
And it will still read the original page query-string.

mod_rewrite REQUEST_FILENAME change after rule without L flag

I'm working on a .htaccess file and have come across some curious behavior with REQUEST_FILENAME that I'd love some clarification about. I have two rules I'm testing out which are like so:
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^.*$ /index.php [QSA]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^/index.php$ /other_page.php [L]
When I try to go to the address site.com/this_file_exists.php I seem to be getting to other_page.php, which means that
REQUEST_FILENAME was a file in the first RewriteCond but not a file
by the time mod_rewrite processed the second RewriteCond.
After changing the second RewriteRule's flags to [L,E=RF:%{REQUEST_FILENAME}], and echoing $_SERVER['REDIRECT_RF'] on other_page.php, I find that the request filename was just /index.php, as opposed to the original filename, which was /full/path/to/this_file_exists.php.
Does mod_rewrite consistently overwrite the REQUEST_FILENAME in this way after matching a RewriteRule? If so, is there a documented way in which it does that?
These lines in mod_rewrite.c
/* Now adjust API's knowledge about r->filename and r->args */
r->filename = newuri;
seem to suggest that the new REQUEST_FILENAME truly is
the rewritten URI.

How do I turn PHP string into slug in Wordpress

I have a page in wordpress I am using with a slug called Bad-Debt-Recovery-in/. I am using a custom php query on that page with strings in the URL's like this
Bad-Debt-Recovery-in/?zipcode=55555&location=Chambers%20County+AL
How can I make this url into a slug like this
Bad-Debt-Recovery-in/55555/Chambers-County/AL/
as a rewrite? Any help would be appreciated!
UPDATE:
This code is actually what I am using. I also made it simpler and created a third variable named "state". One rewrite is for City and one is for County page:
# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^Bad-Debt-Recovery-And-Collection-Agencies-Services-In\/([^\/]+)\/([^\/]+)\/([^\/]+)\/? Bad-Debt-Recovery-And-Collection-Agencies-Services-In/?zipcode=$1&city=$2&state=$3 [QSA,L,NC]
RewriteRule ^Bad-Debt-Recovery-And-Collection-Agency-Services-In\/([^\/]+)\/([^\/]+)\/([^\/]+)\/? Bad-Debt-Recovery-And-Collection-Agency-Services-In/?countyid=$1&county=$2&state=$3 [QSA,L,NC]
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php
</IfModule>
# END WordPress
What you are asking for, and seeking to accomplish is called: Converting a request path into a query string.
Use .htaccess RewriteRule directive to modify the incoming request
RewriteRule ^Bad-Debt-Recovery-in\/([^\/]+)\/([^\/]+)\/([^\/]+)\/? Bad-Debt-Recovery-in/?a=$1&b=$2&c=$3 [QSA,L,NC]
Each ([^\/]+) captures path elements into a variables $1,$2,$3...
The ? at the end simply denotes that the last / is optional or else the last match could fail
the \ simply escape the '/' for literal interpretation
Add as many ([^\/]+) as needed and capture them in the query
zipcode=$1&location=$2+$3
The modifiers at the end [QSA,L,NC] are called flags
QSA appends the query string to the end of the rewrite if any exist
L simply says this is the last rewrite
NC means not case sensitive
This is your final solution:
RewriteRule ^Bad-Debt-Recovery-in\/([^\/]+)\/([^\/]+)\/([^\/]+)\/? Bad-Debt-Recovery-in/?zipcode=$1&location=$2+$3 [QSA,L,NC]
You can use the WP function sanitize_title
Combine this with a php loop i.e.
$url = $_SERVER['REQUEST_URI'];
foreach($_GET as $g){
$url .= '/'.$g;
}
$url = sanitize_title($url);

REQUEST_URI vs HTACCESS

As far as I am aware there are two different ways of 'routing' and using 'friendly urls'
1: Solely using .htaccess:
RewriteRule ^foobar/([^/]+)/([^/]+)$ "index.php?foo=$1&bar=$2" [NC]
or
2: Using .htaccess in conjunction with an index.php 'routing' system:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
# if file not exists
RewriteCond %{REQUEST_FILENAME} !-f
# if dir not exists
RewriteCond %{REQUEST_FILENAME} !-d
# avoid 404s of missing assets in our script
RewriteCond %{REQUEST_URI} !^.*\.(jpe?g|png|gif|css|js)$ [NC]
RewriteRule .* index.php [QSA,L]
</IfModule>
And then inside index.php:
$uri = explode("/",substr($_SERVER['REQUEST_URI'],1));
if((isset($uri[0])) && ($uri[0]!="")) {
$page = $uri[0];
if(is_file(ROOT."/subs/docs/$page/config.php")) {
include(ROOT."/subs/docs/$page/config.php");
}
} else {
$page="home";
}
then include $page somewhere down the line.
My question is, which way is better, or is there some other method I am unaware of? And by better I mean in terms of efficiency, speed, and logic.
In the real life most routing systems are so complicated that 1st option turns .htaccess right into living nightmare.
As a matter of fact, the number of all possible input parameters combinations as so huge, that main application router have to deal only with detecting controller. While each particular controller have to deal with them it's own way.
Frankly, you cannot tell for sure that 2nd parameter have to be assigned to the foo variable and 3rd assigned to the bar.
So, there is no choice but 2nd one.

Categories