PHP-style Serialization of JavaScript Objects

Heavily-scripted data entry forms that expand on-the-fly as the user enters new data make life easier for the user. However, it can be hell for a developer to devise a method for the representation and submission of data with arbitrary length and depth.

A logical thing to do seems to be to take an object-oriented approach to represent all client-side data as one or multiple JavaScript objects. The objects themselves could be arrays or objects with child objects and so on. Of course the objects need to be serialized in some way before they’re posted back. Now, if you’re using PHP at the back-end, you might make use one of the multitude of JSON implementations out there for posting back the data in JSON format and converting the data back to PHP objects (assuming that you want to end up with PHP objects.) JSON is pretty much native to JavaScript (clever use of the toSource method may cut it in most cases) but you need extra PHP code for deserialization.

If you want to take the opposite approach and use PHP-native serialization, then the following function will convert any complex JavaScript object into PHP-serialized string which can be converted back into PHP objects with a single call to the deserialize PHP function. The function makes use of the method for finding out the actual class names of JavaScript objects, that I’ve written about earlier.

Update: This function is now made part of the “Porting PHP to Javascript” project, spearheaded by Kevin van Zonneveld. Move over to Kevin’s blog to see an evolving version of the serialize function and many other PHP functions that have been ported to JavaScript.

/* Returns the class name of the argument or undefined if
   it's not a valid JavaScript object.
*/
function getObjectClass(obj)
{
    if (obj && obj.constructor && obj.constructor.toString)
    {
        var arr = obj.constructor.toString().match(
            /function\s*(\w+)/);

        if (arr && arr.length == 2)
        {
            return arr[1];
        }
    }

    return undefined;
}

/* Serializes the given argument, PHP-style.

   The type mapping is as follows:

   JavaScript Type    PHP Type
   ---------------    --------
   Number             Integer or Decimal
   String             String
   Boolean            Boolean
   Array              Array
   Object             Object
   undefined          Null

   The special JavaScript object null also becomes PHP Null.
   This function may not handle associative arrays or array
   objects with additional properties well. Returns false when
   called with an argument that can't be represented in PHP.
*/
function phpSerialize(val)
{
    switch (typeof(val))
    {
    case "number":
        if (val == NaN || val == Infinity)
        {
            return false;
        }
        return (Math.floor(val) == val ? "i" : "d") + ":" +
            val + ";";
    case "string":
        return "s:" + val.length + ":\"" + val + "\";";
    case "boolean":
        return "b:" + (val ? "1" : "0") + ";";
    case "object":
        if (val == null)
        {
            return "N;";
        }
        else if (val instanceof Array)
        {
            var idxobj = { idx: -1 };

            return "a:" + val.length + ":{" + val.map(
                function (item)
                {
                    this.idx++;

                    var ser = phpSerialize(item);

                    return ser ?
                        phpSerialize(this.idx) + ser :
                        false;
                }, idxobj).filter(
                function (item)
                {
                    return item;
                }).join("") + "}";
        }
        else
        {
            var class_name = getObjectClass(val);

            if (class_name == undefined)
            {
                return false;
            }

            var props = new Array();

            for (var prop in val)
            {
                var ser = phpSerialize(val[prop]);

                if (ser)
                {
                    props.push(phpSerialize(prop) + ser);
                }
            }
            return "O:" + class_name.length + ":\"" +
                class_name + "\":" + props.length + ":{" +
                props.join("") + "}";
        }
    case "undefined":
        return "N;";
    }

    return false;
}

2 Responses to “PHP-style Serialization of JavaScript Objects”

  1. Alexey Luchkovsky Says:

    Cool site, good code…..

    I did similar to your code approach included deserialization of script objects two years ago.

    Thank you

  2. Web 2.0 Announcer Says:

    PHP-style Serialization of JavaScript Objects…

    […]Heavily-scripted data entry forms that expand on-the-fly as the user enters new data make life easier for the user. However, it can be hell for a developer to devise a method for the representation and submission of data with arbitrary length and …

Leave a Reply