我有一个 Foo 类的对象:
class Foo extends Bar {
protected $a;
protected $b;
}
$obj = new Foo();
我想要(并且必须)做的就是将此对象转换为数组,如下所示:
$arr = (array)$obj;
此时是否有任何神奇(或非神奇:))方法正在被调用?或者有其他方法可以拦截吗?我知道我可以写一个简单的方法,例如。
asArray()
在 Foo 中,但我正在寻找一些更“原生”的 PHP 方法。
PHP 中没有
__toArray
魔法方法。 2006 年 一项增强提案已被拒绝,答案如下:
[2006-08-20 11:12 UTC] [电子邮件受保护]
为什么不简单地有一个方法 asArray() 甚至可以作为 接口:
接口 ArrayConversion { function asArray(); }
看,我们有 __toString,因为它在诸如 echo、打印等内部功能。但我们决定不 数组的自动转换已经。所以它永远不会被任何支持 语言构造。也就是说,没有必要这样做,也没有什么 你会战胜上面的界面。事实上你会成功的 php 更加复杂,因为您只需添加一项神奇功能。
因此,它不太可能在未来的任何版本中实现(如果你问我的话,这很遗憾)。
ArrayAccess
接口。这将允许您将对象视为数组而不进行强制转换,并且您可以完全控制成员的使用方式。
遗憾的是,不,转换为数组不会触发任何魔术方法,就像它完成的那样:
$s = (string)$obj;
它会触发
__toString()
方法并且您可以覆盖它。
但是,您可以编写自定义
toArray()
方法。
Serializable
接口感兴趣,它允许您编写自定义序列化器策略。
不确定这个问题是否仍然相关,但是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)
*/
在不更改原始类定义的情况下执行此操作的一种方法是使用反射。这允许您在运行时检查类的属性。
取自手册: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"
}
}
您可以使用 get_object_vars($yourObject) 它将返回可从上下文访问的所有属性名称/值的关联数组。
参见 http://php.net/manual/en/function.get-object-vars.php
如果您想访问受保护或私有属性,我的建议是扩展 ArrayObject,它实现 getArrayCopy() 方法
几种替代解决方案。希望您觉得这很有用。
解决方案#1
<?php
class Foo extends Bar implements \JsonSerializable {
protected int $a = 1;
protected int $b = 2;
public function jsonSerialize(): array
{
return ['a' => $a, 'b' => $b];
}
}
echo json_encode(new Foo()); // will produce: { "a": 1, "b": 2 }
$array = json_decode(json_encode(new Foo()), true);
解决方案#2
<?php
class Foo extends Bar {
public function __construct(private array $data = [])
{}
public function getProp(string $name): mixed
{
return $this->data[$name] ?? null;
}
public function toArray(): array
{
return $this->data;
}
}
$foo = new Foo(['a' => 1, 'b' => 2]);
foreach ($foo->toArray() as $propName => $propValue) {
// do some stuff
}
此外,您还可以在这里搜索其他解决方案:https://www.php.net/manual/en/language.oop5.iterations.php