44

我有一个 Foo 类的对象:

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

$obj = new Foo();

我想要(并且必须)做的是将此对象转换为数组,如下所示:

$arr = (array)$obj;

目前是否有任何魔法(或不是魔法:))方法被调用?或者有没有其他方法可以拦截?我知道我可以写一个简单的方法,例如。asArray()在 Foo 中,但我正在寻找一些更“原生”的 PHP 方式。

4

6 回答 6

49

PHP中没有__toArray神奇的方法。2006 年,一项增强提案被拒绝,答案如下:

[2006-08-20 11:12 UTC] helly@php.net

为什么不简单地将方法 asArray() 甚至作为接口的一部分:

接口 ArrayConversion { 函数 asArray(); }

看,我们有 __toString,因为它在 echo、print 和其他内部函数等语言结构中得到支持。但是我们已经决定反对数组的自动转换。因此,任何语言结构都不会支持它。也就是说,没有必要这样做,并且您不会赢得上述界面。事实上,你会让 php 变得更复杂,因为你只需要添加一个神奇的特性。

因此,它不太可能在未来的任何版本中实现(如果你问我,这很遗憾)。

于 2013-12-27T13:15:57.263 回答
46

您可以让该类实现ArrayAccess接口。这将允许您将对象视为数组而不进行强制转换,并且您可以完全控制成员的使用方式。

于 2012-06-20T14:18:15.377 回答
15

可悲的是,没有,强制转换为数组不会触发任何魔术方法,就像它完成的那样:

$s = (string)$obj;

哪个触发__toString()方法,您可以覆盖哪个。

但是,您可以编写自定义toArray()方法。

您可能还对Serializable允许您编写自定义序列化器策略的接口感兴趣。

于 2012-06-20T14:22:53.783 回答
4

不确定这个问题是否仍然相关,但 php 有内置的 ArrayObject类,它允许将对象视为数组,并且在用作数据库记录或集合的容器时很方便。

就严格类型而言,这可能不是最佳实践,但它允许将对象视为数组,并且两个语句都是有效的。

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

但是,您需要牢记以下行为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 接受两个标志ArrayObject::STD_PROP_LIST作为默认标志和ArrayObject::ARRAY_AS_PROPS备选标志。

这会改变读取值的行为,但不支持以这种方式设置新属性,下面是示例:

$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)
 */

为了使这种行为更加一致,您必须扩展此类并实现魔术方法__get()__set()和。__isset()__unset()

另一个棘手的部分是序列化,默认方法serialize将返回一个副本序列化$storage变量而不是对象本身,作为返回实例的序列化副本的解决方法,您可以在__toString方法中实现默认序列化,这样它的行为正确。

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

使用示例

$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)
 */
于 2016-12-28T16:41:31.740 回答
2

在不更改原始类定义的情况下,一种方法是使用反射。这允许您在运行时检查类的属性。

取自手册:http ://www.php.net/manual/en/reflectionclass.getproperties.php

<?php
class Foo {
    public    $foo  = 1;
    protected $bar  = 2;
    private   $baz  = 3;
}

$foo = new Foo();

$reflect = new ReflectionClass($foo);
$props   = $reflect->getProperties(ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED);

foreach ($props as $prop) {
    print $prop->getName() . "\n";
}

var_dump($props);

?>

The above example will output something similar to:
foo
bar
array(2) {
  [0]=>
  object(ReflectionProperty)#3 (2) {
    ["name"]=>
    string(3) "foo"
    ["class"]=>
    string(3) "Foo"
  }
  [1]=>
  object(ReflectionProperty)#4 (2) {
    ["name"]=>
    string(3) "bar"
    ["class"]=>
    string(3) "Foo"
  }
}
于 2012-09-17T14:31:31.277 回答
1

您可以使用 get_object_vars($yourObject) 将返回从上下文可访问的所有属性名称/值的关联数组。

http://php.net/manual/en/function.get-object-vars.php

如果您想访问受保护或私有属性,我的建议是扩展 ArrayObject,它实现方法 getArrayCopy()

于 2014-05-05T10:23:04.637 回答