Latest Entries »

When to use []

This is a shortcut for array_push. It’s easier and even better if you add one element to the array.

Note: If you use array_push() to add one element to the array it's better to use $array[] = because in that way there is no overhead of calling a function.

If you make a mistake and you try to use this on something else than an array, you will have a warning both case. In the doc, you have something else

Note: array_push() will raise a warning if the first argument is not an array. This differs from the $var[] behaviour where a new array is created.
$arr = 5;
array_push($arr, 4);

// PHP Warning:  array_push() expects parameter 1 to be array, integer given

$arr = 5;
$arr[] = 4;

// PHP Warning:  Cannot use a scalar value as an array

// PHP 5.6.7-1+deb.sury.org~trusty+1 (cli) (built: Mar 24 2015 11:21:10) 
// Copyright (c) 1997-2015 The PHP Group
// Zend Engine v2.6.0, Copyright (c) 1998-2015 Zend Technologies
//     with Zend OPcache v7.0.4-dev, Copyright (c) 1999-2015, by Zend Technologies
//     with Xdebug v2.3.1, Copyright (c) 2002-2015, by Derick Rethans

When not to use []

This is a list of when not to use [] and will result in this error

$a = array();
$b = $a[];
///////////////////////////////////
$a = array();
isset($a[]);
///////////////////////////////////
function test($var) { }
test($arr[]);
///////////////////////////////////
$a = array();	
foreach($a[] as $b) {
}
///////////////////////////////////
$a = array();	
foreach($a[]['test'] as $b) {
}
///////////////////////////////////
$a = array();
foreach($a['test'][] as $b) {
}
///////////////////////////////////

You can find more example here

The problem i had

I came accros some code trying to use variables variables and []. Since the precedence is kind of blurry here, i got this error in this particular scenario.

$test = 'var';
$name = 'test' . $test;

$testvar = array();
$$name[] = 3; // Add something to $testvar

// PHP Fatal error:  Cannot use [] for reading

The solution

1- Use array_push : array_push($$name, 3);
2- Use {} to force precedence : ${$name}[] = 3;

What i have found

I ran it over 3v4l and it seems to work now on php7@20140901 – 20150501

Advertisements

Situation

class test   {  
        private $data = array();        
        public function __get($name) {
            return isset($this------->data[$name]) ? $this->data[$name] : null;
        }

        public function __set($name, $value) {
            $this->data[$name] = $value;
        }

        public function run() {
            $this->test = array('first' => 1);
            $this->test['second'] = 2;
        }
}

$test = new test();
$test->run();

 // OUTPUT :
 // PHP Notice:  Indirect modification of overloaded property test::$test has no effect in /data/test/test22.php on line 13

The problem

You see “test” is not a property of this class. So it’s calling the Magic Method __get() but since it’s return by value, the modification on line 26 has no effect. You will have this problem for array and object.

Solutions

  1. Instead of adding a new item directly, you could use array_merge or use the + operator
  2. You could return __get by reference
  3. You could specify the property directly in the class
   // solution #1
   $this->test += array('second' => 2);
   $this->test = array_merge($this->test, array('second' => 2);

   // solution #2
   public function &__get($name) { ... }

   // solution #2
   private $test = array(); // With the data property

List of related links

CAUTION : PHP reserves all function names starting with __ as magical. It is recommended that you do not use function names with __ in PHP unless you want some documented magic functionality.

WHY ?

I have no idea, but someone ask about it

HOW ?

  • Backup, backup again and again just to be sure
  •  In your 2.4 redis.conf, set the following properties to zero ; hash-max-zipmap-entries, list-max-ziplist-entries, set-max-intset-entries
  • Restart redis and do SAVE command OR BGSAVE
  • Edit dump.rdb file. Change first character to REDIS0001
  • Copy to 2.0 instance and restart redis.

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";
; http://php.net/variables-order

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) {
        $prob[$values[$value]]++;
    }
}
asort($prob);
var_dump($prob);

/** Output something like this:
*  "30" will always be at the top
array
  <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 */
PHP_FUNCTION(array_rand)
{
        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) {
                return;
        }

        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");
                        return;
                }
        }

        /* 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 {
                                        RETURN_LONG(num_key);
                                }
                        } 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);
                                }
                        }
                        num_req--;
                }
                num_avail--;
                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.

Tips and Tricks #1

FOREACH reference:

<?php 
$myArray = array('t','h','i','s','i','s','a','n','a','r','r','a','y');
foreach($myArray as &$value) {
  echo $value;
}
echo PHP_EOL;
foreach($myArray as $v) {
  echo $v;
}
echo PHP_EOL;
foreach($myArray as $value) {
  echo $value;
}
/*
OUTPUT:
thisisanarray
thisisanarray
thisisanarraa
*/

