117

I am building a PHP application in CodeIgniter. CodeIgniter sends all requests to the main controller: index.php. However, I don't like to see index.php in the URI. For example, http://www.example.com/faq/whatever will route to http://www.example.com/index.php/faq/whatever. I need a reliable way for a script to know what it's address is, so it will know what to do with the navigation. I've used mod_rewrite, as per CodeIgniter documentation.

The rule is as follows:

RewriteEngine on
RewriteCond $1 !^(images|inc|favicon\.ico|index\.php|robots\.txt)
RewriteRule ^(.*)$ /index.php/$1 [L] 

Normally, I would just check php_self, but in this case it's always index.php. I can get it from REQUEST_URI, PATH_INFO, etc., but I'm trying to decide which will be most reliable. Does anyone know (or know where to find) the real difference between PHP_SELF, PATH_INFO, SCRIPT_NAME, and REQUEST_URI? Thanks for your help!

Note: I've had to add spaces, as SO sees the underscore, and makes it italic for some reason.

Updated: Fixed the spaces.

imlouisrussell
  • 936
  • 11
  • 30
Eli
  • 97,462
  • 20
  • 76
  • 81

10 Answers10

252

Some practical examples of the differences between these variables:
Example 1. PHP_SELF is different from SCRIPT_NAME only when requested url is in form:
http://example.com/test.php/foo/bar

[PHP_SELF] => /test.php/foo/bar
[SCRIPT_NAME] => /test.php

(this seems to be the only case when PATH_INFO contains sensible information [PATH_INFO] => /foo/bar) Note: this used to be different in some older PHP versions (<= 5.0 ?).

Example 2. REQUEST_URI is different from SCRIPT_NAME when a non-empty query string is entered:
http://example.com/test.php?foo=bar

[SCRIPT_NAME] => /test.php
[REQUEST_URI] => /test.php?foo=bar

Example 3. REQUEST_URI is different from SCRIPT_NAME when server-side redirecton is in effect (for example mod_rewrite on apache):

http://example.com/test.php

[REQUEST_URI] => /test.php
[SCRIPT_NAME] => /test2.php

Example 4. REQUEST_URI is different from SCRIPT_NAME when handling HTTP errors with scripts.
Using apache directive ErrorDocument 404 /404error.php
http://example.com/test.php

[REQUEST_URI] => /test.php
[SCRIPT_NAME] => /404error.php

On IIS server using custom error pages
http://example.com/test.php

[SCRIPT_NAME] => /404error.php
[REQUEST_URI] => /404error.php?404;http://example.com/test.php
Nicholas Franceschina
  • 6,009
  • 6
  • 36
  • 51
