JSON-encoding private class members

In an AJAX application, it often is necessary to transport objects from your server-side PHP to the client-side Javascript. Using JSON to encode objects makes your life very easy on the client side — a quick eval(), and you’ve got an object to work with.

Sharing the object model between server and client can be very convenient. Imagine, for instance, you are building a CMS with a sophisticated front-end GUI. If the GUI code can easily access the same objects as the back-end code, you can avoid wasting a lot of time marshalling data for transport across the PHP/Javascript boundary. Your team will thank you for it, too, as the objects will have the same members they’re familiar with.

Of course, you don’t automatically get the class methods in the JSON-created objects. It can take more work to implement true encapsulation with the Javascript objects. But depending on your needs, you may be able to do without that. In the CMS example, your GUI may need to do little more than read the members’ values to load the GUI, and then set them back when the user wants to save the object. You might not really need any methods. At any rate, what you do with the objects in the Javascript domain is beyond the scope of this article.

I’m much more concerned with *getting* the objects to the Javascript in the first place, which can be harder than you would think. The issues around this get far less discussion than I would expect.

Encapsulation

In a language like PHP that has good support for access modifiers, it is good practice to encapsulate the members of your classes, setting their access to private or protected. If a consumer of the class needs to set a value, it should do it through a setter function, letting the class handle any tasks associated with changing the value.

So far, so good, right?

Now suppose you want to share your PHP object with your client-side Javascript. In PHP 5, you have the handy json_encode() function to encode your object into JSON, ready for consumption by the Javascript code. Unfortunately, json_encode() will only encode the public members of your class. In many well-designed classes, you’ll have no public members at all. So you’ll get empty JSON objects. This is not helpful.

How can you get those private member variables wrapped up in the JSON? One possibility is using get_object_vars(), which returns a hashed array of member variable name/values. If called from outside the object, it will include only public members. But if called from within the object, it will return public, protected, and private members.

You could have a quick way to get to all the private members of any class in your library if you derive them all from a base class that contains a method (for example, get_all_members()) which calls get_object_vars().

Sounds pretty good, but wait, there’s a catch. If you use this strategy, the get_object_vars() in your base class won’t see any private member variables defined in derived classes! (Actually, in PHP 5.1, it could see those private member variables, but this loophole went away in PHP 5.2).

To workaround this, you could do one of two things:

  1. don’t use private members in your derived classes; instead, use protected
  2. implement get_object_vars() in every class, and have it call its way up the inheritance tree, building an array of all members as it goes up the tree.

Neither of these approaches is very appealing, especially if your object model is already established.

There’s one more loophole that exists for getting at the private members: casting your object to an array. Let’s look at what happens when you do this:

Running this code gives you this:

Note that you can see the private and protected members as well as the public ones. The names of these members are not quite ready for use, as they are prefixed with extra characters. In this output, you can’t see it, but there are null characters in the member names. So the private _a is represented as \0foo\0_a. The protected _b is represented as \0*\0_b.

It’s easy enough to use those null characters to clean up the output of the cast. We can wrap this up in a nice convenient class library:

Note that if the object has any member variables referencing other objects, it will recurse through them.

You can now send a JSON-encoded representation of your object, including all private and protected members, with a call like this:

It’s a pretty easy next step to build a convenience function to do the variable extraction and the JSON encoding so you only need one call when you want to generate JSON.

Viability

Note that this solution seems a bit shaky. It relies on the fact that the casting provides a back door into the private members. Given that the PHP team closed some similar back doors when moving from PHP 5.1 to PHP 5.2 (base classes can no longer access private members of derived classes via get_object_vars()), who knows if they might close this back door, too?

I’m sure that some OO purists would disagree, but I think that the json_encode() function should have an optional parameter that forces it to include private members. This would allow you to use good PHP practice in your object design, still have objects that can cross the PHP/Javascript boundary, and not rely on shaky hacks to accomplish it.

Leave a comment

Your email address will not be published. Required fields are marked *