Use SO's URL format for $_GET - php

StackOverflow has a very neat, clean URL format. It looks the same as a directory structure, but there can't be a directory for each question on here! My question is this:
How can I get http://www.site.com/sections/tutorials/tutorial1, for example, to stay like that in the address bar, but convert it to a $_GET request for PHP to mess around with?
I could use a .htaccess file, but I don't want the URL being rewritten - I'd like it to remain clean and friendly. Is my only option here to use PHP's string splitting functions to get some pretend $_GET data?
Thanks,
James

What about this, using .htaccess to split the URL up, the URL won't change but instead point to index.php with various $_GET variables, this could could be increased to cover more URL sections.
# turn rewriting on
RewriteEngine on
# get variables in this order, [object], [object,action], [object,action,selection]
RewriteRule ^([^/\.]+)/?$ /index.php?object=$1 [L,NC,QSA]
RewriteRule ^([^/\.]+)/([^/\.]+)/?$ /index.php?object=$1&action=$2 [L,NC,QSA]
RewriteRule ^([^/\.]+)/([^/\.]+)/([^/\.]+)/?$ /index.php?object=$1&action=$2&selection=$3 [L,NC,QSA]

A PHP Rest framework could do this for you, so I refer you to this question. Most of the frameworks won't load the data from $_GET, but will offer a similar and equally convenient way to read it.

It's actually a RESTful way of building your URI's. Not only SO applies this pattern. I recommend to not re-invent the wheel by taking a look at this question.
In addition you could switch over to a RESTful framework such as CakePHP or CodeIgniter, which are configured by default to use the RESTful pattern.

$_GET does not contain the path compontents from the URL, only the parameters that eventually follow the ?. You could use
$parts = explode('/', pathinfo($_SERVER['REQUEST_URI'], PATHINFO_DIRNAME));
var_dump($parts);
However it seems you should have a read on URL rewriting e.g. with mod_rewrite. "I don't want the URL being rewritten - I'd like it to remain clean and friendly" ... The rewriting happens on the server. The user never sees the "ugly" result.

If you don't want to use mod rewrite the best solution would be using regular expressions agains the $_SERVER['REQUEST_URI'] variable.
i.e:
preg_match('|/(.*)/(.*)|', $_SERVER['REQUEST_URI'], $match);
$_GET['param1'] = $match[1];
$_GET['param2'] = $match[2];
If you want to setup a capture all php script. IE if the script request doesn't exist use a default script, use mod-rewrite to redirect everything to one script i.e. the zend framework (and most of the PHP MVC framework) use this:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]
I think that could be a bit cumbersome.

Related

Extract URL slug using PHP. Building a URL shortener

I'm building a URL shortening web app using PHP. I am able to generate shorter URLs successfully. But I'm not able to redirect the users when they visit the shortened URL.
If the user enters https://example.com/aBc1X, I'd like to capture the aBc1X. I'll then query the database to find the original URL and then redirect.
My question is, how can I extract the aBc1X from the above URL?
P.S. I'll use either Apache or Nginx.
Two things to do for you.
First you have to redirect all traffic to one file which will be your router file. You can do this by placing a few rules in .htaccess file. I will put there some generic rules to start with (this one come from Wordpress):
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^redirect\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /redirect.php [L]
</IfModule>
They tell that everywhere url points to which isn't file or directory will run file redirect.php. You may want to tweak that settings to your needs.
Then in redirect.php you can capture url by looking inside $_SERVER['REQUEST_URI'].
For url http://example.com/any-url-i-want you would have
$_SERVER['REQUEST_URI'] == '/any-url-i-want.
Now the only thing you need is to find the url in database, and do a redirect.
I guess you can handle string operations at this point, either by using parse_url, regular expressions, or simple string cutting.
You want to use;
$_SERVER['REQUEST_URI'];
That will return what you're looking for.
You can see the documentation here
To parse the url
$url = "https://example.com/aBc1X";
$path = ltrim(parse_url($url, PHP_URL_PATH), '/');
Then $path will be aBc1X as desired. Note that any query following a ? will be omitted in this solution. For more details have a look at the documentation of the parse_url function.

Pros/cons of using index.php?q=path/ instead of index.php/path/ when routing URLs?

