A community in which webmasters can ask for help with topics such as PHP coding , MySQL , IT jobs, web design, IT security.
Current location:homephp forumphp talk in 2008 yearPHP: __toString() and json_encode() not playing well together - page 1
User InfoPosts
PHP: __toString() and json_encode() not playing well together#1
I(ve run into an odd problem and I(m not sure how to fix it. I have several classes that are all PHP implementations of JSON objects. Here( an illustration of the issue

class A
{
protected $a;

public function __construct()
{
$this->a = array( new B, new B );
}

public function __toString()
{
return json_encode( $this->a );
}
}

class B
{
protected $b = array( (foo( => (bar( );

public function __toString()
{
return json_encode( $this->b );
}
}

$a = new A();

echo $a;


The output from this is

[{},{}]


When the desired output is

[{"foo":"bar"},{"foo":"bar"}]


The problem is that I was relying on the __toString() hook to do my work for me. But it can(t, because the serialize that json_encode() uses won(t call __toString(). When it encounters a nested object it simply serializes public properties only.

So, the question then become this: Is there a way I can develop a managed interface to JSON classes that both lets me use setters and getters for properties, but also allows me to get the JSON serialization behavior I desire?

If that(s not clear, here(s an example of an implementation that won(t work, since the __set() hook is only called for the initial assignment

class a
{
public function __set( $prop, $value )
{
echo __METHOD__, PHP_EOL;
$this->$prop = $value;
}

public function __toString()
{
return json_encode( $this );
}
}

$a = new a;
$a->foo = (bar(;
$a->foo = (baz(;

echo $a;


I suppose I could also do something like this

class a
{
public $foo;

public function setFoo( $value )
{
$this->foo = $value;
}

public function __toString()
{
return json_encode( $this );
}
}

$a = new a;
$a->setFoo( (bar( );

echo $a;


But then I would have to rely on the diligence of the other developers to use the setters - I can(t force adherence programmtically with this solution.

---> EDIT <---

Now with a test of Rob Elsner(s response

<?php

class a implements IteratorAggregate
{
public $foo = (bar(;
protected $bar = (baz(;

public function getIterator()
{
echo __METHOD__;
}
}

echo json_encode( new a );


When you execute this, you can see that the getIterator() method isn(t ever invoked.

posted date: 2008-12-30 16:09:00


Re: PHP: __toString() and json_encode() not playing well together#2
I had made out the solution of this problem. click to view my topic...

hope that hepls.

posted date: 2008-12-30 16:09:01


Re: PHP: __toString() and json_encode() not playing well together#3
Isn(t your answer in the PHP docs for json_encode?For anyone who has run into the problem of private properties not being added, you can simply implement the IteratorAggregate interface with the getIterator() method. Add the properties you want to be included in the output into an array in the getIterator() method and return it.

posted date: 2008-12-30 17:12:00


Re: PHP: __toString() and json_encode() not playing well together#4
Even if your protected variable was public instead of protected, you won(t have the desired input since this will output the entire object like this:[{"b":{"foo":"bar"}},{"b":{"foo":"bar"}}]Instead of:[{"foo":"bar"},{"foo":"bar"}]It will most likely defeat your purpose, but i(m more inclined to convert to json in the original class with a default getter and calling for the values directlyclass B{ protected $b = array( (foo( => (bar( ); public function __get($name) { return json_encode( $this->$name ); }}Then you could do with them whatever you desire, even nesting the values in an additional array like your class A does, but using json_decode.. it still feels somewhat dirty, but works.class A{ protected $a; public function __construct() { $b1 = new B; $b2 = new B; $this->a = array( json_decode($b1->b), json_decode($b2->b) ); } public function __toString() { return json_encode( $this->a ); }}In the documentation there are some responses to this problem (even if i don(t like most of them, serializing + stripping the properties makes me feel dirty).

posted date: 2008-12-30 17:39:00


Re: PHP: __toString() and json_encode() not playing well together#5
But that doesn't work, or I'm doing it wrong. I edited my question to include this example.

posted date: 2008-12-30 22:51:00


Re: PHP: __toString() and json_encode() not playing well together#6
You(re right the __toString() for the class B is not being called, because there is no reason to. So to call it, you can use a castclass A{ protected $a; public function __construct() { $this->a = array( (string)new B, (string)new B ); } public function __toString() { return json_encode( $this->a ); }}Note: the (string) cast before the new B(s ... this will call the _toString() method of the B class, but it won(t get you what you want, because you will run into the classic "double encoding" problems, because the array is encoded in the B class _toString() method, and it will be encoded again in the A class _toString() method.So there is a choice of decoding the result after the cast, ie: $this->a = array( json_decode((string)new B), json_decode((string)new B) );or you(re going to need to get the array, by creating a toArray() method in the B class that returns the straight array. Which will add some code to the line above because you can(t use a PHP constructor directly (you can(t do a new B()->toArray(); ) So you could have something like:$b1 = new B;$b2 = new B;$this->a = array( $b1->toArray(), $b2->toArray() );

posted date: 2008-12-30 23:28:00


Re: PHP: __toString() and json_encode() not playing well together#7
Alternatively, can you create public variables as these properties, and override __get on that public variable to return the private value, and __set on that variable name to throw an exception?

posted date: 2009-01-01 11:20:00


Re: PHP: __toString() and json_encode() not playing well together#8
Yes, looking at the PHP source and some other comments, I would recommend you declare public properties, unset them in the constructor, and then override __get to return the private data for that variable name.

posted date: 2009-01-01 11:33:00


Re: PHP: __toString() and json_encode() not playing well together#9
That won't work either, because then json_encode won't find the variables since they aren't public anymore.

posted date: 2009-01-05 12:01:00


Re: PHP: __toString() and json_encode() not playing well together#10
A late answers but might be useful for others with the same problem.In PHP < 5.4.0json_encode doesn(t call any method from the object. That is valid for getIterator, __serialize, etc...In PHP > v5.4.0, however, a new interface was introduced, called JsonSerializable.It basically controls the behaviour of the object when json_encode is called on that object.Fiddle (Well, actually is PHP CodePad, but same thing...)Example:class A implements JsonSerializable{ protected $a = array(); public function __construct() { $this->a = array( new B, new B ); } public function jsonSerialize() { return $this->a; }}class B implements JsonSerializable{ protected $b = array( (foo( => (bar( ); public function jsonSerialize() { return $this->b; }}$foo = new A();$json = json_encode($foo);var_dump($json);Outputs:string(29) "[{"foo":"bar"},{"foo":"bar"}]"

posted date: 2013-04-24 14:23:00


Re: PHP: __toString() and json_encode() not playing well together#11
In PHP > v5.4.0 you can implement the interface called JsonSerializable as described in the answer by Tivie.For those of us using PHP < 5.4.0 you can use a solution which employs get_object_vars() from within the object itself and then feeds those to json_encode(). That is what I have done in the following example, using the __toString() method, so that when I cast the object as a string, I get a JSON encoded representation.Also included is an implementation of the IteratorAggregate interface, with its getIterator() method, so that we can iterate over the object properties as if they were an array.<?phpclass TestObject implements IteratorAggregate { public $public = "foo"; protected $protected = "bar"; private $private = 1; private $privateList = array("foo", "bar", "baz" => TRUE); /** * Retrieve the object as a JSON serialized string * * @return string */ public function __toString() { $properties = $this->getAllProperties(); $json = json_encode( $properties, JSON_FORCE_OBJECT | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT ); return $json; } /** * Retrieve an external iterator * * @link http://php.net/manual/en/iteratoraggregate.getiterator.php * @return \Traversable * An instance of an object implementing \Traversable */ public function getIterator() { $properties = $this->getAllProperties(); $iterator = new \ArrayIterator($properties); return $iterator; } /** * Get all the properties of the object * * @return array */ private function getAllProperties() { $all_properties = get_object_vars($this); $properties = array(); while (list ($full_name, $value) = each($all_properties)) { $full_name_components = explode("\0", $full_name); $property_name = array_pop($full_name_components); if ($property_name && isset($value)) $properties[$property_name] = $value; } return $properties; }}$o = new TestObject();print "JSON STRING". PHP_EOL;print "------" . PHP_EOL;print strval($o) . PHP_EOL;print PHP_EOL;print "ITERATE PROPERTIES" . PHP_EOL;print "-------" . PHP_EOL;foreach ($o as $key => $val) print "$key -> $val" . PHP_EOL;print PHP_EOL;?>This code produces the following output:JSON STRING------{"public":"foo","protected":"bar","private":1,"privateList":{"0":"foo","1":"bar","baz":true}}ITERATE PROPERTIES-------public -> fooprotected -> barprivate -> 1privateList -> Array

posted date: 2013-10-23 20:25:00


select page: « 1 »
Copyright ©2008-2017 www.momige.com, all rights reserved.