Import Horde_Serialize from CVS.
authorMichael M Slusarz <slusarz@curecanti.org>
Fri, 13 Feb 2009 04:42:11 +0000 (21:42 -0700)
committerMichael M Slusarz <slusarz@curecanti.org>
Fri, 13 Feb 2009 04:42:11 +0000 (21:42 -0700)
framework/Serialize/doc/Horde/Serialize/examples/json.php [new file with mode: 0644]
framework/Serialize/lib/Horde/Serialize.php [new file with mode: 0644]
framework/Serialize/package.xml [new file with mode: 0644]
framework/Serialize/test/Horde/Serialize/json_assoc_array.phpt [new file with mode: 0644]
framework/Serialize/test/Horde/Serialize/json_empty.phpt [new file with mode: 0644]
framework/Serialize/test/Horde/Serialize/json_encode_decode.phpt [new file with mode: 0644]
framework/Serialize/test/Horde/Serialize/json_nested_array.phpt [new file with mode: 0644]
framework/Serialize/test/Horde/Serialize/json_object.phpt [new file with mode: 0644]
framework/Serialize/test/Horde/Serialize/json_spaces_comments.phpt [new file with mode: 0644]
framework/Serialize/test/Horde/Serialize/json_unquoted_keys.phpt [new file with mode: 0644]

