Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the real purpose of magic method __set_state in PHP?

I'm learning about magic methods, reading every site, taking every example, but nothing makes sense to me. Examples like this:

class A
{
    public $var1;
    public $var2;

    public static function __set_state($an_array) // As of PHP 5.1.0
    {
        $obj = new A;
        $obj->var1 = $an_array['var1'];
        $obj->var2 = $an_array['var2'];
        return $obj;
    }
}

$a = new A;
$a->var1 = 5;
$a->var2 = 'foo';

eval('$b = ' . var_export($a, true) . ';'); // $b = A::__set_state(array(
                                            //    'var1' => 5,
                                            //    'var2' => 'foo',
                                            // ));
var_dump($b);

What is the real purpose? Is for debugging or something?

like image 876
Red Vulpine Avatar asked Sep 27 '17 07:09

Red Vulpine


2 Answers

__set_state can be understood in relation with var_export

If you read the docs:

var_export() gets structured information about the given variable. It is similar to var_dump() with one exception: the returned representation is valid PHP code.

Compare the two:

  1. var_dump($a)

    object(A)#1 (2) { ["var1"]=> int(5) ["var2"]=> string(3) "foo" }

  2. var_export($a)

    A::__set_state(array( 'var1' => 5, 'var2' => 'foo', ))

  3. (bonus) serialize($a)

    O:1:"A":2:{s:4:"var1";i:5;s:4:"var2";s:3:"foo";}

So the second is a valid PHP code, you can run it and end up with similar object.

So what would happen if you removed __set_state method? Let's try:

class A
{
    public $var1;
    public $var2;  
}

$a = new A;
$a->var1 = 5;
$a->var2 = 'foo';

var_export($a); 
// A::__set_state(array( 'var1' => 5, 'var2' => 'foo', ))

Still works, right? Let's call it then

class A
{
    public $var1;
    public $var2;  
}

$a = A::__set_state(array( 'var1' => 5, 'var2' => 'foo', )) 
// Exception: Call to undefined method A::__set_state()

Let's bring it back and see:

class A
{
    public $var1;
    public $var2;

    public static function __set_state($an_array) // As of PHP 5.1.0
    {
        $obj = new A;
        $obj->var1 = $an_array['var1'];
        $obj->var2 = $an_array['var2'];
        return $obj;
    }    
}

$a = A::__set_state(array( 'var1' => 5, 'var2' => 'foo', ));
var_dump($a); 
// object(A)#1 (2) { ["var1"]=> int(5) ["var2"]=> string(3) "foo" } 

So the real purpose of __set_state method is to initialize an object in case someone "serialized" it with var_export.

Why someone would need that? Objects serialized with serialize need to be unserialized with unserialize. But objects saved in files by var_export could just be included because it's just a valid PHP code.

Edit:

I would not really call this method magic (even though it's in the docs described as such) as there's nothing magical about it. PHP just assumes it's there when var_export is called, and it must be there if you want to use whatever var_export produced. I would say it's just a convention that it's called __set_state - no magic there. But it's just my humble opinion.

like image 104
Grzegorz Avatar answered Nov 15 '22 17:11

Grzegorz


__set_state is called in response to an instance of your object being passed to the var_export function.

var_export Outputs or returns a parsable string representation of a variable.

var_export takes an optional second parameter – a boolean that determines whether after the variable passed as the first argument is returned as a string instead of output.

An example:

class Dummy {
  private $value1_;
  private $value2_;

  function __construct() {
    $this->value1_ = 100;
    $this->value2_ = "100";
  }

  static function __set_state(array $array) {
    foreach($array as $k => $v) {
      echo("$k ==> $v <br/>");
    }
  }
}
$aDummy = new Dummy();
//var_export by it self will output Dummy::__set_state(array( 'value1_' => 100, 'value2_' => '100', ))
var_export($aDummy);
eval(var_export($aDummy, true) . ';');

Now the output is this:

value1_ ==> 100

value2_ ==> 100

var_export can produce executable PHP code for the Dummy type with a __set_state method defined.

var_export takes one argument, and it must be an array. It will contain key-value pairs of the properties or fields of the instance on which it is called.

You can put anything you want in the __set_state method.

like image 36
mega6382 Avatar answered Nov 15 '22 17:11

mega6382