Riddle with routing in PHP - php

I made simply router with htaccess and PHP. But this way made impossible to set additional GET parametr in url.
.htaccess
RewriteEngine On
[...]
RewriteCond %{REQUEST_URI} !\.(gif|jpg|png)$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.php?url=$1 [PT,L]
[...]
index.php
[...]
$url = isset($_GET['url']) ? $_GET['url'] : null;
$router = new Router($url);
[...]
and then i'm running dispatcher. For example url www.site.com/user/regierter runs class UserController -> registerAction().But what if i want add another get request like:
www.site.com/user/regierter?layout=red.
How make it possible to dispatch url part but accept another get requests?

Your approach to rewrite isn't favorable for this use case.
It's easier to simply use the internal redirect to get the request from your webserver to PHP and let PHP deal with the request parameters.
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [PT]
This makes Apache httpd redirect any request that isn't a file or directory that exists in the document root, to index.php internally. Meaning, the redirect is completely transparent to the end-user. They see www.example.com/foo while your webserver sees /index.php and PHP just sees /foo. This makes it possible to handle the routing internally regardless of the query string parameters.
$url = $_SERVER['REQUEST_URI'];
$router = new Router($url);
Any parameters in the query string still get parsed in $_GET appropriatly. $_SERVER['REQUEST_URI'] contains the original request URI passed to PHP by your webserver such that www.example.com/foo/bar/?quix=1&quack=2 gets read by PHP as /foo/bar/?quix=1&quack=2 and $_GET['quix'] == 1 and $_GET['quack'] == 2.
So the original query string (i.e. the HTTP request line) remains untouched as far as PHP can tell. The only change made here is in how your web server determines whether to let PHP handle a request or to serve it up directly from the document root.
Now your router can extract all the information it needs from $_SERVER['REQUEST_URI'] directly, with something like parse_url, for example.
So if the request was made to www.site.com/user/register?layout=red var_dump(parse_url($_SERVER['REQUEST_URI'])) would give you something like this.
array(2) {
["path"]=>
string(14) "/user/register"
["query"]=>
string(10) "layout=red"
}
And $_GET['layout'] will still give you "red".

The RewriteRule flag PT|passthrough is only needed, if your target (index.php) is an Alias or similar
That is to say, the use of the [PT] flag causes the result of the RewriteRule to be passed back through URL mapping, so that location-based mappings, such as Alias, Redirect, or ScriptAlias, for example, might have a chance to take effect.
So in your case this doesn't help, the use of flag L|last is enough.
As #arkascha already mentioned, to keep the other get parameters, you must use the QSA|qsappend flag
When the replacement URI contains a query string, the default behavior of RewriteRule is to discard the existing query string, and replace it with the newly generated one. Using the [QSA] flag causes the query strings to be combined.
This means your rule will become
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.php?url=$1 [L,QSA]
This will give you both $_GET['url'] and $_GET['layout'] in your PHP script.

Related

How to redirect all urls with file extension to a differen page in .htaccess

Please everyone, I am trying to prevent direct access to all files and pages in my application, and also to ensure that no one can determine what particular language the site is written in.
So, I want a situation where calls to myapp.com/path/to/page.php, whether it exists or not, internally redirects to myapp.com/page_not_found.php. And similarly, myapp.com/path/to/anywhere redirects to myapp.com/index.php.
I have been able to handle the later using
If the requested path and file doesn't directly match a physical file
RewriteCond %{REQUEST_FILENAME} !-f
and the requested path and file doesn't directly match a physical folder
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/]*)(.*)$ index.php?path=$1&sub=$2 [QSA,L]
But for the former, I don't have any idea where to begin.
To redirect *.php to /page_not_found.php:
RewriteCond %{REQUEST_URI} .*\.(php)
RewriteRule ^(.*)/ /page_not_found.php [R]
If you want to redirect other file types, like say *.asp, change (php) to (php|asp).
To redirect everything else, add this too:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [L]
As a note here, you don't need to pass in the path as a get parameter (i.e. index.php?path=$1...). Instead, from within PHP, read the $_SERVER['REQUEST_URI'] value instead. This stops your original query string from getting messed up (i.e. you can use $_GET as normal). So for example if the original request is myapp.com/path/to/page?id=1 then $_GET['id'] will continue to work as normal and $_SERVER['REQUEST_URI'] is /path/to/page?id=1.
Note that $_SERVER['REQUEST_URI'] can contain a query string so you'll want to split at ? to chop that off:
// Grab the path:
$path=$_SERVER['REQUEST_URI'];
// Split at ? to remove the query string if there is one:
$path=explode('?',$path,2);
// Get the first part only:
$path=$path[0];

.htaccess 301 and PHP query string