I'm writing a simple method to map routes to files and I've come across two ways to do it.
The first, and I guess used by most frameworks, is using the $_SERVER['REQUEST_URI'] variable to extract everything after index.php:
RewriteRule ^(.*)$ index.php [QSA,L]
The second way is used in Drupal, and the route is simply passed as a query string.
RewriteRule ^(.*)$ index.php?q=$1 [QSA,L]
Now, the "Drupal way" seems a lot simpler to me. With the other method you'd have to use "explode" on both $_SERVER['REQUEST_URI'] and $_SERVER['SCRIPT_NAME'] and then use something like array_diff_assoc to remove the script name and subdirectory name, if there is one. It's not THAT much work, but if with the Drupal way you can simply extract the $_GET['q'] value, why nobody does it that way? What are the disadvantages, if any?
Thanks.
The disadvantage of using a q param is, without URL rewriting the URL will look like...
http://domain.com/?q=something
...as opposed to the cleaner (IMO)...
http://domain.com/index.php/something
There is no huge advantage or disadvantage one way or the other with the url being rewritten. However, I will point out everything including and after the final slash is stored in _SERVER[PATH_INFO], so parsing the request URI my not be necessary.
The reason that the shorter URL technique is used mostly is for the cleaner technique and the better SEO that comes from it. Search engines consider these two URL's to "be the same":
http://www.domain.com/?b=something
http://www.domain.com/?b=hello
I do not have a good explanation so here are some links with some really good information on it:
http://blog.hubspot.com/blog/tabid/6307/bid/2261/My-Wife-says-Short-URLs-Yield-Better-Click-Through-Rates-in-SEO-and-she-s-RIGHT.aspx
Short URL or long URL for SEO
http://googlewebmastercentral.blogspot.com/2008/09/dynamic-urls-vs-static-urls.html
Now some people implement the shorter URL's differently, but this is how I have found them to work the best for me:
In .htaccess
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?route=$1 [L,QSA]
In index.php (or some other php file)
if(isset($_GET['route']) && $_GET['route'] != NULL && strlen($_GET['route']) > 0)
{
$split = explode('/', $_GET['route']);
for($i=1; $i <= count($split)-1; $i++)
{
$_GET[$i] = $split[$i];
}
}
This then allows you to use $_GET['1'] (or $_GET[1]) and all subsequent numbers as well.
URL's then look like this:
http://www.domain.com/?b=something
becomes
http://www.domain.com/something
http://www.domain.com/?b=something&a=hello&c=blah
becomes
http://www.domain.com/something/hello/blah
And then the parameters can be accessed via:
$_GET[1] = "something";
$_GET[2] = "hello";
$_GET[3] = "blah";
Hope that helps!

Custom URL with PHP

I have a small question to ask. Is it possible, via php or htaccess, to change a url like: miodominio.com/users.php?idu=x into something like miodominio.com/username ?
I want it to be Facebook style...Where "username" is the username chosen by idu = x.
There are multiple ways to solve this problem, but here's one that always suits my needs.
Guide all your URL requests through the index.php first and resolve the request in your PHP code second.
1) Use an .htaccess file to direct all URL's through index.php. You'll find one way here by the CodeIgniter framework and a more advanced explanation here. I recommend the CodeIgniter .htaccess guide first if you're inexperienced with .htaccess.
2) Second, use the $_SERVER variable in PHP to extract the URL. Probably with the help of the $_SERVER['REQUEST_URI'], you'll find '/username/' which you can then use to extract the user's data and serve it to them.
Good luck and beware of URL injections using this method.
You need to use apache's mod_rewrite for this. It can translate miodominio.com/username to miodominio.com/users.php?idu=x. There are some good guides about this which are easy to find with Google.
You can try to use this mod_rewrite pattern (add it to the .htaccess):
RewriteEngine On
RewriteRule ^([a-zA-Z0-9_-]+)$ users.php?idu=$1
RewriteRule ^([a-zA-Z0-9_-]+)/$ users.php?idu=$1
you have to write a clean URL in your .htaccess file like :
RewriteEngine On
RewriteRule ^([a-zA-Z0-9]+)/$ users.php?idu=$1
Put the following in your .htaccess
RewriteEngine on
RewriteRule ^([a-z0-9_-]+)/?$ /users.php?idu=$1 [NC]
The [NC] will make it case-insensitive, if you accept only lowercase username, remove the [NC] from the last.

Easy mod_rewrite - So I'll never have to think about it again

Not sure how you'll take this question but...
Whenever I try to make my URLs look pretty I always end up messing around for too long and it's simply not worth the trouble. But the end effect is good if it were a simple task.
So what I want to do is create a method which in the end would achive something like...
index.php?do=user&username=MyUsername //This becomes...
/user/MyUsername //...that
index.php?do=page&pagename=customPage //And this becomes...
/page/customPage //...that
index.php?do=lots&where=happens&this=here //This also becomes...
/lots/happens/here //...that
index.php?do=this&and=that&that=this&and=some&more=too //And yes...
/this/that/this/some/more //This becomes this
So then I just make a nice .htacess file that I'll never have to look at again. Everything will be better in the world because we have pretty URLs and my head didn't hurt in the making.
You can use a different approach of throwing the url in a single parameter, and parse it in your application.
So the apache rewrite rule would look like:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]
which will convert your urls as follows:
/user/MyUsername => index.php?q=/user/MyUsername
/page/customPage => index.php?q=/page/customPage
...
In your app, you then have a $_GET['q'] variable, which you can split by '/', and have your arguments in order. In PHP it would be something like:
$args = explode('/', $_GET['q']);
$args will be an array with 'user', 'MyUserName', etc.
This way you will not have to touch your .htaccess again, just your app logic.
For /user/MyUsername ==> index.php?do=user&username=MyUsername and /page/customPage ==>
index.php?do=page&pagename=customPage, you can use:
RewriteRule ^([A-Za-z0-9-]+)/([A-Za-z0-9-]+)$ index.php?do=$1&$1name=$2 [L]
But I don't think you can write a catch-all rule for /lots/happens/here and /this/that/this/some/more because you need to tell mod_rewrite how to translate the two urls.
Remember, mod_rewrite has to translate /lots/happens/here into index.php?do=lots&where=happens&this=here and not the other way around.
The best approach would be to delegate your application to generate the “pretty URLs” as well as parse and interpret them and to use mod_rewrite only to rewrite the requests to your application with a rule like this one:
RewriteRule %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
This rule will rewrite all requests that can not be mapped directly to an existing file to the index.php. The originally requested URL (more exact: the URL path plus query) is then available at $_SERVER['REQUEST_URI'].