The reason is almost obvious when you know you’re using reference and you know how reference works. When you are using reference , $value is a pointer to your element , not a copied value. So when you loop over your array, $value will be each element from your array. Everything is ok until you use the variable again. Everything you will assign to it , will also change the value of the reference. Another example to explain :

<?php   
$myArray = array('t','h','i','s');  
 foreach($myArray as &$value) {
           echo $value;
   }   
  echo PHP_EOL;
  $value = 'reference value changed';
  var_dump($myArray);
/*  OUTPUT:  
this 
array(4) {
  [0]= 
  string(1) "t"
  [1]=---->
  string(1) "h"
  [2]=>
  string(1) "i"
  [3]=>
  &string(23) "reference value changed"
}
*/

You only need to unset the reference. In this case : $value. If you want a more detail explication , check out Johannes Schlüter blog

Why using igbinary ?

  • You don’t need textual representation
  • Store data into memcached or APC
  • Want to serialize complex data structure

Installation ( Ubuntu 10.10 )

If you are using php5.3 like me , you’ll need phpize5 for compiling this extension.

sudo apt-get install php5-dev
wget http://opensource.dynamoid.com/igbinary-1.1.1.tar.gz
tar -xvf igbinary-1.1.1.tar.gz
cd igbinary-1.1.1/
phpize5
./configure CFLAGS="-O2 -g" --enable-igbinary
make
make test
sudo make install
Don't forget to enable this extension in your conf file or php.ini.
;Load igbinary extension
extension=igbinary.so

;Use igbinary as session serializer
session.serialize_handler=igbinary

;Enable or disable compacting of duplicate strings
; # The default is On.
igbinary.compact_strings=On

;Use igbinary as serializer in APC cache (3.1.7 or later)
;apc.serializer=igbinary

Benchmark

Loop size : 100000

sample file ( Album1 )

space serialize unserialize
PHP 1185 1.352 1.651
igbinary 415 1.212 1.060
% 35.02% 89.64% 64.18%

sample file ( Album2 )

space serialize unserialize
PHP 2682 3.203 3.865
igbinary 832 2.509 2.521
% 31.02% 78.33% 65.22%

sample file ( Matrix )

space serialize unserialize
PHP 2682 3.203 3.865
igbinary 832 2.509 2.521
% 31.02% 78.33% 65.22%

sample file ( Scalar )

space serialize unserialize
PHP 534 0.912 0.923
igbinary 258 0.516 0.627
% 48.31% 56.56% 67.88%

sample file ( Simple )

space serialize unserialize
PHP 5 0.042 0.033
igbinary 6 0.029 0.031
% 120% 69.80% 93.96%

sample file

space serialize unserialize
PHP 46 0.075 0.099
igbinary 20 0.129 0.078
% 43.48% 170.86% 78.71%

The benchmark script done by Teddy Grenman creator of igbinary. Those test have been done on a Virtual Ubuntu machine with only 512M of memory. Next step : test it with memcached storing session data.

Superglobal post is empty ($_POST)

Am i that stupid ?

Maybe, maybe not.  You may need to check some stuff first before doing this conclusion. Also, you should read this doc from w3.org.

<?php   echo("type: " . $_SERVER['content_type']);   var_dump(file_get_contents('php://input'));   var_dump($_POST); ?>
If you want to submitting forms with files or data containing non-ASCII characters, keep in mind you should use the content type : multipart/form-data. By default, it's using application/x-www-form-urlencoded. If you want more information about multipart/form-data, you should read RFC2388
If you got content from php://input but not into $_POST , it may coming from your php.ini file. Check post_max_size if you got a valid value.
You may also check out your html file and you don't have duplicate "name" and also a name attribute. If you forget the "name" attribute, the field will not be sent.

What’s the easiest way to sort array with accent ?

Since PHP 5.3 you can use the Internationalization extension ( intl ). It’s a wrapper for ICU library.

I wasn’t able to use it because i got it disable when i compile php.

phpinfo configure

If you have it , it should be something like this.

<?php
$collator = new Collator('fr_CA');
$collator->sort($yourArray);
?>
If its ain't working, try "locale -a" and you will see which one are installed.
If you want to install one :
Ubuntu : /> locale-gen fr_CA
Gentoo : /> Edit the file /etc/locale.gen and run locale-gen (may need sudo)

What can i do without the intl extension ?


<?php
setLocale(LC_COLLATE, 'fr_CA.utf8');
asort($array, SORT_LOCALE_STRING);
?>
To use this flag, you will need PHP >= 5.0.2 OR PHP 4.4.0