I'm wondering if the wonderful world of the SO community can help me with this one.
I have the following URL's that I would like to redirect/rewrite in my .htaccess file.
1. Redirect
I am trying to 301 redirect this URL:
http://example.com/staff-view.php?i=ACCOUNT_ID_EXAMPLE
to
http://example.com/staff-view/ACCOUNT_ID_EXAMPLE/
I have attempted the following:
RewriteEngine On
RewriteCond %{THE_REQUEST} ^[A-Z]{3,}\s/+staff-view\.php\?i=([^\s]+) [NC]
RewriteRule ^ /staff-view/%1/? [R=301,L]
however when navigating to the from URL it does not redirect.
2. PHP Query String
I am using the below to rewrite:
RewriteRule ^staff-view/([^/]+)/?$ /staff-view.php?i=$1 [L,QSA,NC]
and accessing through URL
http://example.com/staff-view/ACCOUNT_ID_EXAMPLE/
it correctly directs me to the right page, but when attempting to access ACCOUNT_ID_EXAMPLE via the following methods:
<?
var_dump($_REQUEST);
var_dump($_GET);
?>
They both are empty:
array(0) { } array(0) { }
I would appreciate any help, if you need any more info, please let me know.
Update 1
Updated .htaccess file:
RewriteEngine On
RewriteRule ^staff/([^/]+)/?$ /staff-view.php?i=$1 [L,QSA,NC]
RewriteBase "/"
RewriteCond %{REQUEST_URI} ^staff-view\.php$
RewriteCond %{QUERY_STRING} ^i=([A-Z0-9_]*)$
RewriteRule ^(.*)$ ^staff/%1/
I have attempted to access the file from:
http://example.com/path/to/site/staff/ACCOUNT_ID
and it DOES NOT work. However, if I access the file from:
http://example2.com/staff/ACCOUNT_ID
it WORKS.
But if I go to http://example2.com/staff-view.php?i=ACCOUNT_ID it does not redirect to http://example2.com/staff/ACCOUNT_ID - this is not the end of the world, but I would like to fix it, but the deep directory issue as a priority :).
Try using this:
RewriteEngine On
RewriteRule ^staff-view/([^/]*)$ /staff-view.php?i=$1 [L]
Trying to parse %{THE_REQUEST} (e.g., "GET /staff-view.php?i=account_id HTTP/1.1") to extract both the path and the query string could work but it's unnecessarily complex. I think it's much simpler to use two rewrite conditions that take advantage of the server variables %{REQUEST_URI} and %{QUERY_STRING} because they are pre-populated with the info you're interested in.
Try the following:
RewriteEngine On
#If you don't have this set in your htaccess,
# Apache may prepend the final path with your on disk dir structure
RewriteBase "/"
#Rewrite if the /staff-view.php page is requested
RewriteCond %{REQUEST_URI} ^/staff-view\.php$
#Rewrite if the query string contains i parameter anywhere. Assumes
# ID can be only digits; include all allowed chars. eg: [a-zA-Z0-9_]
#Don't forget the '&' before {{QUERY_STRING}} otherwise the match
# will fail if i is the first parameter
RewriteCond &%{QUERY_STRING} &i=([0-9]+)
#Rewrite the account ID as part of the path. Append the query string
# in order to preserve other query parameters (eg: if user asked for
# /staff-view.php?i=123&x=boo, you want to preserve x=boo. a 301
# redirect tells the browser to go to the new path and to remember it
#This will stop processing and cause the browser to make a new request
RewriteRule ^(.*)$ /staff-view/%1/ [QSA,R=301,L]
#Finally, we want to silently forward any request matching the last
# redirect target to the actual file that will serve the request.
# The account ID should be of the same format as above: ([0-9]+). The
# [L] flag tells the server to stop looking for new instructions
RewriteRule ^staff-view/([0-9]+)/$ /final.php?i=$1 [QSA,L]
The logic is easy to follow: If the path requested is /staff-view.php, and if the query string contains the i parameter, tell the user's browser to go instead to /staff-view/ID, preserving the other query params. Finally, when the browser asks for this new path, silently (without telling the browser) forward the request to final.php along with the ID and other query params

Is it possible to query a database using a value passed in a URL, and write the result of the query to the URL using mod_rewrite?

