PHP Server API Differences

A useful table: Apache and IIS PHP $_SERVER Superglobal Comparision.

If you’ve written code that’s had to run under more than one web server environment (even PHP as CGI vs. PHP as module under Apache), you’ve probably run into headaches like $_SERVER['SCRIPT_FILENAME'] being unavailable – Server APIs (SAPIs) vary.

Ran into the link table a few days ago and it illustrates potential problems nicely (I assume that the IIS column was created from PHP running as an ISS “module”, not as a CGI executable). Perhaps the two most critical are $_SERVER['SCRIPT_FILENAME'] and $_SERVER['REQUEST_URI'], both of which you’re likely to want if you’re doing anything “frameworky” but are are generally only available to PHP running as an Apache module.

The link doesn’t quite raise the full story though. As I remember, with PHP as a CGI under Apache, $_SERVER['SCRIPT_FILENAME'] is the path to the CGI executable, not the script it is executing – that relates to a request for a PHP constant called __MAIN__ to identify the script where execution began.

Otherwise the various HTTP_ variables in $_SERVER array could vary per-request – these tell you information about the incoming HTTP request. There’s also the informational X_HTTP_ that sometimes show up, such as X_HTTP_FORWARDED_FOR – a convention amongst proxy servers. None of these you should trust by the way.

There’s further fun to be had with Apache 2 and $_SERVER['PATH_TRANSLATED'] – see here. The PHP function php_sapi_name() is useful for determining which environment your scripts are running under. Side note here – believe it returns “apache” when PHP is running as an module under Apache 1.x and 2.x.

And if you were thinking – “Who cares? I’ll just rely on good old $_SERVER['PHP_SELF'] right?” then you should read this – searching Google for “form tutorial PHP_SELF” is the stuff of nightmares.

Some work has been done to provide SAPI-specific functionality to help “understand” parts of the environment (e.g. Apache and NSAPI allow you to get more information about incoming requests) but, IMO, the bottom line is there’s a minefield if you’re writing code that should run under more than one SAPI.

Right now don’t have any particular answers or conclusions. PHP is a cross platform technology, not platform independent. The #1 problem, to my mind, is there doesn’t seem to be any definitive documentation of where the differences lie. There’s a bunch of tips attached to the manual on predefined variables but most of these are of the “this worked for me” kind. In a perfect world all of this might be hidden behind an API that magically works it all out, but I’m not aware of anything that offers this.