How to implement URL pattern interpreter as used by Django and RoR in PHP

What's the best way to implement a URL interpreter / dispatcher, such as found in Django and RoR, in PHP?
It should be able to interpret a query string as follows:
/users/show/4 maps to
area = Users
action = show
Id = 4
/contents/list/20/10 maps to
area = Contents
action = list
Start = 20
Count = 10
/toggle/projects/10/active maps to
action = toggle
area = Projects
id = 10
field = active
Where the query string can be a specified GET / POST variable, or a string passed to the interpreter.
Edit: I'd prefer an implementation that does not use mod_rewrite.
Edit: This question is not about clean urls, but about interpreting a URL. Drupal uses mod_rewrite to redirect requests such as http://host/node/5 to http://host/?q=node/5. It then interprets the value of $_REQUEST['q']. I'm interested in the interpreting part.
If appropriate, you can use one that already exists in an MVC framework.
Check out projects such as -- in no particular order -- Zend Framework, CakePHP, Symfony, Code Ignitor, Kohana, Solar and Akelos.
have a look at the cakephp implementation as an example:
https://trac.cakephp.org/browser/trunk/cake/1.2.x.x/cake/dispatcher.php
https://trac.cakephp.org/browser/trunk/cake/1.2.x.x/cake/libs/router.php
You could also do something with mod_rewrite:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^(.*)$ $1 [L]
RewriteRule ^([a-z]{2})/(.*)$ $2?lang=$1 [QSA,L]
RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]
</IfModule>
This would catch urls like /en/foo /de/foo and pass them to index.php with GET parameters 'lang' amd 'url'. Something similar can be done for 'projects', 'actions' etc
Why specifically would you prefer not to use mod_rewrite? RoR uses mod_rewrite. I'm not sure how Django does this, but mod_php defaults to mapping URLs to files, so unless you create a system that writes a separate PHPfile for every possible URL (a maintenance nightmare), you'll need to use mod_rewrite for clean URLs.
What you are describing in your question should actually be the URL mapper part. For that, you could use a PEAR package called Net_URL_Mapper. For some information on how to use that class, have a look at this unit test.
The way that I do this is very simple.
I use wordpress' .htaccess file:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
What this .htaccess does is when something returns a 404, it sends the user to index.php.
In the above, /index.php is the "interpreter" for the URL.
In index.php, I have something along the lines of:
$req = $_SERVER['REQUEST_URI'];
$req = explode("/",$req);
The second line splits up the URL into sections based on "/". You can have
$area = $req['0'];
$action= $req['1'];
$id = $req['2'];
What I end up doing is:
function get_page($offset) {//offset is the chunk of URL we want to look at
$req = $_SERVER['REQUEST_URI'];
$req = explode("/",$req);
$page = $req[$offset];
return $page;
}
$area = get_page(0);
$action = get_page(1);
$id = get_page(2);
Hope this helps!
Just to second #Cheekysoft's suggestion, check out the Zend_Controller component of the Zend Framework. It is an implementation of the Front Controller pattern that can be used independently of the rest of the framework (assuming you would rather not use a complete MVC framework).
And obviously, CakePHP is the most similar to the RoR style.
I'm doing a PHP framework that does just what you are describing - taking what Django is doing, and bringing it to PHP, and here's how I'm solving this at the moment:
To get the nice and clean RESTful URLs that Django have (http://example.com/do/this/and/that/), you are unfortunately required to have mod_rewrite. But everything isn't as glum as it would seem, because you can achieve almost the same thing with a URI that contains the script's filename (http://example.com/index.php/do/this/and/that/). My mod_rewrite just forwards all calls to that format, so it's almost as usable as without the mod_rewrite trick.
To be truthful, I'm currently doing the latter method by GET (http://example.com/index.php?do/this/and/that/), and fixing stuff in case there are some genuine GET variables passed around. But my initial research says that using the direct slash after the filename should be even easier. You can dig it out with a certain $_SERVER superglobal index and doesn't require any Apache configuration. Can't remember the exact index off-hand, but you can trivially do a phpinfo() testpage to see how stuff look like under the hood.
Hope this helps.

Categories