FAQ
Hi all!

I am developing a small PHP extension and I ATM can't figure out how to get
to $_SERVER['SCRIPT_FILENAME'] content while in PHP_RINIT or PHP_RSHUTDOWN
function. Can someone please hint me with this one?

Thanks,
b.

Search Discussions

  • Johannes Schlüter at Aug 9, 2010 at 12:58 pm

    On Mon, 2010-08-09 at 13:32 +0200, Bostjan Skufca wrote:
    Hi all!

    I am developing a small PHP extension and I ATM can't figure out how to get
    to $_SERVER['SCRIPT_FILENAME'] content while in PHP_RINIT or PHP_RSHUTDOWN
    function. Can someone please hint me with this one?
    The simple answer is: You can't. The closest you can get is a char *
    SG(request_data).request_uri

    The more complex answer is: It is only stored for being used in
    $_SERVER. So you can read it from there. The engine has a "just in time"
    mode to avoid creating _SERVER when not needed, we need it so we first
    have to trigger the creation of that variable, then one can search
    through the global symbol table and then search in there:


    zval **server, **tmp;
    zend_auto_global_disable_jit("_SERVER", sizeof("_SERVER")-1 TSRMLS_CC);
    if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void**)&server) == FAILURE || Z_TYPE_PP(server) != IS_ARRAY) {
    /* error handling */
    return;
    }
    if (zend_hash_find(Z_ARRVAL_PP(server), "SCRIPT_FILENAME", sizeof("SCRIPT_FILENAME"), (void**)&tmp) == FAILURE || Z_TYPE_PP(tmp) != IS_STRING) {
    /* error handling */
    return;
    }
    /* work with tmp */

    As this accesses the userspace $_SERVER you should do this during RINIT,
    else the value might be wrong. While this adds some cost to every
    request (populating $_SERVER plus two hash lookups), it can be optimized
    a tiny bit by precalculating the hash and using zend_hash_quick_find().

    The code above is untested.

    johannes
  • Bostjan Skufca at Aug 9, 2010 at 1:49 pm
    I don't think I made myself exactly clear.

    There are 2 use cases I would like to consider:
    1. CLI: by using "php /full/path/to/script.php" or "cd /full/path/to && php
    script.php"
    2. Apache DSO: going to http://www.domain.com/script.php which in turn
    'executes' script "/full/path/to/script.php"

    In both cases, in RINIT I would like to get full path (real path, if
    possible) of this /full/path/to/script.php. Is this possible or is your way
    the only viable solution (with performance penalty etc)?

    (The $_SERVER['SCRIPT_FILENAME'] was just a method to explain what I would
    like to have, albeit obviously a poor one:)

    Thanks again,
    b.


    2010/8/9 Johannes Schlüter <johannes@php.net>
    On Mon, 2010-08-09 at 13:32 +0200, Bostjan Skufca wrote:
    Hi all!

    I am developing a small PHP extension and I ATM can't figure out how to get
    to $_SERVER['SCRIPT_FILENAME'] content while in PHP_RINIT or
    PHP_RSHUTDOWN
    function. Can someone please hint me with this one?
    The simple answer is: You can't. The closest you can get is a char *
    SG(request_data).request_uri

    The more complex answer is: It is only stored for being used in
    $_SERVER. So you can read it from there. The engine has a "just in time"
    mode to avoid creating _SERVER when not needed, we need it so we first
    have to trigger the creation of that variable, then one can search
    through the global symbol table and then search in there:


    zval **server, **tmp;
    zend_auto_global_disable_jit("_SERVER", sizeof("_SERVER")-1 TSRMLS_CC);
    if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"),
    (void**)&server) == FAILURE || Z_TYPE_PP(server) != IS_ARRAY) {
    /* error handling */
    return;
    }
    if (zend_hash_find(Z_ARRVAL_PP(server), "SCRIPT_FILENAME",
    sizeof("SCRIPT_FILENAME"), (void**)&tmp) == FAILURE || Z_TYPE_PP(tmp) !=
    IS_STRING) {
    /* error handling */
    return;
    }
    /* work with tmp */

    As this accesses the userspace $_SERVER you should do this during RINIT,
    else the value might be wrong. While this adds some cost to every
    request (populating $_SERVER plus two hash lookups), it can be optimized
    a tiny bit by precalculating the hash and using zend_hash_quick_find().

    The code above is untested.

    johannes

  • Bostjan Skufca at Aug 10, 2010 at 3:24 pm
    I've been digging a little deeper and have figured out that I probably could
    retrieve what I want (realpath of first executed file) from included_files
    hash (first entry, obviously). Unfortunately, doing it like this (sampled
    from get_included_files() implementation):

    char *hentry;
    zend_hash_internal_pointer_reset(&EG(included_files));
    zend_hash_get_current_key(&EG(included_files), &hentry, NULL, 1);
    printf("%s", hentry);

    only results in segfaults (in the very first zend_hash_... call). Am I using
    wrong macros or something like that? I've checked the PHP sources (and
    manually compiled function call trace) to make myself sure that
    included_files hash gets destroyed AFTER the macro RSHUTDOWN functions are
    called, so this should not be an issue.

    Another hint, if I may ask for it? :)

    Thanks,
    b.


    2010/8/9 Bostjan Skufca <bostjan@a2o.si>
    I don't think I made myself exactly clear.

    There are 2 use cases I would like to consider:
    1. CLI: by using "php /full/path/to/script.php" or "cd /full/path/to && php
    script.php"
    2. Apache DSO: going to http://www.domain.com/script.php which in turn
    'executes' script "/full/path/to/script.php"

    In both cases, in RINIT I would like to get full path (real path, if
    possible) of this /full/path/to/script.php. Is this possible or is your way
    the only viable solution (with performance penalty etc)?

    (The $_SERVER['SCRIPT_FILENAME'] was just a method to explain what I would
    like to have, albeit obviously a poor one:)

    Thanks again,
    b.


    2010/8/9 Johannes Schlüter <johannes@php.net>
    On Mon, 2010-08-09 at 13:32 +0200, Bostjan Skufca wrote:
    Hi all!

    I am developing a small PHP extension and I ATM can't figure out how to get
    to $_SERVER['SCRIPT_FILENAME'] content while in PHP_RINIT or
    PHP_RSHUTDOWN
    function. Can someone please hint me with this one?
    The simple answer is: You can't. The closest you can get is a char *
    SG(request_data).request_uri

    The more complex answer is: It is only stored for being used in
    $_SERVER. So you can read it from there. The engine has a "just in time"
    mode to avoid creating _SERVER when not needed, we need it so we first
    have to trigger the creation of that variable, then one can search
    through the global symbol table and then search in there:


    zval **server, **tmp;
    zend_auto_global_disable_jit("_SERVER", sizeof("_SERVER")-1 TSRMLS_CC);
    if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"),
    (void**)&server) == FAILURE || Z_TYPE_PP(server) != IS_ARRAY) {
    /* error handling */
    return;
    }
    if (zend_hash_find(Z_ARRVAL_PP(server), "SCRIPT_FILENAME",
    sizeof("SCRIPT_FILENAME"), (void**)&tmp) == FAILURE || Z_TYPE_PP(tmp) !=
    IS_STRING) {
    /* error handling */
    return;
    }
    /* work with tmp */

    As this accesses the userspace $_SERVER you should do this during RINIT,
    else the value might be wrong. While this adds some cost to every
    request (populating $_SERVER plus two hash lookups), it can be optimized
    a tiny bit by precalculating the hash and using zend_hash_quick_find().

    The code above is untested.

    johannes

  • Johannes Schlüter at Aug 10, 2010 at 4:02 pm
    Hi,
    On Tue, 2010-08-10 at 17:24 +0200, Bostjan Skufca wrote:
    I've been digging a little deeper and have figured out that I probably
    could retrieve what I want (realpath of first executed file) from
    included_files hash (first entry, obviously). Unfortunately, doing it
    like this (sampled from get_included_files() implementation):
    Interesting idea. Didn't think about that. But it isn't reliable either:
    If an auto prepend file is set this will be in the list first.
    char *hentry;
    zend_hash_internal_pointer_reset(&EG(included_files));
    zend_hash_get_current_key(&EG(included_files), &hentry, NULL, 1);
    printf("%s", hentry);
    I think you want the value, not the key. To be precise you want the
    value for key 0. -> zend_hash_find()

    But thinking about this idea I had another idea: Use the userland
    stacktrace. While this won't work in RINIT/RSHUTDOWN but you'd have to
    hook into the execution somewhere. Would look something like this:

    char *filename;
    struct _zend_execute_data current_frame = EG(curent_execute_data);
    do {
    if (current_frame->type != ZEND_INTERNAL_FUNCTION) {
    filename = current_frame->filename;
    }
    } while (current_frame = current_frame->prev_execute_data);
    php_printf("%s, filename);

    Again: Not tested at all.



    johannes
  • Bostjan Skufca at Aug 10, 2010 at 4:43 pm
    2010/8/10 Johannes Schlüter <johannes@schlueters.de>
    Hi,
    On Tue, 2010-08-10 at 17:24 +0200, Bostjan Skufca wrote:
    I've been digging a little deeper and have figured out that I probably
    could retrieve what I want (realpath of first executed file) from
    included_files hash (first entry, obviously). Unfortunately, doing it
    like this (sampled from get_included_files() implementation):
    Interesting idea. Didn't think about that. But it isn't reliable either:
    If an auto prepend file is set this will be in the list first.
    So simple thing, so easy to overlook...
    But I believe it is fairly easy to check if autoprepend is enabled and use
    second item from the list in that case, or (in case that is not posible)
    introduce new ini setting for that matter.

    char *hentry;
    zend_hash_internal_pointer_reset(&EG(included_files));
    zend_hash_get_current_key(&EG(included_files), &hentry, NULL, 1);
    printf("%s", hentry);
    I think you want the value, not the key. To be precise you want the
    value for key 0. -> zend_hash_find()
    Aaaa, again, so simple. I feel such a newbie, but obviously not without a
    reason :)


    But thinking about this idea I had another idea: Use the userland
    stacktrace. While this won't work in RINIT/RSHUTDOWN but you'd have to
    hook into the execution somewhere. Would look something like this:

    char *filename;
    struct _zend_execute_data current_frame = EG(curent_execute_data);
    do {
    if (current_frame->type != ZEND_INTERNAL_FUNCTION) {
    filename = current_frame->filename;
    }
    } while (current_frame = current_frame->prev_execute_data);
    php_printf("%s, filename);

    Again: Not tested at all.
    This would probably mean I have to modify PHP itself or call certain
    extension function from within PHP script?
    (That is what I am desperately trying to avoid.)


    Again, thanks for all the help!
    b.
  • Johannes Schlüter at Aug 10, 2010 at 5:33 pm
    Hi,
    On Tue, 2010-08-10 at 18:43 +0200, Bostjan Skufca wrote:

    Interesting idea. Didn't think about that. But it isn't
    reliable either:
    If an auto prepend file is set this will be in the list first.

    So simple thing, so easy to overlook...
    But I believe it is fairly easy to check if autoprepend is enabled and
    use second item from the list in that case, or (in case that is not
    posible) introduce new ini setting for that matter.
    An auto_prepend_file might include other things :-)
    I don't think there's lots of code with auto prepend out these days (I
    only encountered it twice myself, i think) So in most cases it might be
    ok to do if (auto_prepend_set) { error("oh, i can't work!"); }.
    Aaaa, again, so simple. I feel such a newbie, but obviously not
    without a reason :)
    That's fine :-)
    This would probably mean I have to modify PHP itself or call certain
    extension function from within PHP script?
    (That is what I am desperately trying to avoid.)
    Yeah, just wanted to share the idea so it can be proper evaluated which
    way is the best :-)
    Again, thanks for all the help!
    You're welcome.

    johannes
  • Bostjan Skufca at Aug 12, 2010 at 5:38 pm
    Yes, that did it! I was wondering what those SG and EGs are :)

    Anyway, the EG(included_files) would not work, because hash values (file
    paths) are not stored, only keys. Snippet from Zend/zend.h
    ...
    zend_hash_add(&EG(included_files), file_handle->opened_path,
    strlen(file_handle->opened_path)+1, (void *)&dummy, sizeof(int), NULL);
    ...

    Again, thank you all!
    b.


    On 10 August 2010 19:57, Chris Stockton wrote:

    Hello,

    I would check the running sapi and use the sapi specific approach. This is
    cheap performance wise. I.e. SG(request_info).path_translated for Apache I
    believe and cli example you can Prolly find in the cli sapi source. I see
    argv being a good place maybe.

    On Aug 10, 2010 9:44 AM, "Bostjan Skufca" wrote:
    2010/8/10 Johannes Schlüter <johannes@schlueters.de>

    Hi,
    On Tue, 2010-08-10 at 17:24 +0200, Bostjan Skufca wrote:
    I've been digging a little d...
    So simple thing, so easy to overlook...
    But I believe it is fairly easy to check if autoprepend is enabled and use
    second item from the list in that case, or (in case that is not posible)
    introduce new ini setting for that matter.


    char *hentry;
    zend_hash_internal_pointer_reset(&EG(included_files));
    zend_ha...
    Aaaa, again, so simple. I feel such a newbie, but obviously not without a
    reason :)



    But thinking about this idea I had another idea: Use the userland
    stacktrace. While this won't w...
    This would probably mean I have to modify PHP itself or call certain
    extension function from within PHP script?
    (That is what I am desperately trying to avoid.)


    Again, thanks for all the help!
    b.
  • Richard Quadling at Aug 11, 2010 at 10:17 am

    On 9 August 2010 12:32, Bostjan Skufca wrote:
    Hi all!

    I am developing a small PHP extension and I ATM can't figure out how to get
    to $_SERVER['SCRIPT_FILENAME'] content while in PHP_RINIT or PHP_RSHUTDOWN
    function. Can someone please hint me with this one?

    Thanks,
    b.
    If the script is run via the command line (in my limited experience,
    this would be the CLI, CGI, FastCGI SAPIs, others also maybe), can't
    you look through the command line parameters?



    --
    Richard Quadling.

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupphp-internals @
categoriesphp
postedAug 9, '10 at 11:32a
activeAug 12, '10 at 5:38p
posts9
users4
websitephp.net

People

Translate

site design / logo © 2022 Grokbase