魔术函数__sleep无法返回私有属性

时间:2014-02-11 14:29:12

标签: php magic-methods

我知道有一个类似的主题,但我已经尝试了他们的建议并且它没有用。

在php中,当一个对象被序列化时,有一个选项在该类中定义一个魔术函数__sleep,它应该返回一个数组,其中包含将被序列化的所有对象属性的值。但是在php.net中写道,如果我们为从另一个类扩展的类的对象定义__sleep方法,那么我们就无法写入表示父类的私有属性的数组值。问题是他们为这种情况提出了一个解决方案,我并没有真正理解他们试图暗示的内容。

以下是撰写内容:

  

注意:
  __sleep()无法返回父类中私有属性的名称。执行此操作将导致E_NOTICE级别错误。相反,您可以使用Serializable接口。

以下是链接:http://www.php.net/manual/en/language.oop5.magic.php

另外,在this thread中,他们提出了我尝试过的内容并且无法正常工作并发送给我:

  

注意:serialize():“name”作为成员变量从__sleep()返回,但在第43行的C:\ xampp \ htdocs \ questions \ sleep_private_father.php中不存在

这是脚本:

<?php

class a
{
    private $name ;
    private $age ;

    function __construct($name, $age)
    {
        $this->name = $name ;
        $this->age = $age ;
    }

    function __sleep()
    {
        $vec = array("name") ;
        return $vec ;
    }
}

class b extends a
{
    private $last_name ;

    function __construct($name, $age ,$last_name)
    {
        parent::__construct($name, $age) ;
        $this->last_name = $last_name ;
    }

    function __sleep()
    {
        $array = parent::__sleep() ;

        array_push( $array, 'last_name' );
        return $array ;
    }

}

$ob = new b("michal" , 26 , "smith") ;
var_dump($ob) ;
$ob_ser = serialize($ob) ;
var_dump(unserialize($ob_ser)) ;

?>

我也很好奇php.net建议使用serializble接口的方式。

5 个答案:

答案 0 :(得分:1)

私有运算符意味着属性或函数只能在类中使用,并且不能扩展。如果您希望实现机制在子类中存储属性,您应该尝试类似:

  class foo{
     protected $test = 'a';
     public function __sleep() {
           return array('test');
     }
  }
  class bar extends foo{

     public function getTest(){
          return $this->test;
  }

}   

 $bar = new bar();
 $serialized = serialize($bar);
 $object = unserialize($serialized);
 echo $object->getTest();

OR

 class foo{
     protected $test = 'a';
 }
 class bar extends foo{

     public function __sleep() {
        return array('test');
    }

    public function getTest(){
        return $this->test;
    }

 }

 $bar = new bar();
 $serialized = serialize($bar);
 $object = unserialize($serialized);
 echo $object->getTest();

最后一种方式我使用运营商保护而非私人

答案 1 :(得分:0)

请注意您的链接(php.net)意味着:

Note:
It is not possible for __sleep() to return names of private properties 
in parent classes. Doing this will result in an E_NOTICE level error. 
Instead you may use the Serializable interface. 

如果您尝试使用私有属性在__sleep方法数组中重新生成(如果它是父类或子类,则无关紧要),您总会收到通知错误。可以避免此通知并使用可序列化接口获取私有属性的名称。

如果您的类实现了可序列化接口,则不应使用__sleep方法beacuse实现此接口的类不再支持__sleep()和__wakeup()方法。 :

 Serializable {
    /* Methods */
    abstract public string serialize ( void )
    abstract public void unserialize ( string $serialized )
 } 

序列化将在您的对象将序列化状态(对象 - &gt;字符串)时调用,并在您的对象将进行反序列化(字符串 - &gt;对象)时反序列化。您无法读取子类中的私有属性父类(当您尝试从子类中的方法获取此属性时)。例如:

  class foo implements Serializable{
     private $id;
     public function serialize() {
        return serialize('hello world');
     }
     public function unserialize($serialized) {
        $this->id = unserialize($serialized);
     }
     public function get_id_from_foo(){
        return $this->id;
     }
 }

 class bar extends foo{
     public function get_id_from_bar(){
         return $this->id;
     }
 }

 $bar = new bar();
 $serializedBar = serialize($bar);
 $unserializedBar = unserialize($serializedBar);
 echo $unserializedBar->get_id_from_bar();

 //RESULT : Undefined property: bar::$id

但是如果你将foo class private $ id更改为protected $ id,你就会得到'hello world'。

在其他情况下,当您尝试从父级方法extendend获取私有属性时,例如:

class foo implements Serializable{
    private $id;
    public function serialize() {
        return serialize('hello world');
    }
    public function unserialize($serialized) {
        $this->id = unserialize($serialized);
    }
    public function get_foo_id_from_parent(){
        return $this->id;
    }
 }

 class bar extends foo{
     public function set_id(){
         $this->id = 'something else';
     }
    public function get_foo_id_from_bar(){
        return $this->id;
    }
 }

$bar = new bar();
$bar->set_id();
$serializedBar = serialize($bar);
$unserializedBar = unserialize($serializedBar);
echo $unserializedBar->get_foo_id_from_parent();
//RESULT 'hello world'; But this property is from parent!!

答案 2 :(得分:0)

好的,你们都给了我解决我确切问题的想法,这就是得到一个结果,好像我可以在扩展类的对象的sleep方法中定义一个来自父类的私有属性。这是我的解决方案:

<?php 
class foo implements Serializable
{
    private $id;
    private $name ;