Odin
  • 3,278
  • 2
  • 19
  • 19
  • 30
    +1, "An example is not a way to learn, it's the only way to learn." - I always have to recheck this stuff, very nice research on the 404 errors. =) – Alix Axel May 09 '10 at 17:44
  • 20
    +1: 1st time in my life I understood the difference. They should update PHP documentaion with your answer – Marco Demaio Mar 23 '11 at 14:40
  • Example1: [SCRIPT_NAME] => /test.php/ There should be no "/" at the end: Example1: [SCRIPT_NAME] => /test.php Anyway that's what I see in PHP 5.3.6 . Nice examples. – Dawid Ohia Mar 21 '12 at 08:25
  • You are correct JohnM2, i have now checked on PHP 5.4 and the result for URL /pinfo.php/first/second?third=fourth is as follows: QUERY_STRING => third=fourth REQUEST_URI => /pinfo.php/first/second?third=fourth SCRIPT_NAME => /pinfo.php PATH_INFO => /first/second – Odin Apr 10 '12 at 12:24
  • I've tested this on 5.2.17 too and there's no `/` at the end of the `SCRIPT_NAME`. This seems to be consistent in PHP 5.2-5.4 then, considering editing the answer to reflect that. – Fabrício Matté Sep 07 '12 at 02:11
  • You missed off `REQUEST_URI` vs `PHP_SELF` here they differ if you request http://example.com/ : `[REQUEST_URI] => /` and `[PHP_SELF] => /index.php` – icc97 Dec 19 '13 at 15:37
  • This is very helpful especially the Example 4 error handling part. Recently I find that some programmers are using `REQUEST_URI` as conditions, then provide different header/footer respectively. It works in most cases but will have problems with 404/403 pages. I think a better way is to use `SCRIPT_NAME` to get the REAL requested page on Apache. – ChandlerQ Oct 11 '14 at 06:07
  • I know that it is not part of the question, but it is useful to mention that `[REQUEST_URI]` is defined before URL rewriting, including mod_dir rewrite as pointed out by @icc97. `[PHP_SELF] = [SCRIPT_NAME] + [PATH_INFO]` is defined after mod_dir and mod_rewrite, but before aliases are resolved. Aliases can change the complete URL path (which PHP names `[PHP_SELF]`) to obtain the `[SCRIPT_FILENAME]`, which is the actual script on the file system. To be slightly more complete, let's add that the script can include other files and `__FILE__` is the current file. –  Dec 19 '15 at 06:17
  • Example 1 also says that `[REQUEST_URI]` is different from `[SCRIPT_NAME]` when `[PATH_INFO]` is not empty. It was understood, but it was not explicitly written. Example `[REQUEST_URI] => /test.php/foo.bar, [SCRIPT_NAME] => /test.php` –  Dec 19 '15 at 18:38
  • worth noting this apache bug: https://bz.apache.org/bugzilla/show_bug.cgi?id=40102 – Brad Kent Dec 21 '18 at 03:49
  • Also worth putting in an example of domain traversal, as they differ in this way as well. For instance in `http://example.com/something/../test.php`, the REQUEST_URI will be `/something/../test.php` and the SCRIPT_NAME will be `/test.php`. :) – Jamie Robinson Apr 26 '19 at 18:44
54

The PHP documentation can tell you the difference:

'PHP_SELF'

The filename of the currently executing script, relative to the document root. For instance, $_SERVER['PHP_SELF'] in a script at the address http://example.com/test.php/foo.bar would be /test.php/foo.bar. The __FILE__ constant contains the full path and filename of the current (i.e. included) file. If PHP is running as a command-line processor this variable contains the script name since PHP 4.3.0. Previously it was not available.

'SCRIPT_NAME'

Contains the current script's path. This is useful for pages which need to point to themselves. The __FILE__ constant contains the full path and filename of the current (i.e. included) file.

'REQUEST_URI'

The URI which was given in order to access this page; for instance, '/index.html'.

'PATH_INFO'

Contains any client-provided pathname information trailing the actual script filename but preceding the query string, if available. For instance, if the current script was accessed via the URI http://www.example.com/php/path_info.php/some/stuff?foo=bar, then $_SERVER['PATH_INFO'] would contain /some/stuff.

Community
  • 1
  • 1
Paige Ruten
  • 172,675
  • 36
  • 177
  • 197
  • 3
    This is most likely not about the PHP documentation but the CGI :) And there PATH_INFO is documented: http://tools.ietf.org/html/rfc3875#section-4 But there are some known issues that Apache and nginx are not always giving this variable. – SimonSimCity Feb 17 '12 at 08:53
  • 1
    Odin's answer below adds useful explanations that are complemented with examples. I find it hard to understand what these variables represent in a general context with a path_info, a query string, some redirection, some aliases, on different operating systems, from CLI vs SERVER, etc. –  Dec 15 '15 at 22:35
  • 7
    -1 Just as an explanation as to why I downvoted: the entire reason I came to this post is because the documentation is not clear. Odin's answer below provides a clear explanation of the differences between these variables. I feel like it's an insufficient answer to just copy and paste easily found yet also insufficient documentation. I believe most people would have had to already visit the documentation to even know about the list of elements in the $_SERVER variable mentioned above. – dallin Aug 14 '18 at 23:02
