json_encode на классе с волшебными свойствами

Я пытаюсь 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 не работал.

BTW: термин из документации PHP является "динамическими объектами". Существуют ли волшебные свойства на самом деле, спорит о семантике.

5
задан Vegard Larsen 13 February 2010 в 16:18
поделиться

5 ответов

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

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

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
3
ответ дан 18 December 2019 в 05:31
поделиться

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

$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
ответ дан 18 December 2019 в 05:31
поделиться

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

Я спрашиваю о магических свойствах, а не о методах. - Vegard Larsen 1 min ago

Не существует такой вещи, как магическое свойство.

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

Давайте повторим это снова

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

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

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

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

5
ответ дан 18 December 2019 в 05:31
поделиться

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

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();
1
ответ дан 18 December 2019 в 05:31
поделиться

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

0
ответ дан 18 December 2019 в 05:31
поделиться
Другие вопросы по тегам:

Похожие вопросы: