I set up a test of your code: a main page with nothing but require()s, and a bunch of includes, one of which with your code verbatim and the others all empty. When I only had the include with your code required by the parent, the code worked, but when I threw a bunch of empty includes before and after the 'real' include, it started reporting paths to other includes and not the real parent.
Based on my limited understanding of what your code does, it looks like the problem is this line:
PHP Code:
return $aIncluded[$iKey - 1];
My test setup right now looks like this (the common portion of the test files' path was replaced here with $path; it's a literal string in the actual code):
main page (the one being requested via http):
PHP Code:
<?php
require_once("$path/aa/b.php");
require_once("$path/aa/bb.php");
require_once("$path/aa/a.php");
require_once("$path/aa/bbb.php");
?>
b.php, bb.php and bbb.php are all identical:
PHP Code:
<?php
//nothing to see here...
?>
a.php is a copy-paste of your code.
The result: Included parent: $path/aa/bb.php
It seems like this should be a recursive call somehow, but I'm not sure how to make it work. I tried this on a whim, but it failed as well:
PHP Code:
function getIncludedParent($offset = 0)
{
$aIncluded = get_included_files();
$iKey = array_search(__FILE__, $aIncluded);
if(!array_key_exists($iKey - (1 + $offset), $aIncluded))
{
return __FILE__;
}
return getIncludedParent($offset + 1);
}
echo 'Included parent: ' . getIncludedParent();
If I move around the requires, your code consistently returns the require immediately before the one with the code in it, unless it's first in which case it returns the correct parent.
I did a little playing around, and it seems like the parent is always at index 0. Are there cases in which this would not be the case? I don't have time right now to try anything too complex, but adding/moving/removing things seemed to have no effect. The include running the code always sees itself as last. It looks like it stores the files in the order they are called, so first the parent, then the first include, then that include's first include, etc. If this is the case, can't I just do something like:
PHP Code:
$x = get_included_files();
$parent = $x[0];
I assume there's a reason this wouldn't always work, which is why you tried a more complex method, but might it be easier to check for those exceptions and use this method in all other cases? You can't get much more efficient than that (unless you can somehow eliminate the get_included_files call, of course, but that seems to be the function that makes any of this even possible...).
Oh, by the way, in case anyone is still unclear: we're not trying to interrupt calls to move_uploaded_file. We've blocked uploads of PHP containing that function using RATS and our own deployment software; instead, we plan to provide an include that our content providers can call instead. It will basically provide a secure wrapper for move_uploaded_file; it will perform all the security checks on the data that we feel are necessary, since we don't trust many of our content providers to write secure PHP. We also want to be able to monitor and control uploads more tightly as issues arise in the future, so routing them all through a piece of code we control would be quite handy.