27

PATH_INFO is only available when using htaccess like this:

Example 1

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !^(favicon\.ico|robots\.txt)
RewriteRule ^(.*)$ index.php/$1 [L]

Remains the same

[SCRIPT_NAME] => /index.php

Root

http://domain.com/

[PHP_SELF]     => /index.php
[PATH_INFO] IS NOT AVAILABLE (fallback to REQUEST_URI in your script)
[REQUEST_URI]  => /
[QUERY_STRING] => 

Path

http://domain.com/test

[PHP_SELF]     => /index.php/test
[PATH_INFO]    => /test
[REQUEST_URI]  => /test
[QUERY_STRING] => 

Query String

http://domain.com/test?123

[PHP_SELF]     => /index.php/test
[PATH_INFO]    => /test
[REQUEST_URI]  => /test?123
[QUERY_STRING] => 123

Example 2

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !^(favicon\.ico|robots\.txt)
RewriteRule ^(.*)$ index.php?url=$1 [L,QSA]

Remains the same

[SCRIPT_NAME]  => /index.php
[PHP_SELF]     => /index.php
[PATH_INFO] IS NOT AVAILABLE (fallback to REQUEST_URI in your script)

Root

http://domain.com/

[REQUEST_URI]  => /
[QUERY_STRING] => 

Path

http://domain.com/test

[REQUEST_URI]  => /test
[QUERY_STRING] => url=test

Query String

http://domain.com/test?123

[REQUEST_URI]  => /test?123
[QUERY_STRING] => url=test&123

Example 3

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !^(favicon\.ico|robots\.txt)
RewriteRule ^(([a-z]{2})|(([a-z]{2})/)?(.*))$ index.php/$5 [NC,L,E=LANGUAGE:$2$4]

or

RewriteRule ^([a-z]{2})(/(.*))?$ $3 [NC,L,E=LANGUAGE:$1]

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !^(favicon\.ico|robots\.txt)
RewriteRule ^(.*)$ index.php/$1 [L]

Remains the same

[SCRIPT_NAME] => /index.php

Root

http://domain.com/

[PHP_SELF]          => /index.php
[PATH_INFO] IS NOT AVAILABLE (fallback to REQUEST_URI in your script)
[REQUEST_URI]       => /
[QUERY_STRING]      => 
[REDIRECT_LANGUAGE] IS NOT AVAILABLE

Path

http://domain.com/test

[PHP_SELF]          => /index.php/test
[PATH_INFO]         => /test
[REQUEST_URI]       => /test
[QUERY_STRING]      => 
[REDIRECT_LANGUAGE] => 

Language

http://domain.com/en

[PHP_SELF]          => /index.php/
[PATH_INFO]         => /
[REQUEST_URI]       => /en
[QUERY_STRING]      => 
[REDIRECT_LANGUAGE] => en

Language path

http://domain.com/en/test

[PHP_SELF]          => /index.php/test
[PATH_INFO]         => /test
[REQUEST_URI]       => /en/test
[REDIRECT_LANGUAGE] => en

Language Query string

http://domain.com/en/test?123

[PHP_SELF]          => /index.php/test
[PATH_INFO]         => /test
[REQUEST_URI]       => /en/test?123
[QUERY_STRING]      => 123
[REDIRECT_LANGUAGE] => en
icc97
  • 11,395
  • 8
  • 76
  • 90
Mike
  • 1,883
  • 3
  • 23
  • 17
  • 1
    This answer is written in a way that suggests that only url rewritting can create a path_info, but, of course, the path info can be entered directly in the original URL. –  Dec 25 '15 at 02:46
15

PHP Paths

    $_SERVER['REQUEST_URI']    = Web path, requested URI
    $_SERVER['PHP_SELF']    = Web path, requested file + path info
    $_SERVER['SCRIPT_NAME']    = Web path, requested file
    $_SERVER['SCRIPT_FILENAME']   = File path, requested file
    __FILE__    = File path, current file

