Подтвердить что ты не робот

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

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

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

$obj = new Foo();

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

$arr = (array)$obj;

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

4b9b3361

Ответ 1

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

Ответ 2

Нет

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

[2006-08-20 11:12 UTC] [email protected]

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

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

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

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

Ответ 3

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

$s = (string)$obj;

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

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

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

Ответ 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)
 */

Ответ 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"
  }
}

Ответ 6

Вы можете использовать get_object_vars ($ yourObject), который будет возвращать ассоциативный массив всех имен/значений свойств, доступных из контекста.

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

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