Question

Situation

Hello, I'm confused as to PHP's expected/default behavior regarding extensionless PHP files, and/or URL requests that "go past" the actual file that (I want to) processes the request (i.e., PHP's default "fallback" actions before it resorts to completely 404-ing). Here's my situation:

My directory structure on my local server (running nginx 1.5.3 with very basic PHP 5.5.1 setup) looks like the following:

/index
/index.php
/other
/other.php
/rootdir/index
/rootdir/index.php
/rootdir/other
/rootdir/other.php

The contents of all eight files are the same:

<?php
echo $_SERVER['PHP_SELF'] . ', ' . $_SERVER['REQUEST_URI'];
?>

BUT, hitting the respective endpoint produces some strange (to me) results.

Research

GET /index.php
'/index.php, /index.php' # Makes sense...

GET /index.php/something_else
'/index.php, /index.php/something_else' # Also makes sense...

GET /index/something_else
'/index.php, /index/something_else' # Let's call this ANOMALY 1... (see below)

GET /something_else
'/index.php, /something_else' # ANOMALY 2

GET /other.php
'/other.php, /other.php' # Expected...

GET /other.php/something_else
'/index.php, /other.php/something_else' # ANOMALY 3

GET /rootdir/index.php
'/rootdir/index.php, /rootdir/index.php' # Expected...

GET /rootdir/index.php/something_else
'/index.php, /rootdir/index.php/something_else' # ANOMALY 4

GET /rootdir/other.php
'/rootdir/other.php, /rootdir/other.php' # Expected...

GET /rootdir/other.php/something_else
'/index.php, /rootdir/other.php/something_else' # ANOMALY 5

My understanding is that the server redirects to /index.php when it is unable to find what the user is looking for at the request URI; that much makes sense... what I don't understand is:

  1. Why it will do this despite my not having a dedicated 404 page set up (I didn't tell it to try /index.php before 404-ing; I want it to display a legit, non-custom 404 page if something isn't found and/or can't be processed. I figured it should display the default server 404 page when it couldn't find something... apparently that's not always the case...?)
  2. Why it doesn't try /rootdir/index.php when it can't find something within the /rootdir/ subdirectory.

Questions

  1. Would somebody be able to shed some light on what PHP's logic is (or maybe it's nginx's doing; I haven't been able to figure that out yet) with regards to addresses that are not found? Why am I seeing what I am seeing? (Specifically with respect to Anomalies #4 and #5. I expected it to use /rootdir/index.php for handling it's "404," or I expected a real 404 page; the fallback to /index.php was unexpected.)

  2. As a direct corollary (corollical?) question, how can I go about simulating extensionless PHP files that will handle hits that occur "below" them (e.g. in the Anomaly #1 case; that's actually exactly what I want, though it wasn't quite what I expected) without relying on .htaccess, mod_rewriting, or redirects? Or is that a silly question? :-)

References

I'm trying to roll out a custom implementation for handling requests like /some_dir/index.php/fake_subdir and /some_other_dir/index.php/fake_subdir (i.e., different "fallback handlers") without relying on Apache, but the logic behind PHP's (or nginx's?) default fallback behavior is eluding me. These pages are primarily where this question stems from:

Was it helpful?

Solution 2

Well, I figured out what was going on: I had a misconfigured nginx server.

Marc B's answer, though related to Apache, prompted me to check out my PATH_INFO value — lo and behold, PATH_INFO was nonexistent, even for requests like GET /other.php/something_else.

Some more searching turned up the answer. Using nginx's fastcgi_split_path_info to split the URI into a) the path to the script and b) the path that appears following the script name will fail if done after a use of the try_files directive, due to its rewriting the URI to point to a file on the disk. This obliterates any possibility of obtaining a PATH_INFO string.

As a solution, I did three things (some of these are probably redundant, but I did them all, just in case):

  1. In a server's location block, make use of fastcgi_split_path_info before setting up PATH_INFO via

    fastcgi_param PATH_INFO $fastcgi_path_info;
    

    ...and do both before making use of try_files.

  2. Store $fastcgi_path_info as a local variable in case it ever changes later (e.g. with set $path_info $fastcgi_path_info;)

  3. When using try_files, don't use $uri... use $fastcgi_script_name. (Pretty sure this was my biggest mistake.)

Sample Configuration

server {

    # ... *snip* ...

    location ~ \.php {
        # NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
        fastcgi_split_path_info ^(.+\.php)(/.*)$;

        include fastcgi_params;

        fastcgi_index index.php;
        fastcgi_intercept_errors on;
        fastcgi_pass 127.0.0.1:9000;

        try_files $fastcgi_script_name =404;
    }

    # ... *snip* ...

}

Where fastcgi_params contains:

set            $path_info         $fastcgi_path_info;
fastcgi_param  PATH_INFO          $path_info;
fastcgi_param  PATH_TRANSLATED    $document_root$path_info;

fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;

# ... *snip* ...

Reference

The following links explain the problem pretty well and provide an alternate nginx configuration:

OTHER TIPS

 GET /other.php/something_else

This is called PATH_INFO in Apache. As Apache's scanning down a URL's "directories", it will return the first file (or execute the first script) that is actually a file/script, e.g.

GET /foo/bar/baz/index.php/a/b/c/
     ^--dir
         ^--dir
             ^---dir
                 ^---script
                         ^^^^^^^--- path_info

In real terms, the real request is for

GET /foo/bar/baz/index.php

and then Apache will take the unused trailing portion of the directory structure of the URL and turn it into path info. In PHP, you'll have

$_SERVER['REQUEST_URI'] = '/foo/bar/baz/index.php';
$_SERVER['PATH_INFO'] = 'a/b/c';
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top