Archive for February, 2012

Why ENV is empty

The problem :

I want to use $_ENV so i can get the username of the logged user.  Everyone should use getenv() instead, but if you need $_ENV to be active you need to do this.

Edit your php.ini file. You can find which one you are using with "php -i" from cli or with phpinfo() through your browser. Search for "variable_orders"and set it to
variables_order = "GPCSE"

Each letter is for one superglobals variable ( get, post, cookie, server and env ). You need to know that each one you add will cause some performance penalty and this is why normally you don't add the letter "e".

From the php.ini file:

; This directive determines which super global arrays are registered when PHP
; starts up. If the register_globals directive is enabled, it also determines
; what order variables are populated into the global space. G,P,C,E & S are
; abbreviations for the following respective super globals: GET, POST, COOKIE,
; ENV and SERVER. There is a performance penalty paid for the registration of
; these arrays and because ENV is not as commonly used as the others, ENV is
; is not recommended on productions servers. You can still get access to
; the environment variables through getenv() should you need to.
; Default Value: "EGPCS"
; Development Value: "GPCS"
; Production Value: "GPCS";

From the doc:

Sets the order of the EGPCS (Environment, Get, Post, Cookie, and Server) variable parsing. For example, if variables_order is set to "SP" then PHP will create the superglobals $_SERVER and $_POST, but not create $_ENV, $_GET, and $_COOKIE. Setting to "" means no superglobals will be set.

If the deprecated register_globals directive is on, then variables_order also configures the order the ENV, GET, POST, COOKIE and SERVER variables are populated in global scope. So for example if variables_order is set to "EGPCS", register_globals is enabled, and both $_GET['action'] and $_POST['action'] are set, then $action will contain the value of $_POST['action'] as P comes after G

You know what is array_rand and you use it sometimes like rand() ( maybe mt_rand() if you want better random value). Anyway, i was reading this page when i saw the comment by Sebmil. This was pretty weird and i tried to figure it out why. This is in progress, because i did not found the reason.  I'm using php 5.3.6-p10-gentoo but i tested on ubuntu with 5.3.2 and i'm pretty sure it's all around.
$values = range(0, 49);
$prob = array_fill(0, 50, 0);
for ($i = 0; $i < 10000; $i++) {     $rand = array_rand($values, 10);     foreach($rand as $key => $value) {

/** Output something like this:
*  "30" will always be at the top
  <strong>30 => int 1842</strong>
  40 => int 1934
  3 => int 1935
  10 => int 1940
  45 => int 1942
  28 => int 1949
  14 => int 1962
  47 => int 1965
  22 => int 1965
  19 => int 1968

What do i know ?!?

  • If the second params of for is lower ( like 50 ) , the output seem random.
  • If we use rand() in each loop the "lower" value (30) will change. One for each rand() used. When we reach 3 ( about 27 rand() calls ),  the output seem random.
  • If we use rand() or mt_rand() instead of array_rand, the output seem random.
  • php 5.4 trunk array_rand function doesn't seem to have changed.

array_rand() source php 5.3 trunk :

Can you figure out where the bug is ?
/* {{{ proto mixed array_rand(array input [, int num_req])
   Return key/keys for random entry/entries in the array */
        zval *input;
        long randval, num_req = 1;
        int num_avail, key_type;
        char *string_key;
        uint string_key_len;
        ulong num_key;
        HashPosition pos;

        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &input, &num_req) == FAILURE) {

        num_avail = zend_hash_num_elements(Z_ARRVAL_P(input));

        if (ZEND_NUM_ARGS() > 1) {
                if (num_req  num_avail) {
                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Second argument has to be between 1 and the number of elements in the array");

        /* Make the return value an array only if we need to pass back more than one result. */
        if (num_req > 1) {
                array_init_size(return_value, num_req);

        /* We can't use zend_hash_index_find() because the array may have string keys or gaps. */
        zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos);
        while (num_req && (key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(input), &string_key, &string_key_len, &num_key, 0, &pos)) != HASH_KEY_NON_EXISTANT) {

                randval = php_rand(TSRMLS_C);

                if ((double) (randval / (PHP_RAND_MAX + 1.0)) < (double) num_req / (double) num_avail) {
                        /* If we are returning a single result, just do it. */
                        if (Z_TYPE_P(return_value) != IS_ARRAY) {
                                if (key_type == HASH_KEY_IS_STRING) {
                                        RETURN_STRINGL(string_key, string_key_len - 1, 1);
                                } else {
                        } else {
                                /* Append the result to the return value. */
                                if (key_type == HASH_KEY_IS_STRING) {
                                        add_next_index_stringl(return_value, string_key, string_key_len - 1, 1);
                                } else {
                                        add_next_index_long(return_value, num_key);
                zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos);

Possible solution :

  1. Shuffle() your array in each loop
  2. Use multiple time rand()
  3. Don't use array_rand too often.