Кастинг объекта массиву – вызов любого магического метода?

У меня есть объект класса Foo:

class Foo extends Bar { protected $a; protected $b; } $obj = new Foo(); 

То, что я хочу (и должен) сделать, это передать этот объект массиву, например:

 $arr = (array)$obj; 

Есть ли какой-нибудь магический (или не магический :)) метод, который сейчас называется? Или есть другой способ перехватить его? Я знаю, что могу написать простой метод, например. asArray() в Foo, но я ищу еще несколько «родных» способов PHP.

Вы можете реализовать ArrayAccess интерфейса ArrayAccess . Это позволит вам рассматривать объект как массив без кастинга, и вы получаете полный контроль над тем, как используются члены.

нет

В PHP нет магического метода __toArray . Предложение об улучшении было отклонено в 2006 году со следующим ответом:

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

Почему бы просто не использовать метод asArray (), возможно, даже в качестве параметра интерфейса:

интерфейс ArrayConversion {function asArray (); }

Смотрите, у нас есть __toString, поскольку он поддерживается в языковых конструкциях, таких как эхо, печать и другие внутренние функции. Но мы решили отказаться от автоперевозок для массивов. Поэтому он никогда не будет поддерживаться ни в одной языковой конструкции. Тем не менее, для этого нет необходимости, и вы не выиграли бы против вышеуказанного интерфейса. На самом деле вы сделали бы его более сложным, потому что добавили бы еще одну магическую функцию.

Поэтому очень маловероятно, что он будет реализован в любом будущем выпуске (что очень жаль, если вы спросите меня).

К сожалению нет, приведение в массив не вызывает никакого волшебного метода, как это делается с помощью:

 $s = (string)$obj; 

который запускает __toString() и который вы можете переопределить.

Однако вы можете написать собственный метод toArray() .

Вы также можете быть заинтересованы в интерфейсе Serializable который позволяет вам писать собственную стратегию serializer.

Не уверен, что этот вопрос все еще имеет значение, но 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

A вы хотите получить доступ к защищенным или приватным свойствам, мой совет будет заключаться в расширении ArrayObject, который реализует метод getArrayCopy ()