json_encode для класса с магическими свойствами - PullRequest
5 голосов
/ 13 февраля 2010

Я пытаюсь json_encode массив объектов, у всех из которых есть магические свойства с использованием __get и __set. json_encode полностью игнорирует их, в результате чего получается массив пустых объектов (все обычные свойства: private или protected).

Итак, представьте себе этот класс:

class Foo
{
    public function __get($sProperty)
    {
        if ($sProperty == 'foo')
        {
            return 'bar!';
        }
        return null;
    }
}

$object = new Foo();
echo $object->foo; // echoes "foo"
echo $object->bar; // warning
echo json_encode($object); // "{}"

Я пытался реализовать IteratorAggregate и Serializable для класса, но json_encode все еще не видит мои магические свойства. Поскольку я пытаюсь закодировать массив этих объектов, метод AsJSON() в классе также не будет работать.

Обновление! Кажется, вопрос легко понять. Как я могу сказать json_encode, какие "магические свойства" существуют? IteratorAggregate не работает.

Кстати: термин из документации PHP - это "динамические сущности". Существуют ли магические свойства на самом деле, это спор о семантике.

Ответы [ 8 ]

23 голосов
/ 14 ноября 2011

PHP 5.4 имеет интерфейс с именем JsonSerializable.У него есть один метод, jsonSerialize, который вернет данные для кодирования.Эта проблема будет легко решена путем ее реализации.

5 голосов
/ 13 февраля 2010

Из вашего комментария:

Я спрашиваю о магических свойствах, а не о методах. - Вегард Ларсен 1 минуту назад

Магического свойства .

не существует.

Все, что у вас есть сейчас, - это магический метод , который вызывается при попытке доступа к невидимому свойству .

Скажем еще раз

$ obj-> foo не существует

Способ реализации магического метода не касается PHP, и он не может волшебным образом знать, что вы используете свой магический метод, чтобы «волшебным образом» заставить его выглядеть так, как будто $obj->foo.

Если свойство не существует, оно не будет помещено в объект, когда вы json_encode его.

Более того, даже если json_encode знал, что __get активен, он не знал, какое значение использовать для его вызова.

3 голосов
/ 13 февраля 2010

json_encode () не «запрашивает» у объекта какой-либо интерфейс. Он напрямую выбирает указатель HashTable, который представляет свойства объекта, вызывая obj-> get_properties (). Затем он выполняет итерацию (опять же напрямую, без использования таких интерфейсов, как Traversable, Iterator и т. Д.) Над этим HashTable и обрабатывает элементы, помеченные как открытые. см static void json_encode_array() в ext / json / json.c
Это делает невозможным иметь свойство, которое будет отображаться в результате json_encode (), но недоступно как $ obj-> propname.

edit: я не очень много тестировал и забыл о "высокой производительности", но вы можете начать с

interface EncoderData {
  public function getData();
}

function json_encode_ex_as_array(array $v) {
  for($i=0; $i<count($v); $i++) {
    if ( !isset($v[$i]) ) {
      return false;
    }
  }
  return true;
}

define('JSON_ENCODE_EX_SCALAR', 0);
define('JSON_ENCODE_EX_ARRAY', 1);
define('JSON_ENCODE_EX_OBJECT', 2);
define('JSON_ENCODE_EX_EncoderDataObject', 3);

function json_encode_ex($v) {
  if ( is_object($v) ) {
    $type = is_a($v, 'EncoderData') ? JSON_ENCODE_EX_EncoderDataObject : JSON_ENCODE_EX_OBJECT;
  }
  else if ( is_array($v) ) {
    $type = json_encode_ex_as_array($v) ? JSON_ENCODE_EX_ARRAY : JSON_ENCODE_EX_OBJECT;
  }
  else {
    $type = JSON_ENCODE_EX_SCALAR;
  }

  switch($type) {
    case JSON_ENCODE_EX_ARRAY: // array [...]
      foreach($v as $value) {
        $rv[] = json_encode_ex($value);
      }
      $rv = '[' . join(',', $rv) . ']';
      break;
    case JSON_ENCODE_EX_OBJECT: // object { .... }
      $rv = array();
      foreach($v as $key=>$value) {
        $rv[] = json_encode((string)$key) . ':' . json_encode_ex($value);
      }
      $rv = '{' . join(',', $rv) .'}';
      break;
    case JSON_ENCODE_EX_EncoderDataObject:
      $rv = json_encode_ex($v->getData());
      break;
    default:
      $rv = json_encode($v);
  }
  return $rv;
}

