У меня есть объект класса 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 ()