Casting object to array - any magic method being called?

前端 未结 6 1832
梦谈多话
梦谈多话 2020-12-10 00:44

I have an object of class Foo:

class Foo extends Bar {
    protected $a;
    protected $b;
}

$obj = new Foo();

What I want (and have) to d

6条回答
  •  南笙
    南笙 (楼主)
    2020-12-10 01:11

    Not sure if this question still relevant, but php has builtin ArrayObject class, that allows to treat object as array and can be handy when used as container for database record or collection.

    It might be not a best practice in regards to strict types, but it allows to treat object as array and both statements are valid.

    $obj = new ArrayObject(['a' => 'alpha']);
    var_dump($obj['a']); //alpha
    var_dump($obj->getOffset('a'));//alpha
    

    However you need to keep in mind behaviour of ArrayObject

    $obj = new ArrayObject(['a' => 'alpha']);
    //Access Property
    var_dump($obj['a']); //alpha
    var_dump($obj->offsetGet('a'));//alpha
    var_dump($obj->a); //null Notice: Undefined property: ArrayObject::$a
    
    //Serialization
    var_dump(serialize($obj));// string 'C:11:"ArrayObject":41:{x:i:0;a:1:{s:1:"a";s:5:"alpha";};m:a:0:{}}' (length=65)
    var_dump($obj->serialize());// string 'x:i:0;a:1:{s:1:"a";s:5:"alpha";};m:a:0:{}'
    var_dump(serialize($obj) === $obj->serialize());// false !!!
    
    //Setting Properties
    $obj['b'] = 'beta'; //OK
    $obj->c = 'gamma'; //value becomes object property!!!
    var_dump($obj);
    /* OBJECT DUMP
    object(ArrayObject)[13]
      public 'c' => string 'gamma' (length=5)
      private 'storage' =>
        array (size=2)
          'a' => string 'alpha' (length=5)
          'b' => string 'beta' (length=4)
     */
    
    //Property validation as array
    var_dump(isset($obj['a']));//true
    var_dump(isset($obj['b']));//true
    var_dump(isset($obj['c']));//false
    //Property validation as object
    var_dump(isset($obj->a));//false
    var_dump(isset($obj->b));//false
    var_dump(isset($obj->c));//true
    
    //Typecasting
    var_dump((array)$obj);
    /*
    array (size=2)
      'a' => string 'alpha' (length=5)
      'b' => string 'beta' (length=4)
     */
    
    //var_dump((string)$obj);// Catchable fatal error: Object of class ArrayObject could not be converted to string
    

    ArrayObject accepts two flags ArrayObject::STD_PROP_LIST as default and ArrayObject::ARRAY_AS_PROPS as alternative.

    This would change behaviour for reading values but does not support setting new properties in that way, here is example:

    $obj = new ArrayObject(['a' => 'alpha'], ArrayObject::ARRAY_AS_PROPS);
    //Access Property
    var_dump($obj['a']); //alpha
    var_dump($obj->offsetGet('a'));//alpha
    var_dump($obj->a);//alpha
    
    //Serialization
    var_dump(serialize($obj));// string 'C:11:"ArrayObject":41:{x:i:0;a:1:{s:1:"a";s:5:"alpha";};m:a:0:{}}' (length=65)
    var_dump($obj->serialize());// string 'x:i:0;a:1:{s:1:"a";s:5:"alpha";};m:a:0:{}'
    var_dump(serialize($obj) === $obj->serialize());// false !!!
    
    //Setting Properties
    $obj['b'] = 'beta'; //OK
    $obj->c = 'gamma'; //OK
    var_dump($obj);
    /* OBJECT DUMP
    object(ArrayObject)[14]
      private 'storage' =>
        array (size=3)
          'a' => string 'alpha' (length=5)
          'b' => string 'beta' (length=4)
          'c' => string 'gamma' (length=5)
     */
    
    //Property validation as array
    var_dump(isset($obj['a']));//true
    var_dump(isset($obj['b']));//true
    var_dump(isset($obj['c']));//false !!!
    //Property validation as object
    var_dump(isset($obj->a));//true
    var_dump(isset($obj->b));//true
    var_dump(isset($obj->c));//true
    
    //Typecasting
    var_dump((array)$obj);
    /*
    array (size=2)
      'a' => string 'alpha' (length=5)
      'b' => string 'beta' (length=4)
     */
    

    To make this behaviour more consistent you would have to extend this class and implement magic methods __get(), __set(), __isset() and __unset().

    Another tricky part is serialization, default method serialize would return you a copy serialized $storage variable instead of object itself, as workaround to return serialized copy of instance you can implement default serialization in __toString method, this way it's behave correctly.

    class FooObject extends ArrayObject
    {
        public function __get($index)
        {
            if ($this->offsetExists($index)) {
                return $this->offsetGet($index);
            } else {
                throw new UnexpectedValueException('Undefined key ' . $index);
            }
        }
    
        public function __set($index, $value)
        {
            $this->offsetSet($index, $value);
            return $this;
        }
    
        public function __isset($index)
        {
            return $this->offsetExists($index);
        }
    
        public function __unset($index)
        {
            return $this->offsetUnset($index);
        }
    
        public function __toString()
        {
            return serialize($this);
        }
    }
    

    Example of usage

    $obj2 = new FooObject(['a' => 'alpha']);
    //Access Property
    var_dump($obj2['a']); //alpha
    var_dump($obj2->offsetGet('a'));//alpha
    var_dump($obj2->a); //alpha
    
    //Serialization
    var_dump(serialize($obj));// string 'C:11:"ArrayObject":41:{x:i:0;a:1:{s:1:"a";s:5:"alpha";};m:a:0:{}}' (length=65)
    var_dump($obj->serialize());// string 'x:i:0;a:1:{s:1:"a";s:5:"alpha";};m:a:0:{}'
    var_dump(serialize($obj) === $obj->serialize());// false !!!
    
    //Setting Properties
    $obj2['b'] = 'beta'; //OK
    $obj2->c = 'gamma'; //OK
    var_dump($obj2);
    /* OBJECT DUMP
    object(FooObject)[14]
      private 'storage' (ArrayObject) =>
        array (size=3)
          'a' => string 'alpha' (length=5)
          'b' => string 'beta' (length=4)
          'c' => string 'gamma' (length=5)
     */
    
    //Property validation as array
    var_dump(isset($obj2['a']));//true
    var_dump(isset($obj2['b']));//true
    var_dump(isset($obj2['c']));//true
    //Property validation as object
    var_dump(isset($obj2->a));//true
    var_dump(isset($obj2->b));//true
    var_dump(isset($obj2->c));//true
    
    //Typecasting
    var_dump((array)$obj2);
    /*
    array (size=3)
      'a' => string 'alpha' (length=5)
      'b' => string 'beta' (length=4)
      'c' => string 'gamma' (length=5)
     */
    

提交回复
热议问题