Is it possible to use mod_rewrite to write an htaccess rule that takes a url parameter value (for example: id=1, where 'id' is the parameter, and '1' is the parameter value), query a database with the parameter value specified, and then write the value returned from the query as a part of the url of the requested page?
I know the basics of mod_rewrite, for example rewriting a url that appears like:
www.example.com/item.php?id=1
to the following:
www.example.com/item/1
An example of what I would require is writing the following url:
www.example.com/item.php?id=1
to this:
www.example.com/item/name-of-item-based-on-id-specified-in-original-url
However I have no idea if what I am looking to do is possible using mod_rewrite.
If anyone has a solution to this problem I'd be very grateful if you could help me. If what I am trying to do is not possible using htaccess and mod_rewrite, can someone please point me in the direction of how I may go about solving this problem?
It's possible, but you need to use a RewriteMap to define a mapping that you can use within a RewriteRule.
Apache version 2.2 doesn't have direct database access so you'll need to write a script that does the actual query then return the result. You can define this map using the "External Rewriting Program".
So if you have a script that takes "cats" from stdin, then queries the database, and returns "1", you'd define it like so:
RewriteMap item_lookup prg:/path/to/item_lookup.php
That directive has to be in your server or vhost config, it can't be in an htaccess file. But you can use the mapping in an htaccess file:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /item.php?id=${item_lookup:$1} [L]
So this takes the URI /cats and rewrites that to /item.php?id=1.
If you are using apache 2.4, then you can take advantage of the "DBD" map. You can insert a query right into the map definition, bypassing having to use an external script. You'd use it in the same way.
RewriteMap item_lookup "fastdbd:SELECT id FROM items WHERE name = %s"
Then use it in the same way:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /item.php?id=${item_lookup:$1} [L]
Without using a DBD/FastDBD query, I think you're honestly better off just doing the database lookup from item.php, since you'd be duplicating all of that work in a second external script anyways. Just add something like:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^item/([0-9]+)$ /item.php?id=$1 [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([A-Za-z0-9-]+)$ /item.php?name=$1 [L]
And in your item.php script, check for both id and name. If you have a name, do the database lookup in order to turn that into an id. It's much easier to manage, you don't need to have server/vhost config access, and you're not complicating matters by using a rewrite map.

Guidance on URL rewrite and construction

I am about to attempt writing of a photo sharing script and a script/rewrite that transforms numbers into descriptive names. I have a vague idea on how to go about doing this, so I was looking for some general comments/guidance.
Issue 1: I need to have a URL source for a photo which is stored above my root directory. I plan on appending the photo name (which is stored in my database) to my url as a query string, such as: www.mywebsite.com/getphoto.php?12_3.jpg and then writing a php script (getphoto.php) which takes the portion after the '?' and gets that photo from above the root.
Does this make sense and would there be any things to consider?
Issue 2: I want to transform a number at the end of my URL to a descriptive name (ie typing in facebook.com/4 displays facebook.com/zuck). I am not really sure the best way to go about doing this and was hoping for some guidance to get going in the right direction.
Thanks!
For Issue 1: a simple rewrite can handle that, you need to use the [QSA] flag. Something like this:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} ^.*\.(jpeg|jpg|gif|png|bmp)$
RewriteRule ^(.+)$ /getphoto.php?photo=$1 [L,QSA]
This will rewrite behind the scenes the url http://mywebsite.com/12_3.jpg to http://mywebsite.com/getphoto.php?photo=12_3.jpg Note that the 3rd rewrite condition wants the URI to end with an image extension, you may not need it.
For Issue 2, it depends on how something like "4" maps to "zuck". If you are going to hardcode them into your apache config, you can use a RewriteCond:
RewriteEngine On
RewriteCond %{REQUEST_URI} ^/4$
RewriteRule ^.*$ /zuck [L]
RewriteCond %{REQUEST_URI} ^/5$
RewriteRule ^.*$ /mark [L]
etc. (or replace [L] with [R,L] to redirect instead of rewrite, or alternatively just use Redirect)
Redirect /4 /zuck
Redirect /5 /mark
etc.
If the mapping is stored in a database, your going to need to do this dynamically, perhaps as a php script to do a redirect, utilizing something similar to Issue 1. The rewrite rule would rewrite to something like /redirect.php?id=$1 and your redirect.php script would take the id and do a database lookup to see where to redirect the browser.

.htacces to capture the page name and send it as a get method

I am trying to capture a url such as
http://www.mysite.com/somepage.php?sometext=somevalue
and redirect it to.
http://www.mysite.com/index.php?page=somepage.php&sometext=somevalue
I tried searching for such .htaccess online, but couldn't find it.
Can you please help me?
I'm quite sure this is a duplicate, but I'm having a bit of an issue finding it/them [Edit: I found one, though possibly not the best example].
Anyway, this is a fairly standard problem resolved with fairly standard code:
RewriteRule ^(.*)$ index.php?get=$1 [L,QSA]
The RewriteRule captures the entire request as $1, and passes it to index.php as the page GET parameter.
The [QSA] flag on the end says to take any existing GET parameters (sometext=somevalue in your example), and add them as additional GET parameters on the new request. (The [L] flag just says that this should be the last rule executed.)
Note that this will also redirect requests for things like images or CSS files, so it's good to add the following lines directly before this rule:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
These lines say "if the request is for a file or directory that actually exists, don't process the rule." That way, requests for real files will be served directly by Apache, rather than being handled (or more likely, mishandled) by your PHP script.
RewriteRule ^(.*).php?sometext=(.*)$ index.php?page=$1.php&sometext=$2 [QSA,L] #rewrite
RewriteRule ^(.*).php?sometext=(.*)$ http://www.mysite.com/index.php?page=$1.php&sometext=$2 [R=301,L] #redirect

Categories