Приведение объекта в массив - вызывается ли какой-нибудь магический метод?

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

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

$obj = new Foo();

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

$arr = (array)$obj;

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

Ответы на вопрос(6)

ArrayObject class, который позволяет обрабатывать объект как массив и может быть удобен при использовании в качестве контейнера для записи или коллекции базы данных.

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

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

__toArray магический метод в PHP. Анредложение @enhancement было отклонено в 2006 со следующим ответом:

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

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

interface ArrayConversion {function asArray (); }

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

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

 tacone27 дек. 2013 г., 14:16
но эй, разве мы не получили конструкцию goto? Мурашки
 Dienow21 янв. 2014 г., 16:03
Странно, что он должен так сказать, потому что на самом деле у php есть объект для преобразования в массив: Php.net / ручной / EN / ...
 tacone21 янв. 2014 г., 17:55
Вопрос заключается в том, чтобы реализовать собственную логику приведения.

который будет возвращать ассоциативный массив всех имен / значений свойств, доступных из контекста.

Видетьhttp: //php.net/manual/en/function.get-object-vars.ph

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

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

$s = (string)$obj;

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

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

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

а - использовать отражение. Это позволяет вам изучить свойства класса во время выполнения.

Взято из руководства:http: //www.php.net/manual/en/reflectionclass.getproperties.ph

<?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"
  }
}
Решение Вопроса

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

 Daniele Orlando22 дек. 2015 г., 01:38
Хорошо, но где-то в начале ответа укажите, что реализацияArrayAccess interface недостаточно для приведения объекта в массив.
 fyrye22 янв. 2016 г., 16:31
Также следует отметить, что для того, чтобы перебрать элементы, содержащиеся в объекте, который расширяетArrayAccess с помощьюforeach, вы также должны реализовать\IteratorAggregate или\Iterator. Обычно те, кто хочет воспроизвести функциональность массива в объекте, используютimplements \Countable, \IteratorAggregate, \ArrayAccess Видеть \ ArrayObject.
 Hubro29 апр. 2013 г., 10:39
Как бы хорошо, этот ответ на самом деле не касался вопроса. Вы должны хотя бы включить слово "нет" где-то там:)

Ваш ответ на вопрос