class Foo implements EncoderData {
  protected $name;
  protected $child;

  public function __construct($name, $child) {
    $this->name = $name;
    $this->child = $child;

  }
  public function getData() {
    return array('foo'=>'bar!', 'name'=>$this->name, 'child'=>$this->child);
  }
}


$data = array();
for($i=0; $i<10; $i++) {
  $root = null;
  foreach( range('a','d') as $name ) {
    $root = new Foo($name, $root);
  }
  $data[] = 'iteration '.$i;
  $data[] = $root;
  $root = new StdClass;
  $root->i = $i;
  $data[] = $root;
}
$json = json_encode_ex($data);
echo $json, "\n\n\n";
$data = json_decode($json);
var_dump($data);

Существует по крайней мере один недостаток: он не обрабатывает рекурсию, например,

$obj = new StdClass;
$obj->x = new StdClass;
$obj->x->y = $obj;
echo json_encode($obj); // warning: recursion detected...
echo json_encode_ex($obj); // this one runs until it hits the memory limit
2 голосов
/ 13 февраля 2011

Это невероятно старый поток, но, чтобы не путать других авторов, которым интересно, как используются магические методы для получения / установки свойств в PHP и как это влияет на кодировку JSON, вот мое понимание.

Включение в класс методов __get и __set дает вам интерфейс для работы с динамически именованными свойствами для данного класса. Большинство людей определяют внутренний ассоциативный массив, который ведет домашнюю работу.

Причина, по которой ваш пример foo / bar не отображается в кодировке JSON, заключается в том, что вы просто создаете ассоциацию, которая возвращает значение. Это не установка свойства в самом объекте к этому значению.

Если вы сделали что-то вроде:

набор открытых функций ($ name, $ value) { $ this-> data [$ name] = $ value; }

Тогда, если вы позвонили:

$ foo = new ObjectClass; $ Foo-> набор ( 'Foo', 'бар');

теперь в этом массиве есть элемент $ data ['foo'] = 'bar' если вы json_encode объекта, то это отношение будет представлено.

json_encode не кодирует методы, только свойства. Там нет никакого способа обойти это. Для вашего кода, функционального эквивалента вашего волшебного __get, можно было бы включить в конструктор вызов set, который «жестко связывает» значение свойства с именем «foo».

В конечном счете, я не знаю, с какой спецификой вы боретесь, поскольку вы не предоставили много деталей. Однако json_encoding для объекта или массива просто выдаст вам список доступных свойств. Если вам нужно пройти через функцию интерпретации и каким-то образом полагаться на нее, это другая проблема, на которую, к сожалению, я не могу ответить.

Надеюсь, это поможет, хотя это часть устаревшей темы, которая, по иронии судьбы, IMO, содержит решение.

1 голос
/ 13 февраля 2010

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

class Foo
{
    private $prop = array();

    public function __get($sProperty)
    {
       return $this->prop[$sProperty];
    }

    public function __set($sProperty, $value)
    {
       $this->prop[$sProperty] = $value;
    }

    public function getJson(){
        return json_encode($this->prop);
    }
}



$f = new Foo();
$f->foo = 'bar';
$json = $f->getJson();
0 голосов
/ 02 сентября 2011

Я нашел следующее, что помогло мне создать классы с динамическими свойствами и вывести их с помощью json_encode. Может быть, это помогает другим людям.

http://krisjordan.com/dynamic-properties-in-php-with-stdclass

0 голосов
/ 13 февраля 2010

Как он мог узнать ваши свойства?

$object = new Foo();
echo $object->foo; // how do you know to access foo and not oof?
                   // how do you expect json_encode to know to access foo?

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

echo json_encode($object); // "{}"

конечно, он пустой, $ object не имеет общедоступных свойств, просто магический метод и последующий синтаксический сахар (скисший)

0 голосов
/ 13 февраля 2010

Это правильное поведение
JSON может содержать только данные, а не методы - он не зависит от языка, поэтому методы кодирования объектов не имеют смысла.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...