Where

  • File path is a system file path like /var/www/index.php, after alias resolution
  • Web path is a server document path like /index.php from http://foo.com/index.php, and may not even match any file
  • Current file means the included script file, not any script that includes it
  • Requested file means the includer script file, not the included one
  • URI is the HTTP request like /index.php?foo=bar, before any URL rewriting
  • Path info is any extra Apache data located after the script name but before the query string

Order of Operation

  1. Client sends server an HTTP request REQUEST_URI
  2. Server performs any URL rewriting from .htaccess files, etc. to get PHP_SELF
  3. Server separates PHP_SELF into SCRIPT_FILENAME + PATH_INFO
  4. Server performs alias resolution and converts the entire url path to a system file path to get SCRIPT_FILENAME
  5. Resulting script file may include others, where __FILE__ refers to the path to the current file
Beejor
  • 8,606
  • 1
  • 41
  • 31
  • This is good. Here are my comments. First, both $_SERVER['SCRIPT_NAME'] and $_SERVER['SCRIPT_FILENAME'] are script name, except that the later is after aliases are executed. Second, $_SERVER['PHP_SELF'] is not the script, but the script + the path info. Again, $_SERVER['SCRIPT_NAME'] is the script (before aliases). Finally, it is useful to know at what stage, after or before rewrite-rules, after or before aliases, these variables are defined. See my answer. –  Dec 16 '15 at 04:47
  • @Dominic108 I revised my answer based on your suggestions, tidied things up a bit, and added an Order of Operation section. Let me know what you think. Thanks! – Beejor Dec 17 '15 at 02:36
  • In your order, you have to swap `$_SERVER['SCRIPT_NAME']` and  `$_SERVER['PHP_SELF']` , because mod_rewrite create the entire path, which is `$_SERVER['PHP_SELF']` . The separation occurs next. Note that aliases also consider the entire path to define the script filename, but the separation that defined script_name and path_info already occurred, so they will not be affected. –  Dec 17 '15 at 07:46
  • @Dominic108 I revised my answer again. For some reason your edit proposal was rejected, though as far as I know, you are correct that two of my items were out of order. I'm not as familiar with aliases, so I'm relying on your expertise for that part. Thanks again! – Beejor Jan 18 '16 at 01:44
5

You may want to look into the URI Class and make use of $this->uri->uri_string()

Returns a string with the complete URI.

For example, if this is your full URL:

http://example.com/index.php/news/local/345

The function would return this:

/news/local/345

Or you could make use of the segments to drill down specific areas without having to come up with parsing/regex values

Adam
  • 7,800
  • 2
  • 25
  • 24
  • Thank you - this is a good idea, but I am using these in a pre-system hook that will need to run before the controller is up and running. – Eli Nov 11 '08 at 06:26
4

Personally I use the $REQUEST_URI as it references the URI entered and not the location on the server's disc.

Gottlieb Notschnabel
  • 9,408
  • 18
  • 74
  • 116
Xenph Yan
  • 83,019
  • 16
  • 48
  • 55
4

There is very little to add to Odin's answer. I just felt to provide a complete example from the HTTP request to the actual file on the file system to illustrate the effects of URL rewriting and aliases. On the file system the script /var/www/test/php/script.php is

<?php
include ("script_included.php")
?>

where /var/www/test/php/script_included.php is

<?php
echo "REQUEST_URI: " .  $_SERVER['REQUEST_URI'] . "<br>"; 
echo "PHP_SELF: " .  $_SERVER['PHP_SELF'] . "<br>";
echo "QUERY_STRING: " .  $_SERVER['QUERY_STRING'] . "<br>";
echo "SCRIPT_NAME: " .  $_SERVER['SCRIPT_NAME'] . "<br>";
echo "PATH_INFO: " .  $_SERVER['PATH_INFO'] . "<br>";
echo "SCRIPT_FILENAME: " . $_SERVER['SCRIPT_FILENAME'] . "<br>";
echo "__FILE__ : " . __FILE__ . "<br>";  
?>