diff --git a/framework/Serialize/doc/Horde/Serialize/examples/json.php b/framework/Serialize/doc/Horde/Serialize/examples/json.php
new file mode 100644 (file)
index 0000000..9cffe87
--- /dev/null
@@ -0,0 +1,15 @@
+<?php
+/**
+ * @package Horde_Serialize
+ */
+
+require_once 'Horde/Serialize.php';
+
+// Convert a complex value to JSON notation, and send it to the
+// browser.
+$value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
+echo Horde_Serialize::serialize($value, SERIALIZE_JSON);
+// prints: ["foo","bar",[1,2,"baz"],[3,[4]]]
+
+// Accept incoming POST data, assumed to be in JSON notation.
+var_dump(Horde_Serialize::unserialize(file_get_contents('php://stdin'), SERIALIZE_JSON));
diff --git a/framework/Serialize/lib/Horde/Serialize.php b/framework/Serialize/lib/Horde/Serialize.php
new file mode 100644 (file)
index 0000000..24fd3eb
--- /dev/null
@@ -0,0 +1,396 @@
+<?php
+/**
+ * The Serialize:: class provides various methods of encapsulating data.
+ *
+ * Copyright 2001-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author   Stephane Huther <shuther1@free.fr>
+ * @package  Horde_Serialize
+ * @category Horde
+ */
+
+/* TODO */
+define('SERIALIZE_UNKNOWN', -1);
+define('SERIALIZE_NONE', 0);
+define('SERIALIZE_WDDX', 1);
+define('SERIALIZE_BZIP', 2);
+define('SERIALIZE_IMAP8', 3);
+define('SERIALIZE_IMAPUTF7', 4);
+define('SERIALIZE_IMAPUTF8', 5);
+define('SERIALIZE_BASIC', 6);
+define('SERIALIZE_GZ_DEFLATE', 7);
+define('SERIALIZE_GZ_COMPRESS', 8);
+define('SERIALIZE_GZ_ENCOD', 9);
+define('SERIALIZE_BASE64', 10);
+define('SERIALIZE_SQLXML', 11);
+define('SERIALIZE_RAW', 12);
+define('SERIALIZE_URL', 13);
+define('SERIALIZE_UTF7', 14);
+define('SERIALIZE_UTF7_BASIC', 15);
+define('SERIALIZE_JSON', 16);
+define('SERIALIZE_LZF', 17);
+
+class Horde_Serialize
+{
+    /* Type constants */
+    const UNKNOWN = -1;
+    const NONE = 0;
+    const WDDX = 1;
+    const BZIP = 2;
+    const IMAP8 = 3;
+    const IMAPUTF7 = 4;
+    const IMAPUTF8 = 5;
+    const BASIC = 6;
+    const GZ_DEFLATE = 7;
+    const GZ_COMPRESS = 8;
+    const GZ_ENCODE = 9;
+    const BASE64 = 10;
+    const SQLXML = 11;
+    const RAW = 12;
+    const URL = 13;
+    const UTF7 = 14;
+    const UTF7_BASIC = 15;
+    const JSON = 16;
+    const LZF = 17;
+
+    /**
+     * Serialize a value.
+     *
+     * See the list of constants at the top of the file for the serializing
+     * techniques that can be used.
+     *
+     * @param mixed $data    The data to be serialized.
+     * @param mixed $mode    The mode of serialization. Can be either a single
+     *                       mode or array of modes.  If array, will be
+     *                       serialized in the order provided.
+     * @param mixed $params  Any additional parameters the serialization method
+     *                       requires.
+     *
+     * @return string  The serialized data.
+     *                 Returns PEAR_Error on error.
+     */
+    static public function serialize($data, $mode = array(self::BASIC),
+                                     $params = null)
+    {
+        if (!is_array($mode)) {
+            $mode = array($mode);
+        }
+
+        /* Parse through the list of serializing modes. */
+        foreach ($mode as $val) {
+            /* Check to make sure the mode is supported. */
+            if (!self::hasCapability($val)) {
+                return PEAR::raiseError('Unsupported serialization type');
+            }
+            $data = self::_serialize($data, $val, $params);
+            if (is_a($data, 'PEAR_Error')) {
+                break;
+            }
+        }
+
+        return $data;
+    }
+
+    /**
+     * Unserialize a value.
+     *
+     * See the list of constants at the top of the file for the serializing
+     * techniques that can be used.
+     *
+     * @param mixed $data    The data to be unserialized.
+     * @param mixed $mode    The mode of unserialization.  Can be either a
+     *                       single mode or array of modes.  If array, will be
+     *                       unserialized in the order provided.
+     * @param mixed $params  Any additional parameters the unserialization
+     *                       method requires.
+     *
+     * @return string  The unserialized data.
+     *                 Returns PEAR_Error on error.
+     */
+    static public function unserialize($data, $mode = self::BASIC,
+                                       $params = null)
+    {
+        if (!is_array($mode)) {
+            $mode = array($mode);
+        }
+
+        /* Parse through the list of unserializing modes. */
+        foreach ($mode as $val) {
+            /* Check to make sure the mode is supported. */
+            if (!self::hasCapability($val)) {
+                return PEAR::raiseError('Unsupported unserialization type');
+            }
+            $data = self::_unserialize($data, $val, $params);
+            if (is_a($data, 'PEAR_Error')) {
+                break;
+            }
+        }
+
+        return $data;
+    }
+
+    /**
+     * Check whether or not a serialization method is supported.
+     *
+     * @param integer $mode  The serialization method.
+     *
+     * @return boolean  True if supported, false if not.
+     */
+    static public function hasCapability($mode)
+    {
+        switch ($mode) {
+        case self::BZIP:
+            return Util::extensionExists('bz2');
+
+        case self::WDDX:
+            return Util::extensionExists('wddx');
+
+        case self::IMAPUTF7:
+            return class_exists('Horde_Imap_Client');
+
+        case self::IMAP8:
+        case self::IMAPUTF8:
+            return class_exists('Horde_Mime');
+
+        case self::GZ_DEFLATE:
+        case self::GZ_COMPRESS:
+        case self::GZ_ENCODE:
+            return Util::extensionExists('zlib');
+
+        case self::SQLXML:
+            return @include_once 'XML/sql2xml.php';
+
+        case self::LZF:
+            return Util::extensionExists('lzf');
+
+        case self::NONE:
+        case self::BASIC:
+        case self::BASE64:
+        case self::RAW:
+        case self::URL:
+        case self::UTF7:
+        case self::UTF7_BASIC:
+        case self::JSON:
+            return true;
+
+        default:
+            return false;
+        }
+    }
+
+    /**
+     * Serialize data.
+     *
+     * @param mixed $data    The data to be serialized.
+     * @param mixed $mode    The mode of serialization. Can be
+     *                       either a single mode or array of modes.
+     *                       If array, will be serialized in the
+     *                       order provided.
+     * @param mixed $params  Any additional parameters the serialization method
+     *                       requires.
+     *
+     * @return string  A serialized string or PEAR_Error on error.
+     */
+    static protected function _serialize($data, $mode, $params = null)
+    {
+        switch ($mode) {
+        case self::NONE:
+            break;
+
+        // $params['level'] = Level of compression (default: 3)
+        // $params['workfactor'] = How does compression phase behave when given
+        //                         worst case, highly repetitive, input data
+        //                         (default: 30)
+        case self::BZIP:
+            $data = bzcompress($data, isset($params['level']) ? $params['level'] : 3, isset($params['workfactor']) ? $params['workfactor'] : 30);
+            if (is_integer($data)) {
+                $data = false;
+            }
+            break;
+
+        case self::WDDX:
+            $data = wddx_serialize_value($data);
+            break;
+
+        case self::IMAP8:
+            $data = Horde_Mime::quotedPrintableEncode($data);
+            break;
+
+        case self::IMAPUTF7:
+            require_once 'Horde/String.php';
+            $data = Horde_Imap_Client_Utf7imap::Utf8ToUtf7Imap(String::convertCharset($data, 'ISO-8859-1', 'UTF-8'));
+            break;
+
+        case self::IMAPUTF8:
+            $data = Horde_Mime::decode($data, 'UTF-8');
+            break;
+
+        // $params['level'] = Level of compression (default: 3)
+        case self::GZ_DEFLATE:
+            $data = gzdeflate($data, isset($params['level']) ? $params['level'] : 3);
+            break;
+
+        case self::BASIC:
+            $data = serialize($data);
+            break;
+
+        // $params['level'] = Level of compression (default: 3)
+        case self::GZ_COMPRESS:
+            $data = gzcompress($data, isset($params['level']) ? $params['level'] : 3);
+            break;
+
+        case self::BASE64:
+            $data = base64_encode($data);
+            break;
+
+        // $params['level'] = Level of compression (default: 3)
+        case self::GZ_ENCOD:
+            $data = gzencode($data, isset($params['level']) ? $params['level'] : 3);
+            break;
+
+        case self::RAW:
+            $data = rawurlencode($data);
+            break;
+
+        case self::URL:
+            $data = urlencode($data);
+            break;
+
+        case self::SQLXML:
+            require_once 'DB.php';
+            $sql2xml = &new xml_sql2xml();
+            $data = $sql2xml->getXML($data);
+            break;
+
+        // $params = Source character set
+        case self::UTF7:
+            require_once 'Horde/String.php';
+            $data = String::convertCharset($data, $params, 'UTF-7');
+            break;
+
+        // $params = Source character set
+        case self::UTF7_BASIC:
+            $data = self::serialize($data, array(self::UTF7, self::BASIC), $params);
+            break;
+
+        // $params = Source character set
+        case self::JSON:
+            require_once 'Horde/String.php';
+            if (!empty($params)) {
+                $data = String::convertCharset($data, $params, 'UTF-8');
+            }
+            $data = json_encode($data);
+            break;
+
+        case self::LZF:
+            $data = lzf_compress($data);
+            break;
+        }
+
+        if ($data === false) {
+            return PEAR::raiseError('Serialization failed.');
+        }
+        return $data;
+    }
+
+    /**
+     * Unserialize data.
+     *
+     * @param mixed $data    The data to be unserialized.
+     * @param mixed $mode    The mode of unserialization. Can be either a
+     *                       single mode or array of modes.  If array, will be
+     *                       unserialized in the order provided.
+     * @param mixed $params  Any additional parameters the unserialization
+     *                       method requires.
+     *
+     * @return mixed  Unserialized data on success or PEAR_Error on error.
+     */
+    static public function _unserialize(&$data, $mode, $params = null)
+    {
+        switch ($mode) {
+        case self::NONE:
+        case self::SQLXML:
+            break;
+
+        case self::RAW:
+            $data = rawurldecode($data);
+            break;
+
+        case self::URL:
+            $data = urldecode($data);
+            break;
+
+        case self::WDDX:
+            $data = wddx_deserialize($data);
+            break;
+
+        case self::BZIP:
+            // $params['small'] = Use bzip2 'small memory' mode?
+            $data = bzdecompress($data, isset($params['small']) ? $params['small'] : false);
+            break;
+
+        case self::IMAP8:
+            $data = quoted_printable_decode($data);
+            break;
+
+        case self::IMAPUTF7:
+            require_once 'Horde/String.php';
+            $data = String::convertCharset(Horde_Imap_Client_Utf7imap::Utf7ImapToUtf8($data), 'UTF-8', 'ISO-8859-1');
+            break;
+
+        case self::IMAPUTF8:
+            $data = Horde_Mime::encode($data, 'UTF-8');
+            break;
+
+        case self::BASIC:
+            $data2 = @unserialize($data);
+            // Unserialize can return false both on error and if $data is the
+            // false value.
+            if (($data2 === false) && ($data == serialize(false))) {
+                return $data2;
+            }
+            $data = $data2;
+            break;
+
+        case self::GZ_DEFLATE:
+            $data = gzinflate($data);
+            break;
+
+        case self::BASE64:
+            $data = base64_decode($data);
+            break;
+
+        case self::GZ_COMPRESS:
+            $data = gzuncompress($data);
+            break;
+
+        // $params = Output character set
+        case self::UTF7:
+            require_once 'Horde/String.php';
+            $data = String::convertCharset($data, 'utf-7', $params);
+            break;
+
+        // $params = Output character set
+        case self::UTF7_BASIC:
+            $data = self::unserialize($data, array(self::BASIC, self::UTF7), $params);
+            break;
+
+        case self::JSON:
+            $data = json_decode($data);
+            break;
+
+        case self::LZF:
+            $data = @lzf_decompress($data);
+            break;
+        }
+
+        if ($data === false) {
+            return PEAR::raiseError('Unserialization failed.');
+        }
+        return $data;
+    }
+
+}
diff --git a/framework/Serialize/package.xml b/framework/Serialize/package.xml
new file mode 100644 (file)
index 0000000..ead3ce3
--- /dev/null
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<package packagerversion="1.4.9" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
+http://pear.php.net/dtd/tasks-1.0.xsd
+http://pear.php.net/dtd/package-2.0
+http://pear.php.net/dtd/package-2.0.xsd">
+ <name>Serialize</name>
+ <channel>pear.horde.org</channel>
+ <summary>Data Encapulation API</summary>
+ <description>The Horde_Serialize:: class provides various methods of
+ encapsulating data.
+ </description>
+ <lead>
+  <name>Chuck Hagenbuch</name>
+  <user>chuck</user>
+  <email>chuck@horde.org</email>
+  <active>yes</active>
+ </lead>
+ <lead>
+  <name>Jan Schneider</name>
+  <user>jan</user>
+  <email>jan@horde.org</email>
+  <active>yes</active>
+ </lead>
+ <date>2009-02-12</date>
+ <version>
+  <release>0.1.0</release>
+  <api>0.1.0</api>
+ </version>
+ <stability>
+  <release>beta</release>
+  <api>beta</api>
+ </stability>
+ <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
+ <notes>* Initial Horde 4 package.</notes>
+ <contents>
+  <dir name="/">
+   <dir name="lib">
+    <dir name="Horde">
+     <file name="Serialize.php" role="php" />
+    </dir> <!-- /lib/Horde -->
+   </dir> <!-- /lib/ -->
+   <dir name="test">
+    <dir name="Horde">
+     <dir name="Serialize">
+      <file name="json_assoc_array.phpt" role="test" />
+      <file name="json_empty.phpt" role="test" />
+      <file name="json_encode_decode.phpt" role="test" />
+      <file name="json_nested_array.phpt" role="test" />
+      <file name="json_object.phpt" role="test" />
+      <file name="json_spaces_comments.phpt" role="test" />
+      <file name="json_unquoted_keys.phpt" role="test" />
+     </dir> <!-- /test/Horde/Serialize -->
+    </dir> <!-- /test/Horde -->
+   </dir> <!-- /test -->
+  </dir> <!-- / -->
+ </contents>
+ <dependencies>
+  <required>
+   <php>
+    <min>5.2.0</min>
+   </php>
+   <pearinstaller>
+    <min>1.5.0</min>
+   </pearinstaller>
+   <package>
+    <name>Util</name>
+    <channel>pear.horde.org</channel>
+   </package>
+  </required>
+ </dependencies>
+ <phprelease>
+  <filelist>
+   <install name="lib/Horde/Serialize.php" as="Horde/Serialize.php" />
+  </filelist>
+ </phprelease>
+ <changelog>
+  <release>
+   <date>2006-05-08</date>
+   <time>23:12:48</time>
+   <version>
+    <release>0.0.2</release>
+    <api>0.0.2</api>
+   </version>
+   <stability>
+    <release>alpha</release>
+    <api>alpha</api>
+   </stability>
+   <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
+   <notes>Converted to package.xml 2.0 for pear.horde.org</notes>
+  </release>
+  <release>
+   <version>
+    <release>0.0.1</release>
+    <api>0.0.1</api>
+   </version>
+   <stability>
+    <release>alpha</release>
+    <api>alpha</api>
+   </stability>
+   <date>2004-01-01</date>
+   <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
+   <notes>Initial packaging.
+   </notes>
+  </release>
+ </changelog>
+</package>
diff --git a/framework/Serialize/test/Horde/Serialize/json_assoc_array.phpt b/framework/Serialize/test/Horde/Serialize/json_assoc_array.phpt
new file mode 100644 (file)
index 0000000..685cc86
--- /dev/null
@@ -0,0 +1,52 @@
+--TEST--
+JSON associative arrays tests.
+--FILE--
+<?php
+
+error_reporting(E_ALL);
+require_once 'Horde/Serialize.php';
+
+function out($str)
+{
+    echo "$str\n";
+}
+
+$arr = array('car1'=> array('color'=> 'tan', 'model' => 'sedan'),
+             'car2' => array('color' => 'red', 'model' => 'sports'));
+$arr_jo = '{"car1":{"color":"tan","model":"sedan"},"car2":{"color":"red","model":"sports"}}';
+
+$arn = array(0 => array(0 => 'tan\\', 'model\\' => 'sedan'), 1 => array(0 => 'red', 'model' => 'sports'));
+$arn_ja = '[{"0":"tan\\\\","model\\\\":"sedan"},{"0":"red","model":"sports"}]';
+
+$arrs = array(1 => 'one', 2 => 'two', 5 => 'five');
+$arrs_jo = '{"1":"one","2":"two","5":"five"}';
+
+/* Types test */
+
+// strict type should be array
+out(gettype(Horde_Serialize::unserialize($arn_ja, SERIALIZE_JSON)));
+
+echo"============================================================================\n";
+
+/* Encode tests */
+
+// array case - strict: associative array with nested associative arrays
+out(Horde_Serialize::serialize($arr, SERIALIZE_JSON));
+
+// array case - strict: associative array with nested associative arrays, and
+// some numeric keys thrown in
+// Should degrade to a numeric array.
+out(Horde_Serialize::serialize($arn, SERIALIZE_JSON));
+
+// sparse numeric assoc array: associative array numeric keys which are not
+// fully populated in a range of 0 to length-1
+// Test a sparsely populated numerically indexed associative array.
+out(Horde_Serialize::serialize($arrs, SERIALIZE_JSON));
+
+?>
+--EXPECT--
+array
+============================================================================
+{"car1":{"color":"tan","model":"sedan"},"car2":{"color":"red","model":"sports"}}
+[{"0":"tan\\","model\\":"sedan"},{"0":"red","model":"sports"}]
+{"1":"one","2":"two","5":"five"}
\ No newline at end of file
diff --git a/framework/Serialize/test/Horde/Serialize/json_empty.phpt b/framework/Serialize/test/Horde/Serialize/json_empty.phpt
new file mode 100644 (file)
index 0000000..8bd2fbf
--- /dev/null
@@ -0,0 +1,42 @@
+--TEST--
+JSON empties tests.
+--FILE--
+<?php
+
+error_reporting(E_ALL);
+require_once 'Horde/Serialize.php';
+
+function out($str)
+{
+    echo "$str\n";
+}
+
+$obj0_j = '{}';
+$obj1_j = '{ }';
+$obj2_j = '{ /* comment inside */ }';
+
+/* Types tests */
+
+// should be object
+out(gettype(Horde_Serialize::unserialize($obj0_j, SERIALIZE_JSON)));
+// should be empty object
+out(count(get_object_vars(Horde_Serialize::unserialize($obj0_j, SERIALIZE_JSON))));
+
+// should be object, even with space
+out(gettype(Horde_Serialize::unserialize($obj1_j, SERIALIZE_JSON)));
+// should be empty object, even with space
+out(count(get_object_vars(Horde_Serialize::unserialize($obj1_j, SERIALIZE_JSON))));
+
+// should be object, despite comment
+out(gettype(Horde_Serialize::unserialize($obj2_j, SERIALIZE_JSON)));
+// should be empty object, despite comment
+out(count(get_object_vars(Horde_Serialize::unserialize($obj2_j, SERIALIZE_JSON))));
+
+?>
+--EXPECT--
+object
+0
+object
+0
+object
+0
diff --git a/framework/Serialize/test/Horde/Serialize/json_encode_decode.phpt b/framework/Serialize/test/Horde/Serialize/json_encode_decode.phpt
new file mode 100644 (file)
index 0000000..57343c5
--- /dev/null
@@ -0,0 +1,302 @@
+--TEST--
+JSON encode/decode tests.
+--FILE--
+<?php
+
+error_reporting(E_ALL);
+require_once 'Horde/Serialize.php';
+
+function out($str)
+{
+    echo "$str\n";
+}
+
+$obj = new stdClass();
+$obj->a_string = '"he":llo}:{world';
+$obj->an_array = array(1, 2, 3);
+$obj->obj = new stdClass();
+$obj->obj->a_number = 123;
+$obj_j = '{"a_string":"\"he\":llo}:{world","an_array":[1,2,3],"obj":{"a_number":123}}';
+
+$arr = array(null, true, array(1, 2, 3), "hello\"],[world!");
+$arr_j = '[null,true,[1,2,3],"hello\"],[world!"]';
+
+$str1 = 'hello world';
+$str1_j = '"hello world"';
+$str1_j_ = "'hello world'";
+
+$str2 = "hello\t\"world\"";
+$str2_j = '"hello\\t\\"world\\""';
+
+$str3 = "\\\r\n\t\"/";
+$str3_j = '"\\\\\\r\\n\\t\\"\\/"';
+
+$str4 = 'héllö wørłd';
+$str4_j = '"h\u00e9ll\u00f6 w\u00f8r\u0142d"';
+$str4_j_ = '"héllö wørłd"';
+
+/* Encode tests. */
+
+// type case: null
+out(Horde_Serialize::serialize(null, SERIALIZE_JSON));
+// type case: boolean true
+out(Horde_Serialize::serialize(true, SERIALIZE_JSON));
+// type case: boolean false
+out(Horde_Serialize::serialize(false, SERIALIZE_JSON));
+
+// numeric case: 1
+out(Horde_Serialize::serialize(1, SERIALIZE_JSON));
+// numeric case: -1
+out(Horde_Serialize::serialize(-1, SERIALIZE_JSON));
+// numeric case: 1.0
+out(Horde_Serialize::serialize(1.0, SERIALIZE_JSON));
+// numeric case: 1.1
+out(Horde_Serialize::serialize(1.1, SERIALIZE_JSON));
+
+// string case: hello world
+out(Horde_Serialize::serialize($str1, SERIALIZE_JSON));
+// string case: hello world, with tab, double-quotes
+out(Horde_Serialize::serialize($str2, SERIALIZE_JSON));
+// string case: backslash, return, newline, tab, double-quote
+out(Horde_Serialize::serialize($str3, SERIALIZE_JSON));
+// string case: hello world, with unicode
+out(Horde_Serialize::serialize($str4, SERIALIZE_JSON));
+
+// array case: array with elements and nested arrays
+out(Horde_Serialize::serialize($arr, SERIALIZE_JSON));
+// object case: object with properties, nested object and arrays
+out(Horde_Serialize::serialize($obj, SERIALIZE_JSON));
+
+echo"============================================================================\n";
+
+/* Decode tests */
+
+// type case: null
+var_dump(Horde_Serialize::unserialize('null', SERIALIZE_JSON));
+// type case: boolean true
+var_dump(Horde_Serialize::unserialize('true', SERIALIZE_JSON));
+// type case: boolean false
+var_dump(Horde_Serialize::unserialize('false', SERIALIZE_JSON));
+
+// numeric case: 1
+var_dump(Horde_Serialize::unserialize('1', SERIALIZE_JSON));
+// numeric case: -1
+var_dump(Horde_Serialize::unserialize('-1', SERIALIZE_JSON));
+// numeric case: 1.0
+var_dump(Horde_Serialize::unserialize('1.0', SERIALIZE_JSON));
+// numeric case: 1.1
+var_dump(Horde_Serialize::unserialize('1.1', SERIALIZE_JSON));
+
+// string case: hello world
+var_dump(Horde_Serialize::unserialize($str1_j, SERIALIZE_JSON));
+var_dump(Horde_Serialize::unserialize($str1_j_, SERIALIZE_JSON));
+// string case: hello world, with tab, double-quotes
+var_dump(Horde_Serialize::unserialize($str2_j, SERIALIZE_JSON));
+// string case: backslash, return, newline, tab, double-quote
+var_dump(Horde_Serialize::unserialize($str3_j, SERIALIZE_JSON));
+// string case: hello world, with unicode
+var_dump(Horde_Serialize::unserialize($str4_j, SERIALIZE_JSON));
+var_dump(Horde_Serialize::unserialize($str4_j_, SERIALIZE_JSON));
+
+// array case: array with elements and nested arrays
+var_dump(Horde_Serialize::unserialize($arr_j, SERIALIZE_JSON));
+// object case: object with properties, nested object and arrays
+var_dump(Horde_Serialize::unserialize($obj_j, SERIALIZE_JSON));
+
+echo"============================================================================\n";
+
+/* Encode-decode tests */
+
+// type case: null
+var_dump(Horde_Serialize::unserialize(Horde_Serialize::serialize(null, SERIALIZE_JSON), SERIALIZE_JSON));
+// type case: boolean true
+var_dump(Horde_Serialize::unserialize(Horde_Serialize::serialize(true, SERIALIZE_JSON), SERIALIZE_JSON));
+// type case: boolean false
+var_dump(Horde_Serialize::unserialize(Horde_Serialize::serialize(false, SERIALIZE_JSON), SERIALIZE_JSON));
+
+// numeric case: 1
+var_dump(Horde_Serialize::unserialize(Horde_Serialize::serialize(1, SERIALIZE_JSON), SERIALIZE_JSON));
+// numeric case: -1
+var_dump(Horde_Serialize::unserialize(Horde_Serialize::serialize(-1, SERIALIZE_JSON), SERIALIZE_JSON));
+// numeric case: 1.0
+var_dump(Horde_Serialize::unserialize(Horde_Serialize::serialize(1.0, SERIALIZE_JSON), SERIALIZE_JSON));
+// numeric case: 1.1
+var_dump(Horde_Serialize::unserialize(Horde_Serialize::serialize(1.1, SERIALIZE_JSON), SERIALIZE_JSON));
+
+// string case: hello world
+var_dump(Horde_Serialize::unserialize(Horde_Serialize::serialize($str1, SERIALIZE_JSON), SERIALIZE_JSON));
+// string case: hello world, with tab, double-quotes
+var_dump(Horde_Serialize::unserialize(Horde_Serialize::serialize($str2, SERIALIZE_JSON), SERIALIZE_JSON));
+// string case: backslash, return, newline, tab, double-quote
+var_dump(Horde_Serialize::unserialize(Horde_Serialize::serialize($str3, SERIALIZE_JSON), SERIALIZE_JSON));
+// string case: hello world, with unicode
+var_dump(Horde_Serialize::unserialize(Horde_Serialize::serialize($str4, SERIALIZE_JSON), SERIALIZE_JSON));
+
+// array case: array with elements and nested arrays
+var_dump(Horde_Serialize::unserialize(Horde_Serialize::serialize($arr, SERIALIZE_JSON), SERIALIZE_JSON));
+// object case: object with properties, nested object and arrays
+var_dump(Horde_Serialize::unserialize(Horde_Serialize::serialize($obj, SERIALIZE_JSON), SERIALIZE_JSON));
+
+echo"============================================================================\n";
+
+/* Decode-encode tests */
+
+// type case: null
+out(Horde_Serialize::serialize(Horde_Serialize::unserialize('null', SERIALIZE_JSON), SERIALIZE_JSON));
+// type case: boolean true
+out(Horde_Serialize::serialize(Horde_Serialize::unserialize('true', SERIALIZE_JSON), SERIALIZE_JSON));
+// type case: boolean false
+out(Horde_Serialize::serialize(Horde_Serialize::unserialize('false', SERIALIZE_JSON), SERIALIZE_JSON));
+
+// numeric case: 1
+out(Horde_Serialize::serialize(Horde_Serialize::unserialize('1', SERIALIZE_JSON), SERIALIZE_JSON));
+// numeric case: -1
+out(Horde_Serialize::serialize(Horde_Serialize::unserialize('-1', SERIALIZE_JSON), SERIALIZE_JSON));
+// numeric case: 1.0
+out(Horde_Serialize::serialize(Horde_Serialize::unserialize('1.0', SERIALIZE_JSON), SERIALIZE_JSON));
+// numeric case: 1.1
+out(Horde_Serialize::serialize(Horde_Serialize::unserialize('1.1', SERIALIZE_JSON), SERIALIZE_JSON));
+
+// string case: hello world
+out(Horde_Serialize::serialize(Horde_Serialize::unserialize($str1_j, SERIALIZE_JSON), SERIALIZE_JSON));
+// string case: hello world, with tab, double-quotes
+out(Horde_Serialize::serialize(Horde_Serialize::unserialize($str2_j, SERIALIZE_JSON), SERIALIZE_JSON));
+// string case: backslash, return, newline, tab, double-quote
+out(Horde_Serialize::serialize(Horde_Serialize::unserialize($str3_j, SERIALIZE_JSON), SERIALIZE_JSON));
+// string case: hello world, with unicode
+out(Horde_Serialize::serialize(Horde_Serialize::unserialize($str4_j, SERIALIZE_JSON), SERIALIZE_JSON));
+out(Horde_Serialize::serialize(Horde_Serialize::unserialize($str4_j_, SERIALIZE_JSON), SERIALIZE_JSON));
+
+// array case: array with elements and nested arrays
+out(Horde_Serialize::serialize(Horde_Serialize::unserialize($arr_j, SERIALIZE_JSON), SERIALIZE_JSON));
+// object case: object with properties, nested object and arrays
+out(Horde_Serialize::serialize(Horde_Serialize::unserialize($obj_j, SERIALIZE_JSON), SERIALIZE_JSON));
+
+?>
+--EXPECT--
+null
+true
+false
+1
+-1
+1
+1.1
+"hello world"
+"hello\t\"world\""
+"\\\r\n\t\"\/"
+"h\u00e9ll\u00f6 w\u00f8r\u0142d"
+[null,true,[1,2,3],"hello\"],[world!"]
+{"a_string":"\"he\":llo}:{world","an_array":[1,2,3],"obj":{"a_number":123}}
+============================================================================
+NULL
+bool(true)
+bool(false)
+int(1)
+int(-1)
+float(1)
+float(1.1)
+string(11) "hello world"
+string(11) "hello world"
+string(13) "hello      "world""
+string(6) "\
+       "/"
+string(15) "héllö wørłd"
+string(15) "héllö wørłd"
+array(4) {
+  [0]=>
+  NULL
+  [1]=>
+  bool(true)
+  [2]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  [3]=>
+  string(15) "hello"],[world!"
+}
+object(stdClass)(3) {
+  ["a_string"]=>
+  string(16) ""he":llo}:{world"
+  ["an_array"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["obj"]=>
+  object(stdClass)(1) {
+    ["a_number"]=>
+    int(123)
+  }
+}
+============================================================================
+NULL
+bool(true)
+bool(false)
+int(1)
+int(-1)
+int(1)
+float(1.1)
+string(11) "hello world"
+string(13) "hello      "world""
+string(6) "\
+       "/"
+string(15) "héllö wørłd"
+array(4) {
+  [0]=>
+  NULL
+  [1]=>
+  bool(true)
+  [2]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  [3]=>
+  string(15) "hello"],[world!"
+}
+object(stdClass)(3) {
+  ["a_string"]=>
+  string(16) ""he":llo}:{world"
+  ["an_array"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["obj"]=>
+  object(stdClass)(1) {
+    ["a_number"]=>
+    int(123)
+  }
+}
+============================================================================
+null
+true
+false
+1
+-1
+1
+1.1
+"hello world"
+"hello\t\"world\""
+"\\\r\n\t\"\/"
+"h\u00e9ll\u00f6 w\u00f8r\u0142d"
+"h\u00e9ll\u00f6 w\u00f8r\u0142d"
+[null,true,[1,2,3],"hello\"],[world!"]
+{"a_string":"\"he\":llo}:{world","an_array":[1,2,3],"obj":{"a_number":123}}
diff --git a/framework/Serialize/test/Horde/Serialize/json_nested_array.phpt b/framework/Serialize/test/Horde/Serialize/json_nested_array.phpt
new file mode 100644 (file)
index 0000000..7d98ef1
--- /dev/null
@@ -0,0 +1,115 @@
+--TEST--
+JSON nested arrays tests.
+--FILE--
+<?php
+
+error_reporting(E_ALL);
+require_once 'Horde/Serialize.php';
+
+$str1 = '[{"this":"that"}]';
+$str2 = '{"this":["that"]}';
+$str3 = '{"params":[{"foo":["1"],"bar":"1"}]}';
+$str4 = '{"0": {"foo": "bar", "baz": "winkle"}}';
+$str5 = '{"params":[{"options": {"old": [ ], "new": {"0": {"elements": {"old": [], "new": {"0": {"elementName": "aa", "isDefault": false, "elementRank": "0", "priceAdjust": "0", "partNumber": ""}}}, "optionName": "aa", "isRequired": false, "optionDesc": ""}}}}]}';
+
+/* Decode tests */
+
+// simple compactly-nested array
+var_dump(Horde_Serialize::unserialize($str1, SERIALIZE_JSON));
+// simple compactly-nested array
+var_dump(Horde_Serialize::unserialize($str2, SERIALIZE_JSON));
+// complex compactly nested array
+var_dump(Horde_Serialize::unserialize($str3, SERIALIZE_JSON));
+// complex compactly nested array
+var_dump(Horde_Serialize::unserialize($str4, SERIALIZE_JSON));
+// super complex compactly nested array
+var_dump(Horde_Serialize::unserialize($str5, SERIALIZE_JSON));
+
+?>
+--EXPECT--
+array(1) {
+  [0]=>
+  object(stdClass)(1) {
+    ["this"]=>
+    string(4) "that"
+  }
+}
+object(stdClass)(1) {
+  ["this"]=>
+  array(1) {
+    [0]=>
+    string(4) "that"
+  }
+}
+object(stdClass)(1) {
+  ["params"]=>
+  array(1) {
+    [0]=>
+    object(stdClass)(2) {
+      ["foo"]=>
+      array(1) {
+        [0]=>
+        string(1) "1"
+      }
+      ["bar"]=>
+      string(1) "1"
+    }
+  }
+}
+object(stdClass)(1) {
+  [0]=>
+  object(stdClass)(2) {
+    ["foo"]=>
+    string(3) "bar"
+    ["baz"]=>
+    string(6) "winkle"
+  }
+}
+object(stdClass)(1) {
+  ["params"]=>
+  array(1) {
+    [0]=>
+    object(stdClass)(1) {
+      ["options"]=>
+      object(stdClass)(2) {
+        ["old"]=>
+        array(0) {
+        }
+        ["new"]=>
+        object(stdClass)(1) {
+          [0]=>
+          object(stdClass)(4) {
+            ["elements"]=>
+            object(stdClass)(2) {
+              ["old"]=>
+              array(0) {
+              }
+              ["new"]=>
+              object(stdClass)(1) {
+                [0]=>
+                object(stdClass)(5) {
+                  ["elementName"]=>
+                  string(2) "aa"
+                  ["isDefault"]=>
+                  bool(false)
+                  ["elementRank"]=>
+                  string(1) "0"
+                  ["priceAdjust"]=>
+                  string(1) "0"
+                  ["partNumber"]=>
+                  string(0) ""
+                }
+              }
+            }
+            ["optionName"]=>
+            string(2) "aa"
+            ["isRequired"]=>
+            bool(false)
+            ["optionDesc"]=>
+            string(0) ""
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/framework/Serialize/test/Horde/Serialize/json_object.phpt b/framework/Serialize/test/Horde/Serialize/json_object.phpt
new file mode 100644 (file)
index 0000000..4a40324
--- /dev/null
@@ -0,0 +1,41 @@
+--TEST--
+JSON objects tests.
+--FILE--
+<?php
+
+error_reporting(E_ALL);
+require_once 'Horde/Serialize.php';
+
+function out($str)
+{
+    echo "$str\n";
+}
+
+$obj_j = '{"a_string":"\"he\":llo}:{world","an_array":[1,2,3],"obj":{"a_number":123}}';
+
+$obj1->car1->color = 'tan';
+$obj1->car1->model = 'sedan';
+$obj1->car2->color = 'red';
+$obj1->car2->model = 'sports';
+$obj1_j = '{"car1":{"color":"tan","model":"sedan"},"car2":{"color":"red","model":"sports"}}';
+
+/* Types test */
+
+// checking whether decoded type is object
+out(gettype(Horde_Serialize::unserialize($obj_j, SERIALIZE_JSON)));
+
+/* Encode test */
+
+// object - strict: Object with nested objects
+out(Horde_Serialize::serialize($obj1, SERIALIZE_JSON));
+
+/* Decode/encode test */
+
+// object case
+out(Horde_Serialize::serialize(Horde_Serialize::unserialize($obj_j, SERIALIZE_JSON), SERIALIZE_JSON));
+
+?>
+--EXPECT--
+object
+{"car1":{"color":"tan","model":"sedan"},"car2":{"color":"red","model":"sports"}}
+{"a_string":"\"he\":llo}:{world","an_array":[1,2,3],"obj":{"a_number":123}}
diff --git a/framework/Serialize/test/Horde/Serialize/json_spaces_comments.phpt b/framework/Serialize/test/Horde/Serialize/json_spaces_comments.phpt
new file mode 100644 (file)
index 0000000..2dec96c
--- /dev/null
@@ -0,0 +1,145 @@
+--TEST--
+JSON spaces and comments tests.
+--FILE--
+<?php
+
+error_reporting(E_ALL);
+require_once 'Horde/Serialize.php';
+
+$obj_j = '{"a_string":"\"he\":llo}:{world","an_array":[1,2,3],"obj":{"a_number":123}}';
+
+$obj_js = '{"a_string": "\"he\":llo}:{world",
+                "an_array":[1, 2, 3],
+                "obj": {"a_number":123}}';
+
+$obj_jc1 = '{"a_string": "\"he\":llo}:{world",
+                // here is a comment, hoorah
+                "an_array":[1, 2, 3],
+                "obj": {"a_number":123}}';
+
+$obj_jc2 = '/* this here is the sneetch */ "the sneetch"
+                // this has been the sneetch.';
+
+$obj_jc3 = '{"a_string": "\"he\":llo}:{world",
+                /* here is a comment, hoorah */
+                "an_array":[1, 2, 3 /* and here is another */],
+                "obj": {"a_number":123}}';
+
+$obj_jc4 = '{\'a_string\': "\"he\":llo}:{world",
+                /* here is a comment, hoorah */
+                \'an_array\':[1, 2, 3 /* and here is another */],
+                "obj": {"a_number":123}}';
+
+// Base result
+var_dump(Horde_Serialize::unserialize($obj_j, SERIALIZE_JSON));
+
+/* Spaces tests */
+
+// checking whether notation with spaces works
+var_dump(Horde_Serialize::unserialize($obj_js, SERIALIZE_JSON));
+
+/* Comments tests */
+
+// checking whether notation with single line comments works
+var_dump(Horde_Serialize::unserialize($obj_jc1, SERIALIZE_JSON));
+
+// checking whether notation with multiline comments works
+var_dump(Horde_Serialize::unserialize($obj_jc2, SERIALIZE_JSON));
+var_dump(Horde_Serialize::unserialize($obj_jc3, SERIALIZE_JSON));
+
+// checking whether notation with single-quotes and multiline comments works
+var_dump(Horde_Serialize::unserialize($obj_jc4, SERIALIZE_JSON));
+
+?>
+--EXPECT--
+object(stdClass)(3) {
+  ["a_string"]=>
+  string(16) ""he":llo}:{world"
+  ["an_array"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["obj"]=>
+  object(stdClass)(1) {
+    ["a_number"]=>
+    int(123)
+  }
+}
+object(stdClass)(3) {
+  ["a_string"]=>
+  string(16) ""he":llo}:{world"
+  ["an_array"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["obj"]=>
+  object(stdClass)(1) {
+    ["a_number"]=>
+    int(123)
+  }
+}
+object(stdClass)(3) {
+  ["a_string"]=>
+  string(16) ""he":llo}:{world"
+  ["an_array"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["obj"]=>
+  object(stdClass)(1) {
+    ["a_number"]=>
+    int(123)
+  }
+}
+string(11) "the sneetch"
+object(stdClass)(3) {
+  ["a_string"]=>
+  string(16) ""he":llo}:{world"
+  ["an_array"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["obj"]=>
+  object(stdClass)(1) {
+    ["a_number"]=>
+    int(123)
+  }
+}
+object(stdClass)(3) {
+  ["a_string"]=>
+  string(16) ""he":llo}:{world"
+  ["an_array"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["obj"]=>
+  object(stdClass)(1) {
+    ["a_number"]=>
+    int(123)
+  }
+}
diff --git a/framework/Serialize/test/Horde/Serialize/json_unquoted_keys.phpt b/framework/Serialize/test/Horde/Serialize/json_unquoted_keys.phpt
new file mode 100644 (file)
index 0000000..ae54c80
--- /dev/null
@@ -0,0 +1,60 @@
+--TEST--
+JSON unquoted keys tests.
+--FILE--
+<?php
+
+error_reporting(E_ALL);
+require_once 'Horde/Serialize.php';
+
+$ob1->{'0'} = 'tan';
+$ob1->model = 'sedan';
+$ob2->{'0'} = 'red';
+$ob2->model = 'sports';
+$arn = array($ob1, $ob2);
+$arn_ja = '[{0:"tan","model":"sedan"},{"0":"red",model:"sports"}]';
+
+$arrs->{'1'} = 'one';
+$arrs->{'2'} = 'two';
+$arrs->{'5'} = 'fi"ve';
+$arrs_jo = '{"1":"one",2:"two","5":\'fi"ve\'}';
+
+/* Decode tests */
+
+// array case - strict: associative array with unquoted keys, nested
+// associative arrays, and some numeric keys thrown in
+// ...unless the input array has some numeric indeces, in which case the
+// behavior is to degrade to a regular array
+var_dump(Horde_Serialize::unserialize($arn_ja, SERIALIZE_JSON));
+
+// sparse numeric assoc array: associative array with unquoted keys,
+// single-quoted values, numeric keys which are not fully populated in a range
+// of 0 to length-1
+// Test a sparsely populated numerically indexed associative array
+var_dump(Horde_Serialize::unserialize($arrs_jo, SERIALIZE_JSON));
+
+?>
+--EXPECT--
+array(2) {
+  [0]=>
+  object(stdClass)(2) {
+    [0]=>
+    string(3) "tan"
+    ["model"]=>
+    string(5) "sedan"
+  }
+  [1]=>
+  object(stdClass)(2) {
+    [0]=>
+    string(3) "red"
+    ["model"]=>
+    string(6) "sports"
+  }
+}
+object(stdClass)(3) {
+  [1]=>
+  string(3) "one"
+  [2]=>
+  string(3) "two"
+  [5]=>
+  string(5) "fi"ve"
+}