htaccess rewrite a multi-query string into a path - php

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.

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' ];

Custom URL in apache using get parameters

I'm trying to figure out how to rewrite urls using apache webserver and php.
The url below is the real nonrewritten url:
http://localhost:1337/rewritetest/index.php?id=12
And I want to reach it by
http://localhost:1337/rewritetest/index/12
My indexfile looks like this:
<?php
echo $_GET['id'];
?>
Is this possible? The "new" url doesn't include any parameter names so I guess I have to use an order of parameters instead but I dont know how to reach them in that case.
Below is as far I've come with my rewrite:
RewriteCond %{QUERY_STRING} id=([-a-zA-Z0-9_+]+)
RewriteRule ^/?index.php$ %1? [R=301,L]
RewriteRule ^/?([-a-zA-Z0-9_+]+)$ index.php?id=$1 [L]
Anyone have an idea of what I'm doing wrong?
it's located in the same folder as index.php
So, given the .htaccess file is located at /rewritetest/.htaccess (as opposed to the document root ie. /.htaccess) then...
RewriteRule ^/?([-a-zA-Z0-9_+]+)$ index.php?id=$1 [L]
If you request a URL of the form /rewritetest/index/12 then the above RewriteRule pattern won't actually match anything. It tries to match "index/12", but your pattern does not contain a slash so will fail. (Is the + inside the character class intentional?)
Try something like the following instead:
RewriteRule ^(index)/(\d+)$ $1.php?id=$2 [L]
This obviously specifically matches "index" in the URL. If you are always rewriting to index.php then you don't really need "index" in the URL - unless this means something different? This also assumes that the valuue of the id parameter consists only of digits.
To rewrite the more general .../<controller>/26 to .../<controller>.php?id=26 (as mentioned comments) then try something like:
RewriteRule ^([\w-]+)/(\d+)$ $1.php?id=$2 [L]
In per-directory .htaccess files the slash prefix is omitted on the URL-path that is matched by the RewriteRule pattern, so /? is not required. The above pattern also matches something for for the id, not anything. So, /index/ would not match.
If this is a new site then the "redirect" (from /index.php?id=12 back to /index/12) is not necessarily required. That's only really required if you are changing the URL structure on an existing site where old URLs already have inbound links and are indexed by search engines. In which case you could do something like the following before the internal rewrite:
RewriteBase /rewritetest/
RewriteRule %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{QUERY_STRING} ^id=(\d+)
RewriteRule ^(index)\.php$ $1/%1 [R,L]
Or, for a more generic .../<controller>/26 to .../<controller>.php?id=26 (as above) then change the RewriteRule to:
RewriteRule ^([\w-]+)\.php$ $1/%1 [R,L]
The additional check against the REDIRECT_STATUS environment variable is to prevent a rewrite loop after having rewritten the URL to /index.php?id=12 earlier.

Pretty URL via htaccess using any number of parameters

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.

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);

Mod_rewrite in .htaccess - forward anything that starts with index.php to ____

UPDATE: This works:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{THE_REQUEST} ^[A-Z]+\ ([^\s]+)
RewriteRule (.+) /index.cfm?event=checkuri&uri=%1 [QSA]
Some background...
So we already have a catchall redirect in our .htaccess file which is this:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule (.+) /index.cfm?event=checkuri&uri=$1
This ties into a database table that checks the URI for a match. so if we just moved a site that used to have this page:
/some-awesome-article.html
Onto our system, and the new address is
/awesome-article/12442
and someone tried to access the old URI, our system would check for this, find a match, and forward them to the new home: /awesome-article/12442
This system works awesome, with one exception. If the URI is something like /index.php?id=123412 then the whole system falls apart. In fact /index.php/whatever won't work either.
Everything else works except for this. We do not use PHP for our web application (although support says its in an admin console on the server somewhere).
So basically what I need is if index.php is detected anywhere it will forward the URI to our
existing system:
How can i modify this to fix it?
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule (.+) /index.cfm?event=checkuri&uri=$1
Try changing your code to:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule (.+) /index.cfm?event=checkuri&uri=$1 [L,QSA]
QSA is for Query String Append that will make sure to append existing query parameters with the new ones.
Rewriting with mod_rewrite does not work on the full URL. In fact, the regex in the RewriteRule does only get the path and file, but not the query string. And so the backreference $1 will only contain "index.php" and nothing else.
Additionally, the RewriteRule does change the query string because there is one in the target pattern. Because the flag [QSA] (query string append) is not present, the query string of the original request gets replaced instead of appended. So the query string is gone after this rewriting.
This would be a lot easier if you wouldn't mess with the query string. The easiest way of rewriting any url that is not an existing file would be if the second line would be simply RewriteRule (.+) /index.cfm - you could then get all info about the current request, including query string, path and file, in the script.
So now you'd have to fiddle with the query string. Adding [QSA] will pass the query string to your script and you'd have to detect what's inside. This will work only if you do not expect the query string to contain parameters named "event" and "uri" - these will be overwritten by your rewriting. If you need to add the original query string to the URL, it's a bit more complicated, because the string needs to be url-encoded.
Here's how to do that.
Based on your comments, it sounds like you need to use the Query String Append QSA flag on your rule like this:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ /index.cfm?event=checkuri&uri=$1 [QSA,L]
In your example case the rewrite would look like:
/index.cfm?event=checkuri&uri=index.php&id=123412
Sven was very close so I'm giving him the check
This ended up working perfectly:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{THE_REQUEST} ^[A-Z]+\ ([^\s]+)
RewriteRule (.+) /index.cfm?event=checkuri&uri=%1 [QSA]

Categories