    public function serialize() 
    {
        $array_properties_to_serialize = array('id' => $this->id) ;

        // this if will indicate if an object from the foo class invoked the method 
        //or an object from the extend class (bar) 

        if (get_class($this) == "foo") 
        {  
            // foo object invoked the method , so we will want to serialize only the $id property 

           return serialize($array_properties_to_serialize);
        }

        elseif (get_class($this) == "bar")
        {
            // bar object invoked the method so we will want to add to the  array of properties
             //the $last_name property from the bar class, and then serialize it 
            return $array_properties_to_serialize ;
       }
    }

    public function unserialize($serialized) 
    {
         $array_properties_unserialized = unserialize($serialized);
         $this->id = $array_properties_unserialized['id'] ;

         if (get_class($this) == "bar")
         {
            // bar object invoked the method so we will send to the overriding method the $array_properties_unserialized
            // so it could set the properties of the extended object accordingly.
            return $array_properties_unserialized ;
         }
    }

    public function __construct($id,$name)
    {
        $this->name = $name ;
        $this->id = $id ;
    }
 }

 class bar extends foo
 {
    private $last_name ;

    public function serialize()
    {
        $array_properties_to_serialize = parent::serialize() ;
        $array_properties_to_serialize["last_name"] = $this->last_name ;  

        return serialize($array_properties_to_serialize);
    }

    public function unserialize($serialized)
     {
        $array_properties_unserialized = parent::unserialize($serialized) ;
        $this->last_name = $array_properties_unserialized['last_name']; ;
    }

    public function __construct($id, $name , $last_name)
    {
        parent::__construct($id, $name) ;
        $this->last_name = $last_name ;
    }


 }

 echo "bar object: <br>" ;
$bar = new bar(12 , "tom" , "smith");
var_dump($bar) ;
$ob_ser = serialize($bar) ;
$ob_unser = unserialize($ob_ser) ;
echo "unserialized bar object  (didnt include the \$name property): <br>" ;
var_dump($ob_unser) ;

$foo = new foo(11 ,  "frank") ;
echo "foo object: <br>" ;
var_dump($foo) ;
$ob_ser = serialize($foo) ;
$ob_unser = unserialize($ob_ser) ;
echo "unserialized foo object (didnt include the \$name property): <br>" ;
var_dump($ob_unser) ;

?>

答案 3 :(得分:0)

另一种解决方案:

 <?php 
    class foo 
    {
        private $id;
        private $name ;

        public function serialize() 
        {
            $array_properties_to_serialize = array('id' => $this->id) ;           
               return serialize($array_properties_to_serialize);

        }

        // will be called only on a foo object because it doesnt implements serializable
        // when serialize a bar object it will only invoke the serialize method in the bar class
        public function __sleep()
        {
            return array('id') ;
        }


        public function getID()
        {
            return $this->id ;

        }

        public function setId($id)
        {
            $this->id = $id ;
        }

        public function unserialize($serialized) 
        {
             $array_properties_unserialized = unserialize($serialized);
             $this->id = $array_properties_unserialized['id'] ;


        }

        public function __construct($id,$name)
        {
            $this->name = $name ;
            $this->id = $id ;
        }
     }

     class bar extends foo implements Serializable
     {
        private $last_name ;

        public function serialize()
        {
            $array_properties_to_serialize['id'] = $this->getID() ;
            $array_properties_to_serialize["last_name"] = $this->last_name ;  

            return serialize($array_properties_to_serialize);
        }

        public function unserialize($serialized)
         {
            $array_properties_unserialized = unserialize($serialized) ;
            $this->last_name = $array_properties_unserialized['last_name']; 
            $this->setId($array_properties_unserialized['id']) ;
        }

        public function __construct($id, $name , $last_name)
        {
            parent::__construct($id, $name) ;
            $this->last_name = $last_name ;
        }


     }

     echo "bar object: <br>" ;
    $bar = new bar(12 , "tom" , "smith");
    var_dump($bar) ;
    $ob_ser = serialize($bar) ;
    $ob_unser = unserialize($ob_ser) ;
    echo "unserialized bar object  (didnt include the \$name property): <br>" ;
    var_dump($ob_unser) ;

    $foo = new foo(11 ,  "john") ;
    echo "foo object: <br>" ;
    var_dump($foo) ;
    $ob_ser = serialize($foo) ;
    $ob_unser = unserialize($ob_ser) ;
    echo "unserialized foo object (didnt include the \$name property): <br>" ;
    var_dump($ob_unser) ;

    ?>

答案 4 :(得分:0)

如果您这样命名父属性"\0". parent::class. "\0parentProperty"

,则可以进行小技巧
<?php
namespace bed {
class A {
    private $id=5;
    protected $b;
    public $c;
    public function __sleep()
    {
        return ['id'];
    }
}

class B extends A{
    public function __sleep(){
        return  parent::__sleep();
    }
}

class D extends A{
    public function __sleep(){
        return ["\0bed\A\0id"];
    }
}

class CoolD extends A {
    private $d;
    protected $e;
    public $f;

    public function __sleep(){
        $allProperties = [];
        $reflection  = new \ReflectionClass($this);
        do{
            foreach ($reflection->getProperties() as $prop) {
                $allProperties[($prop->isPrivate()
                    ? "\0" . $prop->getDeclaringClass()->getName() . "\0" . $prop->getName()
                    : $prop->getName())] = true;
            }
        }while($reflection = $reflection->getParentClass());
        return array_keys($allProperties);
    }
}

class C extends A{
}

// var_dump(serialize(new C)); - not working
// var_dump(serialize(new B)); - not working
var_dump(serialize(new D));
var_dump(serialize(new CoolD));
}

https://3v4l.org/ZpPhR

相关问题