Thoughts / opinions / links etc.?

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://www.phpism.net Maarten Manders

    It gets even funnier when trying to determine a script’s URI when using mod_rewrite. PHP4, PHP5 as well as Apache1 and 2 handle rewritten URLs in different ways iirc. :-)

  • http://www.phppatterns.com HarryF

    It gets even funnier when trying to determine a script’s URI when using mod_rewrite. PHP4, PHP5 as well as Apache1 and 2 handle rewritten URLs in different ways iirc.

    Good point – have had that nightmare before as well.

  • http://www.phpism.net Maarten Manders

    I’ve written a Class with one Uri recognition method to rule them all. It needs a little bit more testing. I guess I could release it under GPL and look what feedback/contributions I get. As soon as I find some time to review the code.

  • Pingback: Full(o)bloG » Blog Archive » PHP e server

  • http://www.dotcomwebdev.com chris ward

    PHP_SELF is the method i’ve been using for requesting the URI string.

    What perplexes me more, is when I try to figure out the visitor’s true IP address, and not their proxy.

    A quick tip is to make a call to php_info()! It’s a good way to see the initial results of all the server variables at a glance.

    Even more handy when you’re using mod_rewrite and things start to get a bit confusing!

  • VodkaFish

    That chart may or may not be useful. IIS 5.1 is for Windows XP Professional, not really a “real world” example IMO.

    REQUEST_URI
    SCRIPT_FILENAME
    are two things that stand out to me that definitely work in IIS6. I’m not sure about the rest of the list.

  • Pingback: Kymnto Blog » La variabile superglobal $_SERVER in IIS e Apache

  • foo

    Note that this has very little to do with PHP. $_SERVER, as its name implies, tells you the variables set by the web server. The differences all lie in the different web server implementations out there.

  • http://www.dtra.sonnexh.com dtra

    sorry if this is stupid, but what about $_server['script_name']?

  • http://www.phppatterns.com HarryF

    REQUEST_URI
    SCRIPT_FILENAME
    are two things that stand out to me that definitely work in IIS6. I’m not sure about the rest of the list.

    That’s interesting to hear. I guess more detail is required that that table provides.

    Note that this has very little to do with PHP. $_SERVER, as its name implies, tells you the variables set by the web server. The differences all lie in the different web server implementations out there.

    Very true.

    But what’s the approach for those trying to develop scripts which run “anywhere” and may not have time / resources to be able to test against all possible SAPI’s?

    Two particular things that are helpful to have – the path / filename of the script where execution began, available in a global form (i.e. not __FILE__) and the full (or at least relative to web root) requested URL. If these were available in a form that was invariant under any SAPI, life would be easier for people doing “frameworky” stuff.

    Other SAPI independent derivations from there would be nice to have (e.g. if the request is “http://example.com/index.php/foo/bar?x=y” that you can easily get access to “http://example.com/index.php” “/foo/bar?x=y”) but these can also be resolve in PHP given the first two.

    what about $_server[’script_name’]

    Again with PHP as a CGI, this gets populated with the name of the CGI binary, not the file being executed by the binary.

  • Gaetano Giunta

    imho, the best (only?) solution to this age-old problem would be to put up a cheat sheet with a complete list of all global vars (not only $_SERVER) for all combinations of webserver/php api, highlighting those values that differ from setup to setup.
    I have a preliminary table (in excel format) that I can mail to anyone interested.
    Contact g i u n t a . g a e t a n o at sea-aeroportimilano dot it
    btw: koivi.com is down at the moment…

  • http://www.phppatterns.com HarryF

    Hi Gaetano – if you’re willing to publish that list, a good place might be on this PHP wiki: http://wiki.cc/php/Main_Page e.g. http://wiki.cc/php/SAPI

  • Demian Turner

    this method, albeit procedural, assigns a correct value to $_SERVER['PHP_SELF'] regardless of apache 1/2, cgi/module or IIS. You will need to remove logic for the frontScriptName $conf element.


    /**
    * Resolves PHP_SELF var depending on implementation, ie apache, iis, cgi, etc.
    *
    * @abstract
    */
    function resolveServerVars($conf = null)
    {
    // it's apache
    if (!empty($_SERVER['PHP_SELF']) && !empty($_SERVER['REQUEST_URI'])) {

    // however we're running from cgi, so populate PHP_SELF info from REQUEST_URI
    if (strpos(php_sapi_name(), 'cgi') !== false) {
    $_SERVER['PHP_SELF'] = $_SERVER['REQUEST_URI'];

    // a ? is part of $conf['site']['frontScriptName'] and REQUEST_URI has more info
    } elseif ((strlen($_SERVER['REQUEST_URI']) > strlen($_SERVER['PHP_SELF'])
    && strstr($_SERVER['REQUEST_URI'], '?')
    && !isset($conf['setup']))) {
    $_SERVER['PHP_SELF'] = $_SERVER['REQUEST_URI'];

    // we don't want to have index.php in our url, so REQUEST_URI as more info
    } elseif ($conf['site']['frontScriptName'] == false) {
    $_SERVER['PHP_SELF'] = $_SERVER['REQUEST_URI'];
    } else {
    // do nothing, PHP_SELF is valid
    }

    // it's IIS
    } else {
    $frontScriptName = is_null($conf) ? 'index.php' : $conf['site']['frontScriptName'];
    if (substr($_SERVER['SCRIPT_NAME'], -1, 1) != substr($frontScriptName, -1, 1)) {
    $_SERVER['PHP_SELF'] = $_SERVER['SCRIPT_NAME'] . '?' . @$_SERVER['QUERY_STRING'];
    } else {
    $_SERVER['PHP_SELF'] = $_SERVER['SCRIPT_NAME'] . @$_SERVER['QUERY_STRING'];
    }

    }
    }

  • Pingback: SitePoint Blogs » eZ components – new competition for Zend PHP Framework

  • hoodia gordonii

    Hello, i am glad to read the whole content of this blog and am very excited and happy to say that the webmaster has done a very good job here to put all the information content and information at one place, i will must refer this information with reference on my website i.e http://www.gordoniihoodia.net