__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:
var_dump($a)
object(A)#1 (2) { ["var1"]=> int(5) ["var2"]=> string(3) "foo" }
var_export($a)
A::__set_state(array( 'var1' => 5, 'var2' => 'foo', ))
(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.