and /var/www/test/.htaccess is

RewriteEngine On
RewriteRule before_rewrite/script.php/path/(.*) after_rewrite/script.php/path/$1 

and the Apache configuration file includes the alias

Alias /test/after_rewrite/ /var/www/test/php/

and the http request is

www.example.com/test/before_rewrite/script.php/path/info?q=helloword

The output will be

REQUEST_URI: /test/before_rewrite/script.php/path/info?q=helloword
PHP_SELF: /test/after_rewrite/script.php/path/info
QUERY_STRING: q=helloword
SCRIPT_NAME: /test/after_rewrite/script.php
PATH_INFO: /path/info
SCRIPT_FILENAME: /var/www/test/php/script.php
__FILE__ : /var/www/test/php/script_included.php

The following always holds

PHP_SELF = SCRIPT_NAME + PATH_INFO = full url path between domain and query string. 

If there is no mod_rewrite, mod_dir, ErrorDocument rewrite or any form of URL rewriting, we also have

REQUEST_URI = PHP_SELF + ? + QUERY_STRING 

The aliases affect the system file paths SCRIPT_FILENAME and __FILE__, not the URL paths, which are defined before - see exceptions below. Aliases might use the entire URL path, including PATH_INFO. There could be no connection at all between SCRIPT_NAME and SCRIPT_FILENAME .

It is not totally exact that aliases are not resolved at the time the URL path [PHP_SELF] = [SCRIPT_NAME] + [PATH_INFO] is defined, because aliases are considered to search the file system and we know from example 4 in Odin's answer that the file system is searched to determine if the file exists, but this is only relevant when the file is not found. Similarly, mod_dir calls mod_alias to search the file system, but this is only relevant if you have an alias such as Alias \index.php \var\www\index.php and the request uri is a directory.

  • Hi Dominic108, thanks for the revision. I do think it's useful to include the rewrite info. To me it was implied, but to others it may not be that intuitive. – Beejor Dec 17 '15 at 01:57
1

If you ever forget which variables do what, you can write a little script that uses phpinfo() and call it from a URL with a query string. Since server software installations present the variables that PHP returns it's always a good idea to check the machine's output in case rewrites at the server config file are causing different results than expected. Save it as something like _inf0.php:

<?php
    $my_ip = '0.0.0.0';

   if($_SERVER['REMOTE_ADDR']==$my_ip){
     phpinfo();
   } else {
     //something
   }

Then you would call /_inf0.php?q=500

AbsoluteƵERØ
  • 7,816
  • 2
  • 24
  • 35
0

easy peasy simple answer:

assume scheme://host/path/script.php
after script.php there will be two possiblitiy:
1- query string like scheme://host/path/script.php?qs=a
2- more path info like scheme://host/path/script.php/more/path


now we have a very simple rules:

property what is included e.g.: http://localhost/dashboard/paln5/st1.php/a/b?c=d
REQUEST_URI everything afterward is included /dashboard/paln5/st1.php/a/b?c=d
PHP_SELF just more path included /dashboard/paln5/st1.php/a/b
SCRIPT_NAME nothing afterward is included /dashboard/paln5/st1.php
-1

Backup a second, you've taken the wrong approach to begin with. Why not just do this

RewriteEngine on
RewriteCond $1 !^(images|inc|favicon\.ico|index\.php|robots\.txt)
RewriteRule ^(.*)$ /index.php?url=$1 [L]

instead? Then grab it with $_GET['url'];

Kate Gregory
  • 18,808
  • 8
  • 56
  • 85
  • Why reinvent the wheel? This data is much more easily accessed! – Kenneth Oct 10 '11 at 00:22
  • And there is additional complexity if the original request is expected to have a query string. In its current state, the above code will simply overwrite the query string. If you merge query strings (`QSA` flag) then query string params can potentially be overwritten (for instance, if you needed a `url` param on the initial request) or worse, be vulnerable to XSS attacks. – MrWhite Jan 23 '17 at 13:25