Take IV: No, Really, This Is It!
It turned out be easier than I thought to get rid of [DPI]. The final set:
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteRule ^(.*)/(.*)$ $1_$2 [L]
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{REQUEST_URI} !\.php$
RewriteRule ^(.*)$ $1.php
Now is this acceptable? :-)
Take III: The (Not-So) Final Solution
Ok, after much testing and banging-head-on keyboard, I finally came up with something satisfactory. I'm definitely keeping this one in case I ever switch back to Apache for some reason.
The lightbulb went off in my head when I broke the problem up into two parts: replace / with _; then, add .php at the very end. Here's what I finally came up with:
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteRule ^(.*)/(.*)$ $1_$2 [DPI,N]
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{REQUEST_URI} !\.php$
RewriteRule ^(.*)$ $1.php [QSA]
Now, as I mentioned above, the biggest thing was to split the two operations up. Once I did that, everything became much easier. The two RewriteConds are really neat, because they allow you to access files by their usual names, even with all of this other rewriting going on.
I noticed that I was getting infinite path redirection (which Apache couldn't detect), like shop/basket_checkout/basket_checkout/basket/checkout; that's when I realized Apache itself was appending paths to the end of the replaced path! Thus [DPI] fixes that.
The extra RewriteCond in the second block should be obvious: it prevents infinite redirection (ie if foo.php is unavailable, no point in trying foo.php.php, etc).
Let us know how this works for you.
Take II: Nice try, but no dice
Whoops, even simpler than I first thought. Change [N] to [C] and you're good to go. As the documentation for [N] says:
Be careful not to create an infinite loop!
Which is exactly what happened here. ;-)
I nearly took down my test VM on the first try -- that was fun!
It does strange things, though, when the php file doesn't actually exist. I'll test some more.