【问题标题】:magic function __sleep cannot return private properties魔术函数 __sleep 不能返回私有属性
【发布时间】:2014-02-11 14:29:12
【问题描述】:

我知道有一个类似的帖子,但我尝试了他们的建议,但没有奏效。

在 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接口的方式。

【问题讨论】:

标签: php magic-methods


【解决方案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();

 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();

最后我使用 operator protected 代替 private

【讨论】:

  • 但我不明白如何在父类中具有私有属性的对象上使用 __sleep 方法。在php.net他们建议使用序列化接口,这是什么意思?
【解决方案2】:

如果你将父属性命名为 "\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

【讨论】:

    【解决方案3】:

    您的链接(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 方法,因为实现这个接口的类不再支持 __sleep() 和 __wakeup() 方法。 :

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

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

      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 类的私有 $id 更改为受保护的 $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!!
    

    【讨论】:

      【解决方案4】:

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

      <?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) ;
      
      ?>
      

      【讨论】:

        【解决方案5】:

        另一种解决方案:

         <?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) ;
        
            ?>
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-03-11
          • 2018-10-26
          • 2014-10-14
          • 2017-03-18
          • 2011-08-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多