Я пытаюсь 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 является "динамическими объектами". Существуют ли волшебные свойства на самом деле, спорит о семантике.
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
Откуда он мог узнать ваши свойства?
$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 не имеет общедоступных свойств, только волшебный метод и вытекающий из него синтаксический сахар (испортился)
Из вашего комментария:
Я спрашиваю о магических свойствах, а не о методах. - Vegard Larsen 1 min ago
Все, что у вас есть сейчас - это магический метод, который вызывается, когда вы пытаетесь получить доступ к невидимому свойству.
Давайте повторим это снова
То, как реализован магический метод, не волнует PHP, и он не может знать, что вы используете магический метод, чтобы "волшебным образом" создать впечатление, что существует $obj->foo
.
Если свойство не существует, оно не будет помещено в объект, когда вы json_encode
его.
Более того, даже если бы json_encode
знал, что __get
активно, он бы не знал, какое значение использовать для его вызова.
Я бы создал для объекта метод, возвращающий внутренний массив.
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();
Это правильное поведение
.
JSON может содержать только данные, а не методы - он предназначен для независимости от языка, поэтому кодирование методов объекта не имеет смысла.