From 5debbd2be8bf1431de470ed8792ce55f1e5c08c1 Mon Sep 17 00:00:00 2001
From: advect <advect@gmail.com>
Date: Sun, 16 Jan 2011 17:35:10 +0900
Subject: [PATCH] php: added unpack of class object converter

---
 php/ChangeLog         |   6 +
 php/config.m4         |   2 +-
 php/config.w32        |   2 +-
 php/msgpack.c         |  54 ++--
 php/msgpack_class.c   |  92 ++++---
 php/msgpack_convert.c | 292 +++++++++++++++++++++
 php/msgpack_convert.h |   7 +
 php/msgpack_errors.h  |  17 ++
 php/msgpack_pack.c    | 113 ++++----
 php/msgpack_pack.h    |   2 +
 php/msgpack_unpack.c  | 188 +++++++-------
 php/package.xml       | 145 ++++++-----
 php/php-msgpack.spec  |   2 +-
 php/php_msgpack.h     |   2 +-
 php/tests/007.phpt    |   4 +-
 php/tests/008.phpt    |   2 +-
 php/tests/009.phpt    |   6 +-
 php/tests/009b.phpt   |   6 +-
 php/tests/013.phpt    |   2 +-
 php/tests/014.phpt    |   2 +-
 php/tests/024.phpt    |   4 +-
 php/tests/024b.phpt   |   4 +-
 php/tests/024c.phpt   |   4 +-
 php/tests/028.phpt    |   2 +-
 php/tests/028b.phpt   |   2 +-
 php/tests/028c.phpt   |   2 +-
 php/tests/028d.phpt   |   2 +-
 php/tests/061.phpt    |  10 +-
 php/tests/061b.phpt   |  10 +-
 php/tests/061c.phpt   |  10 +-
 php/tests/065.phpt    |  10 +-
 php/tests/065b.phpt   |  10 +-
 php/tests/065c.phpt   |  10 +-
 php/tests/070.phpt    |  10 +-
 php/tests/070b.phpt   |  10 +-
 php/tests/070c.phpt   |  10 +-
 php/tests/071.phpt    |  10 +-
 php/tests/071b.phpt   |  10 +-
 php/tests/071c.phpt   |  10 +-
 php/tests/072.phpt    |  10 +-
 php/tests/072b.phpt   |  10 +-
 php/tests/072c.phpt   |  10 +-
 php/tests/073.phpt    |  10 +-
 php/tests/073b.phpt   |  10 +-
 php/tests/073c.phpt   |  10 +-
 php/tests/080.phpt    |  10 +-
 php/tests/081.phpt    |  10 +-
 php/tests/082.phpt    |  10 +-
 php/tests/083.phpt    |  10 +-
 php/tests/084.phpt    |  10 +-
 php/tests/085.phpt    |  10 +-
 php/tests/086.phpt    |  10 +-
 php/tests/087.phpt    |  23 +-
 php/tests/087d.phpt   | 306 ----------------------
 php/tests/088.phpt    |  23 +-
 php/tests/088d.phpt   | 349 -------------------------
 php/tests/089.phpt    |  28 +-
 php/tests/089d.phpt   | 351 -------------------------
 php/tests/090.phpt    | 575 +++++++++++++++++++++++++++++++++++++++++
 php/tests/090b.phpt   | 563 ++++++++++++++++++++++++++++++++++++++++
 php/tests/091.phpt    | 577 +++++++++++++++++++++++++++++++++++++++++
 php/tests/091b.phpt   | 563 ++++++++++++++++++++++++++++++++++++++++
 php/tests/092.phpt    | 442 +++++++++++++++++++++++++++++++
 php/tests/092b.phpt   | 430 +++++++++++++++++++++++++++++++
 php/tests/093.phpt    | 444 ++++++++++++++++++++++++++++++++
 php/tests/093b.phpt   | 430 +++++++++++++++++++++++++++++++
 php/tests/094.phpt    | 578 +++++++++++++++++++++++++++++++++++++++++
 php/tests/094b.phpt   | 566 ++++++++++++++++++++++++++++++++++++++++
 php/tests/095.phpt    | 580 +++++++++++++++++++++++++++++++++++++++++
 php/tests/095b.phpt   | 566 ++++++++++++++++++++++++++++++++++++++++
 php/tests/096.phpt    | 445 ++++++++++++++++++++++++++++++++
 php/tests/096b.phpt   | 433 +++++++++++++++++++++++++++++++
 php/tests/097.phpt    | 447 ++++++++++++++++++++++++++++++++
 php/tests/097b.phpt   | 433 +++++++++++++++++++++++++++++++
 php/tests/098.phpt    | 445 ++++++++++++++++++++++++++++++++
 php/tests/098b.phpt   | 440 +++++++++++++++++++++++++++++++
 php/tests/099.phpt    | 584 +++++++++++++++++++++++++++++++++++++++++
 php/tests/099b.phpt   | 572 +++++++++++++++++++++++++++++++++++++++++
 php/tests/100.phpt    | 586 ++++++++++++++++++++++++++++++++++++++++++
 php/tests/100b.phpt   | 572 +++++++++++++++++++++++++++++++++++++++++
 php/tests/101.phpt    | 451 ++++++++++++++++++++++++++++++++
 php/tests/101b.phpt   | 439 +++++++++++++++++++++++++++++++
 php/tests/102.phpt    | 453 ++++++++++++++++++++++++++++++++
 php/tests/102b.phpt   | 439 +++++++++++++++++++++++++++++++
 php/tests/103.phpt    | 451 ++++++++++++++++++++++++++++++++
 php/tests/103b.phpt   | 446 ++++++++++++++++++++++++++++++++
 86 files changed, 14795 insertions(+), 1451 deletions(-)
 create mode 100644 php/msgpack_convert.c
 create mode 100644 php/msgpack_convert.h
 create mode 100644 php/msgpack_errors.h
 delete mode 100644 php/tests/087d.phpt
 delete mode 100644 php/tests/088d.phpt
 delete mode 100644 php/tests/089d.phpt
 create mode 100644 php/tests/090.phpt
 create mode 100644 php/tests/090b.phpt
 create mode 100644 php/tests/091.phpt
 create mode 100644 php/tests/091b.phpt
 create mode 100644 php/tests/092.phpt
 create mode 100644 php/tests/092b.phpt
 create mode 100644 php/tests/093.phpt
 create mode 100644 php/tests/093b.phpt
 create mode 100644 php/tests/094.phpt
 create mode 100644 php/tests/094b.phpt
 create mode 100644 php/tests/095.phpt
 create mode 100644 php/tests/095b.phpt
 create mode 100644 php/tests/096.phpt
 create mode 100644 php/tests/096b.phpt
 create mode 100644 php/tests/097.phpt
 create mode 100644 php/tests/097b.phpt
 create mode 100644 php/tests/098.phpt
 create mode 100644 php/tests/098b.phpt
 create mode 100644 php/tests/099.phpt
 create mode 100644 php/tests/099b.phpt
 create mode 100644 php/tests/100.phpt
 create mode 100644 php/tests/100b.phpt
 create mode 100644 php/tests/101.phpt
 create mode 100644 php/tests/101b.phpt
 create mode 100644 php/tests/102.phpt
 create mode 100644 php/tests/102b.phpt
 create mode 100644 php/tests/103.phpt
 create mode 100644 php/tests/103b.phpt

diff --git a/php/ChangeLog b/php/ChangeLog
index 83db4914..c02ab1f3 100644
--- a/php/ChangeLog
+++ b/php/ChangeLog
@@ -1,5 +1,11 @@
 msgpack extension changelog
 
+Version 0.4.0
+-------------
+	* Fix array pack.
+	* Fix test code.
+	* Add unpack of class object converter.
+
 Version 0.3.4
 -------------
 	* Support PHP 5.3.x version on Windows.
diff --git a/php/config.m4 b/php/config.m4
index 19df8afe..5bde72d6 100644
--- a/php/config.m4
+++ b/php/config.m4
@@ -22,7 +22,7 @@ Make sure that the comment is aligned:
 [  --with-msgpack             Include msgpack support])
 
 if test "$PHP_MSGPACK" != "no"; then
-  PHP_NEW_EXTENSION(msgpack, msgpack.c msgpack_pack.c msgpack_unpack.c msgpack_class.c, $ext_shared)
+  PHP_NEW_EXTENSION(msgpack, msgpack.c msgpack_pack.c msgpack_unpack.c msgpack_class.c msgpack_convert.c, $ext_shared)
 
   ifdef([PHP_INSTALL_HEADERS],
   [
diff --git a/php/config.w32 b/php/config.w32
index 59f51409..a0267ce5 100644
--- a/php/config.w32
+++ b/php/config.w32
@@ -5,5 +5,5 @@ ARG_ENABLE("msgpack", "for msgpack support", "yes");
 
 if (PHP_MSGPACK != "no") {
    EXTENSION("msgpack", "msgpack.c", PHP_MSGPACK_SHARED, "");
-   ADD_SOURCES(configure_module_dirname, "msgpack_pack.c msgpack_unpack.c msgpack_class.c", "msgpack");
+   ADD_SOURCES(configure_module_dirname, "msgpack_pack.c msgpack_unpack.c msgpack_class.c msgpack_convert.c", "msgpack");
 }
diff --git a/php/msgpack.c b/php/msgpack.c
index 0d48be11..6e9253a7 100644
--- a/php/msgpack.c
+++ b/php/msgpack.c
@@ -15,6 +15,8 @@
 #include "msgpack_pack.h"
 #include "msgpack_unpack.h"
 #include "msgpack_class.h"
+#include "msgpack_convert.h"
+#include "msgpack_errors.h"
 #include "msgpack/version.h"
 
 static ZEND_FUNCTION(msgpack_serialize);
@@ -26,6 +28,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_msgpack_unserialize, 0, 0, 1)
     ZEND_ARG_INFO(0, str)
+    ZEND_ARG_INFO(0, object)
 ZEND_END_ARG_INFO()
 
 PHP_INI_BEGIN()
@@ -248,42 +251,26 @@ PHP_MSGPACK_API void php_msgpack_unserialize(
     switch (ret)
     {
         case MSGPACK_UNPACK_PARSE_ERROR:
-        {
             msgpack_unserialize_var_destroy(&var_hash, 1);
-            if (MSGPACK_G(error_display))
-            {
-                zend_error(E_WARNING,
-                           "[msgpack] (%s) Parse error", __FUNCTION__);
-            }
+            MSGPACK_WARNING("[msgpack] (%s) Parse error", __FUNCTION__);
             break;
-        }
         case MSGPACK_UNPACK_CONTINUE:
-        {
             msgpack_unserialize_var_destroy(&var_hash, 1);
-            if (MSGPACK_G(error_display))
-            {
-                zend_error(E_WARNING,
-                           "[msgpack] (%s) Insufficient data for unserializing",
-                           __FUNCTION__);
-            }
+            MSGPACK_WARNING(
+                "[msgpack] (%s) Insufficient data for unserializing",
+                __FUNCTION__);
             break;
-        }
         case MSGPACK_UNPACK_EXTRA_BYTES:
         case MSGPACK_UNPACK_SUCCESS:
             msgpack_unserialize_var_destroy(&var_hash, 0);
-            if (off < (size_t)str_len && MSGPACK_G(error_display))
+            if (off < (size_t)str_len)
             {
-                zend_error(E_WARNING,
-                           "[msgpack] (%s) Extra bytes", __FUNCTION__);
+                MSGPACK_WARNING("[msgpack] (%s) Extra bytes", __FUNCTION__);
             }
             break;
         default:
             msgpack_unserialize_var_destroy(&var_hash, 0);
-            if (MSGPACK_G(error_display))
-            {
-                zend_error(E_WARNING,
-                           "[msgpack] (%s) Unknown result", __FUNCTION__);
-            }
+            MSGPACK_WARNING("[msgpack] (%s) Unknown result", __FUNCTION__);
             break;
     }
 }
@@ -310,9 +297,11 @@ static ZEND_FUNCTION(msgpack_unserialize)
 {
     char *str;
     int str_len;
+    zval *object = NULL;
 
     if (zend_parse_parameters(
-            ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE)
+            ZEND_NUM_ARGS() TSRMLS_CC, "s|z",
+            &str, &str_len, &object) == FAILURE)
     {
         return;
     }
@@ -322,5 +311,20 @@ static ZEND_FUNCTION(msgpack_unserialize)
         RETURN_NULL();
     }
 
-    php_msgpack_unserialize(return_value, str, str_len TSRMLS_CC);
+    if (object == NULL)
+    {
+        php_msgpack_unserialize(return_value, str, str_len TSRMLS_CC);
+    }
+    else
+    {
+        zval *zv;
+
+        ALLOC_INIT_ZVAL(zv);
+        php_msgpack_unserialize(zv, str, str_len TSRMLS_CC);
+
+        if (msgpack_convert_object(return_value, object, &zv) != SUCCESS)
+        {
+            RETURN_NULL();
+        }
+    }
 }
diff --git a/php/msgpack_class.c b/php/msgpack_class.c
index 903712df..3cd21a35 100644
--- a/php/msgpack_class.c
+++ b/php/msgpack_class.c
@@ -5,6 +5,8 @@
 #include "msgpack_pack.h"
 #include "msgpack_unpack.h"
 #include "msgpack_class.h"
+#include "msgpack_convert.h"
+#include "msgpack_errors.h"
 
 typedef struct {
     zend_object object;
@@ -96,6 +98,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_msgpack_base_unpack, 0, 0, 1)
     ZEND_ARG_INFO(0, str)
+    ZEND_ARG_INFO(0, object)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_msgpack_base_unpacker, 0, 0, 0)
@@ -144,6 +147,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_msgpack_unpacker_execute, 1, 0, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_msgpack_unpacker_data, 0, 0, 0)
+    ZEND_ARG_INFO(0, object)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_msgpack_unpacker_reset, 0, 0, 0)
@@ -289,12 +293,8 @@ static ZEND_METHOD(msgpack, setOption)
             base->php_only = Z_BVAL_P(value);
             break;
         default:
-            if (MSGPACK_G(error_display))
-            {
-                zend_error(E_WARNING,
-                           "[msgpack] (MessagePack::setOption) "
-                           "error setting msgpack option");
-            }
+            MSGPACK_WARNING("[msgpack] (MessagePack::setOption) "
+                            "error setting msgpack option");
             RETURN_FALSE;
             break;
     }
@@ -330,11 +330,13 @@ static ZEND_METHOD(msgpack, unpack)
 {
     char *str;
     int str_len;
+    zval *object = NULL;
     int php_only = MSGPACK_G(php_only);
     MSGPACK_BASE_OBJECT;
 
     if (zend_parse_parameters(
-            ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE)
+            ZEND_NUM_ARGS() TSRMLS_CC, "s|z",
+            &str, &str_len, &object) == FAILURE)
     {
         return;
     }
@@ -346,7 +348,22 @@ static ZEND_METHOD(msgpack, unpack)
 
     MSGPACK_G(php_only) = base->php_only;
 
-    php_msgpack_unserialize(return_value, str, str_len TSRMLS_CC);
+    if (object == NULL)
+    {
+        php_msgpack_unserialize(return_value, str, str_len TSRMLS_CC);
+    }
+    else
+    {
+        zval *zv;
+
+        ALLOC_INIT_ZVAL(zv);
+        php_msgpack_unserialize(zv, str, str_len TSRMLS_CC);
+
+        if (msgpack_convert_object(return_value, object, &zv) != SUCCESS)
+        {
+            RETURN_NULL();
+        }
+    }
 
     MSGPACK_G(php_only) = php_only;
 }
@@ -429,12 +446,8 @@ static ZEND_METHOD(msgpack_unpacker, setOption)
             unpacker->php_only = Z_BVAL_P(value);
             break;
         default:
-            if (MSGPACK_G(error_display))
-            {
-                zend_error(E_WARNING,
-                           "[msgpack] (MessagePackUnpacker::setOption) "
-                           "error setting msgpack option");
-            }
+            MSGPACK_WARNING("[msgpack] (MessagePackUnpacker::setOption) "
+                            "error setting msgpack option");
             RETURN_FALSE;
             break;
     }
@@ -468,7 +481,7 @@ static ZEND_METHOD(msgpack_unpacker, execute)
 {
     char *str = NULL, *data;
     long str_len = 0;
-    zval *offset;
+    zval *offset = NULL;
     int ret;
     size_t len, off;
     int error_display = MSGPACK_G(error_display);
@@ -484,20 +497,16 @@ static ZEND_METHOD(msgpack_unpacker, execute)
 
     if (str != NULL)
     {
-        if (ZEND_NUM_ARGS() < 2)
-        {
-            if (MSGPACK_G(error_display))
-            {
-                zend_error(E_WARNING,
-                           "[msgpack] (MessagePackUnpacker::execute) "
-                           "expects exactly 2 parameters");
-            }
-            RETURN_FALSE;
-        }
-
         data = (char *)str;
         len = (size_t)str_len;
-        off = Z_LVAL_P(offset);
+        if (offset != NULL)
+        {
+            off = Z_LVAL_P(offset);
+        }
+        else
+        {
+            off = 0;
+        }
     }
     else
     {
@@ -538,7 +547,10 @@ static ZEND_METHOD(msgpack_unpacker, execute)
 
     if (str != NULL)
     {
-        ZVAL_LONG(offset, off);
+        if (offset != NULL)
+        {
+            ZVAL_LONG(offset, off);
+        }
     }
     else
     {
@@ -560,11 +572,33 @@ static ZEND_METHOD(msgpack_unpacker, execute)
 
 static ZEND_METHOD(msgpack_unpacker, data)
 {
+    zval *object = NULL;
     MSGPACK_UNPACKER_OBJECT;
 
+    if (zend_parse_parameters(
+            ZEND_NUM_ARGS() TSRMLS_CC, "|z", &object) == FAILURE)
+    {
+        return;
+    }
+
     if (unpacker->retval != NULL)
     {
-        ZVAL_ZVAL(return_value, unpacker->retval, 1, 0);
+        if (object == NULL)
+        {
+            ZVAL_ZVAL(return_value, unpacker->retval, 1, 0);
+        }
+        else
+        {
+            zval *zv;
+
+            ALLOC_INIT_ZVAL(zv);
+            ZVAL_ZVAL(zv, unpacker->retval, 1, 0);
+
+            if (msgpack_convert_object(return_value, object, &zv) != SUCCESS)
+            {
+                RETURN_NULL();
+            }
+        }
 
         MSGPACK_METHOD(msgpack_unpacker, reset, NULL, getThis());
 
diff --git a/php/msgpack_convert.c b/php/msgpack_convert.c
new file mode 100644
index 00000000..47092836
--- /dev/null
+++ b/php/msgpack_convert.c
@@ -0,0 +1,292 @@
+
+#include "php.h"
+
+#include "php_msgpack.h"
+#include "msgpack_convert.h"
+#include "msgpack_errors.h"
+
+#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 3)
+#   define Z_REFCOUNT_P(pz)    ((pz)->refcount)
+#   define Z_SET_ISREF_P(pz)   (pz)->is_ref = 1
+#   define Z_UNSET_ISREF_P(pz) (pz)->is_ref = 0
+#endif
+
+#define MSGPACK_CONVERT_COPY_ZVAL(_pz, _ppz)   \
+    ALLOC_INIT_ZVAL(_pz);                      \
+    *(_pz) = **(_ppz);                         \
+    if (PZVAL_IS_REF(*(_ppz))) {               \
+        if (Z_REFCOUNT_P(*(_ppz)) > 0) {       \
+            zval_copy_ctor(_pz);               \
+        } else {                               \
+            FREE_ZVAL(*(_ppz));                \
+        }                                      \
+        INIT_PZVAL(_pz);                       \
+        Z_SET_ISREF_P(_pz);                    \
+    } else {                                   \
+        zval_copy_ctor(_pz);                   \
+        INIT_PZVAL(_pz);                       \
+    }
+
+#define MSGPACK_CONVERT_UPDATE_PROPERTY(_ht, _key, _key_len, _val, _var)  \
+    if (zend_symtable_update(                                             \
+            _ht, _key, _key_len, &_val, sizeof(_val), NULL) == SUCCESS) { \
+        zend_hash_add(_var, _key, _key_len, &_val, sizeof(_val), NULL);   \
+        return SUCCESS;                                                   \
+    }
+
+inline int msgpack_convert_long_to_properties(
+    HashTable *ht, HashTable **properties, HashPosition *prop_pos,
+    uint key_index, zval *val, HashTable *var)
+{
+    if (*properties != NULL)
+    {
+        char *prop_key;
+        uint prop_key_len;
+        ulong prop_key_index;
+
+        for (;; zend_hash_move_forward_ex(*properties, prop_pos))
+        {
+            if (zend_hash_get_current_key_ex(
+                    *properties, &prop_key, &prop_key_len,
+                    &prop_key_index, 0, prop_pos) == HASH_KEY_IS_STRING)
+            {
+                if (var == NULL ||
+                    !zend_hash_exists(var, prop_key, prop_key_len))
+                {
+                    zend_hash_move_forward_ex(*properties, prop_pos);
+                    return zend_symtable_update(
+                        ht, prop_key, prop_key_len,
+                        &val, sizeof(val), NULL);
+                }
+            }
+            else
+            {
+                break;
+            }
+        }
+
+        *properties = NULL;
+    }
+
+    return zend_hash_index_update(ht, key_index, &val, sizeof(val), NULL);
+}
+
+inline int msgpack_convert_string_to_properties(
+    zval *object, char *key, uint key_len, zval *val, HashTable *var)
+{
+    zval **data = NULL;
+    HashTable *ht;
+    zend_class_entry *ce;
+    char *prot_name, *priv_name;
+    int prop_name_len;
+    TSRMLS_FETCH();
+
+    ht = HASH_OF(object);
+    ce = zend_get_class_entry(object TSRMLS_CC);
+
+    /* private */
+    zend_mangle_property_name(
+        &priv_name, &prop_name_len, ce->name, ce->name_length, key, key_len, 1);
+    if (zend_hash_find(
+            ht, priv_name, prop_name_len, (void **)&data) == SUCCESS)
+    {
+        MSGPACK_CONVERT_UPDATE_PROPERTY(ht, priv_name, prop_name_len, val, var);
+    }
+
+    /* protected */
+    zend_mangle_property_name(
+        &prot_name, &prop_name_len, "*", 1, key, key_len, 1);
+    if (zend_hash_find(
+            ht, prot_name, prop_name_len, (void **)&data) == SUCCESS)
+    {
+        MSGPACK_CONVERT_UPDATE_PROPERTY(ht, prot_name, prop_name_len, val, var);
+    }
+
+    /* public */
+    MSGPACK_CONVERT_UPDATE_PROPERTY(ht, key, key_len, val, var);
+
+    return FAILURE;
+}
+
+int msgpack_convert_object(zval *return_value, zval *object, zval **value)
+{
+    zend_class_entry *ce, **pce;
+    HashTable *properties = NULL;
+    HashPosition prop_pos;
+    TSRMLS_FETCH();
+
+    switch (Z_TYPE_P(object))
+    {
+        case IS_STRING:
+            if (zend_lookup_class(
+                    Z_STRVAL_P(object), Z_STRLEN_P(object),
+                    &pce TSRMLS_CC) != SUCCESS)
+            {
+                MSGPACK_ERROR("[msgpack] (%s) Class '%s' not found",
+                              __FUNCTION__, Z_STRVAL_P(object));
+                return FAILURE;
+            }
+            ce = *pce;
+            break;
+        case IS_OBJECT:
+            ce = zend_get_class_entry(object TSRMLS_CC);
+            break;
+        default:
+            MSGPACK_ERROR("[msgpack] (%s) Object type is unsupported",
+                          __FUNCTION__);
+            return FAILURE;
+    }
+
+    if (Z_TYPE_PP(value) == IS_OBJECT)
+    {
+        zend_class_entry *vce;
+
+        vce = zend_get_class_entry(*value TSRMLS_CC);
+        if (strcmp(ce->name, vce->name) == 0)
+        {
+            *return_value = **value;
+            zval_copy_ctor(return_value);
+            zval_ptr_dtor(value);
+            return SUCCESS;
+        }
+    }
+
+    object_init_ex(return_value, ce);
+    properties = Z_OBJ_HT_P(return_value)->get_properties(
+        return_value TSRMLS_CC);
+    if (HASH_OF(object))
+    {
+        properties = HASH_OF(object);
+    }
+    zend_hash_internal_pointer_reset_ex(properties, &prop_pos);
+
+    switch (Z_TYPE_PP(value))
+    {
+        case IS_ARRAY:
+        {
+            char *key;
+            uint key_len;
+            int key_type;
+            ulong key_index;
+            zval **data;
+            HashPosition pos;
+            HashTable *ht, *ret;
+            HashTable *var = NULL;
+            int num;
+
+            ht = HASH_OF(*value);
+            ret = HASH_OF(return_value);
+
+            num = zend_hash_num_elements(ht);
+            if (num <= 0)
+            {
+                zval_ptr_dtor(value);
+                break;
+            }
+
+            ALLOC_HASHTABLE(var);
+            zend_hash_init(var, num, NULL, NULL, 0);
+
+            /* string */
+            if (ht->nNumOfElements != ht->nNextFreeElement)
+            {
+                zend_hash_internal_pointer_reset_ex(ht, &pos);
+                for (;; zend_hash_move_forward_ex(ht, &pos))
+                {
+                    key_type = zend_hash_get_current_key_ex(
+                        ht, &key, &key_len, &key_index, 0, &pos);
+
+                    if (key_type == HASH_KEY_NON_EXISTANT)
+                    {
+                        break;
+                    }
+
+                    if (zend_hash_get_current_data_ex(
+                            ht, (void *)&data, &pos) != SUCCESS)
+                    {
+                        continue;
+                    }
+
+                    if (key_type == HASH_KEY_IS_STRING)
+                    {
+                        zval *val;
+                        MSGPACK_CONVERT_COPY_ZVAL(val, data);
+                        if (msgpack_convert_string_to_properties(
+                                return_value, key, key_len, val, var) != SUCCESS)
+                        {
+                            zval_ptr_dtor(&val);
+                            MSGPACK_WARNING(
+                                "[msgpack] (%s) "
+                                "illegal offset type, skip this decoding",
+                                __FUNCTION__);
+                        }
+                    }
+                }
+            }
+
+            /* index */
+            zend_hash_internal_pointer_reset_ex(ht, &pos);
+            for (;; zend_hash_move_forward_ex(ht, &pos))
+            {
+                key_type = zend_hash_get_current_key_ex(
+                    ht, &key, &key_len, &key_index, 0, &pos);
+
+                if (key_type == HASH_KEY_NON_EXISTANT)
+                {
+                    break;
+                }
+
+                if (zend_hash_get_current_data_ex(
+                        ht, (void *)&data, &pos) != SUCCESS)
+                {
+                    continue;
+                }
+
+                switch (key_type)
+                {
+                    case HASH_KEY_IS_LONG:
+                    {
+                        zval *val;
+                        MSGPACK_CONVERT_COPY_ZVAL(val, data);
+                        if (msgpack_convert_long_to_properties(
+                                ret, &properties, &prop_pos,
+                                key_index, val, var) != SUCCESS)
+                        {
+                            zval_ptr_dtor(&val);
+                            MSGPACK_WARNING(
+                                "[msgpack] (%s) "
+                                "illegal offset type, skip this decoding",
+                                __FUNCTION__);
+                        }
+                        break;
+                    }
+                    case HASH_KEY_IS_STRING:
+                        break;
+                    default:
+                        MSGPACK_WARNING(
+                            "[msgpack] (%s) key is not string nor array",
+                            __FUNCTION__);
+                        break;
+                }
+            }
+
+            zend_hash_destroy(var);
+            FREE_HASHTABLE(var);
+
+            zval_ptr_dtor(value);
+            break;
+        }
+        default:
+            if (msgpack_convert_long_to_properties(
+                    HASH_OF(return_value), &properties, &prop_pos,
+                    0, *value, NULL) != SUCCESS)
+            {
+                MSGPACK_WARNING(
+                    "[msgpack] (%s) illegal offset type, skip this decoding",
+                    __FUNCTION__);
+            }
+            break;
+    }
+
+    return SUCCESS;
+}
diff --git a/php/msgpack_convert.h b/php/msgpack_convert.h
new file mode 100644
index 00000000..411f706b
--- /dev/null
+++ b/php/msgpack_convert.h
@@ -0,0 +1,7 @@
+
+#ifndef MSGPACK_CONVERT_H
+#define MSGPACK_CONVERT_H
+
+int msgpack_convert_object(zval *return_value, zval *object, zval **value);
+
+#endif
diff --git a/php/msgpack_errors.h b/php/msgpack_errors.h
new file mode 100644
index 00000000..2cbce201
--- /dev/null
+++ b/php/msgpack_errors.h
@@ -0,0 +1,17 @@
+
+#ifndef MSGPACK_ERRORS_H
+#define MSGPACK_ERRORS_H
+
+#define MSGPACK_NOTICE(...)                \
+    if (MSGPACK_G(error_display)) {        \
+        zend_error(E_NOTICE, __VA_ARGS__); \
+    }
+
+#define MSGPACK_WARNING(...)                \
+    if (MSGPACK_G(error_display)) {         \
+        zend_error(E_WARNING, __VA_ARGS__); \
+    }
+
+#define MSGPACK_ERROR(...) zend_error(E_ERROR, __VA_ARGS__)
+
+#endif
diff --git a/php/msgpack_pack.c b/php/msgpack_pack.c
index 36a1ade2..a6dee566 100644
--- a/php/msgpack_pack.c
+++ b/php/msgpack_pack.c
@@ -7,6 +7,7 @@
 
 #include "php_msgpack.h"
 #include "msgpack_pack.h"
+#include "msgpack_errors.h"
 
 #include "msgpack/pack_define.h"
 #define msgpack_pack_user smart_str*
@@ -18,7 +19,7 @@
     smart_str_appendl(user, (const void*)buf, len)
 #include "msgpack/pack_template.h"
 
-#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 3)
+#if ZEND_MODULE_API_NO < 20090626
 #   define Z_ISREF_P(pz) PZVAL_IS_REF(pz)
 #endif
 
@@ -123,15 +124,10 @@ inline static void msgpack_serialize_class(
 
             if (Z_TYPE_PP(name) != IS_STRING)
             {
-                if (MSGPACK_G(error_display))
-                {
-                    zend_error(E_NOTICE,
-                               "[msgpack] (%s) "
-                               "__sleep should return an array only "
-                               "containing the names of "
-                               "instance-variables to serialize.",
-                               __FUNCTION__);
-                }
+                MSGPACK_NOTICE(
+                    "[msgpack] (%s) __sleep should return an array only "
+                    "containing the names of instance-variables to serialize",
+                    __FUNCTION__);
                 continue;
             }
 
@@ -201,14 +197,10 @@ inline static void msgpack_serialize_class(
 
                         pefree(prot_name, ce->type & ZEND_INTERNAL_CLASS);
 
-                        if (MSGPACK_G(error_display))
-                        {
-                            zend_error(E_NOTICE,
-                                       "[msgpack] (%s) "
-                                       "\"%s\" returned as member variable from "
-                                       "__sleep() but does not exist",
-                                       __FUNCTION__, Z_STRVAL_PP(name));
-                        }
+                        MSGPACK_NOTICE(
+                            "[msgpack] (%s) \"%s\" returned as member "
+                            "variable from __sleep() but does not exist",
+                            __FUNCTION__, Z_STRVAL_PP(name));
 
                         msgpack_serialize_string(
                             buf, Z_STRVAL_PP(name), Z_STRLEN_PP(name));
@@ -291,18 +283,20 @@ inline static void msgpack_serialize_array(
         hash = 0;
         msgpack_pack_array(buf, n);
     }
+    else if (Z_ISREF_P(val) && MSGPACK_G(php_only))
+    {
+        msgpack_pack_map(buf, n + 1);
+        msgpack_pack_nil(buf);
+        msgpack_pack_long(buf, MSGPACK_SERIALIZE_TYPE_REFERENCE);
+    }
+    else if (ht->nNumOfElements == ht->nNextFreeElement)
+    {
+        hash = 0;
+        msgpack_pack_array(buf, n);
+    }
     else
     {
-        if (Z_ISREF_P(val) && MSGPACK_G(php_only))
-        {
-            msgpack_pack_map(buf, n + 1);
-            msgpack_pack_nil(buf);
-            msgpack_pack_long(buf, MSGPACK_SERIALIZE_TYPE_REFERENCE);
-        }
-        else
-        {
-            msgpack_pack_map(buf, n);
-        }
+        msgpack_pack_map(buf, n);
     }
 
     if (n > 0)
@@ -341,13 +335,9 @@ inline static void msgpack_serialize_array(
                         break;
                     default:
                         msgpack_serialize_string(buf, "", sizeof(""));
-                        if (MSGPACK_G(error_display))
-                        {
-                            zend_error(E_WARNING,
-                                       "[msgpack] (%s) "
-                                       "key is not string nor array",
-                                       __FUNCTION__);
-                        }
+                        MSGPACK_WARNING(
+                            "[msgpack] (%s) key is not string nor array",
+                            __FUNCTION__);
                         break;
                 }
             }
@@ -446,15 +436,10 @@ inline static void msgpack_serialize_object(
                 }
                 else
                 {
-                    if (MSGPACK_G(error_display))
-                    {
-                        zend_error(E_NOTICE,
-                                   "[msgpack] (%s) "
-                                   "__sleep should return an array only "
-                                   "containing the names of instance-variables "
-                                   "to serialize",
-                                   __FUNCTION__);
-                    }
+                    MSGPACK_NOTICE(
+                        "[msgpack] (%s) __sleep should return an array only "
+                        "containing the names of instance-variables "
+                        "to serialize", __FUNCTION__);
                     msgpack_pack_nil(buf);
                 }
                 zval_ptr_dtor(&retval_ptr);
@@ -483,24 +468,39 @@ void msgpack_serialize_zval(
         msgpack_var_add(
             var_hash, val, (void *)&var_already TSRMLS_CC) == FAILURE)
     {
-        if (Z_ISREF_P(val) && Z_TYPE_P(val) == IS_ARRAY)
+        if (Z_ISREF_P(val))
         {
-            msgpack_pack_map(buf, 2);
+            if (Z_TYPE_P(val) == IS_ARRAY)
+            {
+                msgpack_pack_map(buf, 2);
 
-            msgpack_pack_nil(buf);
-            msgpack_pack_long(buf, MSGPACK_SERIALIZE_TYPE_RECURSIVE);
+                msgpack_pack_nil(buf);
+                msgpack_pack_long(buf, MSGPACK_SERIALIZE_TYPE_RECURSIVE);
 
-            msgpack_pack_long(buf, 0);
-            msgpack_pack_long(buf, *var_already);
+                msgpack_pack_long(buf, 0);
+                msgpack_pack_long(buf, *var_already);
 
-            return;
+                return;
+            }
+            else if (Z_TYPE_P(val) == IS_OBJECT)
+            {
+                msgpack_pack_map(buf, 2);
+
+                msgpack_pack_nil(buf);
+                msgpack_pack_long(buf, MSGPACK_SERIALIZE_TYPE_OBJECT_REFERENCE);
+
+                msgpack_pack_long(buf, 0);
+                msgpack_pack_long(buf, *var_already);
+
+                return;
+            }
         }
         else if (Z_TYPE_P(val) == IS_OBJECT)
         {
             msgpack_pack_map(buf, 2);
 
             msgpack_pack_nil(buf);
-            msgpack_pack_long(buf, MSGPACK_SERIALIZE_TYPE_RECURSIVE);
+            msgpack_pack_long(buf, MSGPACK_SERIALIZE_TYPE_OBJECT);
 
             msgpack_pack_long(buf, 0);
             msgpack_pack_long(buf, *var_already);
@@ -554,12 +554,9 @@ void msgpack_serialize_zval(
             }
             break;
         default:
-            if (MSGPACK_G(error_display))
-            {
-                zend_error(E_WARNING,
-                           "[msgpack] (%s) type is unsupported, encoded as null",
-                           __FUNCTION__);
-            }
+            MSGPACK_WARNING(
+                "[msgpack] (%s) type is unsupported, encoded as null",
+                __FUNCTION__);
             msgpack_pack_nil(buf);
             break;
     }
diff --git a/php/msgpack_pack.h b/php/msgpack_pack.h
index 16f3e7bc..0d65b01a 100644
--- a/php/msgpack_pack.h
+++ b/php/msgpack_pack.h
@@ -10,6 +10,8 @@ enum msgpack_serialize_type
     MSGPACK_SERIALIZE_TYPE_REFERENCE =  1,
     MSGPACK_SERIALIZE_TYPE_RECURSIVE,
     MSGPACK_SERIALIZE_TYPE_CUSTOM_OBJECT,
+    MSGPACK_SERIALIZE_TYPE_OBJECT,
+    MSGPACK_SERIALIZE_TYPE_OBJECT_REFERENCE,
 };
 
 void msgpack_serialize_zval(
diff --git a/php/msgpack_unpack.c b/php/msgpack_unpack.c
index 3c8e6ee2..bcc235f3 100644
--- a/php/msgpack_unpack.c
+++ b/php/msgpack_unpack.c
@@ -6,6 +6,7 @@
 #include "php_msgpack.h"
 #include "msgpack_pack.h"
 #include "msgpack_unpack.h"
+#include "msgpack_errors.h"
 
 #if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 3)
 #   define Z_ADDREF_PP(ppz)      ZVAL_ADDREF(*(ppz))
@@ -228,12 +229,8 @@ inline static zend_class_entry* msgpack_unserialize_class(
                 CG(function_table), NULL, user_func, &retval_ptr,
                 1, args, 0, NULL TSRMLS_CC) != SUCCESS)
         {
-            if (MSGPACK_G(error_display))
-            {
-                zend_error(E_WARNING,
-                           "[msgpack] (%s) defined (%s) but not found",
-                           __FUNCTION__, class_name);
-            }
+            MSGPACK_WARNING("[msgpack] (%s) defined (%s) but not found",
+                            __FUNCTION__, class_name);
 
             incomplete_class = 1;
             ce = PHP_IC_ENTRY;
@@ -253,13 +250,9 @@ inline static zend_class_entry* msgpack_unserialize_class(
         }
         else
         {
-            if (MSGPACK_G(error_display))
-            {
-                zend_error(E_WARNING,
-                           "[msgpack] (%s) "
-                           "Function %s() hasn't defined the class "
-                           "it was called for", __FUNCTION__, class_name);
-            }
+            MSGPACK_WARNING("[msgpack] (%s) Function %s() hasn't defined "
+                            "the class it was called for",
+                            __FUNCTION__, class_name);
 
             incomplete_class = 1;
             ce = PHP_IC_ENTRY;
@@ -272,12 +265,7 @@ inline static zend_class_entry* msgpack_unserialize_class(
 
     if (EG(exception))
     {
-        if (MSGPACK_G(error_display))
-        {
-            zend_error(E_WARNING,
-                       "[msgpack] (%s) Exception error", __FUNCTION__);
-        }
-
+        MSGPACK_WARNING("[msgpack] (%s) Exception error", __FUNCTION__);
         return NULL;
     }
 
@@ -552,6 +540,12 @@ int msgpack_unserialize_map_item(
                     case MSGPACK_SERIALIZE_TYPE_CUSTOM_OBJECT:
                         unpack->type = MSGPACK_SERIALIZE_TYPE_CUSTOM_OBJECT;
                         break;
+                    case MSGPACK_SERIALIZE_TYPE_OBJECT_REFERENCE:
+                        unpack->type = MSGPACK_SERIALIZE_TYPE_OBJECT_REFERENCE;
+                        break;
+                    case MSGPACK_SERIALIZE_TYPE_OBJECT:
+                        unpack->type = MSGPACK_SERIALIZE_TYPE_OBJECT;
+                        break;
                     default:
                         break;
                 }
@@ -573,78 +567,88 @@ int msgpack_unserialize_map_item(
 
             return 0;
         }
-        else if (unpack->type == MSGPACK_SERIALIZE_TYPE_CUSTOM_OBJECT)
+        else
         {
-            unpack->type = MSGPACK_SERIALIZE_TYPE_NONE;
-
-            ce = msgpack_unserialize_class(
-                container, Z_STRVAL_P(key), Z_STRLEN_P(key));
-
-            if (ce == NULL)
+            switch (unpack->type)
             {
-                MSGPACK_UNSERIALIZE_FINISH_MAP_ITEM(unpack, key, val);
+                case MSGPACK_SERIALIZE_TYPE_CUSTOM_OBJECT:
+                    unpack->type = MSGPACK_SERIALIZE_TYPE_NONE;
 
-                return 0;
-            }
+                    ce = msgpack_unserialize_class(
+                        container, Z_STRVAL_P(key), Z_STRLEN_P(key));
+                    if (ce == NULL)
+                    {
+                        MSGPACK_UNSERIALIZE_FINISH_MAP_ITEM(unpack, key, val);
+
+                        return 0;
+                    }
 
 #if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 0)
-            /* implementing Serializable */
-            if (ce->unserialize == NULL)
-            {
-                if (MSGPACK_G(error_display))
-                {
-                    zend_error(E_WARNING,
-                               "[msgpack] (%s) Class %s has no unserializer",
-                               __FUNCTION__, ce->name);
-                }
+                    /* implementing Serializable */
+                    if (ce->unserialize == NULL)
+                    {
+                        MSGPACK_WARNING(
+                            "[msgpack] (%s) Class %s has no unserializer",
+                            __FUNCTION__, ce->name);
 
-                MSGPACK_UNSERIALIZE_FINISH_MAP_ITEM(unpack, key, val);
+                        MSGPACK_UNSERIALIZE_FINISH_MAP_ITEM(unpack, key, val);
 
-                return 0;
-            }
+                        return 0;
+                    }
 
-            ce->unserialize(
-                container, ce,
-                (const unsigned char *)Z_STRVAL_P(val), Z_STRLEN_P(val) + 1,
-                NULL TSRMLS_CC);
+                    ce->unserialize(
+                        container, ce,
+                        (const unsigned char *)Z_STRVAL_P(val),
+                        Z_STRLEN_P(val) + 1, NULL TSRMLS_CC);
 #endif
 
-            MSGPACK_UNSERIALIZE_FINISH_MAP_ITEM(unpack, key, val);
+                    MSGPACK_UNSERIALIZE_FINISH_MAP_ITEM(unpack, key, val);
 
-            return 0;
-        }
-        else if (unpack->type == MSGPACK_SERIALIZE_TYPE_RECURSIVE)
-        {
-            zval **rval;
-
-            unpack->type = MSGPACK_SERIALIZE_TYPE_NONE;
-            if (msgpack_var_access(
-                    unpack->var_hash, Z_LVAL_P(val) - 1, &rval) != SUCCESS)
-            {
-                if (MSGPACK_G(error_display))
+                    return 0;
+                case MSGPACK_SERIALIZE_TYPE_RECURSIVE:
+                case MSGPACK_SERIALIZE_TYPE_OBJECT:
+                case MSGPACK_SERIALIZE_TYPE_OBJECT_REFERENCE:
                 {
-                    zend_error(E_WARNING,
-                               "[msgpack] (%s) Invalid references value: %ld",
-                               __FUNCTION__, Z_LVAL_P(val) - 1);
+                    zval **rval;
+                    int type = unpack->type;
+
+                    unpack->type = MSGPACK_SERIALIZE_TYPE_NONE;
+                    if (msgpack_var_access(
+                            unpack->var_hash,
+                            Z_LVAL_P(val) - 1, &rval) != SUCCESS)
+                    {
+                        MSGPACK_WARNING(
+                            "[msgpack] (%s) Invalid references value: %ld",
+                            __FUNCTION__, Z_LVAL_P(val) - 1);
+
+                        MSGPACK_UNSERIALIZE_FINISH_MAP_ITEM(unpack, key, val);
+
+                        return 0;
+                    }
+
+                    if (container != NULL)
+                    {
+                        zval_ptr_dtor(container);
+                    }
+
+                    *container = *rval;
+
+                    Z_ADDREF_PP(container);
+
+                    if (type == MSGPACK_SERIALIZE_TYPE_OBJECT)
+                    {
+                        Z_UNSET_ISREF_PP(container);
+                    }
+                    else if (type == MSGPACK_SERIALIZE_TYPE_OBJECT_REFERENCE)
+                    {
+                        Z_SET_ISREF_PP(container);
+                    }
+
+                    MSGPACK_UNSERIALIZE_FINISH_MAP_ITEM(unpack, key, val);
+
+                    return 0;
                 }
-
-                MSGPACK_UNSERIALIZE_FINISH_MAP_ITEM(unpack, key, val);
-
-                return 0;
             }
-
-            if (container != NULL)
-            {
-                zval_ptr_dtor(container);
-            }
-
-            *container = *rval;
-
-            Z_ADDREF_PP(container);
-
-            MSGPACK_UNSERIALIZE_FINISH_MAP_ITEM(unpack, key, val);
-
-            return 0;
         }
     }
 
@@ -661,13 +665,9 @@ int msgpack_unserialize_map_item(
                     sizeof(val), NULL) == FAILURE)
             {
                 zval_ptr_dtor(&val);
-                if (MSGPACK_G(error_display))
-                {
-                    zend_error(E_WARNING,
-                               "[msgpack] (%s) "
-                               "illegal offset type, skip this decoding",
-                               __FUNCTION__);
-                }
+                MSGPACK_WARNING(
+                    "[msgpack] (%s) illegal offset type, skip this decoding",
+                    __FUNCTION__);
             }
             break;
         case IS_STRING:
@@ -676,24 +676,16 @@ int msgpack_unserialize_map_item(
                     &val, sizeof(val), NULL) == FAILURE)
             {
                 zval_ptr_dtor(&val);
-                if (MSGPACK_G(error_display))
-                {
-                    zend_error(E_WARNING,
-                               "[msgpack] (%s) "
-                               "illegal offset type, skip this decoding",
-                               __FUNCTION__);
-                }
+                MSGPACK_WARNING(
+                    "[msgpack] (%s) illegal offset type, skip this decoding",
+                    __FUNCTION__);
             }
             break;
         default:
             zval_ptr_dtor(&val);
-            if (MSGPACK_G(error_display))
-            {
-                zend_error(E_WARNING,
-                           "[msgpack] (%s) "
-                           "illegal offset type, skip this decoding",
-                           __FUNCTION__);
-            }
+            MSGPACK_WARNING(
+                "[msgpack] (%s) illegal offset type, skip this decoding",
+                __FUNCTION__);
             break;
     }
 
diff --git a/php/package.xml b/php/package.xml
index 31a480b6..4bb29000 100644
--- a/php/package.xml
+++ b/php/package.xml
@@ -10,11 +10,11 @@
     <email>advect@gmail.com</email>
     <active>yes</active>
   </lead>
-  <date>2010-12-28</date>
-  <time>15:40:02</time>
+  <date>2011-01-16</date>
+  <time>16:05:34</time>
   <version>
-    <release>0.3.4</release>
-    <api>0.3.4</api>
+    <release>0.4.0</release>
+    <api>0.4.0</api>
   </version>
   <stability>
     <release>beta</release>
@@ -27,18 +27,22 @@
       <file md5sum="12b9d8867e5fde4af426c134283e2082" name="LICENSE" role="doc" />
       <file md5sum="8daeb22744f11b57da9d0966608e2fc1" name="README" role="doc" />
       <file md5sum="c40d516627022a54003ac2b74a82688a" name="CREDITS" role="doc" />
-      <file md5sum="7eec74d33cff6f8aaeb158bd049a3998" name="ChangeLog" role="doc" />
+      <file md5sum="4175fc9f98d3c3755abc4ed9f8340f98" name="ChangeLog" role="doc" />
       <file md5sum="d41d8cd98f00b204e9800998ecf8427e" name="EXPERIMENTAL" role="doc" />
-      <file md5sum="8cc392f13f5a89d033cf73e612377b95" name="config.m4" role="src" />
-      <file md5sum="712ddaf861db6e52a7cf4885c6eced70" name="config.w32" role="src" />
-      <file md5sum="c9216741b72004ddf1a1347398220433" name="msgpack.c" role="src" />
-      <file md5sum="1b97cb7ae1cb157115ba8df0b3331229" name="msgpack_class.c" role="src" />
+      <file md5sum="6747376fed353e6bd2490c5c4b3a096c" name="config.m4" role="src" />
+      <file md5sum="195040cca1f961b6356d296e60a532ce" name="config.w32" role="src" />
+      <file md5sum="8eadc10d9a8345f3ffa9614348cdb939" name="msgpack.php" role="src" />
+      <file md5sum="3348110bba3168ea57f5ba973bf6fcb5" name="msgpack.c" role="src" />
+      <file md5sum="654f52c1edc96100c71190d072421f20" name="msgpack_class.c" role="src" />
       <file md5sum="b8eadd32c3eafb67645bb4ffe16f687c" name="msgpack_class.h" role="src" />
-      <file md5sum="d08dda734f564bda69136293aac67c9f" name="msgpack_pack.c" role="src" />
-      <file md5sum="eae6797621ca53f8a7b0c2d9cb8a4d21" name="msgpack_pack.h" role="src" />
-      <file md5sum="163d17e17abc50915668c64145283049" name="msgpack_unpack.c" role="src" />
+      <file md5sum="18951ba7ba70fe79fa551ea25fb11dd2" name="msgpack_convert.c" role="src" />
+      <file md5sum="0958d54ea4a922746c7e72734a5a9b95" name="msgpack_convert.h" role="src" />
+      <file md5sum="7d25383ea3cff751ca993c98b1b5e57e" name="msgpack_errors.h" role="src" />
+      <file md5sum="ffaeb53d003a0961bcb6837545815877" name="msgpack_pack.c" role="src" />
+      <file md5sum="06f85a181c2070272c0ecd0dc3161161" name="msgpack_pack.h" role="src" />
+      <file md5sum="d341e1a3b400b45f844a94ecd5198248" name="msgpack_unpack.c" role="src" />
       <file md5sum="1a31c49f353c8deec86affd1564909d1" name="msgpack_unpack.h" role="src" />
-      <file md5sum="9c3b5db959fbd5df57f2c2af8e69eaef" name="php_msgpack.h" role="src" />
+      <file md5sum="46846c8d7a2c9a3cb81564dd66357f20" name="php_msgpack.h" role="src" />
       <file md5sum="82079e9a298ecdda2122757ddfbf576e" name="msgpack/pack_define.h" role="src" />
       <file md5sum="af3f5ca341be55d866fe151c45186d22" name="msgpack/pack_template.h" role="src" />
       <file md5sum="09510085da29090ea0f3919c2e708f46" name="msgpack/unpack_define.h" role="src" />
@@ -51,15 +55,15 @@
       <file md5sum="21c14487ba126c00d5d42d2dd03c76c3" name="tests/004.phpt" role="test" />
       <file md5sum="99b8bb5df5c98b6e27541245b39bb13f" name="tests/005.phpt" role="test" />
       <file md5sum="e0488f07f26c74d1c918634c39555d6e" name="tests/006.phpt" role="test" />
-      <file md5sum="9574605f18eef124b55ede353d255511" name="tests/007.phpt" role="test" />
-      <file md5sum="dc3584b04311da2a627c6a271a782d2c" name="tests/008.phpt" role="test" />
-      <file md5sum="e16926de566700cac0086472adf728c3" name="tests/009.phpt" role="test" />
-      <file md5sum="05bd0e0989f34098514286c8964f9aff" name="tests/009b.phpt" role="test" />
+      <file md5sum="b64692d608852aeb2482d2dd05634426" name="tests/007.phpt" role="test" />
+      <file md5sum="211dd4d1d52a660b8d598b298cc05df9" name="tests/008.phpt" role="test" />
+      <file md5sum="645c378bd5bc693ab99b1d340e09b250" name="tests/009.phpt" role="test" />
+      <file md5sum="952e140db05c0e59aec3b2a1ba5894a2" name="tests/009b.phpt" role="test" />
       <file md5sum="ddb421cd083baaca3ce3a5263f731dcd" name="tests/010.phpt" role="test" />
       <file md5sum="5d5ecd404959caea3ee96c48e6765669" name="tests/012.phpt" role="test" />
       <file md5sum="049a4b38ad7fa71228d0092171bf9ef2" name="tests/012c.phpt" role="test" />
-      <file md5sum="377f261d48b4f7fc78689aae481ac952" name="tests/013.phpt" role="test" />
-      <file md5sum="31758388a32df170cc497291d1e4385d" name="tests/014.phpt" role="test" />
+      <file md5sum="462898206b0ccd49b0055c96b78a2282" name="tests/013.phpt" role="test" />
+      <file md5sum="690e24be70da60cc9becad0027e762d5" name="tests/014.phpt" role="test" />
       <file md5sum="266edf8fd821d58adcfebef1d9beb045" name="tests/015.phpt" role="test" />
       <file md5sum="beb07e05272c62ec81dad284fa5b36ce" name="tests/015b.phpt" role="test" />
       <file md5sum="abc8ed11d14cf131b8d40be510c36e6b" name="tests/015d.phpt" role="test" />
@@ -74,9 +78,9 @@
       <file md5sum="af470d224342fa2f24212e73a1aca1ca" name="tests/021.phpt" role="test" />
       <file md5sum="23a30dd1eca67eaaf5b640d2153c8c5f" name="tests/022.phpt" role="test" />
       <file md5sum="7fad3357febe18a1db8abca7c50614d1" name="tests/023.phpt" role="test" />
-      <file md5sum="94a334cf86fea8f68b2ff7f3eb56e253" name="tests/024.phpt" role="test" />
-      <file md5sum="83176fa1d4422fee304ea8aea15cac44" name="tests/024b.phpt" role="test" />
-      <file md5sum="a9e1ad0f198191cbe559fc9eff740c7d" name="tests/024c.phpt" role="test" />
+      <file md5sum="d0ba1b06180c264b4f75c56c0eac4458" name="tests/024.phpt" role="test" />
+      <file md5sum="9e336d2611dfb39e235268f242b99755" name="tests/024b.phpt" role="test" />
+      <file md5sum="28c1eb029e8dfd71a4dfd4e7dede019e" name="tests/024c.phpt" role="test" />
       <file md5sum="c48c256fc9a61eda85149df4da56a116" name="tests/025.phpt" role="test" />
       <file md5sum="3a034382e2e40ea9adcfbb1443e22a87" name="tests/025c.phpt" role="test" />
       <file md5sum="ee2fb07b3c06aacb1286d6274c75300c" name="tests/026.phpt" role="test" />
@@ -84,10 +88,10 @@
       <file md5sum="7128b56e93a8de75dda938b4fab661fd" name="tests/026d.phpt" role="test" />
       <file md5sum="282e770306a16651c6157942c65be644" name="tests/027.phpt" role="test" />
       <file md5sum="c8eadc5b775edb2451b895f89ec1fc41" name="tests/027d.phpt" role="test" />
-      <file md5sum="a79c2460a3164cac53df7fab35b99280" name="tests/028.phpt" role="test" />
-      <file md5sum="c25e25c0b159fe98d3f3124466756067" name="tests/028b.phpt" role="test" />
-      <file md5sum="ae8e5e84e50ab051ea58fadd365cd66d" name="tests/028c.phpt" role="test" />
-      <file md5sum="63ef8435656b9c23bf1f24aad607f8c1" name="tests/028d.phpt" role="test" />
+      <file md5sum="59e5a4d0f21579c1e9867e764340eb24" name="tests/028.phpt" role="test" />
+      <file md5sum="831e1e3e3d74ff6090966f91d8715c7b" name="tests/028b.phpt" role="test" />
+      <file md5sum="c93199e139706ed79f77b8fe99b5c57d" name="tests/028c.phpt" role="test" />
+      <file md5sum="19d5993e30880056145dae4ac140961a" name="tests/028d.phpt" role="test" />
       <file md5sum="f380a42cf429a8fb72c50f9d38228109" name="tests/029.phpt" role="test" />
       <file md5sum="7de90bedec2c170da12144bb3399dfe5" name="tests/030.phpt" role="test" />
       <file md5sum="3f37b5f1f4da46a1afeafea516d56490" name="tests/031.phpt" role="test" />
@@ -105,50 +109,75 @@
       <file md5sum="6ee4b49e6e82289e7ea7bf2e37a14212" name="tests/060.phpt" role="test" />
       <file md5sum="4b5a8bd201c8b27bbf392f42586a98ca" name="tests/060b.phpt" role="test" />
       <file md5sum="e245df195174210507fe6e3f02b97144" name="tests/060c.phpt" role="test" />
-      <file md5sum="9abfe03959d99d2999f8124983c6a393" name="tests/061.phpt" role="test" />
-      <file md5sum="77a0522b5d46ed73799a86f4ab2ca844" name="tests/061b.phpt" role="test" />
-      <file md5sum="33138f031180da877490240503128e81" name="tests/061c.phpt" role="test" />
+      <file md5sum="8ac3126adb9ec49eac7f495f5354bc23" name="tests/061.phpt" role="test" />
+      <file md5sum="9b9f2721cbdd656b5e7936173cf9d160" name="tests/061b.phpt" role="test" />
+      <file md5sum="da9e32144da2aea8c5f498078d2e81a1" name="tests/061c.phpt" role="test" />
       <file md5sum="187689f3d1d8fe3636ea7d6188e7e559" name="tests/062.phpt" role="test" />
       <file md5sum="9c91266595d01080b813fb2a056fca82" name="tests/063.phpt" role="test" />
       <file md5sum="9d05c3bfd49ad9c7cf00b9b056552c69" name="tests/064.phpt" role="test" />
       <file md5sum="8db3eeb829880716d4630b6e448877f5" name="tests/064b.phpt" role="test" />
       <file md5sum="0019c5df7135bcb1fa5f7d38a99cd099" name="tests/064c.phpt" role="test" />
-      <file md5sum="e2f1f67b1f2aba44747f1abef8899275" name="tests/065.phpt" role="test" />
-      <file md5sum="4bc30d5c195413b44e4223b45e66c819" name="tests/065b.phpt" role="test" />
-      <file md5sum="e89f1fc1240c2737d16bea70d93b33f5" name="tests/065c.phpt" role="test" />
+      <file md5sum="1bdbd6f93abb361a0c093d3783ee11ff" name="tests/065.phpt" role="test" />
+      <file md5sum="0dc03b9cac5f95a786a1719a7555bf1c" name="tests/065b.phpt" role="test" />
+      <file md5sum="3c2403702f5d31067fcb4d0c53dfb068" name="tests/065c.phpt" role="test" />
       <file md5sum="bad0c2dee56d9f7a8dd0161ed863915e" name="tests/066.phpt" role="test" />
       <file md5sum="d15e923a2bca68f5fb7e285da725c248" name="tests/067.phpt" role="test" />
-      <file md5sum="a64f3db62c391e1c18356d47aeccbe64" name="tests/070.phpt" role="test" />
-      <file md5sum="b348c6c26395f6a1439b4bec33549059" name="tests/070b.phpt" role="test" />
-      <file md5sum="c55954bc5e6fea7d800e3e6349d2536c" name="tests/070c.phpt" role="test" />
-      <file md5sum="9d3022d964b1e9cc1966b4777e1c986a" name="tests/071.phpt" role="test" />
-      <file md5sum="2ec2bcdc412bfe52b2e50a6a36000994" name="tests/071b.phpt" role="test" />
-      <file md5sum="f84d6495c3655b8839adbc9b679bd238" name="tests/071c.phpt" role="test" />
-      <file md5sum="82d0ded38b3ed2445c23f2ddb4c1a23e" name="tests/072.phpt" role="test" />
-      <file md5sum="8a23366b9c309d5c7c6e6f9927fb6b13" name="tests/072b.phpt" role="test" />
-      <file md5sum="74f12ecea570b682bd8de68d60146443" name="tests/072c.phpt" role="test" />
-      <file md5sum="9ed0bcbc7830c1e716c0ab26ab7a46d0" name="tests/073.phpt" role="test" />
-      <file md5sum="092e33b2f560df3110f3c21bdcb5c823" name="tests/073b.phpt" role="test" />
-      <file md5sum="19231469eb0ecb68c424fa3db6720998" name="tests/073c.phpt" role="test" />
-      <file md5sum="50ef1b19cea2b81bcb7cf3f3ff1d040b" name="tests/080.phpt" role="test" />
-      <file md5sum="5ee3dfab84acdee232622aae101ec146" name="tests/081.phpt" role="test" />
-      <file md5sum="c7f9bb84e313d1d675318373938c026a" name="tests/082.phpt" role="test" />
-      <file md5sum="d11ef3361b876d53d2d84b6612373463" name="tests/083.phpt" role="test" />
-      <file md5sum="01c6e99e89d080be939d2145b9552aa4" name="tests/084.phpt" role="test" />
-      <file md5sum="0e6f002a83f987b35a3b0d50886ab509" name="tests/085.phpt" role="test" />
-      <file md5sum="ffc10aeb072866202046c0a0bcac281e" name="tests/086.phpt" role="test" />
-      <file md5sum="71203179b832143b7e5efc2aba9837fc" name="tests/087.phpt" role="test" />
-      <file md5sum="c3130e75e684fa42bcfdbdd5338ddaec" name="tests/087d.phpt" role="test" />
-      <file md5sum="72c67a293e47af26840d509baf26c81d" name="tests/088.phpt" role="test" />
-      <file md5sum="5f712f6c29c88a6116d90fd54de666ad" name="tests/088d.phpt" role="test" />
-      <file md5sum="fb70fa45d6c74bed8c5f9380143c4f42" name="tests/089.phpt" role="test" />
-      <file md5sum="d2c93179cef10dd2ca77bbbc42127585" name="tests/089d.phpt" role="test" />
+      <file md5sum="4057b290693f84ae1bd3053a1ebc25dd" name="tests/070.phpt" role="test" />
+      <file md5sum="395a04ef87a3a63953a42e1434bda80c" name="tests/070b.phpt" role="test" />
+      <file md5sum="0741ce324457decc95792fbcee70e930" name="tests/070c.phpt" role="test" />
+      <file md5sum="addf9d7eddd6a2e84be4af03a98416c3" name="tests/071.phpt" role="test" />
+      <file md5sum="3f752a3b8d579fe1e91de06e80d57197" name="tests/071b.phpt" role="test" />
+      <file md5sum="6a97b56ff05b21d8266a5ea37fcea081" name="tests/071c.phpt" role="test" />
+      <file md5sum="95117434072c89c609672e6f0a569b2b" name="tests/072.phpt" role="test" />
+      <file md5sum="4feabcf2792e2a80b261ee8a89a2b3e8" name="tests/072b.phpt" role="test" />
+      <file md5sum="33f62f9b3d7e5cae66e0c7b264a9dd68" name="tests/072c.phpt" role="test" />
+      <file md5sum="fe1d3229f9d3695ff587365de703dc0c" name="tests/073.phpt" role="test" />
+      <file md5sum="d3010d8a4f03966e64a48f10bc6868b4" name="tests/073b.phpt" role="test" />
+      <file md5sum="c774bc1bb903ee9e1a0f53f29dcfc512" name="tests/073c.phpt" role="test" />
+      <file md5sum="48ca6ddb000967871b553496707f6cbe" name="tests/080.phpt" role="test" />
+      <file md5sum="e2dd4e560717fa7b0718d7370ec1afb8" name="tests/081.phpt" role="test" />
+      <file md5sum="bdbdb5ac59480f15e311d60289cfc1a7" name="tests/082.phpt" role="test" />
+      <file md5sum="6d16ffffa5eaa04f8acca8b67e759f9c" name="tests/083.phpt" role="test" />
+      <file md5sum="4005e6303740cc7cb8cdf5bee6d05103" name="tests/084.phpt" role="test" />
+      <file md5sum="367f2bb3fd016a2ac55e4d0a5e0c9cec" name="tests/085.phpt" role="test" />
+      <file md5sum="658b1c4aa59ee2aa405febae768035fd" name="tests/086.phpt" role="test" />
+      <file md5sum="731e2f603e62c378b14160ae077839bd" name="tests/087.phpt" role="test" />
+      <file md5sum="5745832846722e08916b7dfc0489df15" name="tests/088.phpt" role="test" />
+      <file md5sum="5aafee8947d22e1c0de89f925bdf1649" name="tests/089.phpt" role="test" />
+      <file md5sum="5c12abf7868b4f96fd8819c6682063cd" name="tests/090.phpt" role="test" />
+      <file md5sum="a9895d6af644edff26d1e600a5801b84" name="tests/090b.phpt" role="test" />
+      <file md5sum="34e37d556a05faeed61317d0e9e01152" name="tests/091.phpt" role="test" />
+      <file md5sum="7d33c2ac3290f26daa587d9a34de80cb" name="tests/091b.phpt" role="test" />
+      <file md5sum="fa889c2093d7909fbebf7f7be0b9eaa2" name="tests/092.phpt" role="test" />
+      <file md5sum="086fb77d14a4b26fc7ff64b43559d49d" name="tests/092b.phpt" role="test" />
+      <file md5sum="f53e4a7ea728c85bd4e31b996bf4a50f" name="tests/093.phpt" role="test" />
+      <file md5sum="4f27b69cdabe232e2403e6bb4559f418" name="tests/093b.phpt" role="test" />
+      <file md5sum="3e81bb754a5e014713652b682e149cbd" name="tests/094.phpt" role="test" />
+      <file md5sum="36c0ca7322f400ea1624207f98b643c7" name="tests/094b.phpt" role="test" />
+      <file md5sum="927d3b859769b6d20db8c7eb5bebd351" name="tests/095.phpt" role="test" />
+      <file md5sum="33a3e473833d19b8b0c7d6a559d53592" name="tests/095b.phpt" role="test" />
+      <file md5sum="43a6f327f064e9533f8c09871566d7bc" name="tests/096.phpt" role="test" />
+      <file md5sum="9b7c2604bfcd99a16c26c6be98c866a8" name="tests/096b.phpt" role="test" />
+      <file md5sum="878f9a8345f63406d11bc44ed90350ed" name="tests/097.phpt" role="test" />
+      <file md5sum="e39b692da05e7362e388e0c80d419463" name="tests/097b.phpt" role="test" />
+      <file md5sum="734e3a9c37ffb28d352f85e2638ed6d1" name="tests/098.phpt" role="test" />
+      <file md5sum="2777634b35f4143ab903a855c19446fe" name="tests/098b.phpt" role="test" />
+      <file md5sum="1a18b4403a7c3f2b2a37259a4c03f5fa" name="tests/099.phpt" role="test" />
+      <file md5sum="31da8c01336bb819472a405a85f66477" name="tests/099b.phpt" role="test" />
+      <file md5sum="4bc4def03d39ae7029bfaef612cf2a18" name="tests/100.phpt" role="test" />
+      <file md5sum="515144778f3e998c4c88fcd5c2ac6f2f" name="tests/100b.phpt" role="test" />
+      <file md5sum="9efe9f1d907ac42a27970d3fe5c210e8" name="tests/101.phpt" role="test" />
+      <file md5sum="44fab12e51da9df02ab98f8dfbf3a2b4" name="tests/101b.phpt" role="test" />
+      <file md5sum="8a4eaae887f17ea08816bf24fa63c8fc" name="tests/102.phpt" role="test" />
+      <file md5sum="025d7ce0533df77af4e6621386fd4a0b" name="tests/102b.phpt" role="test" />
+      <file md5sum="cd76b1b0cd4d8905ed896dbac07df670" name="tests/103.phpt" role="test" />
+      <file md5sum="76bab9f004e10e76761c34b2338f9fee" name="tests/103b.phpt" role="test" />
     </dir>
   </contents>
   <dependencies>
     <required>
       <php>
-        <min>5.1.0</min>
+        <min>5.0.0</min>
       </php>
       <pearinstaller>
         <min>1.4.3</min>
diff --git a/php/php-msgpack.spec b/php/php-msgpack.spec
index 86125cb0..c79dfc86 100644
--- a/php/php-msgpack.spec
+++ b/php/php-msgpack.spec
@@ -3,7 +3,7 @@
 
 Summary: PHP extension for interfacing with MessagePack
 Name: php-msgpack
-Version: 0.3.4
+Version: 0.4.0
 Release: 1%{?dist}
 Source: php-msgpack-%{version}.tar.gz
 License: New BSD License
diff --git a/php/php_msgpack.h b/php/php_msgpack.h
index 80dce02e..87b28228 100644
--- a/php/php_msgpack.h
+++ b/php/php_msgpack.h
@@ -2,7 +2,7 @@
 #ifndef PHP_MSGPACK_H
 #define PHP_MSGPACK_H
 
-#define MSGPACK_EXTENSION_VERSION "0.3.4"
+#define MSGPACK_EXTENSION_VERSION "0.4.0"
 
 #include "ext/standard/php_smart_str.h"
 
diff --git a/php/tests/007.phpt b/php/tests/007.phpt
index db41185b..2bafc7b4 100644
--- a/php/tests/007.phpt
+++ b/php/tests/007.phpt
@@ -28,7 +28,7 @@ array(0) {
 }
 OK
 array(1, 2, 3)
-83000101020203
+93010203
 array(3) {
   [0]=>
   int(1)
@@ -39,7 +39,7 @@ array(3) {
 }
 OK
 array(array(1, 2, 3), arr...
-83008300010102020301830004010502060283000701080209
+93930102039304050693070809
 array(3) {
   [0]=>
   array(3) {
diff --git a/php/tests/008.phpt b/php/tests/008.phpt
index 661c1cd6..382a1d11 100644
--- a/php/tests/008.phpt
+++ b/php/tests/008.phpt
@@ -24,7 +24,7 @@ test('array("" => "empty")', array("" => "empty"));
 ?>
 --EXPECT--
 array("foo", "foo", "foo")
-8300a3666f6f01a3666f6f02a3666f6f
+93a3666f6fa3666f6fa3666f6f
 array(3) {
   [0]=>
   string(3) "foo"
diff --git a/php/tests/009.phpt b/php/tests/009.phpt
index 5189e3de..da72a7b9 100644
--- a/php/tests/009.phpt
+++ b/php/tests/009.phpt
@@ -39,7 +39,7 @@ var_dump(msgpack_unserialize(msgpack_serialize($a)));
 
 --EXPECT--
 array($a, $a)
-82008100a3666f6f018100a3666f6f
+9291a3666f6f91a3666f6f
 array(2) {
   [0]=>
   array(1) {
@@ -54,7 +54,7 @@ array(2) {
 }
 OK
 array(&$a, &$a)
-820082c00100a3666f6f0182c0020002
+9282c00100a3666f6f82c0020002
 array(2) {
   [0]=>
   &array(1) {
@@ -69,7 +69,7 @@ array(2) {
 }
 OK
 cyclic
-810082c0010082c0010082c0020002
+9182c0010082c0010082c0020002
 array(1) {
   [0]=>
   &array(1) {
diff --git a/php/tests/009b.phpt b/php/tests/009b.phpt
index 4d02fc0e..1bc195f8 100644
--- a/php/tests/009b.phpt
+++ b/php/tests/009b.phpt
@@ -39,7 +39,7 @@ var_dump(msgpack_unserialize(msgpack_serialize($a)));
 
 --EXPECT--
 array($a, $a)
-82008100a3666f6f018100a3666f6f
+9291a3666f6f91a3666f6f
 array(2) {
   [0]=>
   array(1) {
@@ -54,7 +54,7 @@ array(2) {
 }
 OK
 array(&$a, &$a)
-820082c00100a3666f6f0182c0020002
+9282c00100a3666f6f82c0020002
 array(2) {
   [0]=>
   &array(1) {
@@ -69,7 +69,7 @@ array(2) {
 }
 OK
 cyclic
-810082c0010082c0010082c0020002
+9182c0010082c0010082c0020002
 array(1) {
   [0]=>
   &array(1) {
diff --git a/php/tests/013.phpt b/php/tests/013.phpt
index 73f7d335..caf3434e 100644
--- a/php/tests/013.phpt
+++ b/php/tests/013.phpt
@@ -34,7 +34,7 @@ test('object', $o, false);
 ?>
 --EXPECTF--
 object
-820083c0a34f626aa16101a162020183c0a34f626aa16103a16204
+9283c0a34f626aa16101a1620283c0a34f626aa16103a16204
 array(2) {
   [0]=>
   object(Obj)#%d (2) {
diff --git a/php/tests/014.phpt b/php/tests/014.phpt
index b9c7c671..c74f5f4d 100644
--- a/php/tests/014.phpt
+++ b/php/tests/014.phpt
@@ -34,7 +34,7 @@ test('object', $a, false);
 ?>
 --EXPECTF--
 object
-820084c001c0a34f626aa16101a162020182c0020002
+9284c001c0a34f626aa16101a1620282c0050002
 array(2) {
   [0]=>
   &object(Obj)#%d (2) {
diff --git a/php/tests/024.phpt b/php/tests/024.phpt
index 36c7c250..589b788f 100644
--- a/php/tests/024.phpt
+++ b/php/tests/024.phpt
@@ -113,7 +113,7 @@ object(Obj2)#%d (7) {
 }
 OK
 objectrecarr
-82c0a44f626a33aa004f626a33006f626a73840084c0a34f626aa16100a4002a006200a6004f626a0063040184c0a34f626aa16100a4002a006201a6004f626a0063040284c0a34f626aa16100a4002a006202a6004f626a0063040384c0a34f626aa16100a4002a006203a6004f626a006304
+82c0a44f626a33aa004f626a33006f626a739484c0a34f626aa16100a4002a006200a6004f626a00630484c0a34f626aa16100a4002a006201a6004f626a00630484c0a34f626aa16100a4002a006202a6004f626a00630484c0a34f626aa16100a4002a006203a6004f626a006304
 object(Obj3)#%d (1) {
   [%r"?objs"?:("Obj3":)?private"?%r]=>
   array(4) {
@@ -157,7 +157,7 @@ object(Obj3)#%d (1) {
 }
 OK
 objectselfrec
-83c0a44f626a34a7004f626a34006164a9004f626a34006f626a82c0020001
+83c0a44f626a34a7004f626a34006164a9004f626a34006f626a82c0040001
 object(Obj4)#%d (2) {
   [%r"?a"?:("Obj4":)?private"?%r]=>
   int(100)
diff --git a/php/tests/024b.phpt b/php/tests/024b.phpt
index 9d2e833f..35fc0be9 100644
--- a/php/tests/024b.phpt
+++ b/php/tests/024b.phpt
@@ -116,7 +116,7 @@ object(Obj2)#%d (7) {
 }
 OK
 objectrecarr
-82c0a44f626a33aa004f626a33006f626a73840084c0a34f626aa16100a4002a006200a6004f626a0063040184c0a34f626aa16100a4002a006201a6004f626a0063040284c0a34f626aa16100a4002a006202a6004f626a0063040384c0a34f626aa16100a4002a006203a6004f626a006304
+82c0a44f626a33aa004f626a33006f626a739484c0a34f626aa16100a4002a006200a6004f626a00630484c0a34f626aa16100a4002a006201a6004f626a00630484c0a34f626aa16100a4002a006202a6004f626a00630484c0a34f626aa16100a4002a006203a6004f626a006304
 object(Obj3)#%d (1) {
   [%r"?objs"?:("Obj3":)?private"?%r]=>
   array(4) {
@@ -160,7 +160,7 @@ object(Obj3)#%d (1) {
 }
 OK
 objectselfrec
-83c0a44f626a34a7004f626a34006164a9004f626a34006f626a82c0020001
+83c0a44f626a34a7004f626a34006164a9004f626a34006f626a82c0040001
 object(Obj4)#%d (2) {
   [%r"?a"?:("Obj4":)?private"?%r]=>
   int(100)
diff --git a/php/tests/024c.phpt b/php/tests/024c.phpt
index fd3cb515..a3e6ca30 100644
--- a/php/tests/024c.phpt
+++ b/php/tests/024c.phpt
@@ -111,7 +111,7 @@ object(Obj2)#%d (7) {
 }
 OK
 objectrecarr
-82c0a44f626a33aa004f626a33006f626a73840084c0a34f626aa16100a4002a006200a6004f626a0063040184c0a34f626aa16100a4002a006201a6004f626a0063040284c0a34f626aa16100a4002a006202a6004f626a0063040384c0a34f626aa16100a4002a006203a6004f626a006304
+82c0a44f626a33aa004f626a33006f626a739484c0a34f626aa16100a4002a006200a6004f626a00630484c0a34f626aa16100a4002a006201a6004f626a00630484c0a34f626aa16100a4002a006202a6004f626a00630484c0a34f626aa16100a4002a006203a6004f626a006304
 object(Obj3)#%d (1) {
   ["objs:private"]=>
   array(4) {
@@ -155,7 +155,7 @@ object(Obj3)#%d (1) {
 }
 OK
 objectselfrec
-83c0a44f626a34a7004f626a34006164a9004f626a34006f626a82c0020001
+83c0a44f626a34a7004f626a34006164a9004f626a34006f626a82c0040001
 object(Obj4)#%d (2) {
   ["a:private"]=>
   int(100)
diff --git a/php/tests/028.phpt b/php/tests/028.phpt
index 65fa8db2..6cd9bb25 100644
--- a/php/tests/028.phpt
+++ b/php/tests/028.phpt
@@ -102,7 +102,7 @@ var_dump($_SESSION);
 ?>
 --EXPECTF--
 read
-write: 84c001a36f6c6484c0a3466f6fa700466f6f00643184c0a3426172a2643182c0020002a70042617200643282c0020002a5002a00643382c0020002a5002a00643282c0020003a2643382c0020003a474657374a6666f6f626172a36e657784c0a3426172a2643184c0a3466f6fa700466f6f00643182c0020009a5002a00643282c0020009a2643382c0020009a70042617200643282c002000aa5002a00643382c002000a
+write: 84c001a36f6c6484c0a3466f6fa700466f6f00643184c0a3426172a2643182c0040002a70042617200643282c0040002a5002a00643382c0040002a5002a00643282c0040003a2643382c0040003a474657374a6666f6f626172a36e657784c0a3426172a2643184c0a3466f6fa700466f6f00643182c0040009a5002a00643282c0040009a2643382c0040009a70042617200643282c004000aa5002a00643382c004000a
 array(3) {
   ["old"]=>
   object(Foo)#3 (3) {
diff --git a/php/tests/028b.phpt b/php/tests/028b.phpt
index d184f4e7..651dc961 100644
--- a/php/tests/028b.phpt
+++ b/php/tests/028b.phpt
@@ -105,7 +105,7 @@ var_dump($_SESSION);
 ?>
 --EXPECTF--
 read
-write: 84c001a36f6c6484c0a3466f6fa700466f6f00643184c0a3426172a2643182c0020002a70042617200643282c0020002a5002a00643382c0020002a5002a00643282c0020003a2643382c0020003a474657374a6666f6f626172a36e657784c0a3426172a2643184c0a3466f6fa700466f6f00643182c0020009a5002a00643282c0020009a2643382c0020009a70042617200643282c002000aa5002a00643382c002000a
+write: 84c001a36f6c6484c0a3466f6fa700466f6f00643184c0a3426172a2643182c0040002a70042617200643282c0040002a5002a00643382c0040002a5002a00643282c0040003a2643382c0040003a474657374a6666f6f626172a36e657784c0a3426172a2643184c0a3466f6fa700466f6f00643182c0040009a5002a00643282c0040009a2643382c0040009a70042617200643282c004000aa5002a00643382c004000a
 array(3) {
   ["old"]=>
   object(Foo)#3 (3) {
diff --git a/php/tests/028c.phpt b/php/tests/028c.phpt
index 3e698acd..a841c974 100644
--- a/php/tests/028c.phpt
+++ b/php/tests/028c.phpt
@@ -101,7 +101,7 @@ var_dump($_SESSION);
 ?>
 --EXPECTF--
 read
-write: 83a36f6c6484c0a3466f6fa700466f6f00643184c0a3426172a2643182c0020002a70042617200643282c0020002a5002a00643382c0020002a5002a00643282c0020003a2643382c0020003a474657374a6666f6f626172a36e657784c0a3426172a2643184c0a3466f6fa700466f6f00643182c0020009a5002a00643282c0020009a2643382c0020009a70042617200643282c002000aa5002a00643382c002000a
+write: 83a36f6c6484c0a3466f6fa700466f6f00643184c0a3426172a2643182c0040002a70042617200643282c0040002a5002a00643382c0040002a5002a00643282c0040003a2643382c0040003a474657374a6666f6f626172a36e657784c0a3426172a2643184c0a3466f6fa700466f6f00643182c0040009a5002a00643282c0040009a2643382c0040009a70042617200643282c004000aa5002a00643382c004000a
 array(3) {
   ["old"]=>
   object(Foo)#3 (3) {
diff --git a/php/tests/028d.phpt b/php/tests/028d.phpt
index c316694e..ef4c8b4d 100644
--- a/php/tests/028d.phpt
+++ b/php/tests/028d.phpt
@@ -100,7 +100,7 @@ var_dump($_SESSION);
 ?>
 --EXPECTF--
 read
-write: 83a36f6c6484c0a3466f6fa700466f6f00643184c0a3426172a2643182c0020002a70042617200643282c0020002a5002a00643382c0020002a5002a00643282c0020003a2643382c0020003a474657374a6666f6f626172a36e657784c0a3426172a2643184c0a3466f6fa700466f6f00643182c0020009a5002a00643282c0020009a2643382c0020009a70042617200643282c002000aa5002a00643382c002000a
+write: 83a36f6c6484c0a3466f6fa700466f6f00643184c0a3426172a2643182c0040002a70042617200643282c0040002a5002a00643382c0040002a5002a00643282c0040003a2643382c0040003a474657374a6666f6f626172a36e657784c0a3426172a2643184c0a3466f6fa700466f6f00643182c0040009a5002a00643282c0040009a2643382c0040009a70042617200643282c004000aa5002a00643382c004000a
 array(3) {
   ["old"]=>
   object(Foo)#3 (3) {
diff --git a/php/tests/061.phpt b/php/tests/061.phpt
index e67774b2..74e27ac2 100644
--- a/php/tests/061.phpt
+++ b/php/tests/061.phpt
@@ -52,8 +52,8 @@ function test($type, $variable, $test = null) {
 
 test('null', null);
 
-test('boo:l true', true);
-test('bool: true', false);
+test('bool: true', true);
+test('bool: false', false);
 
 test('zero: 0', 0);
 test('small: 1', 1);
@@ -68,9 +68,9 @@ test('double: 123.456', 123.456);
 test('empty: ""', "");
 test('string: "foobar"', "foobar");
 
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
+test('array: empty', array(), false);
+test('array(1, 2, 3)', array(1, 2, 3), false);
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
 
 test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
 test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
diff --git a/php/tests/061b.phpt b/php/tests/061b.phpt
index 553bb3f5..083a6df9 100644
--- a/php/tests/061b.phpt
+++ b/php/tests/061b.phpt
@@ -55,8 +55,8 @@ function test($type, $variable, $test = null) {
 
 test('null', null);
 
-test('boo:l true', true);
-test('bool: true', false);
+test('bool: true', true);
+test('bool: false', false);
 
 test('zero: 0', 0);
 test('small: 1', 1);
@@ -71,9 +71,9 @@ test('double: 123.456', 123.456);
 test('empty: ""', "");
 test('string: "foobar"', "foobar");
 
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
+test('array: empty', array(), false);
+test('array(1, 2, 3)', array(1, 2, 3), false);
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
 
 test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
 test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
diff --git a/php/tests/061c.phpt b/php/tests/061c.phpt
index 2356dea2..89ae1bbb 100644
--- a/php/tests/061c.phpt
+++ b/php/tests/061c.phpt
@@ -50,8 +50,8 @@ function test($type, $variable, $test = null) {
 
 test('null', null);
 
-test('boo:l true', true);
-test('bool: true', false);
+test('bool: true', true);
+test('bool: false', false);
 
 test('zero: 0', 0);
 test('small: 1', 1);
@@ -66,9 +66,9 @@ test('double: 123.456', 123.456);
 test('empty: ""', "");
 test('string: "foobar"', "foobar");
 
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
+test('array: empty', array(), false);
+test('array(1, 2, 3)', array(1, 2, 3), false);
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
 
 test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
 test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
diff --git a/php/tests/065.phpt b/php/tests/065.phpt
index 8139f24e..5c13b7f5 100644
--- a/php/tests/065.phpt
+++ b/php/tests/065.phpt
@@ -54,8 +54,8 @@ function test($type, $variable, $test = null) {
 
 test('null', null);
 
-test('boo:l true', true);
-test('bool: true', false);
+test('bool: true', true);
+test('bool: false', false);
 
 test('zero: 0', 0);
 test('small: 1', 1);
@@ -70,9 +70,9 @@ test('double: 123.456', 123.456);
 test('empty: ""', "");
 test('string: "foobar"', "foobar");
 
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
+test('array: empty', array(), false);
+test('array(1, 2, 3)', array(1, 2, 3), false);
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
 
 test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
 test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
diff --git a/php/tests/065b.phpt b/php/tests/065b.phpt
index 80d784d5..1f9a7362 100644
--- a/php/tests/065b.phpt
+++ b/php/tests/065b.phpt
@@ -57,8 +57,8 @@ function test($type, $variable, $test = null) {
 
 test('null', null);
 
-test('boo:l true', true);
-test('bool: true', false);
+test('bool: true', true);
+test('bool: false', false);
 
 test('zero: 0', 0);
 test('small: 1', 1);
@@ -73,9 +73,9 @@ test('double: 123.456', 123.456);
 test('empty: ""', "");
 test('string: "foobar"', "foobar");
 
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
+test('array: empty', array(), false);
+test('array(1, 2, 3)', array(1, 2, 3), false);
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
 
 test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
 test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
diff --git a/php/tests/065c.phpt b/php/tests/065c.phpt
index 3c9db227..365fdfee 100644
--- a/php/tests/065c.phpt
+++ b/php/tests/065c.phpt
@@ -52,8 +52,8 @@ function test($type, $variable, $test = null) {
 
 test('null', null);
 
-test('boo:l true', true);
-test('bool: true', false);
+test('bool: true', true);
+test('bool: false', false);
 
 test('zero: 0', 0);
 test('small: 1', 1);
@@ -68,9 +68,9 @@ test('double: 123.456', 123.456);
 test('empty: ""', "");
 test('string: "foobar"', "foobar");
 
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
+test('array: empty', array(), false);
+test('array(1, 2, 3)', array(1, 2, 3), false);
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
 
 test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
 test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
diff --git a/php/tests/070.phpt b/php/tests/070.phpt
index fd915fd6..b9b1b549 100644
--- a/php/tests/070.phpt
+++ b/php/tests/070.phpt
@@ -31,8 +31,8 @@ function test($type, $variable, $test = null) {
 
 test('null', null);
 
-test('boo:l true', true);
-test('bool: true', false);
+test('bool: true', true);
+test('bool: false', false);
 
 test('zero: 0', 0);
 test('small: 1', 1);
@@ -47,9 +47,9 @@ test('double: 123.456', 123.456);
 test('empty: ""', "");
 test('string: "foobar"', "foobar");
 
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
+test('array: empty', array(), false);
+test('array(1, 2, 3)', array(1, 2, 3), false);
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
 
 test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
 test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
diff --git a/php/tests/070b.phpt b/php/tests/070b.phpt
index e46c3d68..96b828a0 100644
--- a/php/tests/070b.phpt
+++ b/php/tests/070b.phpt
@@ -34,8 +34,8 @@ function test($type, $variable, $test = null) {
 
 test('null', null);
 
-test('boo:l true', true);
-test('bool: true', false);
+test('bool: true', true);
+test('bool: false', false);
 
 test('zero: 0', 0);
 test('small: 1', 1);
@@ -50,9 +50,9 @@ test('double: 123.456', 123.456);
 test('empty: ""', "");
 test('string: "foobar"', "foobar");
 
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
+test('array: empty', array(), false);
+test('array(1, 2, 3)', array(1, 2, 3), false);
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
 
 test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
 test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
diff --git a/php/tests/070c.phpt b/php/tests/070c.phpt
index eefb9935..7ec3791d 100644
--- a/php/tests/070c.phpt
+++ b/php/tests/070c.phpt
@@ -29,8 +29,8 @@ function test($type, $variable, $test = null) {
 
 test('null', null);
 
-test('boo:l true', true);
-test('bool: true', false);
+test('bool: true', true);
+test('bool: false', false);
 
 test('zero: 0', 0);
 test('small: 1', 1);
@@ -45,9 +45,9 @@ test('double: 123.456', 123.456);
 test('empty: ""', "");
 test('string: "foobar"', "foobar");
 
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
+test('array: empty', array(), false);
+test('array(1, 2, 3)', array(1, 2, 3), false);
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
 
 test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
 test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
diff --git a/php/tests/071.phpt b/php/tests/071.phpt
index bc64c60d..88f63c9b 100644
--- a/php/tests/071.phpt
+++ b/php/tests/071.phpt
@@ -33,8 +33,8 @@ function test($type, $variable, $test = null) {
 
 test('null', null);
 
-test('boo:l true', true);
-test('bool: true', false);
+test('bool: true', true);
+test('bool: false', false);
 
 test('zero: 0', 0);
 test('small: 1', 1);
@@ -49,9 +49,9 @@ test('double: 123.456', 123.456);
 test('empty: ""', "");
 test('string: "foobar"', "foobar");
 
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
+test('array: empty', array(), false);
+test('array(1, 2, 3)', array(1, 2, 3), false);
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
 
 test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
 test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
diff --git a/php/tests/071b.phpt b/php/tests/071b.phpt
index 2c97412e..944506b9 100644
--- a/php/tests/071b.phpt
+++ b/php/tests/071b.phpt
@@ -36,8 +36,8 @@ function test($type, $variable, $test = null) {
 
 test('null', null);
 
-test('boo:l true', true);
-test('bool: true', false);
+test('bool: true', true);
+test('bool: false', false);
 
 test('zero: 0', 0);
 test('small: 1', 1);
@@ -52,9 +52,9 @@ test('double: 123.456', 123.456);
 test('empty: ""', "");
 test('string: "foobar"', "foobar");
 
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
+test('array: empty', array(), false);
+test('array(1, 2, 3)', array(1, 2, 3), false);
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
 
 test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
 test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
diff --git a/php/tests/071c.phpt b/php/tests/071c.phpt
index 91744637..86652e70 100644
--- a/php/tests/071c.phpt
+++ b/php/tests/071c.phpt
@@ -31,8 +31,8 @@ function test($type, $variable, $test = null) {
 
 test('null', null);
 
-test('boo:l true', true);
-test('bool: true', false);
+test('bool: true', true);
+test('bool: false', false);
 
 test('zero: 0', 0);
 test('small: 1', 1);
@@ -47,9 +47,9 @@ test('double: 123.456', 123.456);
 test('empty: ""', "");
 test('string: "foobar"', "foobar");
 
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
+test('array: empty', array(), false);
+test('array(1, 2, 3)', array(1, 2, 3), false);
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
 
 test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
 test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
diff --git a/php/tests/072.phpt b/php/tests/072.phpt
index 348bd34b..356f4797 100644
--- a/php/tests/072.phpt
+++ b/php/tests/072.phpt
@@ -73,8 +73,8 @@ function test($type, $variable, $test = null) {
 
 test('null', null);
 
-test('boo:l true', true);
-test('bool: true', false);
+test('bool: true', true);
+test('bool: false', false);
 
 test('zero: 0', 0);
 test('small: 1', 1);
@@ -89,9 +89,9 @@ test('double: 123.456', 123.456);
 test('empty: ""', "");
 test('string: "foobar"', "foobar");
 
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
+test('array: empty', array(), false);
+test('array(1, 2, 3)', array(1, 2, 3), false);
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
 
 test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
 test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
diff --git a/php/tests/072b.phpt b/php/tests/072b.phpt
index 8499186a..6f9e4905 100644
--- a/php/tests/072b.phpt
+++ b/php/tests/072b.phpt
@@ -76,8 +76,8 @@ function test($type, $variable, $test = null) {
 
 test('null', null);
 
-test('boo:l true', true);
-test('bool: true', false);
+test('bool: true', true);
+test('bool: false', false);
 
 test('zero: 0', 0);
 test('small: 1', 1);
@@ -92,9 +92,9 @@ test('double: 123.456', 123.456);
 test('empty: ""', "");
 test('string: "foobar"', "foobar");
 
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
+test('array: empty', array(), false);
+test('array(1, 2, 3)', array(1, 2, 3), false);
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
 
 test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
 test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
diff --git a/php/tests/072c.phpt b/php/tests/072c.phpt
index 540d0a89..018876fc 100644
--- a/php/tests/072c.phpt
+++ b/php/tests/072c.phpt
@@ -71,8 +71,8 @@ function test($type, $variable, $test = null) {
 
 test('null', null);
 
-test('boo:l true', true);
-test('bool: true', false);
+test('bool: true', true);
+test('bool: false', false);
 
 test('zero: 0', 0);
 test('small: 1', 1);
@@ -87,9 +87,9 @@ test('double: 123.456', 123.456);
 test('empty: ""', "");
 test('string: "foobar"', "foobar");
 
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
+test('array: empty', array(), false);
+test('array(1, 2, 3)', array(1, 2, 3), false);
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
 
 test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
 test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
diff --git a/php/tests/073.phpt b/php/tests/073.phpt
index c1eb9f49..2ea89f25 100644
--- a/php/tests/073.phpt
+++ b/php/tests/073.phpt
@@ -74,8 +74,8 @@ function test($type, $variable, $test = null) {
 
 test('null', null);
 
-test('boo:l true', true);
-test('bool: true', false);
+test('bool: true', true);
+test('bool: false', false);
 
 test('zero: 0', 0);
 test('small: 1', 1);
@@ -90,9 +90,9 @@ test('double: 123.456', 123.456);
 test('empty: ""', "");
 test('string: "foobar"', "foobar");
 
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
+test('array: empty', array(), false);
+test('array(1, 2, 3)', array(1, 2, 3), false);
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
 
 test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
 test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
diff --git a/php/tests/073b.phpt b/php/tests/073b.phpt
index 2ef95ae8..09bb373e 100644
--- a/php/tests/073b.phpt
+++ b/php/tests/073b.phpt
@@ -77,8 +77,8 @@ function test($type, $variable, $test = null) {
 
 test('null', null);
 
-test('boo:l true', true);
-test('bool: true', false);
+test('bool: true', true);
+test('bool: false', false);
 
 test('zero: 0', 0);
 test('small: 1', 1);
@@ -93,9 +93,9 @@ test('double: 123.456', 123.456);
 test('empty: ""', "");
 test('string: "foobar"', "foobar");
 
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
+test('array: empty', array(), false);
+test('array(1, 2, 3)', array(1, 2, 3), false);
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
 
 test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
 test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
diff --git a/php/tests/073c.phpt b/php/tests/073c.phpt
index 583ae1be..eaa47cc4 100644
--- a/php/tests/073c.phpt
+++ b/php/tests/073c.phpt
@@ -72,8 +72,8 @@ function test($type, $variable, $test = null) {
 
 test('null', null);
 
-test('boo:l true', true);
-test('bool: true', false);
+test('bool: true', true);
+test('bool: false', false);
 
 test('zero: 0', 0);
 test('small: 1', 1);
@@ -88,9 +88,9 @@ test('double: 123.456', 123.456);
 test('empty: ""', "");
 test('string: "foobar"', "foobar");
 
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
+test('array: empty', array(), false);
+test('array(1, 2, 3)', array(1, 2, 3), false);
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
 
 test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
 test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
diff --git a/php/tests/080.phpt b/php/tests/080.phpt
index aba1cb63..6cc9171c 100644
--- a/php/tests/080.phpt
+++ b/php/tests/080.phpt
@@ -27,8 +27,8 @@ function test($type, $variable, $test = null) {
 
 test('null', null);
 
-test('boo:l true', true);
-test('bool: true', false);
+test('bool: true', true);
+test('bool: false', false);
 
 test('zero: 0', 0);
 test('small: 1', 1);
@@ -43,9 +43,9 @@ test('double: 123.456', 123.456);
 test('empty: ""', "");
 test('string: "foobar"', "foobar");
 
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
+test('array: empty', array(), false);
+test('array(1, 2, 3)', array(1, 2, 3), false);
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
 
 test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
 test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
diff --git a/php/tests/081.phpt b/php/tests/081.phpt
index d22daaa0..18daa8bc 100644
--- a/php/tests/081.phpt
+++ b/php/tests/081.phpt
@@ -29,8 +29,8 @@ function test($type, $variable, $test = null) {
 
 test('null', null);
 
-test('boo:l true', true);
-test('bool: true', false);
+test('bool: true', true);
+test('bool: false', false);
 
 test('zero: 0', 0);
 test('small: 1', 1);
@@ -45,9 +45,9 @@ test('double: 123.456', 123.456);
 test('empty: ""', "");
 test('string: "foobar"', "foobar");
 
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
+test('array: empty', array(), false);
+test('array(1, 2, 3)', array(1, 2, 3), false);
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
 
 test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
 test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
diff --git a/php/tests/082.phpt b/php/tests/082.phpt
index 45a9d938..b8414abf 100644
--- a/php/tests/082.phpt
+++ b/php/tests/082.phpt
@@ -72,8 +72,8 @@ function test($type, $variable, $test = null)
 
 test('null', null);
 
-test('boo:l true', true);
-test('bool: true', false);
+test('bool: true', true);
+test('bool: false', false);
 
 test('zero: 0', 0);
 test('small: 1', 1);
@@ -88,9 +88,9 @@ test('double: 123.456', 123.456);
 test('empty: ""', "");
 test('string: "foobar"', "foobar");
 
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
+test('array: empty', array(), false);
+test('array(1, 2, 3)', array(1, 2, 3), false);
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
 
 test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
 test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
diff --git a/php/tests/083.phpt b/php/tests/083.phpt
index 203d8299..b866ae3f 100644
--- a/php/tests/083.phpt
+++ b/php/tests/083.phpt
@@ -73,8 +73,8 @@ function test($type, $variable, $test = null)
 
 test('null', null);
 
-test('boo:l true', true);
-test('bool: true', false);
+test('bool: true', true);
+test('bool: false', false);
 
 test('zero: 0', 0);
 test('small: 1', 1);
@@ -89,9 +89,9 @@ test('double: 123.456', 123.456);
 test('empty: ""', "");
 test('string: "foobar"', "foobar");
 
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
+test('array: empty', array(), false);
+test('array(1, 2, 3)', array(1, 2, 3), false);
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
 
 test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
 test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
diff --git a/php/tests/084.phpt b/php/tests/084.phpt
index 74d061b5..7525fbc3 100644
--- a/php/tests/084.phpt
+++ b/php/tests/084.phpt
@@ -27,8 +27,8 @@ function test($type, $variable, $test = null) {
 
 test('null', null);
 
-test('boo:l true', true);
-test('bool: true', false);
+test('bool: true', true);
+test('bool: false', false);
 
 test('zero: 0', 0);
 test('small: 1', 1);
@@ -43,9 +43,9 @@ test('double: 123.456', 123.456);
 test('empty: ""', "");
 test('string: "foobar"', "foobar");
 
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
+test('array: empty', array(), false);
+test('array(1, 2, 3)', array(1, 2, 3), false);
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
 
 test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
 test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
diff --git a/php/tests/085.phpt b/php/tests/085.phpt
index 72aacc8b..806a9ba3 100644
--- a/php/tests/085.phpt
+++ b/php/tests/085.phpt
@@ -70,8 +70,8 @@ function test($type, $variable, $test = null)
 
 test('null', null);
 
-test('boo:l true', true);
-test('bool: true', false);
+test('bool: true', true);
+test('bool: false', false);
 
 test('zero: 0', 0);
 test('small: 1', 1);
@@ -86,9 +86,9 @@ test('double: 123.456', 123.456);
 test('empty: ""', "");
 test('string: "foobar"', "foobar");
 
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
+test('array: empty', array(), false);
+test('array(1, 2, 3)', array(1, 2, 3), false);
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
 
 test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
 test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
diff --git a/php/tests/086.phpt b/php/tests/086.phpt
index aeaa3328..a15d09bc 100644
--- a/php/tests/086.phpt
+++ b/php/tests/086.phpt
@@ -71,8 +71,8 @@ function test($type, $variable, $test = null)
 
 test('null', null);
 
-test('boo:l true', true);
-test('bool: true', false);
+test('bool: true', true);
+test('bool: false', false);
 
 test('zero: 0', 0);
 test('small: 1', 1);
@@ -87,9 +87,9 @@ test('double: 123.456', 123.456);
 test('empty: ""', "");
 test('string: "foobar"', "foobar");
 
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
+test('array: empty', array(), false);
+test('array(1, 2, 3)', array(1, 2, 3), false);
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
 
 test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
 test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
diff --git a/php/tests/087.phpt b/php/tests/087.phpt
index a7386a18..ede0c30e 100644
--- a/php/tests/087.phpt
+++ b/php/tests/087.phpt
@@ -1,10 +1,6 @@
 --TEST--
 disabled php only for class methods (set option)
 --SKIPIF--
-<?php
-if (version_compare(PHP_VERSION, '5.1.0') < 0) {
-    echo "skip tests in PHP 5.1 or newer";
-}
 --FILE--
 <?php
 if(!extension_loaded('msgpack')) {
@@ -13,7 +9,14 @@ if(!extension_loaded('msgpack')) {
 
 function test($type, $variable, $test = null) {
     $msgpack = new MessagePack();
-    $msgpack->setOption(MessagePack::OPT_PHPONLY, false);
+    if (version_compare(PHP_VERSION, '5.1.0') < 0)
+    {
+        $msgpack->setOption(MESSAGEPACK_OPT_PHPONLY, false);
+    }
+    else
+    {
+        $msgpack->setOption(MessagePack::OPT_PHPONLY, false);
+    }
 
     $serialized = $msgpack->pack($variable);
     $unserialized = $msgpack->unpack($serialized);
@@ -32,8 +35,8 @@ function test($type, $variable, $test = null) {
 
 test('null', null);
 
-test('boo:l true', true);
-test('bool: true', false);
+test('bool: true', true);
+test('bool: false', false);
 
 test('zero: 0', 0);
 test('small: 1', 1);
@@ -48,9 +51,9 @@ test('double: 123.456', 123.456);
 test('empty: ""', "");
 test('string: "foobar"', "foobar");
 
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
+test('array: empty', array(), false);
+test('array(1, 2, 3)', array(1, 2, 3), false);
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
 
 test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
 test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
diff --git a/php/tests/087d.phpt b/php/tests/087d.phpt
deleted file mode 100644
index 72a58f5e..00000000
--- a/php/tests/087d.phpt
+++ /dev/null
@@ -1,306 +0,0 @@
---TEST--
-disabled php only for class methods (set option)
---SKIPIF--
-<?php
-if (version_compare(PHP_VERSION, '5.1.0') >= 0) {
-    echo "skip tests in PHP 5.0 or older";
-}
---FILE--
-<?php
-if(!extension_loaded('msgpack')) {
-    dl('msgpack.' . PHP_SHLIB_SUFFIX);
-}
-
-function test($type, $variable, $test = null) {
-    $msgpack = new MessagePack();
-    $msgpack->setOption(MESSAGEPACK_OPT_PHPONLY, false);
-
-    $serialized = $msgpack->pack($variable);
-    $unserialized = $msgpack->unpack($serialized);
-
-    var_dump($unserialized);
-
-    if (!is_bool($test))
-    {
-        echo $unserialized === $variable ? 'OK' : 'ERROR', PHP_EOL;
-    }
-    else
-    {
-        echo $test || $unserialized == $variable ? 'OK' : 'ERROR', PHP_EOL;
-    }
-}
-
-test('null', null);
-
-test('boo:l true', true);
-test('bool: true', false);
-
-test('zero: 0', 0);
-test('small: 1', 1);
-test('small: -1', -1);
-test('medium: 1000', 1000);
-test('medium: -1000', -1000);
-test('large: 100000', 100000);
-test('large: -100000', -100000);
-
-test('double: 123.456', 123.456);
-
-test('empty: ""', "");
-test('string: "foobar"', "foobar");
-
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
-
-test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
-test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
-test('array("kek" => "lol", "lol" => "kek")', array("kek" => "lol", "lol" => "kek"), false);
-test('array("" => "empty")', array("" => "empty"), false);
-
-$a = array('foo');
-test('array($a, $a)', array($a, $a), false);
-test('array(&$a, &$a)', array(&$a, &$a), false);
-
-$a = array(null);
-$b = array(&$a);
-$a[0] = &$b;
-
-test('cyclic', $a, true);
-
-$a = array(
-    'a' => array(
-        'b' => 'c',
-        'd' => 'e'
-        ),
-    'f' => array(
-        'g' => 'h'
-        )
-    );
-
-test('array', $a, false);
-
-class Obj {
-    public $a;
-    protected $b;
-    private $c;
-
-    function __construct($a, $b, $c) {
-        $this->a = $a;
-        $this->b = $b;
-        $this->c = $c;
-    }
-}
-
-test('object', new Obj(1, 2, 3), true);
-
-test('object', array(new Obj(1, 2, 3), new Obj(4, 5, 6)), true);
-
-$o = new Obj(1, 2, 3);
-
-test('object', array(&$o, &$o), true);
---EXPECTF--
-NULL
-OK
-bool(true)
-OK
-bool(false)
-OK
-int(0)
-OK
-int(1)
-OK
-int(-1)
-OK
-int(1000)
-OK
-int(-1000)
-OK
-int(100000)
-OK
-int(-100000)
-OK
-float(123.456)
-OK
-string(0) ""
-OK
-string(6) "foobar"
-OK
-array(0) {
-}
-OK
-array(3) {
-  [0]=>
-  int(1)
-  [1]=>
-  int(2)
-  [2]=>
-  int(3)
-}
-OK
-array(3) {
-  [0]=>
-  array(3) {
-    [0]=>
-    int(1)
-    [1]=>
-    int(2)
-    [2]=>
-    int(3)
-  }
-  [1]=>
-  array(3) {
-    [0]=>
-    int(4)
-    [1]=>
-    int(5)
-    [2]=>
-    int(6)
-  }
-  [2]=>
-  array(3) {
-    [0]=>
-    int(7)
-    [1]=>
-    int(8)
-    [2]=>
-    int(9)
-  }
-}
-OK
-array(3) {
-  [0]=>
-  string(3) "foo"
-  [1]=>
-  string(3) "foo"
-  [2]=>
-  string(3) "foo"
-}
-OK
-array(2) {
-  ["one"]=>
-  int(1)
-  ["two"]=>
-  int(2)
-}
-OK
-array(2) {
-  ["kek"]=>
-  string(3) "lol"
-  ["lol"]=>
-  string(3) "kek"
-}
-OK
-array(1) {
-  [""]=>
-  string(5) "empty"
-}
-OK
-array(2) {
-  [0]=>
-  array(1) {
-    [0]=>
-    string(3) "foo"
-  }
-  [1]=>
-  array(1) {
-    [0]=>
-    string(3) "foo"
-  }
-}
-OK
-array(2) {
-  [0]=>
-  array(1) {
-    [0]=>
-    string(3) "foo"
-  }
-  [1]=>
-  array(1) {
-    [0]=>
-    string(3) "foo"
-  }
-}
-OK
-array(1) {
-  [0]=>
-  array(1) {
-    [0]=>
-    array(1) {
-      [0]=>
-      array(1) {
-        [0]=>
-        array(1) {
-          [0]=>
-          NULL
-        }
-      }
-    }
-  }
-}
-OK
-array(2) {
-  ["a"]=>
-  array(2) {
-    ["b"]=>
-    string(1) "c"
-    ["d"]=>
-    string(1) "e"
-  }
-  ["f"]=>
-  array(1) {
-    ["g"]=>
-    string(1) "h"
-  }
-}
-OK
-array(3) {
-  [0]=>
-  int(1)
-  [1]=>
-  int(2)
-  [2]=>
-  int(3)
-}
-OK
-array(2) {
-  [0]=>
-  array(3) {
-    [0]=>
-    int(1)
-    [1]=>
-    int(2)
-    [2]=>
-    int(3)
-  }
-  [1]=>
-  array(3) {
-    [0]=>
-    int(4)
-    [1]=>
-    int(5)
-    [2]=>
-    int(6)
-  }
-}
-OK
-array(2) {
-  [0]=>
-  array(3) {
-    [0]=>
-    int(1)
-    [1]=>
-    int(2)
-    [2]=>
-    int(3)
-  }
-  [1]=>
-  array(3) {
-    [0]=>
-    int(1)
-    [1]=>
-    int(2)
-    [2]=>
-    int(3)
-  }
-}
-OK
diff --git a/php/tests/088.phpt b/php/tests/088.phpt
index 1d44fd3f..ce51229b 100644
--- a/php/tests/088.phpt
+++ b/php/tests/088.phpt
@@ -1,10 +1,6 @@
 --TEST--
 disabled php only for class methods unpacker (set option)
 --SKIPIF--
-<?php
-if (version_compare(PHP_VERSION, '5.1.0') < 0) {
-    echo "skip tests in PHP 5.1 or newer";
-}
 --FILE--
 <?php
 if(!extension_loaded('msgpack')) {
@@ -14,7 +10,14 @@ if(!extension_loaded('msgpack')) {
 function test($type, $variable, $test = null)
 {
     $msgpack = new MessagePack();
-    $msgpack->setOption(MessagePack::OPT_PHPONLY, false);
+    if (version_compare(PHP_VERSION, '5.1.0') < 0)
+    {
+        $msgpack->setOption(MESSAGEPACK_OPT_PHPONLY, false);
+    }
+    else
+    {
+        $msgpack->setOption(MessagePack::OPT_PHPONLY, false);
+    }
 
     $serialized = $msgpack->pack($variable);
     $unpacker = $msgpack->unpacker();
@@ -75,8 +78,8 @@ function test($type, $variable, $test = null)
 
 test('null', null);
 
-test('boo:l true', true);
-test('bool: true', false);
+test('bool: true', true);
+test('bool: false', false);
 
 test('zero: 0', 0);
 test('small: 1', 1);
@@ -91,9 +94,9 @@ test('double: 123.456', 123.456);
 test('empty: ""', "");
 test('string: "foobar"', "foobar");
 
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
+test('array: empty', array(), false);
+test('array(1, 2, 3)', array(1, 2, 3), false);
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
 
 test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
 test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
diff --git a/php/tests/088d.phpt b/php/tests/088d.phpt
deleted file mode 100644
index 6623221a..00000000
--- a/php/tests/088d.phpt
+++ /dev/null
@@ -1,349 +0,0 @@
---TEST--
-disabled php only for class methods unpacker (set option)
---SKIPIF--
-<?php
-if (version_compare(PHP_VERSION, '5.1.0') >= 0) {
-    echo "skip tests in PHP 5.1.0 or older";
-}
---FILE--
-<?php
-if(!extension_loaded('msgpack')) {
-    dl('msgpack.' . PHP_SHLIB_SUFFIX);
-}
-
-function test($type, $variable, $test = null)
-{
-    $msgpack = new MessagePack();
-    $msgpack->setOption(MESSAGEPACK_OPT_PHPONLY, false);
-
-    $serialized = $msgpack->pack($variable);
-    $unpacker = $msgpack->unpacker();
-
-    $length = strlen($serialized);
-
-    if (rand(0, 1))
-    {
-        for ($i = 0; $i < $length;)
-        {
-            $len = rand(1, 10);
-            $str = substr($serialized, $i, $len);
-
-            $unpacker->feed($str);
-            if ($unpacker->execute())
-            {
-                $unserialized = $unpacker->data();
-                var_dump($unserialized);
-                $unpacker->reset();
-            }
-
-            $i += $len;
-        }
-    }
-    else
-    {
-        $str = "";
-        $offset = 0;
-
-        for ($i = 0; $i < $length;)
-        {
-            $len = rand(1, 10);
-            $str .= substr($serialized, $i, $len);
-
-            if ($unpacker->execute($str, $offset))
-            {
-                $unserialized = $unpacker->data();
-                var_dump($unserialized);
-
-                $unpacker->reset();
-                $str = "";
-                $offset = 0;
-            }
-
-            $i += $len;
-        }
-    }
-
-    if (!is_bool($test))
-    {
-        echo $unserialized === $variable ? 'OK' : 'ERROR', PHP_EOL;
-    }
-    else
-    {
-        echo $test || $unserialized == $variable ? 'OK' : 'ERROR', PHP_EOL;
-    }
-}
-
-test('null', null);
-
-test('boo:l true', true);
-test('bool: true', false);
-
-test('zero: 0', 0);
-test('small: 1', 1);
-test('small: -1', -1);
-test('medium: 1000', 1000);
-test('medium: -1000', -1000);
-test('large: 100000', 100000);
-test('large: -100000', -100000);
-
-test('double: 123.456', 123.456);
-
-test('empty: ""', "");
-test('string: "foobar"', "foobar");
-
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
-
-test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
-test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
-test('array("kek" => "lol", "lol" => "kek")', array("kek" => "lol", "lol" => "kek"), false);
-test('array("" => "empty")', array("" => "empty"), false);
-
-$a = array('foo');
-test('array($a, $a)', array($a, $a), false);
-test('array(&$a, &$a)', array(&$a, &$a), false);
-
-$a = array(null);
-$b = array(&$a);
-$a[0] = &$b;
-
-test('cyclic', $a, true);
-
-$a = array(
-    'a' => array(
-        'b' => 'c',
-        'd' => 'e'
-        ),
-    'f' => array(
-        'g' => 'h'
-        )
-    );
-
-test('array', $a, false);
-
-class Obj {
-    public $a;
-    protected $b;
-    private $c;
-
-    function __construct($a, $b, $c) {
-        $this->a = $a;
-        $this->b = $b;
-        $this->c = $c;
-    }
-}
-
-test('object', new Obj(1, 2, 3), true);
-
-test('object', array(new Obj(1, 2, 3), new Obj(4, 5, 6)), true);
-
-$o = new Obj(1, 2, 3);
-
-test('object', array(&$o, &$o), true);
---EXPECTF--
-NULL
-OK
-bool(true)
-OK
-bool(false)
-OK
-int(0)
-OK
-int(1)
-OK
-int(-1)
-OK
-int(1000)
-OK
-int(-1000)
-OK
-int(100000)
-OK
-int(-100000)
-OK
-float(123.456)
-OK
-string(0) ""
-OK
-string(6) "foobar"
-OK
-array(0) {
-}
-OK
-array(3) {
-  [0]=>
-  int(1)
-  [1]=>
-  int(2)
-  [2]=>
-  int(3)
-}
-OK
-array(3) {
-  [0]=>
-  array(3) {
-    [0]=>
-    int(1)
-    [1]=>
-    int(2)
-    [2]=>
-    int(3)
-  }
-  [1]=>
-  array(3) {
-    [0]=>
-    int(4)
-    [1]=>
-    int(5)
-    [2]=>
-    int(6)
-  }
-  [2]=>
-  array(3) {
-    [0]=>
-    int(7)
-    [1]=>
-    int(8)
-    [2]=>
-    int(9)
-  }
-}
-OK
-array(3) {
-  [0]=>
-  string(3) "foo"
-  [1]=>
-  string(3) "foo"
-  [2]=>
-  string(3) "foo"
-}
-OK
-array(2) {
-  ["one"]=>
-  int(1)
-  ["two"]=>
-  int(2)
-}
-OK
-array(2) {
-  ["kek"]=>
-  string(3) "lol"
-  ["lol"]=>
-  string(3) "kek"
-}
-OK
-array(1) {
-  [""]=>
-  string(5) "empty"
-}
-OK
-array(2) {
-  [0]=>
-  array(1) {
-    [0]=>
-    string(3) "foo"
-  }
-  [1]=>
-  array(1) {
-    [0]=>
-    string(3) "foo"
-  }
-}
-OK
-array(2) {
-  [0]=>
-  array(1) {
-    [0]=>
-    string(3) "foo"
-  }
-  [1]=>
-  array(1) {
-    [0]=>
-    string(3) "foo"
-  }
-}
-OK
-array(1) {
-  [0]=>
-  array(1) {
-    [0]=>
-    array(1) {
-      [0]=>
-      array(1) {
-        [0]=>
-        array(1) {
-          [0]=>
-          NULL
-        }
-      }
-    }
-  }
-}
-OK
-array(2) {
-  ["a"]=>
-  array(2) {
-    ["b"]=>
-    string(1) "c"
-    ["d"]=>
-    string(1) "e"
-  }
-  ["f"]=>
-  array(1) {
-    ["g"]=>
-    string(1) "h"
-  }
-}
-OK
-array(3) {
-  [0]=>
-  int(1)
-  [1]=>
-  int(2)
-  [2]=>
-  int(3)
-}
-OK
-array(2) {
-  [0]=>
-  array(3) {
-    [0]=>
-    int(1)
-    [1]=>
-    int(2)
-    [2]=>
-    int(3)
-  }
-  [1]=>
-  array(3) {
-    [0]=>
-    int(4)
-    [1]=>
-    int(5)
-    [2]=>
-    int(6)
-  }
-}
-OK
-array(2) {
-  [0]=>
-  array(3) {
-    [0]=>
-    int(1)
-    [1]=>
-    int(2)
-    [2]=>
-    int(3)
-  }
-  [1]=>
-  array(3) {
-    [0]=>
-    int(1)
-    [1]=>
-    int(2)
-    [2]=>
-    int(3)
-  }
-}
-OK
diff --git a/php/tests/089.phpt b/php/tests/089.phpt
index 77be7071..e3a2d7ea 100644
--- a/php/tests/089.phpt
+++ b/php/tests/089.phpt
@@ -14,12 +14,26 @@ if(!extension_loaded('msgpack')) {
 function test($type, $variable, $test = null)
 {
     $msgpack = new MessagePack();
-    $msgpack->setOption(MessagePack::OPT_PHPONLY, false);
+    if (version_compare(PHP_VERSION, '5.1.0') < 0)
+    {
+        $msgpack->setOption(MESSAGEPACK_OPT_PHPONLY, false);
+    }
+    else
+    {
+        $msgpack->setOption(MessagePack::OPT_PHPONLY, false);
+    }
 
     $serialized = $msgpack->pack($variable);
 
     $unpacker = new MessagePackUnpacker();
-    $unpacker->setOption(MessagePack::OPT_PHPONLY, false);
+    if (version_compare(PHP_VERSION, '5.1.0') < 0)
+    {
+        $unpacker->setOption(MESSAGEPACK_OPT_PHPONLY, false);
+    }
+    else
+    {
+        $unpacker->setOption(MessagePack::OPT_PHPONLY, false);
+    }
 
     $length = strlen($serialized);
 
@@ -77,8 +91,8 @@ function test($type, $variable, $test = null)
 
 test('null', null);
 
-test('boo:l true', true);
-test('bool: true', false);
+test('bool: true', true);
+test('bool: false', false);
 
 test('zero: 0', 0);
 test('small: 1', 1);
@@ -93,9 +107,9 @@ test('double: 123.456', 123.456);
 test('empty: ""', "");
 test('string: "foobar"', "foobar");
 
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
+test('array: empty', array(), false);
+test('array(1, 2, 3)', array(1, 2, 3), false);
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
 
 test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
 test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
diff --git a/php/tests/089d.phpt b/php/tests/089d.phpt
deleted file mode 100644
index d6cfc48c..00000000
--- a/php/tests/089d.phpt
+++ /dev/null
@@ -1,351 +0,0 @@
---TEST--
-disabled php only for class unpacker (set option)
---SKIPIF--
-<?php
-if (version_compare(PHP_VERSION, '5.1.0') >= 0) {
-    echo "skip tests in PHP 5.0 or older";
-}
---FILE--
-<?php
-if(!extension_loaded('msgpack')) {
-    dl('msgpack.' . PHP_SHLIB_SUFFIX);
-}
-
-function test($type, $variable, $test = null)
-{
-    $msgpack = new MessagePack();
-    $msgpack->setOption(MESSAGEPACK_OPT_PHPONLY, false);
-
-    $serialized = $msgpack->pack($variable);
-
-    $unpacker = new MessagePackUnpacker();
-    $unpacker->setOption(MESSAGEPACK_OPT_PHPONLY, false);
-
-    $length = strlen($serialized);
-
-    if (rand(0, 1))
-    {
-        for ($i = 0; $i < $length;)
-        {
-            $len = rand(1, 10);
-            $str = substr($serialized, $i, $len);
-
-            $unpacker->feed($str);
-            if ($unpacker->execute())
-            {
-                $unserialized = $unpacker->data();
-                var_dump($unserialized);
-                $unpacker->reset();
-            }
-
-            $i += $len;
-        }
-    }
-    else
-    {
-        $str = "";
-        $offset = 0;
-
-        for ($i = 0; $i < $length;)
-        {
-            $len = rand(1, 10);
-            $str .= substr($serialized, $i, $len);
-
-            if ($unpacker->execute($str, $offset))
-            {
-                $unserialized = $unpacker->data();
-                var_dump($unserialized);
-
-                $unpacker->reset();
-                $str = "";
-                $offset = 0;
-            }
-
-            $i += $len;
-        }
-    }
-
-    if (!is_bool($test))
-    {
-        echo $unserialized === $variable ? 'OK' : 'ERROR', PHP_EOL;
-    }
-    else
-    {
-        echo $test || $unserialized == $variable ? 'OK' : 'ERROR', PHP_EOL;
-    }
-}
-
-test('null', null);
-
-test('boo:l true', true);
-test('bool: true', false);
-
-test('zero: 0', 0);
-test('small: 1', 1);
-test('small: -1', -1);
-test('medium: 1000', 1000);
-test('medium: -1000', -1000);
-test('large: 100000', 100000);
-test('large: -100000', -100000);
-
-test('double: 123.456', 123.456);
-
-test('empty: ""', "");
-test('string: "foobar"', "foobar");
-
-test('empty: array', array(), false);
-test('empty: array(1, 2, 3)', array(1, 2, 3), false);
-test('empty: array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), false);
-
-test('array("foo", "foo", "foo")', array("foo", "foo", "foo"), false);
-test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), false);
-test('array("kek" => "lol", "lol" => "kek")', array("kek" => "lol", "lol" => "kek"), false);
-test('array("" => "empty")', array("" => "empty"), false);
-
-$a = array('foo');
-test('array($a, $a)', array($a, $a), false);
-test('array(&$a, &$a)', array(&$a, &$a), false);
-
-$a = array(null);
-$b = array(&$a);
-$a[0] = &$b;
-
-test('cyclic', $a, true);
-
-$a = array(
-    'a' => array(
-        'b' => 'c',
-        'd' => 'e'
-        ),
-    'f' => array(
-        'g' => 'h'
-        )
-    );
-
-test('array', $a, false);
-
-class Obj {
-    public $a;
-    protected $b;
-    private $c;
-
-    function __construct($a, $b, $c) {
-        $this->a = $a;
-        $this->b = $b;
-        $this->c = $c;
-    }
-}
-
-test('object', new Obj(1, 2, 3), true);
-
-test('object', array(new Obj(1, 2, 3), new Obj(4, 5, 6)), true);
-
-$o = new Obj(1, 2, 3);
-
-test('object', array(&$o, &$o), true);
---EXPECTF--
-NULL
-OK
-bool(true)
-OK
-bool(false)
-OK
-int(0)
-OK
-int(1)
-OK
-int(-1)
-OK
-int(1000)
-OK
-int(-1000)
-OK
-int(100000)
-OK
-int(-100000)
-OK
-float(123.456)
-OK
-string(0) ""
-OK
-string(6) "foobar"
-OK
-array(0) {
-}
-OK
-array(3) {
-  [0]=>
-  int(1)
-  [1]=>
-  int(2)
-  [2]=>
-  int(3)
-}
-OK
-array(3) {
-  [0]=>
-  array(3) {
-    [0]=>
-    int(1)
-    [1]=>
-    int(2)
-    [2]=>
-    int(3)
-  }
-  [1]=>
-  array(3) {
-    [0]=>
-    int(4)
-    [1]=>
-    int(5)
-    [2]=>
-    int(6)
-  }
-  [2]=>
-  array(3) {
-    [0]=>
-    int(7)
-    [1]=>
-    int(8)
-    [2]=>
-    int(9)
-  }
-}
-OK
-array(3) {
-  [0]=>
-  string(3) "foo"
-  [1]=>
-  string(3) "foo"
-  [2]=>
-  string(3) "foo"
-}
-OK
-array(2) {
-  ["one"]=>
-  int(1)
-  ["two"]=>
-  int(2)
-}
-OK
-array(2) {
-  ["kek"]=>
-  string(3) "lol"
-  ["lol"]=>
-  string(3) "kek"
-}
-OK
-array(1) {
-  [""]=>
-  string(5) "empty"
-}
-OK
-array(2) {
-  [0]=>
-  array(1) {
-    [0]=>
-    string(3) "foo"
-  }
-  [1]=>
-  array(1) {
-    [0]=>
-    string(3) "foo"
-  }
-}
-OK
-array(2) {
-  [0]=>
-  array(1) {
-    [0]=>
-    string(3) "foo"
-  }
-  [1]=>
-  array(1) {
-    [0]=>
-    string(3) "foo"
-  }
-}
-OK
-array(1) {
-  [0]=>
-  array(1) {
-    [0]=>
-    array(1) {
-      [0]=>
-      array(1) {
-        [0]=>
-        array(1) {
-          [0]=>
-          NULL
-        }
-      }
-    }
-  }
-}
-OK
-array(2) {
-  ["a"]=>
-  array(2) {
-    ["b"]=>
-    string(1) "c"
-    ["d"]=>
-    string(1) "e"
-  }
-  ["f"]=>
-  array(1) {
-    ["g"]=>
-    string(1) "h"
-  }
-}
-OK
-array(3) {
-  [0]=>
-  int(1)
-  [1]=>
-  int(2)
-  [2]=>
-  int(3)
-}
-OK
-array(2) {
-  [0]=>
-  array(3) {
-    [0]=>
-    int(1)
-    [1]=>
-    int(2)
-    [2]=>
-    int(3)
-  }
-  [1]=>
-  array(3) {
-    [0]=>
-    int(4)
-    [1]=>
-    int(5)
-    [2]=>
-    int(6)
-  }
-}
-OK
-array(2) {
-  [0]=>
-  array(3) {
-    [0]=>
-    int(1)
-    [1]=>
-    int(2)
-    [2]=>
-    int(3)
-  }
-  [1]=>
-  array(3) {
-    [0]=>
-    int(1)
-    [1]=>
-    int(2)
-    [2]=>
-    int(3)
-  }
-}
-OK
diff --git a/php/tests/090.phpt b/php/tests/090.phpt
new file mode 100644
index 00000000..b2a1224c
--- /dev/null
+++ b/php/tests/090.phpt
@@ -0,0 +1,575 @@
+--TEST--
+unpack of object converter (string)
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.2.0') < 0) {
+    echo "skip tests in PHP 5.2 or newer";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack'))
+{
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+error_reporting(0);
+
+function test($type, $variable, $object, $result = null)
+{
+    $serialized = msgpack_pack($variable);
+    $unserialized = msgpack_unpack($serialized, $object);
+    var_dump($unserialized);
+    if ($result)
+    {
+        echo $unserialized == $result ? 'OK' : 'ERROR', PHP_EOL;
+    }
+    else
+    {
+        echo 'SKIP', PHP_EOL;
+    }
+}
+
+class Obj
+{
+    public $a;
+    protected $b;
+    private $c;
+
+    public function __construct($a = null, $b = null, $c = null, $d = null)
+    {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+        if (is_array($d))
+        {
+            foreach ($d as $key => $val)
+            {
+                $this->{$key} = $val;
+            }
+        }
+    }
+}
+
+test('null', null, 'Obj', new Obj(null, null, null));
+
+test('bool: true', true, 'Obj', new Obj(true, null, null));
+test('bool: false', false, 'Obj', new Obj(false, null, null));
+
+test('zero: 0', 0, 'Obj', new Obj(0, null, null));
+test('small: 1', 1, 'Obj', new Obj(1, null, null));
+test('small: -1', -1, 'Obj', new Obj(-1, null, null));
+test('medium: 1000', 1000, 'Obj', new Obj(1000, null, null));
+test('medium: -1000', -1000, 'Obj', new Obj(-1000, null, null));
+test('large: 100000', 100000, 'Obj', new Obj(100000, null, null));
+test('large: -100000', -100000, 'Obj', new Obj(-100000, null, null));
+
+test('double: 123.456', 123.456, 'Obj', new Obj(123.456, null, null));
+
+test('empty: ""', "", 'Obj', new Obj("", null, null));
+test('string: "foobar"', "foobar", 'Obj', new Obj("foobar", null, null));
+
+test('array: empty', array(), 'Obj', new Obj(null, null, null));
+test('array(1, 2, 3)', array(1, 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), 'Obj', new Obj(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)));
+test('array(1, 2, 3, 4)', array(1, 2, 3, 4), 'Obj');
+
+test('array("foo", "foobar", "foohoge")', array("foo", "foobar", "hoge"), 'Obj', new Obj("foo", "foobar", "hoge"));
+test('array("a" => 1, "b" => 2))', array("a" => 1, "b" => 2), 'Obj', new Obj(1, 2, null));
+test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), 'Obj', new Obj(null, null, null, array("one" => 1, "two" => 2)));
+test('array("" => "empty")', array("" => "empty"), 'Obj');
+
+test('array("a" => 1, "b" => 2, 3))', array("a" => 1, "b" => 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(3, "a" => 1, "b" => 2))', array(3, "a" => 1, "b" => 2), 'Obj', new Obj(1, 2, 3));
+test('array("a" => 1, 3, "b" => 2))', array("a" => 1, 3, "b" => 2), 'Obj', new Obj(1, 2, 3));
+
+$a = array('foo');
+test('array($a, $a)', array($a, $a), 'Obj', new Obj($a, $a, null));
+test('array(&$a, &$a)', array(&$a, &$a), 'Obj', new Obj($a, $a, null));
+
+test('array(&$a, $a)', array($a, &$a), 'Obj', new Obj($a, $a, null));
+test('array(&$a, $a)', array(&$a, $a), 'Obj', new Obj($a, $a, null));
+
+$a = array(
+    'a' => array(
+        'b' => 'c',
+        'd' => 'e'
+        ),
+    'f' => array(
+        'g' => 'h'
+        )
+    );
+test('array', $a, 'Obj', new Obj(null, null, null, $a));
+
+$o = new Obj(1, 2, 3);
+test('object', $o, 'Obj', new Obj(1, 2, 3));
+
+class Obj2 {
+    public $A;
+    protected $B;
+    private $C;
+
+    function __construct($a, $b, $c) {
+        $this->A = $a;
+        $this->B = $b;
+        $this->C = $c;
+    }
+}
+
+$o = new Obj2(1, 2, 3);
+test('object', $o, 'Obj', new Obj($o));
+
+$o1 = new Obj2(1, 2, 3);
+$o2 = new Obj2(4, 5, 6);
+test('object', array($o1, $o2), 'Obj', new Obj($o1, $o2));
+
+$o = new Obj2(1, 2, 3);
+test('object', array(&$o, &$o), 'Obj', new Obj($o, $o));
+
+$o = new Obj2(1, 2, 3);
+test('object', array(&$o, $o), 'Obj', new Obj($o, $o));
+
+$o = new Obj2(1, 2, 3);
+test('object', array($o, &$o), 'Obj', new Obj($o, $o));
+
+--EXPECTF--
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(true)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(false)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(0)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(100000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-100000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  float(123.456)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(0) ""
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(6) "foobar"
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  array(3) {
+    [0]=>
+    int(7)
+    [1]=>
+    int(8)
+    [2]=>
+    int(9)
+  }
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+  [3]=>
+  int(4)
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(3) "foo"
+  [%r"?b"?:protected"?%r]=>
+  string(6) "foobar"
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  string(4) "hoge"
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (5) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  ["one"]=>
+  int(1)
+  ["two"]=>
+  int(2)
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  [""]=>
+  string(5) "empty"
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  ["f"]=>
+  array(1) {
+    ["g"]=>
+    string(1) "h"
+  }
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(4)
+    [%r"?B"?:protected"?%r]=>
+    int(5)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(6)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
diff --git a/php/tests/090b.phpt b/php/tests/090b.phpt
new file mode 100644
index 00000000..874f0987
--- /dev/null
+++ b/php/tests/090b.phpt
@@ -0,0 +1,563 @@
+--TEST--
+unpack of object converter (string)
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
+    echo "skip tests in PHP 5.1 or older";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack'))
+{
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+error_reporting(0);
+
+function test($type, $variable, $object, $result = null)
+{
+    $serialized = msgpack_pack($variable);
+    $unserialized = msgpack_unpack($serialized, $object);
+    var_dump($unserialized);
+    if ($result)
+    {
+        echo $unserialized == $result ? 'OK' : 'ERROR', PHP_EOL;
+    }
+    else
+    {
+        echo 'SKIP', PHP_EOL;
+    }
+}
+
+class Obj
+{
+    public $a;
+    protected $b;
+    private $c;
+
+    public function __construct($a = null, $b = null, $c = null, $d = null)
+    {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+        if (is_array($d))
+        {
+            foreach ($d as $key => $val)
+            {
+                $this->{$key} = $val;
+            }
+        }
+    }
+}
+
+test('null', null, 'Obj', new Obj(null, null, null));
+
+test('bool: true', true, 'Obj', new Obj(true, null, null));
+test('bool: false', false, 'Obj', new Obj(false, null, null));
+
+test('zero: 0', 0, 'Obj', new Obj(0, null, null));
+test('small: 1', 1, 'Obj', new Obj(1, null, null));
+test('small: -1', -1, 'Obj', new Obj(-1, null, null));
+test('medium: 1000', 1000, 'Obj', new Obj(1000, null, null));
+test('medium: -1000', -1000, 'Obj', new Obj(-1000, null, null));
+test('large: 100000', 100000, 'Obj', new Obj(100000, null, null));
+test('large: -100000', -100000, 'Obj', new Obj(-100000, null, null));
+
+test('double: 123.456', 123.456, 'Obj', new Obj(123.456, null, null));
+
+test('empty: ""', "", 'Obj', new Obj("", null, null));
+test('string: "foobar"', "foobar", 'Obj', new Obj("foobar", null, null));
+
+test('array: empty', array(), 'Obj', new Obj(null, null, null));
+test('array(1, 2, 3)', array(1, 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), 'Obj', new Obj(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)));
+test('array(1, 2, 3, 4)', array(1, 2, 3, 4), 'Obj');
+
+test('array("foo", "foobar", "foohoge")', array("foo", "foobar", "hoge"), 'Obj', new Obj("foo", "foobar", "hoge"));
+test('array("a" => 1, "b" => 2))', array("a" => 1, "b" => 2), 'Obj', new Obj(1, 2, null));
+test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), 'Obj', new Obj(null, null, null, array("one" => 1, "two" => 2)));
+
+test('array("a" => 1, "b" => 2, 3))', array("a" => 1, "b" => 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(3, "a" => 1, "b" => 2))', array(3, "a" => 1, "b" => 2), 'Obj', new Obj(1, 2, 3));
+test('array("a" => 1, 3, "b" => 2))', array("a" => 1, 3, "b" => 2), 'Obj', new Obj(1, 2, 3));
+
+$a = array('foo');
+test('array($a, $a)', array($a, $a), 'Obj', new Obj($a, $a, null));
+test('array(&$a, &$a)', array(&$a, &$a), 'Obj', new Obj($a, $a, null));
+
+test('array(&$a, $a)', array($a, &$a), 'Obj', new Obj($a, $a, null));
+test('array(&$a, $a)', array(&$a, $a), 'Obj', new Obj($a, $a, null));
+
+$a = array(
+    'a' => array(
+        'b' => 'c',
+        'd' => 'e'
+        ),
+    'f' => array(
+        'g' => 'h'
+        )
+    );
+test('array', $a, 'Obj', new Obj(null, null, null, $a));
+
+$o = new Obj(1, 2, 3);
+test('object', $o, 'Obj', new Obj(1, 2, 3));
+
+class Obj2 {
+    public $A;
+    protected $B;
+    private $C;
+
+    function __construct($a, $b, $c) {
+        $this->A = $a;
+        $this->B = $b;
+        $this->C = $c;
+    }
+}
+
+$o = new Obj2(1, 2, 3);
+test('object', $o, 'Obj', new Obj($o));
+
+$o1 = new Obj2(1, 2, 3);
+$o2 = new Obj2(4, 5, 6);
+test('object', array($o1, $o2), 'Obj', new Obj($o1, $o2));
+
+$o = new Obj2(1, 2, 3);
+test('object', array(&$o, &$o), 'Obj', new Obj($o, $o));
+
+$o = new Obj2(1, 2, 3);
+test('object', array(&$o, $o), 'Obj', new Obj($o, $o));
+
+$o = new Obj2(1, 2, 3);
+test('object', array($o, &$o), 'Obj', new Obj($o, $o));
+
+--EXPECTF--
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(true)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(false)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(0)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(100000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-100000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  float(123.456)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(0) ""
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(6) "foobar"
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  ["c:private"]=>
+  array(3) {
+    [0]=>
+    int(7)
+    [1]=>
+    int(8)
+    [2]=>
+    int(9)
+  }
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+  [3]=>
+  int(4)
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(3) "foo"
+  ["b:protected"]=>
+  string(6) "foobar"
+  ["c:private"]=>
+  string(4) "hoge"
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (5) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+  ["one"]=>
+  int(1)
+  ["two"]=>
+  int(2)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+  ["f"]=>
+  array(1) {
+    ["g"]=>
+    string(1) "h"
+  }
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(4)
+    ["B:protected"]=>
+    int(5)
+    ["C:private"]=>
+    int(6)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
diff --git a/php/tests/091.phpt b/php/tests/091.phpt
new file mode 100644
index 00000000..33bb875e
--- /dev/null
+++ b/php/tests/091.phpt
@@ -0,0 +1,577 @@
+--TEST--
+unpack of object converter (object)
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.2.0') < 0) {
+    echo "skip tests in PHP 5.2 or newer";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack'))
+{
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+error_reporting(0);
+
+function test($type, $variable, $object, $result = null)
+{
+    $serialized = msgpack_pack($variable);
+    $unserialized = msgpack_unpack($serialized, $object);
+    var_dump($unserialized);
+    if ($result)
+    {
+        echo $unserialized == $result ? 'OK' : 'ERROR', PHP_EOL;
+    }
+    else
+    {
+        echo 'SKIP', PHP_EOL;
+    }
+}
+
+class Obj
+{
+    public $a;
+    protected $b;
+    private $c;
+
+    public function __construct($a = null, $b = null, $c = null, $d = null)
+    {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+        if (is_array($d))
+        {
+            foreach ($d as $key => $val)
+            {
+                $this->{$key} = $val;
+            }
+        }
+    }
+}
+
+$object = new Obj();
+
+test('null', null, new Obj(), new Obj(null, null, null));
+
+test('bool: true', true, new Obj(), new Obj(true, null, null));
+test('bool: false', false, new Obj(), new Obj(false, null, null));
+
+test('zero: 0', 0, new Obj(), new Obj(0, null, null));
+test('small: 1', 1, new Obj(), new Obj(1, null, null));
+test('small: -1', -1, new Obj(), new Obj(-1, null, null));
+test('medium: 1000', 1000, new Obj(), new Obj(1000, null, null));
+test('medium: -1000', -1000, new Obj(), new Obj(-1000, null, null));
+test('large: 100000', 100000, new Obj(), new Obj(100000, null, null));
+test('large: -100000', -100000, new Obj(), new Obj(-100000, null, null));
+
+test('double: 123.456', 123.456, new Obj(), new Obj(123.456, null, null));
+
+test('empty: ""', "", new Obj(), new Obj("", null, null));
+test('string: "foobar"', "foobar", new Obj(), new Obj("foobar", null, null));
+
+test('array: empty', array(), new Obj(), new Obj(null, null, null));
+test('array(1, 2, 3)', array(1, 2, 3), new Obj(), new Obj(1, 2, 3));
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), new Obj(), new Obj(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)));
+test('array(1, 2, 3, 4)', array(1, 2, 3, 4), new Obj());
+
+test('array("foo", "foobar", "foohoge")', array("foo", "foobar", "hoge"), new Obj(), new Obj("foo", "foobar", "hoge"));
+test('array("a" => 1, "b" => 2))', array("a" => 1, "b" => 2), new Obj(), new Obj(1, 2, null));
+test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), new Obj(), new Obj(null, null, null, array("one" => 1, "two" => 2)));
+test('array("" => "empty")', array("" => "empty"), new Obj());
+
+test('array("a" => 1, "b" => 2, 3))', array("a" => 1, "b" => 2, 3), new Obj(), new Obj(1, 2, 3));
+test('array(3, "a" => 1, "b" => 2))', array(3, "a" => 1, "b" => 2), new Obj(), new Obj(1, 2, 3));
+test('array("a" => 1, 3, "b" => 2))', array("a" => 1, 3, "b" => 2), new Obj(), new Obj(1, 2, 3));
+
+$a = array('foo');
+test('array($a, $a)', array($a, $a), new Obj(), new Obj($a, $a, null));
+test('array(&$a, &$a)', array(&$a, &$a), new Obj(), new Obj($a, $a, null));
+
+test('array(&$a, $a)', array($a, &$a), new Obj(), new Obj($a, $a, null));
+test('array(&$a, $a)', array(&$a, $a), new Obj(), new Obj($a, $a, null));
+
+$a = array(
+    'a' => array(
+        'b' => 'c',
+        'd' => 'e'
+        ),
+    'f' => array(
+        'g' => 'h'
+        )
+    );
+test('array', $a, new Obj(), new Obj(null, null, null, $a));
+
+$o = new Obj(1, 2, 3);
+test('object', $o, new Obj(), new Obj(1, 2, 3));
+
+class Obj2 {
+    public $A;
+    protected $B;
+    private $C;
+
+    function __construct($a, $b, $c) {
+        $this->A = $a;
+        $this->B = $b;
+        $this->C = $c;
+    }
+}
+
+$o = new Obj2(1, 2, 3);
+test('object', $o, new Obj(), new Obj($o));
+
+$o1 = new Obj2(1, 2, 3);
+$o2 = new Obj2(4, 5, 6);
+test('object', array($o1, $o2), new Obj(), new Obj($o1, $o2));
+
+$o = new Obj2(1, 2, 3);
+test('object', array(&$o, &$o), new Obj(), new Obj($o, $o));
+
+$o = new Obj2(1, 2, 3);
+test('object', array(&$o, $o), new Obj(), new Obj($o, $o));
+
+$o = new Obj2(1, 2, 3);
+test('object', array($o, &$o), new Obj(), new Obj($o, $o));
+
+--EXPECTF--
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(true)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(false)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(0)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(100000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-100000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  float(123.456)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(0) ""
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(6) "foobar"
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  array(3) {
+    [0]=>
+    int(7)
+    [1]=>
+    int(8)
+    [2]=>
+    int(9)
+  }
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+  [3]=>
+  int(4)
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(3) "foo"
+  [%r"?b"?:protected"?%r]=>
+  string(6) "foobar"
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  string(4) "hoge"
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (5) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  ["one"]=>
+  int(1)
+  ["two"]=>
+  int(2)
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  [""]=>
+  string(5) "empty"
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  ["f"]=>
+  array(1) {
+    ["g"]=>
+    string(1) "h"
+  }
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(4)
+    [%r"?B"?:protected"?%r]=>
+    int(5)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(6)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
diff --git a/php/tests/091b.phpt b/php/tests/091b.phpt
new file mode 100644
index 00000000..c917b136
--- /dev/null
+++ b/php/tests/091b.phpt
@@ -0,0 +1,563 @@
+--TEST--
+unpack of object converter (object)
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
+    echo "skip tests in PHP 5.1 or older";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack'))
+{
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+error_reporting(0);
+
+function test($type, $variable, $object, $result = null)
+{
+    $serialized = msgpack_pack($variable);
+    $unserialized = msgpack_unpack($serialized, $object);
+    var_dump($unserialized);
+    if ($result)
+    {
+        echo $unserialized == $result ? 'OK' : 'ERROR', PHP_EOL;
+    }
+    else
+    {
+        echo 'SKIP', PHP_EOL;
+    }
+}
+
+class Obj
+{
+    public $a;
+    protected $b;
+    private $c;
+
+    public function __construct($a = null, $b = null, $c = null, $d = null)
+    {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+        if (is_array($d))
+        {
+            foreach ($d as $key => $val)
+            {
+                $this->{$key} = $val;
+            }
+        }
+    }
+}
+
+test('null', null, new Obj(), new Obj(null, null, null));
+
+test('bool: true', true, new Obj(), new Obj(true, null, null));
+test('bool: false', false, new Obj(), new Obj(false, null, null));
+
+test('zero: 0', 0, new Obj(), new Obj(0, null, null));
+test('small: 1', 1, new Obj(), new Obj(1, null, null));
+test('small: -1', -1, new Obj(), new Obj(-1, null, null));
+test('medium: 1000', 1000, new Obj(), new Obj(1000, null, null));
+test('medium: -1000', -1000, new Obj(), new Obj(-1000, null, null));
+test('large: 100000', 100000, new Obj(), new Obj(100000, null, null));
+test('large: -100000', -100000, new Obj(), new Obj(-100000, null, null));
+
+test('double: 123.456', 123.456, new Obj(), new Obj(123.456, null, null));
+
+test('empty: ""', "", new Obj(), new Obj("", null, null));
+test('string: "foobar"', "foobar", new Obj(), new Obj("foobar", null, null));
+
+test('array: empty', array(), new Obj(), new Obj(null, null, null));
+test('array(1, 2, 3)', array(1, 2, 3), new Obj(), new Obj(1, 2, 3));
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), new Obj(), new Obj(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)));
+test('array(1, 2, 3, 4)', array(1, 2, 3, 4), new Obj());
+
+test('array("foo", "foobar", "foohoge")', array("foo", "foobar", "hoge"), new Obj(), new Obj("foo", "foobar", "hoge"));
+test('array("a" => 1, "b" => 2))', array("a" => 1, "b" => 2), new Obj(), new Obj(1, 2, null));
+test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), new Obj(), new Obj(null, null, null, array("one" => 1, "two" => 2)));
+
+test('array("a" => 1, "b" => 2, 3))', array("a" => 1, "b" => 2, 3), new Obj(), new Obj(1, 2, 3));
+test('array(3, "a" => 1, "b" => 2))', array(3, "a" => 1, "b" => 2), new Obj(), new Obj(1, 2, 3));
+test('array("a" => 1, 3, "b" => 2))', array("a" => 1, 3, "b" => 2), new Obj(), new Obj(1, 2, 3));
+
+$a = array('foo');
+test('array($a, $a)', array($a, $a), new Obj(), new Obj($a, $a, null));
+test('array(&$a, &$a)', array(&$a, &$a), new Obj(), new Obj($a, $a, null));
+
+test('array(&$a, $a)', array($a, &$a), new Obj(), new Obj($a, $a, null));
+test('array(&$a, $a)', array(&$a, $a), new Obj(), new Obj($a, $a, null));
+
+$a = array(
+    'a' => array(
+        'b' => 'c',
+        'd' => 'e'
+        ),
+    'f' => array(
+        'g' => 'h'
+        )
+    );
+test('array', $a, new Obj(), new Obj(null, null, null, $a));
+
+$o = new Obj(1, 2, 3);
+test('object', $o, new Obj(), new Obj(1, 2, 3));
+
+class Obj2 {
+    public $A;
+    protected $B;
+    private $C;
+
+    function __construct($a, $b, $c) {
+        $this->A = $a;
+        $this->B = $b;
+        $this->C = $c;
+    }
+}
+
+$o = new Obj2(1, 2, 3);
+test('object', $o, new Obj(), new Obj($o));
+
+$o1 = new Obj2(1, 2, 3);
+$o2 = new Obj2(4, 5, 6);
+test('object', array($o1, $o2), new Obj(), new Obj($o1, $o2));
+
+$o = new Obj2(1, 2, 3);
+test('object', array(&$o, &$o), new Obj(), new Obj($o, $o));
+
+$o = new Obj2(1, 2, 3);
+test('object', array(&$o, $o), new Obj(), new Obj($o, $o));
+
+$o = new Obj2(1, 2, 3);
+test('object', array($o, &$o), new Obj(), new Obj($o, $o));
+
+--EXPECTF--
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(true)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(false)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(0)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(100000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-100000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  float(123.456)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(0) ""
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(6) "foobar"
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  ["c:private"]=>
+  array(3) {
+    [0]=>
+    int(7)
+    [1]=>
+    int(8)
+    [2]=>
+    int(9)
+  }
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+  [3]=>
+  int(4)
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(3) "foo"
+  ["b:protected"]=>
+  string(6) "foobar"
+  ["c:private"]=>
+  string(4) "hoge"
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (5) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+  ["one"]=>
+  int(1)
+  ["two"]=>
+  int(2)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+  ["f"]=>
+  array(1) {
+    ["g"]=>
+    string(1) "h"
+  }
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(4)
+    ["B:protected"]=>
+    int(5)
+    ["C:private"]=>
+    int(6)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
diff --git a/php/tests/092.phpt b/php/tests/092.phpt
new file mode 100644
index 00000000..9c5dd1a4
--- /dev/null
+++ b/php/tests/092.phpt
@@ -0,0 +1,442 @@
+--TEST--
+unpack of object converter (string: php_only=0)
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.2.0') < 0) {
+    echo "skip tests in PHP 5.2 or newer";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack'))
+{
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+error_reporting(0);
+ini_set('msgpack.php_only', 0);
+
+function test($type, $variable, $object, $result = null)
+{
+    $serialized = msgpack_pack($variable);
+    $unserialized = msgpack_unpack($serialized, $object);
+    var_dump($unserialized);
+    if ($result)
+    {
+        echo $unserialized == $result ? 'OK' : 'ERROR', PHP_EOL;
+    }
+    else
+    {
+        echo 'SKIP', PHP_EOL;
+    }
+}
+
+class Obj
+{
+    public $a;
+    protected $b;
+    private $c;
+
+    public function __construct($a = null, $b = null, $c = null, $d = null)
+    {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+        if (is_array($d))
+        {
+            foreach ($d as $key => $val)
+            {
+                $this->{$key} = $val;
+            }
+        }
+    }
+}
+
+test('null', null, 'Obj', new Obj(null, null, null));
+
+test('bool: true', true, 'Obj', new Obj(true, null, null));
+test('bool: false', false, 'Obj', new Obj(false, null, null));
+
+test('zero: 0', 0, 'Obj', new Obj(0, null, null));
+test('small: 1', 1, 'Obj', new Obj(1, null, null));
+test('small: -1', -1, 'Obj', new Obj(-1, null, null));
+test('medium: 1000', 1000, 'Obj', new Obj(1000, null, null));
+test('medium: -1000', -1000, 'Obj', new Obj(-1000, null, null));
+test('large: 100000', 100000, 'Obj', new Obj(100000, null, null));
+test('large: -100000', -100000, 'Obj', new Obj(-100000, null, null));
+
+test('double: 123.456', 123.456, 'Obj', new Obj(123.456, null, null));
+
+test('empty: ""', "", 'Obj', new Obj("", null, null));
+test('string: "foobar"', "foobar", 'Obj', new Obj("foobar", null, null));
+
+test('array: empty', array(), 'Obj', new Obj(null, null, null));
+test('array(1, 2, 3)', array(1, 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), 'Obj', new Obj(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)));
+test('array(1, 2, 3, 4)', array(1, 2, 3, 4), 'Obj');
+
+test('array("foo", "foobar", "foohoge")', array("foo", "foobar", "hoge"), 'Obj', new Obj("foo", "foobar", "hoge"));
+test('array("a" => 1, "b" => 2))', array("a" => 1, "b" => 2), 'Obj', new Obj(1, 2, null));
+test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), 'Obj', new Obj(null, null, null, array("one" => 1, "two" => 2)));
+test('array("" => "empty")', array("" => "empty"), 'Obj');
+
+test('array("a" => 1, "b" => 2, 3))', array("a" => 1, "b" => 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(3, "a" => 1, "b" => 2))', array(3, "a" => 1, "b" => 2), 'Obj', new Obj(1, 2, 3));
+test('array("a" => 1, 3, "b" => 2))', array("a" => 1, 3, "b" => 2), 'Obj', new Obj(1, 2, 3));
+
+$a = array('foo');
+test('array($a, $a)', array($a, $a), 'Obj', new Obj($a, $a, null));
+
+$a = array(
+    'a' => array(
+        'b' => 'c',
+        'd' => 'e'
+        ),
+    'f' => array(
+        'g' => 'h'
+        )
+    );
+test('array', $a, 'Obj', new Obj(null, null, null, $a));
+
+$o = new Obj(1, 2, 3);
+test('object', $o, 'Obj', new Obj(1, 2, 3));
+
+class Obj2 {
+    public $A;
+    protected $B;
+    private $C;
+
+    function __construct($a, $b, $c) {
+        $this->A = $a;
+        $this->B = $b;
+        $this->C = $c;
+    }
+}
+
+$o = new Obj2(1, 2, 3);
+test('object', $o, 'Obj', new Obj(1, 2, 3));
+
+$o1 = new Obj2(1, 2, 3);
+$o2 = new Obj2(4, 5, 6);
+test('object', array($o1, $o2), 'Obj', new Obj(array(1, 2, 3), array(4, 5, 6)));
+
+--EXPECTF--
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(true)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(false)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(0)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(100000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-100000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  float(123.456)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(0) ""
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(6) "foobar"
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  array(3) {
+    [0]=>
+    int(7)
+    [1]=>
+    int(8)
+    [2]=>
+    int(9)
+  }
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+  [3]=>
+  int(4)
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(3) "foo"
+  [%r"?b"?:protected"?%r]=>
+  string(6) "foobar"
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  string(4) "hoge"
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (5) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  ["one"]=>
+  int(1)
+  ["two"]=>
+  int(2)
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  [""]=>
+  string(5) "empty"
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  ["f"]=>
+  array(1) {
+    ["g"]=>
+    string(1) "h"
+  }
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
diff --git a/php/tests/092b.phpt b/php/tests/092b.phpt
new file mode 100644
index 00000000..0e99a715
--- /dev/null
+++ b/php/tests/092b.phpt
@@ -0,0 +1,430 @@
+--TEST--
+unpack of object converter (string: php_only=0)
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
+    echo "skip tests in PHP 5.1 or older";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack'))
+{
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+error_reporting(0);
+ini_set('msgpack.php_only', 0);
+
+function test($type, $variable, $object, $result = null)
+{
+    $serialized = msgpack_pack($variable);
+    $unserialized = msgpack_unpack($serialized, $object);
+    var_dump($unserialized);
+    if ($result)
+    {
+        echo $unserialized == $result ? 'OK' : 'ERROR', PHP_EOL;
+    }
+    else
+    {
+        echo 'SKIP', PHP_EOL;
+    }
+}
+
+class Obj
+{
+    public $a;
+    protected $b;
+    private $c;
+
+    public function __construct($a = null, $b = null, $c = null, $d = null)
+    {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+        if (is_array($d))
+        {
+            foreach ($d as $key => $val)
+            {
+                $this->{$key} = $val;
+            }
+        }
+    }
+}
+
+test('null', null, 'Obj', new Obj(null, null, null));
+
+test('bool: true', true, 'Obj', new Obj(true, null, null));
+test('bool: false', false, 'Obj', new Obj(false, null, null));
+
+test('zero: 0', 0, 'Obj', new Obj(0, null, null));
+test('small: 1', 1, 'Obj', new Obj(1, null, null));
+test('small: -1', -1, 'Obj', new Obj(-1, null, null));
+test('medium: 1000', 1000, 'Obj', new Obj(1000, null, null));
+test('medium: -1000', -1000, 'Obj', new Obj(-1000, null, null));
+test('large: 100000', 100000, 'Obj', new Obj(100000, null, null));
+test('large: -100000', -100000, 'Obj', new Obj(-100000, null, null));
+
+test('double: 123.456', 123.456, 'Obj', new Obj(123.456, null, null));
+
+test('empty: ""', "", 'Obj', new Obj("", null, null));
+test('string: "foobar"', "foobar", 'Obj', new Obj("foobar", null, null));
+
+test('array: empty', array(), 'Obj', new Obj(null, null, null));
+test('array(1, 2, 3)', array(1, 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), 'Obj', new Obj(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)));
+test('array(1, 2, 3, 4)', array(1, 2, 3, 4), 'Obj');
+
+test('array("foo", "foobar", "foohoge")', array("foo", "foobar", "hoge"), 'Obj', new Obj("foo", "foobar", "hoge"));
+test('array("a" => 1, "b" => 2))', array("a" => 1, "b" => 2), 'Obj', new Obj(1, 2, null));
+test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), 'Obj', new Obj(null, null, null, array("one" => 1, "two" => 2)));
+
+test('array("a" => 1, "b" => 2, 3))', array("a" => 1, "b" => 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(3, "a" => 1, "b" => 2))', array(3, "a" => 1, "b" => 2), 'Obj', new Obj(1, 2, 3));
+test('array("a" => 1, 3, "b" => 2))', array("a" => 1, 3, "b" => 2), 'Obj', new Obj(1, 2, 3));
+
+$a = array('foo');
+test('array($a, $a)', array($a, $a), 'Obj', new Obj($a, $a, null));
+
+$a = array(
+    'a' => array(
+        'b' => 'c',
+        'd' => 'e'
+        ),
+    'f' => array(
+        'g' => 'h'
+        )
+    );
+test('array', $a, 'Obj', new Obj(null, null, null, $a));
+
+$o = new Obj(1, 2, 3);
+test('object', $o, 'Obj', new Obj(1, 2, 3));
+
+class Obj2 {
+    public $A;
+    protected $B;
+    private $C;
+
+    function __construct($a, $b, $c) {
+        $this->A = $a;
+        $this->B = $b;
+        $this->C = $c;
+    }
+}
+
+$o = new Obj2(1, 2, 3);
+test('object', $o, 'Obj', new Obj(1, 2, 3));
+
+$o1 = new Obj2(1, 2, 3);
+$o2 = new Obj2(4, 5, 6);
+test('object', array($o1, $o2), 'Obj', new Obj(array(1, 2, 3), array(4, 5, 6)));
+
+--EXPECTF--
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(true)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(false)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(0)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(100000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-100000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  float(123.456)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(0) ""
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(6) "foobar"
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  ["c:private"]=>
+  array(3) {
+    [0]=>
+    int(7)
+    [1]=>
+    int(8)
+    [2]=>
+    int(9)
+  }
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+  [3]=>
+  int(4)
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(3) "foo"
+  ["b:protected"]=>
+  string(6) "foobar"
+  ["c:private"]=>
+  string(4) "hoge"
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (5) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+  ["one"]=>
+  int(1)
+  ["two"]=>
+  int(2)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+  ["f"]=>
+  array(1) {
+    ["g"]=>
+    string(1) "h"
+  }
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
diff --git a/php/tests/093.phpt b/php/tests/093.phpt
new file mode 100644
index 00000000..f66511ef
--- /dev/null
+++ b/php/tests/093.phpt
@@ -0,0 +1,444 @@
+--TEST--
+unpack of object converter (object: php_only=0)
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.2.0') < 0) {
+    echo "skip tests in PHP 5.2 or newer";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack'))
+{
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+error_reporting(0);
+ini_set('msgpack.php_only', 0);
+
+function test($type, $variable, $object, $result = null)
+{
+    $serialized = msgpack_pack($variable);
+    $unserialized = msgpack_unpack($serialized, $object);
+    var_dump($unserialized);
+    if ($result)
+    {
+        echo $unserialized == $result ? 'OK' : 'ERROR', PHP_EOL;
+    }
+    else
+    {
+        echo 'SKIP', PHP_EOL;
+    }
+}
+
+class Obj
+{
+    public $a;
+    protected $b;
+    private $c;
+
+    public function __construct($a = null, $b = null, $c = null, $d = null)
+    {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+        if (is_array($d))
+        {
+            foreach ($d as $key => $val)
+            {
+                $this->{$key} = $val;
+            }
+        }
+    }
+}
+
+$object = new Obj();
+
+test('null', null, new Obj(), new Obj(null, null, null));
+
+test('bool: true', true, new Obj(), new Obj(true, null, null));
+test('bool: false', false, new Obj(), new Obj(false, null, null));
+
+test('zero: 0', 0, new Obj(), new Obj(0, null, null));
+test('small: 1', 1, new Obj(), new Obj(1, null, null));
+test('small: -1', -1, new Obj(), new Obj(-1, null, null));
+test('medium: 1000', 1000, new Obj(), new Obj(1000, null, null));
+test('medium: -1000', -1000, new Obj(), new Obj(-1000, null, null));
+test('large: 100000', 100000, new Obj(), new Obj(100000, null, null));
+test('large: -100000', -100000, new Obj(), new Obj(-100000, null, null));
+
+test('double: 123.456', 123.456, new Obj(), new Obj(123.456, null, null));
+
+test('empty: ""', "", new Obj(), new Obj("", null, null));
+test('string: "foobar"', "foobar", new Obj(), new Obj("foobar", null, null));
+
+test('array: empty', array(), new Obj(), new Obj(null, null, null));
+test('array(1, 2, 3)', array(1, 2, 3), new Obj(), new Obj(1, 2, 3));
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), new Obj(), new Obj(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)));
+test('array(1, 2, 3, 4)', array(1, 2, 3, 4), new Obj());
+
+test('array("foo", "foobar", "foohoge")', array("foo", "foobar", "hoge"), new Obj(), new Obj("foo", "foobar", "hoge"));
+test('array("a" => 1, "b" => 2))', array("a" => 1, "b" => 2), new Obj(), new Obj(1, 2, null));
+test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), new Obj(), new Obj(null, null, null, array("one" => 1, "two" => 2)));
+test('array("" => "empty")', array("" => "empty"), new Obj());
+
+test('array("a" => 1, "b" => 2, 3))', array("a" => 1, "b" => 2, 3), new Obj(), new Obj(1, 2, 3));
+test('array(3, "a" => 1, "b" => 2))', array(3, "a" => 1, "b" => 2), new Obj(), new Obj(1, 2, 3));
+test('array("a" => 1, 3, "b" => 2))', array("a" => 1, 3, "b" => 2), new Obj(), new Obj(1, 2, 3));
+
+$a = array('foo');
+test('array($a, $a)', array($a, $a), new Obj(), new Obj($a, $a, null));
+
+$a = array(
+    'a' => array(
+        'b' => 'c',
+        'd' => 'e'
+        ),
+    'f' => array(
+        'g' => 'h'
+        )
+    );
+test('array', $a, new Obj(), new Obj(null, null, null, $a));
+
+$o = new Obj(1, 2, 3);
+test('object', $o, new Obj(), new Obj(1, 2, 3));
+
+class Obj2 {
+    public $A;
+    protected $B;
+    private $C;
+
+    function __construct($a, $b, $c) {
+        $this->A = $a;
+        $this->B = $b;
+        $this->C = $c;
+    }
+}
+
+$o = new Obj2(1, 2, 3);
+test('object', $o, new Obj(), new Obj(1, 2, 3));
+
+$o1 = new Obj2(1, 2, 3);
+$o2 = new Obj2(4, 5, 6);
+test('object', array($o1, $o2), new Obj(), new Obj(array(1, 2, 3), array(4, 5, 6)));
+
+--EXPECTF--
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(true)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(false)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(0)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(100000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-100000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  float(123.456)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(0) ""
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(6) "foobar"
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  array(3) {
+    [0]=>
+    int(7)
+    [1]=>
+    int(8)
+    [2]=>
+    int(9)
+  }
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+  [3]=>
+  int(4)
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(3) "foo"
+  [%r"?b"?:protected"?%r]=>
+  string(6) "foobar"
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  string(4) "hoge"
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (5) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  ["one"]=>
+  int(1)
+  ["two"]=>
+  int(2)
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  [""]=>
+  string(5) "empty"
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  ["f"]=>
+  array(1) {
+    ["g"]=>
+    string(1) "h"
+  }
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
diff --git a/php/tests/093b.phpt b/php/tests/093b.phpt
new file mode 100644
index 00000000..c5acd3fe
--- /dev/null
+++ b/php/tests/093b.phpt
@@ -0,0 +1,430 @@
+--TEST--
+unpack of object converter (object: php_only=0)
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
+    echo "skip tests in PHP 5.1 or older";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack'))
+{
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+error_reporting(0);
+ini_set('msgpack.php_only', 0);
+
+function test($type, $variable, $object, $result = null)
+{
+    $serialized = msgpack_pack($variable);
+    $unserialized = msgpack_unpack($serialized, $object);
+    var_dump($unserialized);
+    if ($result)
+    {
+        echo $unserialized == $result ? 'OK' : 'ERROR', PHP_EOL;
+    }
+    else
+    {
+        echo 'SKIP', PHP_EOL;
+    }
+}
+
+class Obj
+{
+    public $a;
+    protected $b;
+    private $c;
+
+    public function __construct($a = null, $b = null, $c = null, $d = null)
+    {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+        if (is_array($d))
+        {
+            foreach ($d as $key => $val)
+            {
+                $this->{$key} = $val;
+            }
+        }
+    }
+}
+
+test('null', null, new Obj(), new Obj(null, null, null));
+
+test('bool: true', true, new Obj(), new Obj(true, null, null));
+test('bool: false', false, new Obj(), new Obj(false, null, null));
+
+test('zero: 0', 0, new Obj(), new Obj(0, null, null));
+test('small: 1', 1, new Obj(), new Obj(1, null, null));
+test('small: -1', -1, new Obj(), new Obj(-1, null, null));
+test('medium: 1000', 1000, new Obj(), new Obj(1000, null, null));
+test('medium: -1000', -1000, new Obj(), new Obj(-1000, null, null));
+test('large: 100000', 100000, new Obj(), new Obj(100000, null, null));
+test('large: -100000', -100000, new Obj(), new Obj(-100000, null, null));
+
+test('double: 123.456', 123.456, new Obj(), new Obj(123.456, null, null));
+
+test('empty: ""', "", new Obj(), new Obj("", null, null));
+test('string: "foobar"', "foobar", new Obj(), new Obj("foobar", null, null));
+
+test('array: empty', array(), new Obj(), new Obj(null, null, null));
+test('array(1, 2, 3)', array(1, 2, 3), new Obj(), new Obj(1, 2, 3));
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), new Obj(), new Obj(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)));
+test('array(1, 2, 3, 4)', array(1, 2, 3, 4), new Obj());
+
+test('array("foo", "foobar", "foohoge")', array("foo", "foobar", "hoge"), new Obj(), new Obj("foo", "foobar", "hoge"));
+test('array("a" => 1, "b" => 2))', array("a" => 1, "b" => 2), new Obj(), new Obj(1, 2, null));
+test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), new Obj(), new Obj(null, null, null, array("one" => 1, "two" => 2)));
+
+test('array("a" => 1, "b" => 2, 3))', array("a" => 1, "b" => 2, 3), new Obj(), new Obj(1, 2, 3));
+test('array(3, "a" => 1, "b" => 2))', array(3, "a" => 1, "b" => 2), new Obj(), new Obj(1, 2, 3));
+test('array("a" => 1, 3, "b" => 2))', array("a" => 1, 3, "b" => 2), new Obj(), new Obj(1, 2, 3));
+
+$a = array('foo');
+test('array($a, $a)', array($a, $a), new Obj(), new Obj($a, $a, null));
+
+$a = array(
+    'a' => array(
+        'b' => 'c',
+        'd' => 'e'
+        ),
+    'f' => array(
+        'g' => 'h'
+        )
+    );
+test('array', $a, new Obj(), new Obj(null, null, null, $a));
+
+$o = new Obj(1, 2, 3);
+test('object', $o, new Obj(), new Obj(1, 2, 3));
+
+class Obj2 {
+    public $A;
+    protected $B;
+    private $C;
+
+    function __construct($a, $b, $c) {
+        $this->A = $a;
+        $this->B = $b;
+        $this->C = $c;
+    }
+}
+
+$o = new Obj2(1, 2, 3);
+test('object', $o, new Obj(), new Obj(1, 2, 3));
+
+$o1 = new Obj2(1, 2, 3);
+$o2 = new Obj2(4, 5, 6);
+test('object', array($o1, $o2), new Obj(), new Obj(array(1, 2, 3), array(4, 5, 6)));
+
+--EXPECTF--
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(true)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(false)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(0)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(100000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-100000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  float(123.456)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(0) ""
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(6) "foobar"
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  ["c:private"]=>
+  array(3) {
+    [0]=>
+    int(7)
+    [1]=>
+    int(8)
+    [2]=>
+    int(9)
+  }
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+  [3]=>
+  int(4)
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(3) "foo"
+  ["b:protected"]=>
+  string(6) "foobar"
+  ["c:private"]=>
+  string(4) "hoge"
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (5) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+  ["one"]=>
+  int(1)
+  ["two"]=>
+  int(2)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+  ["f"]=>
+  array(1) {
+    ["g"]=>
+    string(1) "h"
+  }
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
diff --git a/php/tests/094.phpt b/php/tests/094.phpt
new file mode 100644
index 00000000..a0a40d6d
--- /dev/null
+++ b/php/tests/094.phpt
@@ -0,0 +1,578 @@
+--TEST--
+unpack of object converter : class unpack (string)
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.2.0') < 0) {
+    echo "skip tests in PHP 5.2 or newer";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack'))
+{
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+error_reporting(0);
+
+function test($type, $variable, $object, $result = null)
+{
+    $msgpack = new MessagePack();
+
+    $serialized = $msgpack->pack($variable);
+    $unserialized = $msgpack->unpack($serialized, $object);
+
+    var_dump($unserialized);
+    if ($result)
+    {
+        echo $unserialized == $result ? 'OK' : 'ERROR', PHP_EOL;
+    }
+    else
+    {
+        echo 'SKIP', PHP_EOL;
+    }
+}
+
+class Obj
+{
+    public $a;
+    protected $b;
+    private $c;
+
+    public function __construct($a = null, $b = null, $c = null, $d = null)
+    {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+        if (is_array($d))
+        {
+            foreach ($d as $key => $val)
+            {
+                $this->{$key} = $val;
+            }
+        }
+    }
+}
+
+test('null', null, 'Obj', new Obj(null, null, null));
+
+test('bool: true', true, 'Obj', new Obj(true, null, null));
+test('bool: false', false, 'Obj', new Obj(false, null, null));
+
+test('zero: 0', 0, 'Obj', new Obj(0, null, null));
+test('small: 1', 1, 'Obj', new Obj(1, null, null));
+test('small: -1', -1, 'Obj', new Obj(-1, null, null));
+test('medium: 1000', 1000, 'Obj', new Obj(1000, null, null));
+test('medium: -1000', -1000, 'Obj', new Obj(-1000, null, null));
+test('large: 100000', 100000, 'Obj', new Obj(100000, null, null));
+test('large: -100000', -100000, 'Obj', new Obj(-100000, null, null));
+
+test('double: 123.456', 123.456, 'Obj', new Obj(123.456, null, null));
+
+test('empty: ""', "", 'Obj', new Obj("", null, null));
+test('string: "foobar"', "foobar", 'Obj', new Obj("foobar", null, null));
+
+test('array: empty', array(), 'Obj', new Obj(null, null, null));
+test('array(1, 2, 3)', array(1, 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), 'Obj', new Obj(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)));
+test('array(1, 2, 3, 4)', array(1, 2, 3, 4), 'Obj');
+
+test('array("foo", "foobar", "foohoge")', array("foo", "foobar", "hoge"), 'Obj', new Obj("foo", "foobar", "hoge"));
+test('array("a" => 1, "b" => 2))', array("a" => 1, "b" => 2), 'Obj', new Obj(1, 2, null));
+test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), 'Obj', new Obj(null, null, null, array("one" => 1, "two" => 2)));
+test('array("" => "empty")', array("" => "empty"), 'Obj');
+
+test('array("a" => 1, "b" => 2, 3))', array("a" => 1, "b" => 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(3, "a" => 1, "b" => 2))', array(3, "a" => 1, "b" => 2), 'Obj', new Obj(1, 2, 3));
+test('array("a" => 1, 3, "b" => 2))', array("a" => 1, 3, "b" => 2), 'Obj', new Obj(1, 2, 3));
+
+$a = array('foo');
+test('array($a, $a)', array($a, $a), 'Obj', new Obj($a, $a, null));
+test('array(&$a, &$a)', array(&$a, &$a), 'Obj', new Obj($a, $a, null));
+
+test('array(&$a, $a)', array($a, &$a), 'Obj', new Obj($a, $a, null));
+test('array(&$a, $a)', array(&$a, $a), 'Obj', new Obj($a, $a, null));
+
+$a = array(
+    'a' => array(
+        'b' => 'c',
+        'd' => 'e'
+        ),
+    'f' => array(
+        'g' => 'h'
+        )
+    );
+test('array', $a, 'Obj', new Obj(null, null, null, $a));
+
+$o = new Obj(1, 2, 3);
+test('object', $o, 'Obj', new Obj(1, 2, 3));
+
+class Obj2 {
+    public $A;
+    protected $B;
+    private $C;
+
+    function __construct($a, $b, $c) {
+        $this->A = $a;
+        $this->B = $b;
+        $this->C = $c;
+    }
+}
+
+$o = new Obj2(1, 2, 3);
+test('object', $o, 'Obj', new Obj($o));
+
+$o1 = new Obj2(1, 2, 3);
+$o2 = new Obj2(4, 5, 6);
+test('object', array($o1, $o2), 'Obj', new Obj($o1, $o2));
+
+$o = new Obj2(1, 2, 3);
+test('object', array(&$o, &$o), 'Obj', new Obj($o, $o));
+
+$o = new Obj2(1, 2, 3);
+test('object', array(&$o, $o), 'Obj', new Obj($o, $o));
+
+$o = new Obj2(1, 2, 3);
+test('object', array($o, &$o), 'Obj', new Obj($o, $o));
+
+--EXPECTF--
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(true)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(false)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(0)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(100000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-100000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  float(123.456)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(0) ""
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(6) "foobar"
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  array(3) {
+    [0]=>
+    int(7)
+    [1]=>
+    int(8)
+    [2]=>
+    int(9)
+  }
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+  [3]=>
+  int(4)
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(3) "foo"
+  [%r"?b"?:protected"?%r]=>
+  string(6) "foobar"
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  string(4) "hoge"
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (5) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  ["one"]=>
+  int(1)
+  ["two"]=>
+  int(2)
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  [""]=>
+  string(5) "empty"
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  ["f"]=>
+  array(1) {
+    ["g"]=>
+    string(1) "h"
+  }
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(4)
+    [%r"?B"?:protected"?%r]=>
+    int(5)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(6)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
diff --git a/php/tests/094b.phpt b/php/tests/094b.phpt
new file mode 100644
index 00000000..5d6ade54
--- /dev/null
+++ b/php/tests/094b.phpt
@@ -0,0 +1,566 @@
+--TEST--
+unpack of object converter : class unpack (string)
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
+    echo "skip tests in PHP 5.1 or older";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack'))
+{
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+error_reporting(0);
+
+function test($type, $variable, $object, $result = null)
+{
+    $msgpack = new MessagePack();
+
+    $serialized = $msgpack->pack($variable);
+    $unserialized = $msgpack->unpack($serialized, $object);
+
+    var_dump($unserialized);
+    if ($result)
+    {
+        echo $unserialized == $result ? 'OK' : 'ERROR', PHP_EOL;
+    }
+    else
+    {
+        echo 'SKIP', PHP_EOL;
+    }
+}
+
+class Obj
+{
+    public $a;
+    protected $b;
+    private $c;
+
+    public function __construct($a = null, $b = null, $c = null, $d = null)
+    {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+        if (is_array($d))
+        {
+            foreach ($d as $key => $val)
+            {
+                $this->{$key} = $val;
+            }
+        }
+    }
+}
+
+test('null', null, 'Obj', new Obj(null, null, null));
+
+test('bool: true', true, 'Obj', new Obj(true, null, null));
+test('bool: false', false, 'Obj', new Obj(false, null, null));
+
+test('zero: 0', 0, 'Obj', new Obj(0, null, null));
+test('small: 1', 1, 'Obj', new Obj(1, null, null));
+test('small: -1', -1, 'Obj', new Obj(-1, null, null));
+test('medium: 1000', 1000, 'Obj', new Obj(1000, null, null));
+test('medium: -1000', -1000, 'Obj', new Obj(-1000, null, null));
+test('large: 100000', 100000, 'Obj', new Obj(100000, null, null));
+test('large: -100000', -100000, 'Obj', new Obj(-100000, null, null));
+
+test('double: 123.456', 123.456, 'Obj', new Obj(123.456, null, null));
+
+test('empty: ""', "", 'Obj', new Obj("", null, null));
+test('string: "foobar"', "foobar", 'Obj', new Obj("foobar", null, null));
+
+test('array: empty', array(), 'Obj', new Obj(null, null, null));
+test('array(1, 2, 3)', array(1, 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), 'Obj', new Obj(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)));
+test('array(1, 2, 3, 4)', array(1, 2, 3, 4), 'Obj');
+
+test('array("foo", "foobar", "foohoge")', array("foo", "foobar", "hoge"), 'Obj', new Obj("foo", "foobar", "hoge"));
+test('array("a" => 1, "b" => 2))', array("a" => 1, "b" => 2), 'Obj', new Obj(1, 2, null));
+test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), 'Obj', new Obj(null, null, null, array("one" => 1, "two" => 2)));
+
+test('array("a" => 1, "b" => 2, 3))', array("a" => 1, "b" => 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(3, "a" => 1, "b" => 2))', array(3, "a" => 1, "b" => 2), 'Obj', new Obj(1, 2, 3));
+test('array("a" => 1, 3, "b" => 2))', array("a" => 1, 3, "b" => 2), 'Obj', new Obj(1, 2, 3));
+
+$a = array('foo');
+test('array($a, $a)', array($a, $a), 'Obj', new Obj($a, $a, null));
+test('array(&$a, &$a)', array(&$a, &$a), 'Obj', new Obj($a, $a, null));
+
+test('array(&$a, $a)', array($a, &$a), 'Obj', new Obj($a, $a, null));
+test('array(&$a, $a)', array(&$a, $a), 'Obj', new Obj($a, $a, null));
+
+$a = array(
+    'a' => array(
+        'b' => 'c',
+        'd' => 'e'
+        ),
+    'f' => array(
+        'g' => 'h'
+        )
+    );
+test('array', $a, 'Obj', new Obj(null, null, null, $a));
+
+$o = new Obj(1, 2, 3);
+test('object', $o, 'Obj', new Obj(1, 2, 3));
+
+class Obj2 {
+    public $A;
+    protected $B;
+    private $C;
+
+    function __construct($a, $b, $c) {
+        $this->A = $a;
+        $this->B = $b;
+        $this->C = $c;
+    }
+}
+
+$o = new Obj2(1, 2, 3);
+test('object', $o, 'Obj', new Obj($o));
+
+$o1 = new Obj2(1, 2, 3);
+$o2 = new Obj2(4, 5, 6);
+test('object', array($o1, $o2), 'Obj', new Obj($o1, $o2));
+
+$o = new Obj2(1, 2, 3);
+test('object', array(&$o, &$o), 'Obj', new Obj($o, $o));
+
+$o = new Obj2(1, 2, 3);
+test('object', array(&$o, $o), 'Obj', new Obj($o, $o));
+
+$o = new Obj2(1, 2, 3);
+test('object', array($o, &$o), 'Obj', new Obj($o, $o));
+
+--EXPECTF--
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(true)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(false)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(0)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(100000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-100000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  float(123.456)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(0) ""
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(6) "foobar"
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  ["c:private"]=>
+  array(3) {
+    [0]=>
+    int(7)
+    [1]=>
+    int(8)
+    [2]=>
+    int(9)
+  }
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+  [3]=>
+  int(4)
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(3) "foo"
+  ["b:protected"]=>
+  string(6) "foobar"
+  ["c:private"]=>
+  string(4) "hoge"
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (5) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+  ["one"]=>
+  int(1)
+  ["two"]=>
+  int(2)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+  ["f"]=>
+  array(1) {
+    ["g"]=>
+    string(1) "h"
+  }
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(4)
+    ["B:protected"]=>
+    int(5)
+    ["C:private"]=>
+    int(6)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
diff --git a/php/tests/095.phpt b/php/tests/095.phpt
new file mode 100644
index 00000000..a0feafca
--- /dev/null
+++ b/php/tests/095.phpt
@@ -0,0 +1,580 @@
+--TEST--
+unpack of object converter : class unpack (object)
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.2.0') < 0) {
+    echo "skip tests in PHP 5.2 or newer";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack'))
+{
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+error_reporting(0);
+
+function test($type, $variable, $object, $result = null)
+{
+    $msgpack = new MessagePack();
+
+    $serialized = $msgpack->pack($variable);
+    $unserialized = $msgpack->unpack($serialized, $object);
+
+    var_dump($unserialized);
+    if ($result)
+    {
+        echo $unserialized == $result ? 'OK' : 'ERROR', PHP_EOL;
+    }
+    else
+    {
+        echo 'SKIP', PHP_EOL;
+    }
+}
+
+class Obj
+{
+    public $a;
+    protected $b;
+    private $c;
+
+    public function __construct($a = null, $b = null, $c = null, $d = null)
+    {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+        if (is_array($d))
+        {
+            foreach ($d as $key => $val)
+            {
+                $this->{$key} = $val;
+            }
+        }
+    }
+}
+
+$object = new Obj();
+
+test('null', null, new Obj(), new Obj(null, null, null));
+
+test('bool: true', true, new Obj(), new Obj(true, null, null));
+test('bool: false', false, new Obj(), new Obj(false, null, null));
+
+test('zero: 0', 0, new Obj(), new Obj(0, null, null));
+test('small: 1', 1, new Obj(), new Obj(1, null, null));
+test('small: -1', -1, new Obj(), new Obj(-1, null, null));
+test('medium: 1000', 1000, new Obj(), new Obj(1000, null, null));
+test('medium: -1000', -1000, new Obj(), new Obj(-1000, null, null));
+test('large: 100000', 100000, new Obj(), new Obj(100000, null, null));
+test('large: -100000', -100000, new Obj(), new Obj(-100000, null, null));
+
+test('double: 123.456', 123.456, new Obj(), new Obj(123.456, null, null));
+
+test('empty: ""', "", new Obj(), new Obj("", null, null));
+test('string: "foobar"', "foobar", new Obj(), new Obj("foobar", null, null));
+
+test('array: empty', array(), new Obj(), new Obj(null, null, null));
+test('array(1, 2, 3)', array(1, 2, 3), new Obj(), new Obj(1, 2, 3));
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), new Obj(), new Obj(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)));
+test('array(1, 2, 3, 4)', array(1, 2, 3, 4), new Obj());
+
+test('array("foo", "foobar", "foohoge")', array("foo", "foobar", "hoge"), new Obj(), new Obj("foo", "foobar", "hoge"));
+test('array("a" => 1, "b" => 2))', array("a" => 1, "b" => 2), new Obj(), new Obj(1, 2, null));
+test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), new Obj(), new Obj(null, null, null, array("one" => 1, "two" => 2)));
+test('array("" => "empty")', array("" => "empty"), new Obj());
+
+test('array("a" => 1, "b" => 2, 3))', array("a" => 1, "b" => 2, 3), new Obj(), new Obj(1, 2, 3));
+test('array(3, "a" => 1, "b" => 2))', array(3, "a" => 1, "b" => 2), new Obj(), new Obj(1, 2, 3));
+test('array("a" => 1, 3, "b" => 2))', array("a" => 1, 3, "b" => 2), new Obj(), new Obj(1, 2, 3));
+
+$a = array('foo');
+test('array($a, $a)', array($a, $a), new Obj(), new Obj($a, $a, null));
+test('array(&$a, &$a)', array(&$a, &$a), new Obj(), new Obj($a, $a, null));
+
+test('array(&$a, $a)', array($a, &$a), new Obj(), new Obj($a, $a, null));
+test('array(&$a, $a)', array(&$a, $a), new Obj(), new Obj($a, $a, null));
+
+$a = array(
+    'a' => array(
+        'b' => 'c',
+        'd' => 'e'
+        ),
+    'f' => array(
+        'g' => 'h'
+        )
+    );
+test('array', $a, new Obj(), new Obj(null, null, null, $a));
+
+$o = new Obj(1, 2, 3);
+test('object', $o, new Obj(), new Obj(1, 2, 3));
+
+class Obj2 {
+    public $A;
+    protected $B;
+    private $C;
+
+    function __construct($a, $b, $c) {
+        $this->A = $a;
+        $this->B = $b;
+        $this->C = $c;
+    }
+}
+
+$o = new Obj2(1, 2, 3);
+test('object', $o, new Obj(), new Obj($o));
+
+$o1 = new Obj2(1, 2, 3);
+$o2 = new Obj2(4, 5, 6);
+test('object', array($o1, $o2), new Obj(), new Obj($o1, $o2));
+
+$o = new Obj2(1, 2, 3);
+test('object', array(&$o, &$o), new Obj(), new Obj($o, $o));
+
+$o = new Obj2(1, 2, 3);
+test('object', array(&$o, $o), new Obj(), new Obj($o, $o));
+
+$o = new Obj2(1, 2, 3);
+test('object', array($o, &$o), new Obj(), new Obj($o, $o));
+
+--EXPECTF--
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(true)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(false)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(0)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(100000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-100000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  float(123.456)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(0) ""
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(6) "foobar"
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  array(3) {
+    [0]=>
+    int(7)
+    [1]=>
+    int(8)
+    [2]=>
+    int(9)
+  }
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+  [3]=>
+  int(4)
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(3) "foo"
+  [%r"?b"?:protected"?%r]=>
+  string(6) "foobar"
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  string(4) "hoge"
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (5) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  ["one"]=>
+  int(1)
+  ["two"]=>
+  int(2)
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  [""]=>
+  string(5) "empty"
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  ["f"]=>
+  array(1) {
+    ["g"]=>
+    string(1) "h"
+  }
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(4)
+    [%r"?B"?:protected"?%r]=>
+    int(5)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(6)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
diff --git a/php/tests/095b.phpt b/php/tests/095b.phpt
new file mode 100644
index 00000000..6a10402b
--- /dev/null
+++ b/php/tests/095b.phpt
@@ -0,0 +1,566 @@
+--TEST--
+unpack of object converter : class unpack (object)
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
+    echo "skip tests in PHP 5.1 or older";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack'))
+{
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+error_reporting(0);
+
+function test($type, $variable, $object, $result = null)
+{
+    $msgpack = new MessagePack();
+
+    $serialized = $msgpack->pack($variable);
+    $unserialized = $msgpack->unpack($serialized, $object);
+
+    var_dump($unserialized);
+    if ($result)
+    {
+        echo $unserialized == $result ? 'OK' : 'ERROR', PHP_EOL;
+    }
+    else
+    {
+        echo 'SKIP', PHP_EOL;
+    }
+}
+
+class Obj
+{
+    public $a;
+    protected $b;
+    private $c;
+
+    public function __construct($a = null, $b = null, $c = null, $d = null)
+    {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+        if (is_array($d))
+        {
+            foreach ($d as $key => $val)
+            {
+                $this->{$key} = $val;
+            }
+        }
+    }
+}
+
+test('null', null, new Obj(), new Obj(null, null, null));
+
+test('bool: true', true, new Obj(), new Obj(true, null, null));
+test('bool: false', false, new Obj(), new Obj(false, null, null));
+
+test('zero: 0', 0, new Obj(), new Obj(0, null, null));
+test('small: 1', 1, new Obj(), new Obj(1, null, null));
+test('small: -1', -1, new Obj(), new Obj(-1, null, null));
+test('medium: 1000', 1000, new Obj(), new Obj(1000, null, null));
+test('medium: -1000', -1000, new Obj(), new Obj(-1000, null, null));
+test('large: 100000', 100000, new Obj(), new Obj(100000, null, null));
+test('large: -100000', -100000, new Obj(), new Obj(-100000, null, null));
+
+test('double: 123.456', 123.456, new Obj(), new Obj(123.456, null, null));
+
+test('empty: ""', "", new Obj(), new Obj("", null, null));
+test('string: "foobar"', "foobar", new Obj(), new Obj("foobar", null, null));
+
+test('array: empty', array(), new Obj(), new Obj(null, null, null));
+test('array(1, 2, 3)', array(1, 2, 3), new Obj(), new Obj(1, 2, 3));
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), new Obj(), new Obj(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)));
+test('array(1, 2, 3, 4)', array(1, 2, 3, 4), new Obj());
+
+test('array("foo", "foobar", "foohoge")', array("foo", "foobar", "hoge"), new Obj(), new Obj("foo", "foobar", "hoge"));
+test('array("a" => 1, "b" => 2))', array("a" => 1, "b" => 2), new Obj(), new Obj(1, 2, null));
+test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), new Obj(), new Obj(null, null, null, array("one" => 1, "two" => 2)));
+
+test('array("a" => 1, "b" => 2, 3))', array("a" => 1, "b" => 2, 3), new Obj(), new Obj(1, 2, 3));
+test('array(3, "a" => 1, "b" => 2))', array(3, "a" => 1, "b" => 2), new Obj(), new Obj(1, 2, 3));
+test('array("a" => 1, 3, "b" => 2))', array("a" => 1, 3, "b" => 2), new Obj(), new Obj(1, 2, 3));
+
+$a = array('foo');
+test('array($a, $a)', array($a, $a), new Obj(), new Obj($a, $a, null));
+test('array(&$a, &$a)', array(&$a, &$a), new Obj(), new Obj($a, $a, null));
+
+test('array(&$a, $a)', array($a, &$a), new Obj(), new Obj($a, $a, null));
+test('array(&$a, $a)', array(&$a, $a), new Obj(), new Obj($a, $a, null));
+
+$a = array(
+    'a' => array(
+        'b' => 'c',
+        'd' => 'e'
+        ),
+    'f' => array(
+        'g' => 'h'
+        )
+    );
+test('array', $a, new Obj(), new Obj(null, null, null, $a));
+
+$o = new Obj(1, 2, 3);
+test('object', $o, new Obj(), new Obj(1, 2, 3));
+
+class Obj2 {
+    public $A;
+    protected $B;
+    private $C;
+
+    function __construct($a, $b, $c) {
+        $this->A = $a;
+        $this->B = $b;
+        $this->C = $c;
+    }
+}
+
+$o = new Obj2(1, 2, 3);
+test('object', $o, new Obj(), new Obj($o));
+
+$o1 = new Obj2(1, 2, 3);
+$o2 = new Obj2(4, 5, 6);
+test('object', array($o1, $o2), new Obj(), new Obj($o1, $o2));
+
+$o = new Obj2(1, 2, 3);
+test('object', array(&$o, &$o), new Obj(), new Obj($o, $o));
+
+$o = new Obj2(1, 2, 3);
+test('object', array(&$o, $o), new Obj(), new Obj($o, $o));
+
+$o = new Obj2(1, 2, 3);
+test('object', array($o, &$o), new Obj(), new Obj($o, $o));
+
+--EXPECTF--
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(true)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(false)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(0)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(100000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-100000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  float(123.456)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(0) ""
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(6) "foobar"
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  ["c:private"]=>
+  array(3) {
+    [0]=>
+    int(7)
+    [1]=>
+    int(8)
+    [2]=>
+    int(9)
+  }
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+  [3]=>
+  int(4)
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(3) "foo"
+  ["b:protected"]=>
+  string(6) "foobar"
+  ["c:private"]=>
+  string(4) "hoge"
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (5) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+  ["one"]=>
+  int(1)
+  ["two"]=>
+  int(2)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+  ["f"]=>
+  array(1) {
+    ["g"]=>
+    string(1) "h"
+  }
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(4)
+    ["B:protected"]=>
+    int(5)
+    ["C:private"]=>
+    int(6)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
diff --git a/php/tests/096.phpt b/php/tests/096.phpt
new file mode 100644
index 00000000..06ff2152
--- /dev/null
+++ b/php/tests/096.phpt
@@ -0,0 +1,445 @@
+--TEST--
+unpack of object converter : class unpack (string: php_only=0)
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.2.0') < 0) {
+    echo "skip tests in PHP 5.2 or newer";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack'))
+{
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+error_reporting(0);
+ini_set('msgpack.php_only', 0);
+
+function test($type, $variable, $object, $result = null)
+{
+    $msgpack = new MessagePack();
+
+    $serialized = $msgpack->pack($variable);
+    $unserialized = $msgpack->unpack($serialized, $object);
+
+    var_dump($unserialized);
+    if ($result)
+    {
+        echo $unserialized == $result ? 'OK' : 'ERROR', PHP_EOL;
+    }
+    else
+    {
+        echo 'SKIP', PHP_EOL;
+    }
+}
+
+class Obj
+{
+    public $a;
+    protected $b;
+    private $c;
+
+    public function __construct($a = null, $b = null, $c = null, $d = null)
+    {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+        if (is_array($d))
+        {
+            foreach ($d as $key => $val)
+            {
+                $this->{$key} = $val;
+            }
+        }
+    }
+}
+
+test('null', null, 'Obj', new Obj(null, null, null));
+
+test('bool: true', true, 'Obj', new Obj(true, null, null));
+test('bool: false', false, 'Obj', new Obj(false, null, null));
+
+test('zero: 0', 0, 'Obj', new Obj(0, null, null));
+test('small: 1', 1, 'Obj', new Obj(1, null, null));
+test('small: -1', -1, 'Obj', new Obj(-1, null, null));
+test('medium: 1000', 1000, 'Obj', new Obj(1000, null, null));
+test('medium: -1000', -1000, 'Obj', new Obj(-1000, null, null));
+test('large: 100000', 100000, 'Obj', new Obj(100000, null, null));
+test('large: -100000', -100000, 'Obj', new Obj(-100000, null, null));
+
+test('double: 123.456', 123.456, 'Obj', new Obj(123.456, null, null));
+
+test('empty: ""', "", 'Obj', new Obj("", null, null));
+test('string: "foobar"', "foobar", 'Obj', new Obj("foobar", null, null));
+
+test('array: empty', array(), 'Obj', new Obj(null, null, null));
+test('array(1, 2, 3)', array(1, 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), 'Obj', new Obj(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)));
+test('array(1, 2, 3, 4)', array(1, 2, 3, 4), 'Obj');
+
+test('array("foo", "foobar", "foohoge")', array("foo", "foobar", "hoge"), 'Obj', new Obj("foo", "foobar", "hoge"));
+test('array("a" => 1, "b" => 2))', array("a" => 1, "b" => 2), 'Obj', new Obj(1, 2, null));
+test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), 'Obj', new Obj(null, null, null, array("one" => 1, "two" => 2)));
+test('array("" => "empty")', array("" => "empty"), 'Obj');
+
+test('array("a" => 1, "b" => 2, 3))', array("a" => 1, "b" => 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(3, "a" => 1, "b" => 2))', array(3, "a" => 1, "b" => 2), 'Obj', new Obj(1, 2, 3));
+test('array("a" => 1, 3, "b" => 2))', array("a" => 1, 3, "b" => 2), 'Obj', new Obj(1, 2, 3));
+
+$a = array('foo');
+test('array($a, $a)', array($a, $a), 'Obj', new Obj($a, $a, null));
+
+$a = array(
+    'a' => array(
+        'b' => 'c',
+        'd' => 'e'
+        ),
+    'f' => array(
+        'g' => 'h'
+        )
+    );
+test('array', $a, 'Obj', new Obj(null, null, null, $a));
+
+$o = new Obj(1, 2, 3);
+test('object', $o, 'Obj', new Obj(1, 2, 3));
+
+class Obj2 {
+    public $A;
+    protected $B;
+    private $C;
+
+    function __construct($a, $b, $c) {
+        $this->A = $a;
+        $this->B = $b;
+        $this->C = $c;
+    }
+}
+
+$o = new Obj2(1, 2, 3);
+test('object', $o, 'Obj', new Obj(1, 2, 3));
+
+$o1 = new Obj2(1, 2, 3);
+$o2 = new Obj2(4, 5, 6);
+test('object', array($o1, $o2), 'Obj', new Obj(array(1, 2, 3), array(4, 5, 6)));
+
+--EXPECTF--
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(true)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(false)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(0)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(100000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-100000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  float(123.456)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(0) ""
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(6) "foobar"
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  array(3) {
+    [0]=>
+    int(7)
+    [1]=>
+    int(8)
+    [2]=>
+    int(9)
+  }
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+  [3]=>
+  int(4)
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(3) "foo"
+  [%r"?b"?:protected"?%r]=>
+  string(6) "foobar"
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  string(4) "hoge"
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (5) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  ["one"]=>
+  int(1)
+  ["two"]=>
+  int(2)
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  [""]=>
+  string(5) "empty"
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  ["f"]=>
+  array(1) {
+    ["g"]=>
+    string(1) "h"
+  }
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
diff --git a/php/tests/096b.phpt b/php/tests/096b.phpt
new file mode 100644
index 00000000..8379614c
--- /dev/null
+++ b/php/tests/096b.phpt
@@ -0,0 +1,433 @@
+--TEST--
+unpack of object converter : class unpack (string: php_only=0)
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
+    echo "skip tests in PHP 5.1 or older";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack'))
+{
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+error_reporting(0);
+ini_set('msgpack.php_only', 0);
+
+function test($type, $variable, $object, $result = null)
+{
+    $msgpack = new MessagePack();
+
+    $serialized = $msgpack->pack($variable);
+    $unserialized = $msgpack->unpack($serialized, $object);
+
+    var_dump($unserialized);
+    if ($result)
+    {
+        echo $unserialized == $result ? 'OK' : 'ERROR', PHP_EOL;
+    }
+    else
+    {
+        echo 'SKIP', PHP_EOL;
+    }
+}
+
+class Obj
+{
+    public $a;
+    protected $b;
+    private $c;
+
+    public function __construct($a = null, $b = null, $c = null, $d = null)
+    {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+        if (is_array($d))
+        {
+            foreach ($d as $key => $val)
+            {
+                $this->{$key} = $val;
+            }
+        }
+    }
+}
+
+test('null', null, 'Obj', new Obj(null, null, null));
+
+test('bool: true', true, 'Obj', new Obj(true, null, null));
+test('bool: false', false, 'Obj', new Obj(false, null, null));
+
+test('zero: 0', 0, 'Obj', new Obj(0, null, null));
+test('small: 1', 1, 'Obj', new Obj(1, null, null));
+test('small: -1', -1, 'Obj', new Obj(-1, null, null));
+test('medium: 1000', 1000, 'Obj', new Obj(1000, null, null));
+test('medium: -1000', -1000, 'Obj', new Obj(-1000, null, null));
+test('large: 100000', 100000, 'Obj', new Obj(100000, null, null));
+test('large: -100000', -100000, 'Obj', new Obj(-100000, null, null));
+
+test('double: 123.456', 123.456, 'Obj', new Obj(123.456, null, null));
+
+test('empty: ""', "", 'Obj', new Obj("", null, null));
+test('string: "foobar"', "foobar", 'Obj', new Obj("foobar", null, null));
+
+test('array: empty', array(), 'Obj', new Obj(null, null, null));
+test('array(1, 2, 3)', array(1, 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), 'Obj', new Obj(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)));
+test('array(1, 2, 3, 4)', array(1, 2, 3, 4), 'Obj');
+
+test('array("foo", "foobar", "foohoge")', array("foo", "foobar", "hoge"), 'Obj', new Obj("foo", "foobar", "hoge"));
+test('array("a" => 1, "b" => 2))', array("a" => 1, "b" => 2), 'Obj', new Obj(1, 2, null));
+test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), 'Obj', new Obj(null, null, null, array("one" => 1, "two" => 2)));
+
+test('array("a" => 1, "b" => 2, 3))', array("a" => 1, "b" => 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(3, "a" => 1, "b" => 2))', array(3, "a" => 1, "b" => 2), 'Obj', new Obj(1, 2, 3));
+test('array("a" => 1, 3, "b" => 2))', array("a" => 1, 3, "b" => 2), 'Obj', new Obj(1, 2, 3));
+
+$a = array('foo');
+test('array($a, $a)', array($a, $a), 'Obj', new Obj($a, $a, null));
+
+$a = array(
+    'a' => array(
+        'b' => 'c',
+        'd' => 'e'
+        ),
+    'f' => array(
+        'g' => 'h'
+        )
+    );
+test('array', $a, 'Obj', new Obj(null, null, null, $a));
+
+$o = new Obj(1, 2, 3);
+test('object', $o, 'Obj', new Obj(1, 2, 3));
+
+class Obj2 {
+    public $A;
+    protected $B;
+    private $C;
+
+    function __construct($a, $b, $c) {
+        $this->A = $a;
+        $this->B = $b;
+        $this->C = $c;
+    }
+}
+
+$o = new Obj2(1, 2, 3);
+test('object', $o, 'Obj', new Obj(1, 2, 3));
+
+$o1 = new Obj2(1, 2, 3);
+$o2 = new Obj2(4, 5, 6);
+test('object', array($o1, $o2), 'Obj', new Obj(array(1, 2, 3), array(4, 5, 6)));
+
+--EXPECTF--
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(true)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(false)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(0)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(100000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-100000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  float(123.456)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(0) ""
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(6) "foobar"
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  ["c:private"]=>
+  array(3) {
+    [0]=>
+    int(7)
+    [1]=>
+    int(8)
+    [2]=>
+    int(9)
+  }
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+  [3]=>
+  int(4)
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(3) "foo"
+  ["b:protected"]=>
+  string(6) "foobar"
+  ["c:private"]=>
+  string(4) "hoge"
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (5) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+  ["one"]=>
+  int(1)
+  ["two"]=>
+  int(2)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+  ["f"]=>
+  array(1) {
+    ["g"]=>
+    string(1) "h"
+  }
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
diff --git a/php/tests/097.phpt b/php/tests/097.phpt
new file mode 100644
index 00000000..055a5e47
--- /dev/null
+++ b/php/tests/097.phpt
@@ -0,0 +1,447 @@
+--TEST--
+unpack of object converter : class unpack (object: php_only=0)
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.2.0') < 0) {
+    echo "skip tests in PHP 5.2 or newer";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack'))
+{
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+error_reporting(0);
+ini_set('msgpack.php_only', 0);
+
+function test($type, $variable, $object, $result = null)
+{
+    $msgpack = new MessagePack();
+
+    $serialized = $msgpack->pack($variable);
+    $unserialized = $msgpack->unpack($serialized, $object);
+
+    var_dump($unserialized);
+    if ($result)
+    {
+        echo $unserialized == $result ? 'OK' : 'ERROR', PHP_EOL;
+    }
+    else
+    {
+        echo 'SKIP', PHP_EOL;
+    }
+}
+
+class Obj
+{
+    public $a;
+    protected $b;
+    private $c;
+
+    public function __construct($a = null, $b = null, $c = null, $d = null)
+    {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+        if (is_array($d))
+        {
+            foreach ($d as $key => $val)
+            {
+                $this->{$key} = $val;
+            }
+        }
+    }
+}
+
+$object = new Obj();
+
+test('null', null, new Obj(), new Obj(null, null, null));
+
+test('bool: true', true, new Obj(), new Obj(true, null, null));
+test('bool: false', false, new Obj(), new Obj(false, null, null));
+
+test('zero: 0', 0, new Obj(), new Obj(0, null, null));
+test('small: 1', 1, new Obj(), new Obj(1, null, null));
+test('small: -1', -1, new Obj(), new Obj(-1, null, null));
+test('medium: 1000', 1000, new Obj(), new Obj(1000, null, null));
+test('medium: -1000', -1000, new Obj(), new Obj(-1000, null, null));
+test('large: 100000', 100000, new Obj(), new Obj(100000, null, null));
+test('large: -100000', -100000, new Obj(), new Obj(-100000, null, null));
+
+test('double: 123.456', 123.456, new Obj(), new Obj(123.456, null, null));
+
+test('empty: ""', "", new Obj(), new Obj("", null, null));
+test('string: "foobar"', "foobar", new Obj(), new Obj("foobar", null, null));
+
+test('array: empty', array(), new Obj(), new Obj(null, null, null));
+test('array(1, 2, 3)', array(1, 2, 3), new Obj(), new Obj(1, 2, 3));
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), new Obj(), new Obj(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)));
+test('array(1, 2, 3, 4)', array(1, 2, 3, 4), new Obj());
+
+test('array("foo", "foobar", "foohoge")', array("foo", "foobar", "hoge"), new Obj(), new Obj("foo", "foobar", "hoge"));
+test('array("a" => 1, "b" => 2))', array("a" => 1, "b" => 2), new Obj(), new Obj(1, 2, null));
+test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), new Obj(), new Obj(null, null, null, array("one" => 1, "two" => 2)));
+test('array("" => "empty")', array("" => "empty"), new Obj());
+
+test('array("a" => 1, "b" => 2, 3))', array("a" => 1, "b" => 2, 3), new Obj(), new Obj(1, 2, 3));
+test('array(3, "a" => 1, "b" => 2))', array(3, "a" => 1, "b" => 2), new Obj(), new Obj(1, 2, 3));
+test('array("a" => 1, 3, "b" => 2))', array("a" => 1, 3, "b" => 2), new Obj(), new Obj(1, 2, 3));
+
+$a = array('foo');
+test('array($a, $a)', array($a, $a), new Obj(), new Obj($a, $a, null));
+
+$a = array(
+    'a' => array(
+        'b' => 'c',
+        'd' => 'e'
+        ),
+    'f' => array(
+        'g' => 'h'
+        )
+    );
+test('array', $a, new Obj(), new Obj(null, null, null, $a));
+
+$o = new Obj(1, 2, 3);
+test('object', $o, new Obj(), new Obj(1, 2, 3));
+
+class Obj2 {
+    public $A;
+    protected $B;
+    private $C;
+
+    function __construct($a, $b, $c) {
+        $this->A = $a;
+        $this->B = $b;
+        $this->C = $c;
+    }
+}
+
+$o = new Obj2(1, 2, 3);
+test('object', $o, new Obj(), new Obj(1, 2, 3));
+
+$o1 = new Obj2(1, 2, 3);
+$o2 = new Obj2(4, 5, 6);
+test('object', array($o1, $o2), new Obj(), new Obj(array(1, 2, 3), array(4, 5, 6)));
+
+--EXPECTF--
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(true)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(false)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(0)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(100000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-100000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  float(123.456)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(0) ""
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(6) "foobar"
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  array(3) {
+    [0]=>
+    int(7)
+    [1]=>
+    int(8)
+    [2]=>
+    int(9)
+  }
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+  [3]=>
+  int(4)
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(3) "foo"
+  [%r"?b"?:protected"?%r]=>
+  string(6) "foobar"
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  string(4) "hoge"
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (5) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  ["one"]=>
+  int(1)
+  ["two"]=>
+  int(2)
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  [""]=>
+  string(5) "empty"
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  ["f"]=>
+  array(1) {
+    ["g"]=>
+    string(1) "h"
+  }
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
diff --git a/php/tests/097b.phpt b/php/tests/097b.phpt
new file mode 100644
index 00000000..8f516fae
--- /dev/null
+++ b/php/tests/097b.phpt
@@ -0,0 +1,433 @@
+--TEST--
+unpack of object converter : class unpack (object: php_only=0)
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
+    echo "skip tests in PHP 5.1 or older";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack'))
+{
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+error_reporting(0);
+ini_set('msgpack.php_only', 0);
+
+function test($type, $variable, $object, $result = null)
+{
+    $msgpack = new MessagePack();
+
+    $serialized = $msgpack->pack($variable);
+    $unserialized = $msgpack->unpack($serialized, $object);
+
+    var_dump($unserialized);
+    if ($result)
+    {
+        echo $unserialized == $result ? 'OK' : 'ERROR', PHP_EOL;
+    }
+    else
+    {
+        echo 'SKIP', PHP_EOL;
+    }
+}
+
+class Obj
+{
+    public $a;
+    protected $b;
+    private $c;
+
+    public function __construct($a = null, $b = null, $c = null, $d = null)
+    {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+        if (is_array($d))
+        {
+            foreach ($d as $key => $val)
+            {
+                $this->{$key} = $val;
+            }
+        }
+    }
+}
+
+test('null', null, new Obj(), new Obj(null, null, null));
+
+test('bool: true', true, new Obj(), new Obj(true, null, null));
+test('bool: false', false, new Obj(), new Obj(false, null, null));
+
+test('zero: 0', 0, new Obj(), new Obj(0, null, null));
+test('small: 1', 1, new Obj(), new Obj(1, null, null));
+test('small: -1', -1, new Obj(), new Obj(-1, null, null));
+test('medium: 1000', 1000, new Obj(), new Obj(1000, null, null));
+test('medium: -1000', -1000, new Obj(), new Obj(-1000, null, null));
+test('large: 100000', 100000, new Obj(), new Obj(100000, null, null));
+test('large: -100000', -100000, new Obj(), new Obj(-100000, null, null));
+
+test('double: 123.456', 123.456, new Obj(), new Obj(123.456, null, null));
+
+test('empty: ""', "", new Obj(), new Obj("", null, null));
+test('string: "foobar"', "foobar", new Obj(), new Obj("foobar", null, null));
+
+test('array: empty', array(), new Obj(), new Obj(null, null, null));
+test('array(1, 2, 3)', array(1, 2, 3), new Obj(), new Obj(1, 2, 3));
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), new Obj(), new Obj(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)));
+test('array(1, 2, 3, 4)', array(1, 2, 3, 4), new Obj());
+
+test('array("foo", "foobar", "foohoge")', array("foo", "foobar", "hoge"), new Obj(), new Obj("foo", "foobar", "hoge"));
+test('array("a" => 1, "b" => 2))', array("a" => 1, "b" => 2), new Obj(), new Obj(1, 2, null));
+test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), new Obj(), new Obj(null, null, null, array("one" => 1, "two" => 2)));
+
+test('array("a" => 1, "b" => 2, 3))', array("a" => 1, "b" => 2, 3), new Obj(), new Obj(1, 2, 3));
+test('array(3, "a" => 1, "b" => 2))', array(3, "a" => 1, "b" => 2), new Obj(), new Obj(1, 2, 3));
+test('array("a" => 1, 3, "b" => 2))', array("a" => 1, 3, "b" => 2), new Obj(), new Obj(1, 2, 3));
+
+$a = array('foo');
+test('array($a, $a)', array($a, $a), new Obj(), new Obj($a, $a, null));
+
+$a = array(
+    'a' => array(
+        'b' => 'c',
+        'd' => 'e'
+        ),
+    'f' => array(
+        'g' => 'h'
+        )
+    );
+test('array', $a, new Obj(), new Obj(null, null, null, $a));
+
+$o = new Obj(1, 2, 3);
+test('object', $o, new Obj(), new Obj(1, 2, 3));
+
+class Obj2 {
+    public $A;
+    protected $B;
+    private $C;
+
+    function __construct($a, $b, $c) {
+        $this->A = $a;
+        $this->B = $b;
+        $this->C = $c;
+    }
+}
+
+$o = new Obj2(1, 2, 3);
+test('object', $o, new Obj(), new Obj(1, 2, 3));
+
+$o1 = new Obj2(1, 2, 3);
+$o2 = new Obj2(4, 5, 6);
+test('object', array($o1, $o2), new Obj(), new Obj(array(1, 2, 3), array(4, 5, 6)));
+
+--EXPECTF--
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(true)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(false)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(0)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(100000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-100000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  float(123.456)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(0) ""
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(6) "foobar"
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  ["c:private"]=>
+  array(3) {
+    [0]=>
+    int(7)
+    [1]=>
+    int(8)
+    [2]=>
+    int(9)
+  }
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+  [3]=>
+  int(4)
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(3) "foo"
+  ["b:protected"]=>
+  string(6) "foobar"
+  ["c:private"]=>
+  string(4) "hoge"
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (5) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+  ["one"]=>
+  int(1)
+  ["two"]=>
+  int(2)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+  ["f"]=>
+  array(1) {
+    ["g"]=>
+    string(1) "h"
+  }
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
diff --git a/php/tests/098.phpt b/php/tests/098.phpt
new file mode 100644
index 00000000..23f34273
--- /dev/null
+++ b/php/tests/098.phpt
@@ -0,0 +1,445 @@
+--TEST--
+unpack of object converter: class unpack (string: OPT_PHPONLY=false)
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.2.0') < 0) {
+    echo "skip tests in PHP 5.2 or newer";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack'))
+{
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+error_reporting(0);
+
+function test($type, $variable, $object, $result = null)
+{
+    $msgpack = new MessagePack();
+    $msgpack->setOption(MessagePack::OPT_PHPONLY, false);
+
+    $serialized = $msgpack->pack($variable);
+    $unserialized = $msgpack->unpack($serialized, $object);
+
+    var_dump($unserialized);
+    if ($result)
+    {
+        echo $unserialized == $result ? 'OK' : 'ERROR', PHP_EOL;
+    }
+    else
+    {
+        echo 'SKIP', PHP_EOL;
+    }
+}
+
+class Obj
+{
+    public $a;
+    protected $b;
+    private $c;
+
+    public function __construct($a = null, $b = null, $c = null, $d = null)
+    {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+        if (is_array($d))
+        {
+            foreach ($d as $key => $val)
+            {
+                $this->{$key} = $val;
+            }
+        }
+    }
+}
+
+test('null', null, 'Obj', new Obj(null, null, null));
+
+test('bool: true', true, 'Obj', new Obj(true, null, null));
+test('bool: false', false, 'Obj', new Obj(false, null, null));
+
+test('zero: 0', 0, 'Obj', new Obj(0, null, null));
+test('small: 1', 1, 'Obj', new Obj(1, null, null));
+test('small: -1', -1, 'Obj', new Obj(-1, null, null));
+test('medium: 1000', 1000, 'Obj', new Obj(1000, null, null));
+test('medium: -1000', -1000, 'Obj', new Obj(-1000, null, null));
+test('large: 100000', 100000, 'Obj', new Obj(100000, null, null));
+test('large: -100000', -100000, 'Obj', new Obj(-100000, null, null));
+
+test('double: 123.456', 123.456, 'Obj', new Obj(123.456, null, null));
+
+test('empty: ""', "", 'Obj', new Obj("", null, null));
+test('string: "foobar"', "foobar", 'Obj', new Obj("foobar", null, null));
+
+test('array: empty', array(), 'Obj', new Obj(null, null, null));
+test('array(1, 2, 3)', array(1, 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), 'Obj', new Obj(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)));
+test('array(1, 2, 3, 4)', array(1, 2, 3, 4), 'Obj');
+
+test('array("foo", "foobar", "foohoge")', array("foo", "foobar", "hoge"), 'Obj', new Obj("foo", "foobar", "hoge"));
+test('array("a" => 1, "b" => 2))', array("a" => 1, "b" => 2), 'Obj', new Obj(1, 2, null));
+test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), 'Obj', new Obj(null, null, null, array("one" => 1, "two" => 2)));
+test('array("" => "empty")', array("" => "empty"), 'Obj');
+
+test('array("a" => 1, "b" => 2, 3))', array("a" => 1, "b" => 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(3, "a" => 1, "b" => 2))', array(3, "a" => 1, "b" => 2), 'Obj', new Obj(1, 2, 3));
+test('array("a" => 1, 3, "b" => 2))', array("a" => 1, 3, "b" => 2), 'Obj', new Obj(1, 2, 3));
+
+$a = array('foo');
+test('array($a, $a)', array($a, $a), 'Obj', new Obj($a, $a, null));
+
+$a = array(
+    'a' => array(
+        'b' => 'c',
+        'd' => 'e'
+        ),
+    'f' => array(
+        'g' => 'h'
+        )
+    );
+test('array', $a, 'Obj', new Obj(null, null, null, $a));
+
+$o = new Obj(1, 2, 3);
+test('object', $o, 'Obj', new Obj(1, 2, 3));
+
+class Obj2 {
+    public $A;
+    protected $B;
+    private $C;
+
+    function __construct($a, $b, $c) {
+        $this->A = $a;
+        $this->B = $b;
+        $this->C = $c;
+    }
+}
+
+$o = new Obj2(1, 2, 3);
+test('object', $o, 'Obj', new Obj(1, 2, 3));
+
+$o1 = new Obj2(1, 2, 3);
+$o2 = new Obj2(4, 5, 6);
+test('object', array($o1, $o2), 'Obj', new Obj(array(1, 2, 3), array(4, 5, 6)));
+
+--EXPECTF--
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(true)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(false)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(0)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(100000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-100000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  float(123.456)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(0) ""
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(6) "foobar"
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  array(3) {
+    [0]=>
+    int(7)
+    [1]=>
+    int(8)
+    [2]=>
+    int(9)
+  }
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+  [3]=>
+  int(4)
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(3) "foo"
+  [%r"?b"?:protected"?%r]=>
+  string(6) "foobar"
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  string(4) "hoge"
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (5) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  ["one"]=>
+  int(1)
+  ["two"]=>
+  int(2)
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  [""]=>
+  string(5) "empty"
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  ["f"]=>
+  array(1) {
+    ["g"]=>
+    string(1) "h"
+  }
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
diff --git a/php/tests/098b.phpt b/php/tests/098b.phpt
new file mode 100644
index 00000000..1e328872
--- /dev/null
+++ b/php/tests/098b.phpt
@@ -0,0 +1,440 @@
+--TEST--
+unpack of object converter: class unpack (string: OPT_PHPONLY=false)
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
+    echo "skip tests in PHP 5.1 or older";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack'))
+{
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+error_reporting(0);
+
+function test($type, $variable, $object, $result = null)
+{
+    $msgpack = new MessagePack();
+    if (version_compare(PHP_VERSION, '5.1.0') <= 0)
+    {
+        $msgpack->setOption(MESSAGEPACK_OPT_PHPONLY, false);
+    }
+    else
+    {
+        $msgpack->setOption(MessagePack::OPT_PHPONLY, false);
+    }
+
+    $serialized = $msgpack->pack($variable);
+    $unserialized = $msgpack->unpack($serialized, $object);
+
+    var_dump($unserialized);
+    if ($result)
+    {
+        echo $unserialized == $result ? 'OK' : 'ERROR', PHP_EOL;
+    }
+    else
+    {
+        echo 'SKIP', PHP_EOL;
+    }
+}
+
+class Obj
+{
+    public $a;
+    protected $b;
+    private $c;
+
+    public function __construct($a = null, $b = null, $c = null, $d = null)
+    {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+        if (is_array($d))
+        {
+            foreach ($d as $key => $val)
+            {
+                $this->{$key} = $val;
+            }
+        }
+    }
+}
+
+test('null', null, 'Obj', new Obj(null, null, null));
+
+test('bool: true', true, 'Obj', new Obj(true, null, null));
+test('bool: false', false, 'Obj', new Obj(false, null, null));
+
+test('zero: 0', 0, 'Obj', new Obj(0, null, null));
+test('small: 1', 1, 'Obj', new Obj(1, null, null));
+test('small: -1', -1, 'Obj', new Obj(-1, null, null));
+test('medium: 1000', 1000, 'Obj', new Obj(1000, null, null));
+test('medium: -1000', -1000, 'Obj', new Obj(-1000, null, null));
+test('large: 100000', 100000, 'Obj', new Obj(100000, null, null));
+test('large: -100000', -100000, 'Obj', new Obj(-100000, null, null));
+
+test('double: 123.456', 123.456, 'Obj', new Obj(123.456, null, null));
+
+test('empty: ""', "", 'Obj', new Obj("", null, null));
+test('string: "foobar"', "foobar", 'Obj', new Obj("foobar", null, null));
+
+test('array: empty', array(), 'Obj', new Obj(null, null, null));
+test('array(1, 2, 3)', array(1, 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), 'Obj', new Obj(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)));
+test('array(1, 2, 3, 4)', array(1, 2, 3, 4), 'Obj');
+
+test('array("foo", "foobar", "foohoge")', array("foo", "foobar", "hoge"), 'Obj', new Obj("foo", "foobar", "hoge"));
+test('array("a" => 1, "b" => 2))', array("a" => 1, "b" => 2), 'Obj', new Obj(1, 2, null));
+test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), 'Obj', new Obj(null, null, null, array("one" => 1, "two" => 2)));
+
+test('array("a" => 1, "b" => 2, 3))', array("a" => 1, "b" => 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(3, "a" => 1, "b" => 2))', array(3, "a" => 1, "b" => 2), 'Obj', new Obj(1, 2, 3));
+test('array("a" => 1, 3, "b" => 2))', array("a" => 1, 3, "b" => 2), 'Obj', new Obj(1, 2, 3));
+
+$a = array('foo');
+test('array($a, $a)', array($a, $a), 'Obj', new Obj($a, $a, null));
+
+$a = array(
+    'a' => array(
+        'b' => 'c',
+        'd' => 'e'
+        ),
+    'f' => array(
+        'g' => 'h'
+        )
+    );
+test('array', $a, 'Obj', new Obj(null, null, null, $a));
+
+$o = new Obj(1, 2, 3);
+test('object', $o, 'Obj', new Obj(1, 2, 3));
+
+class Obj2 {
+    public $A;
+    protected $B;
+    private $C;
+
+    function __construct($a, $b, $c) {
+        $this->A = $a;
+        $this->B = $b;
+        $this->C = $c;
+    }
+}
+
+$o = new Obj2(1, 2, 3);
+test('object', $o, 'Obj', new Obj(1, 2, 3));
+
+$o1 = new Obj2(1, 2, 3);
+$o2 = new Obj2(4, 5, 6);
+test('object', array($o1, $o2), 'Obj', new Obj(array(1, 2, 3), array(4, 5, 6)));
+
+--EXPECTF--
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(true)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(false)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(0)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(100000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-100000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  float(123.456)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(0) ""
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(6) "foobar"
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  ["c:private"]=>
+  array(3) {
+    [0]=>
+    int(7)
+    [1]=>
+    int(8)
+    [2]=>
+    int(9)
+  }
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+  [3]=>
+  int(4)
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(3) "foo"
+  ["b:protected"]=>
+  string(6) "foobar"
+  ["c:private"]=>
+  string(4) "hoge"
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (5) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+  ["one"]=>
+  int(1)
+  ["two"]=>
+  int(2)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+  ["f"]=>
+  array(1) {
+    ["g"]=>
+    string(1) "h"
+  }
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
diff --git a/php/tests/099.phpt b/php/tests/099.phpt
new file mode 100644
index 00000000..01da2ac3
--- /dev/null
+++ b/php/tests/099.phpt
@@ -0,0 +1,584 @@
+--TEST--
+unpack of object converter : class unpacker (string)
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.2.0') < 0) {
+    echo "skip tests in PHP 5.2 or newer";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack'))
+{
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+error_reporting(0);
+
+function test($type, $variable, $object, $result = null)
+{
+    $msgpack = new MessagePack();
+
+    $serialized = $msgpack->pack($variable);
+
+    $unserialized = null;
+    $unpacker = $msgpack->unpacker();
+    if ($unpacker->execute($serialized))
+    {
+        $unserialized = $unpacker->data($object);
+    }
+
+    var_dump($unserialized);
+    if ($result)
+    {
+        echo $unserialized == $result ? 'OK' : 'ERROR', PHP_EOL;
+    }
+    else
+    {
+        echo 'SKIP', PHP_EOL;
+    }
+}
+
+class Obj
+{
+    public $a;
+    protected $b;
+    private $c;
+
+    public function __construct($a = null, $b = null, $c = null, $d = null)
+    {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+        if (is_array($d))
+        {
+            foreach ($d as $key => $val)
+            {
+                $this->{$key} = $val;
+            }
+        }
+    }
+}
+
+test('null', null, 'Obj', new Obj(null, null, null));
+
+test('bool: true', true, 'Obj', new Obj(true, null, null));
+test('bool: false', false, 'Obj', new Obj(false, null, null));
+
+test('zero: 0', 0, 'Obj', new Obj(0, null, null));
+test('small: 1', 1, 'Obj', new Obj(1, null, null));
+test('small: -1', -1, 'Obj', new Obj(-1, null, null));
+test('medium: 1000', 1000, 'Obj', new Obj(1000, null, null));
+test('medium: -1000', -1000, 'Obj', new Obj(-1000, null, null));
+test('large: 100000', 100000, 'Obj', new Obj(100000, null, null));
+test('large: -100000', -100000, 'Obj', new Obj(-100000, null, null));
+
+test('double: 123.456', 123.456, 'Obj', new Obj(123.456, null, null));
+
+test('empty: ""', "", 'Obj', new Obj("", null, null));
+test('string: "foobar"', "foobar", 'Obj', new Obj("foobar", null, null));
+
+test('array: empty', array(), 'Obj', new Obj(null, null, null));
+test('array(1, 2, 3)', array(1, 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), 'Obj', new Obj(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)));
+test('array(1, 2, 3, 4)', array(1, 2, 3, 4), 'Obj');
+
+test('array("foo", "foobar", "foohoge")', array("foo", "foobar", "hoge"), 'Obj', new Obj("foo", "foobar", "hoge"));
+test('array("a" => 1, "b" => 2))', array("a" => 1, "b" => 2), 'Obj', new Obj(1, 2, null));
+test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), 'Obj', new Obj(null, null, null, array("one" => 1, "two" => 2)));
+test('array("" => "empty")', array("" => "empty"), 'Obj');
+
+test('array("a" => 1, "b" => 2, 3))', array("a" => 1, "b" => 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(3, "a" => 1, "b" => 2))', array(3, "a" => 1, "b" => 2), 'Obj', new Obj(1, 2, 3));
+test('array("a" => 1, 3, "b" => 2))', array("a" => 1, 3, "b" => 2), 'Obj', new Obj(1, 2, 3));
+
+$a = array('foo');
+test('array($a, $a)', array($a, $a), 'Obj', new Obj($a, $a, null));
+test('array(&$a, &$a)', array(&$a, &$a), 'Obj', new Obj($a, $a, null));
+
+test('array(&$a, $a)', array($a, &$a), 'Obj', new Obj($a, $a, null));
+test('array(&$a, $a)', array(&$a, $a), 'Obj', new Obj($a, $a, null));
+
+$a = array(
+    'a' => array(
+        'b' => 'c',
+        'd' => 'e'
+        ),
+    'f' => array(
+        'g' => 'h'
+        )
+    );
+test('array', $a, 'Obj', new Obj(null, null, null, $a));
+
+$o = new Obj(1, 2, 3);
+test('object', $o, 'Obj', new Obj(1, 2, 3));
+
+class Obj2 {
+    public $A;
+    protected $B;
+    private $C;
+
+    function __construct($a, $b, $c) {
+        $this->A = $a;
+        $this->B = $b;
+        $this->C = $c;
+    }
+}
+
+$o = new Obj2(1, 2, 3);
+test('object', $o, 'Obj', new Obj($o));
+
+$o1 = new Obj2(1, 2, 3);
+$o2 = new Obj2(4, 5, 6);
+test('object', array($o1, $o2), 'Obj', new Obj($o1, $o2));
+
+$o = new Obj2(1, 2, 3);
+test('object', array(&$o, &$o), 'Obj', new Obj($o, $o));
+
+$o = new Obj2(1, 2, 3);
+test('object', array(&$o, $o), 'Obj', new Obj($o, $o));
+
+$o = new Obj2(1, 2, 3);
+test('object', array($o, &$o), 'Obj', new Obj($o, $o));
+
+--EXPECTF--
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(true)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(false)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(0)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(100000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-100000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  float(123.456)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(0) ""
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(6) "foobar"
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  array(3) {
+    [0]=>
+    int(7)
+    [1]=>
+    int(8)
+    [2]=>
+    int(9)
+  }
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+  [3]=>
+  int(4)
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(3) "foo"
+  [%r"?b"?:protected"?%r]=>
+  string(6) "foobar"
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  string(4) "hoge"
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (5) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  ["one"]=>
+  int(1)
+  ["two"]=>
+  int(2)
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  [""]=>
+  string(5) "empty"
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  ["f"]=>
+  array(1) {
+    ["g"]=>
+    string(1) "h"
+  }
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(4)
+    [%r"?B"?:protected"?%r]=>
+    int(5)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(6)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
diff --git a/php/tests/099b.phpt b/php/tests/099b.phpt
new file mode 100644
index 00000000..b44e90fd
--- /dev/null
+++ b/php/tests/099b.phpt
@@ -0,0 +1,572 @@
+--TEST--
+unpack of object converter : class unpacker (string)
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
+    echo "skip tests in PHP 5.1 or older";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack'))
+{
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+error_reporting(0);
+
+function test($type, $variable, $object, $result = null)
+{
+    $msgpack = new MessagePack();
+
+    $serialized = $msgpack->pack($variable);
+
+    $unserialized = null;
+    $unpacker = $msgpack->unpacker();
+    if ($unpacker->execute($serialized))
+    {
+        $unserialized = $unpacker->data($object);
+    }
+
+    var_dump($unserialized);
+    if ($result)
+    {
+        echo $unserialized == $result ? 'OK' : 'ERROR', PHP_EOL;
+    }
+    else
+    {
+        echo 'SKIP', PHP_EOL;
+    }
+}
+
+class Obj
+{
+    public $a;
+    protected $b;
+    private $c;
+
+    public function __construct($a = null, $b = null, $c = null, $d = null)
+    {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+        if (is_array($d))
+        {
+            foreach ($d as $key => $val)
+            {
+                $this->{$key} = $val;
+            }
+        }
+    }
+}
+
+test('null', null, 'Obj', new Obj(null, null, null));
+
+test('bool: true', true, 'Obj', new Obj(true, null, null));
+test('bool: false', false, 'Obj', new Obj(false, null, null));
+
+test('zero: 0', 0, 'Obj', new Obj(0, null, null));
+test('small: 1', 1, 'Obj', new Obj(1, null, null));
+test('small: -1', -1, 'Obj', new Obj(-1, null, null));
+test('medium: 1000', 1000, 'Obj', new Obj(1000, null, null));
+test('medium: -1000', -1000, 'Obj', new Obj(-1000, null, null));
+test('large: 100000', 100000, 'Obj', new Obj(100000, null, null));
+test('large: -100000', -100000, 'Obj', new Obj(-100000, null, null));
+
+test('double: 123.456', 123.456, 'Obj', new Obj(123.456, null, null));
+
+test('empty: ""', "", 'Obj', new Obj("", null, null));
+test('string: "foobar"', "foobar", 'Obj', new Obj("foobar", null, null));
+
+test('array: empty', array(), 'Obj', new Obj(null, null, null));
+test('array(1, 2, 3)', array(1, 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), 'Obj', new Obj(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)));
+test('array(1, 2, 3, 4)', array(1, 2, 3, 4), 'Obj');
+
+test('array("foo", "foobar", "foohoge")', array("foo", "foobar", "hoge"), 'Obj', new Obj("foo", "foobar", "hoge"));
+test('array("a" => 1, "b" => 2))', array("a" => 1, "b" => 2), 'Obj', new Obj(1, 2, null));
+test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), 'Obj', new Obj(null, null, null, array("one" => 1, "two" => 2)));
+
+test('array("a" => 1, "b" => 2, 3))', array("a" => 1, "b" => 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(3, "a" => 1, "b" => 2))', array(3, "a" => 1, "b" => 2), 'Obj', new Obj(1, 2, 3));
+test('array("a" => 1, 3, "b" => 2))', array("a" => 1, 3, "b" => 2), 'Obj', new Obj(1, 2, 3));
+
+$a = array('foo');
+test('array($a, $a)', array($a, $a), 'Obj', new Obj($a, $a, null));
+test('array(&$a, &$a)', array(&$a, &$a), 'Obj', new Obj($a, $a, null));
+
+test('array(&$a, $a)', array($a, &$a), 'Obj', new Obj($a, $a, null));
+test('array(&$a, $a)', array(&$a, $a), 'Obj', new Obj($a, $a, null));
+
+$a = array(
+    'a' => array(
+        'b' => 'c',
+        'd' => 'e'
+        ),
+    'f' => array(
+        'g' => 'h'
+        )
+    );
+test('array', $a, 'Obj', new Obj(null, null, null, $a));
+
+$o = new Obj(1, 2, 3);
+test('object', $o, 'Obj', new Obj(1, 2, 3));
+
+class Obj2 {
+    public $A;
+    protected $B;
+    private $C;
+
+    function __construct($a, $b, $c) {
+        $this->A = $a;
+        $this->B = $b;
+        $this->C = $c;
+    }
+}
+
+$o = new Obj2(1, 2, 3);
+test('object', $o, 'Obj', new Obj($o));
+
+$o1 = new Obj2(1, 2, 3);
+$o2 = new Obj2(4, 5, 6);
+test('object', array($o1, $o2), 'Obj', new Obj($o1, $o2));
+
+$o = new Obj2(1, 2, 3);
+test('object', array(&$o, &$o), 'Obj', new Obj($o, $o));
+
+$o = new Obj2(1, 2, 3);
+test('object', array(&$o, $o), 'Obj', new Obj($o, $o));
+
+$o = new Obj2(1, 2, 3);
+test('object', array($o, &$o), 'Obj', new Obj($o, $o));
+
+--EXPECTF--
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(true)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(false)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(0)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(100000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-100000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  float(123.456)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(0) ""
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(6) "foobar"
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  ["c:private"]=>
+  array(3) {
+    [0]=>
+    int(7)
+    [1]=>
+    int(8)
+    [2]=>
+    int(9)
+  }
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+  [3]=>
+  int(4)
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(3) "foo"
+  ["b:protected"]=>
+  string(6) "foobar"
+  ["c:private"]=>
+  string(4) "hoge"
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (5) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+  ["one"]=>
+  int(1)
+  ["two"]=>
+  int(2)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+  ["f"]=>
+  array(1) {
+    ["g"]=>
+    string(1) "h"
+  }
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(4)
+    ["B:protected"]=>
+    int(5)
+    ["C:private"]=>
+    int(6)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
diff --git a/php/tests/100.phpt b/php/tests/100.phpt
new file mode 100644
index 00000000..0b825637
--- /dev/null
+++ b/php/tests/100.phpt
@@ -0,0 +1,586 @@
+--TEST--
+unpack of object converter : class unpacker (object)
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.2.0') < 0) {
+    echo "skip tests in PHP 5.2 or newer";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack'))
+{
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+error_reporting(0);
+
+function test($type, $variable, $object, $result = null)
+{
+    $msgpack = new MessagePack();
+
+    $serialized = $msgpack->pack($variable);
+
+    $unserialized = null;
+    $unpacker = $msgpack->unpacker();
+    if ($unpacker->execute($serialized))
+    {
+        $unserialized = $unpacker->data($object);
+    }
+
+    var_dump($unserialized);
+    if ($result)
+    {
+        echo $unserialized == $result ? 'OK' : 'ERROR', PHP_EOL;
+    }
+    else
+    {
+        echo 'SKIP', PHP_EOL;
+    }
+}
+
+class Obj
+{
+    public $a;
+    protected $b;
+    private $c;
+
+    public function __construct($a = null, $b = null, $c = null, $d = null)
+    {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+        if (is_array($d))
+        {
+            foreach ($d as $key => $val)
+            {
+                $this->{$key} = $val;
+            }
+        }
+    }
+}
+
+$object = new Obj();
+
+test('null', null, new Obj(), new Obj(null, null, null));
+
+test('bool: true', true, new Obj(), new Obj(true, null, null));
+test('bool: false', false, new Obj(), new Obj(false, null, null));
+
+test('zero: 0', 0, new Obj(), new Obj(0, null, null));
+test('small: 1', 1, new Obj(), new Obj(1, null, null));
+test('small: -1', -1, new Obj(), new Obj(-1, null, null));
+test('medium: 1000', 1000, new Obj(), new Obj(1000, null, null));
+test('medium: -1000', -1000, new Obj(), new Obj(-1000, null, null));
+test('large: 100000', 100000, new Obj(), new Obj(100000, null, null));
+test('large: -100000', -100000, new Obj(), new Obj(-100000, null, null));
+
+test('double: 123.456', 123.456, new Obj(), new Obj(123.456, null, null));
+
+test('empty: ""', "", new Obj(), new Obj("", null, null));
+test('string: "foobar"', "foobar", new Obj(), new Obj("foobar", null, null));
+
+test('array: empty', array(), new Obj(), new Obj(null, null, null));
+test('array(1, 2, 3)', array(1, 2, 3), new Obj(), new Obj(1, 2, 3));
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), new Obj(), new Obj(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)));
+test('array(1, 2, 3, 4)', array(1, 2, 3, 4), new Obj());
+
+test('array("foo", "foobar", "foohoge")', array("foo", "foobar", "hoge"), new Obj(), new Obj("foo", "foobar", "hoge"));
+test('array("a" => 1, "b" => 2))', array("a" => 1, "b" => 2), new Obj(), new Obj(1, 2, null));
+test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), new Obj(), new Obj(null, null, null, array("one" => 1, "two" => 2)));
+test('array("" => "empty")', array("" => "empty"), new Obj());
+
+test('array("a" => 1, "b" => 2, 3))', array("a" => 1, "b" => 2, 3), new Obj(), new Obj(1, 2, 3));
+test('array(3, "a" => 1, "b" => 2))', array(3, "a" => 1, "b" => 2), new Obj(), new Obj(1, 2, 3));
+test('array("a" => 1, 3, "b" => 2))', array("a" => 1, 3, "b" => 2), new Obj(), new Obj(1, 2, 3));
+
+$a = array('foo');
+test('array($a, $a)', array($a, $a), new Obj(), new Obj($a, $a, null));
+test('array(&$a, &$a)', array(&$a, &$a), new Obj(), new Obj($a, $a, null));
+
+test('array(&$a, $a)', array($a, &$a), new Obj(), new Obj($a, $a, null));
+test('array(&$a, $a)', array(&$a, $a), new Obj(), new Obj($a, $a, null));
+
+$a = array(
+    'a' => array(
+        'b' => 'c',
+        'd' => 'e'
+        ),
+    'f' => array(
+        'g' => 'h'
+        )
+    );
+test('array', $a, new Obj(), new Obj(null, null, null, $a));
+
+$o = new Obj(1, 2, 3);
+test('object', $o, new Obj(), new Obj(1, 2, 3));
+
+class Obj2 {
+    public $A;
+    protected $B;
+    private $C;
+
+    function __construct($a, $b, $c) {
+        $this->A = $a;
+        $this->B = $b;
+        $this->C = $c;
+    }
+}
+
+$o = new Obj2(1, 2, 3);
+test('object', $o, new Obj(), new Obj($o));
+
+$o1 = new Obj2(1, 2, 3);
+$o2 = new Obj2(4, 5, 6);
+test('object', array($o1, $o2), new Obj(), new Obj($o1, $o2));
+
+$o = new Obj2(1, 2, 3);
+test('object', array(&$o, &$o), new Obj(), new Obj($o, $o));
+
+$o = new Obj2(1, 2, 3);
+test('object', array(&$o, $o), new Obj(), new Obj($o, $o));
+
+$o = new Obj2(1, 2, 3);
+test('object', array($o, &$o), new Obj(), new Obj($o, $o));
+
+--EXPECTF--
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(true)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(false)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(0)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(100000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-100000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  float(123.456)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(0) ""
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(6) "foobar"
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  array(3) {
+    [0]=>
+    int(7)
+    [1]=>
+    int(8)
+    [2]=>
+    int(9)
+  }
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+  [3]=>
+  int(4)
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(3) "foo"
+  [%r"?b"?:protected"?%r]=>
+  string(6) "foobar"
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  string(4) "hoge"
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (5) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  ["one"]=>
+  int(1)
+  ["two"]=>
+  int(2)
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  [""]=>
+  string(5) "empty"
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  ["f"]=>
+  array(1) {
+    ["g"]=>
+    string(1) "h"
+  }
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(4)
+    [%r"?B"?:protected"?%r]=>
+    int(5)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(6)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    [%r"?B"?:protected"?%r]=>
+    int(2)
+    [%r"?C"?:("Obj2":)?private"?%r]=>
+    int(3)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
diff --git a/php/tests/100b.phpt b/php/tests/100b.phpt
new file mode 100644
index 00000000..d78da002
--- /dev/null
+++ b/php/tests/100b.phpt
@@ -0,0 +1,572 @@
+--TEST--
+unpack of object converter : class unpacker (object)
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
+    echo "skip tests in PHP 5.1 or older";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack'))
+{
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+error_reporting(0);
+
+function test($type, $variable, $object, $result = null)
+{
+    $msgpack = new MessagePack();
+
+    $serialized = $msgpack->pack($variable);
+
+    $unserialized = null;
+    $unpacker = $msgpack->unpacker();
+    if ($unpacker->execute($serialized))
+    {
+        $unserialized = $unpacker->data($object);
+    }
+
+    var_dump($unserialized);
+    if ($result)
+    {
+        echo $unserialized == $result ? 'OK' : 'ERROR', PHP_EOL;
+    }
+    else
+    {
+        echo 'SKIP', PHP_EOL;
+    }
+}
+
+class Obj
+{
+    public $a;
+    protected $b;
+    private $c;
+
+    public function __construct($a = null, $b = null, $c = null, $d = null)
+    {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+        if (is_array($d))
+        {
+            foreach ($d as $key => $val)
+            {
+                $this->{$key} = $val;
+            }
+        }
+    }
+}
+
+test('null', null, new Obj(), new Obj(null, null, null));
+
+test('bool: true', true, new Obj(), new Obj(true, null, null));
+test('bool: false', false, new Obj(), new Obj(false, null, null));
+
+test('zero: 0', 0, new Obj(), new Obj(0, null, null));
+test('small: 1', 1, new Obj(), new Obj(1, null, null));
+test('small: -1', -1, new Obj(), new Obj(-1, null, null));
+test('medium: 1000', 1000, new Obj(), new Obj(1000, null, null));
+test('medium: -1000', -1000, new Obj(), new Obj(-1000, null, null));
+test('large: 100000', 100000, new Obj(), new Obj(100000, null, null));
+test('large: -100000', -100000, new Obj(), new Obj(-100000, null, null));
+
+test('double: 123.456', 123.456, new Obj(), new Obj(123.456, null, null));
+
+test('empty: ""', "", new Obj(), new Obj("", null, null));
+test('string: "foobar"', "foobar", new Obj(), new Obj("foobar", null, null));
+
+test('array: empty', array(), new Obj(), new Obj(null, null, null));
+test('array(1, 2, 3)', array(1, 2, 3), new Obj(), new Obj(1, 2, 3));
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), new Obj(), new Obj(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)));
+test('array(1, 2, 3, 4)', array(1, 2, 3, 4), new Obj());
+
+test('array("foo", "foobar", "foohoge")', array("foo", "foobar", "hoge"), new Obj(), new Obj("foo", "foobar", "hoge"));
+test('array("a" => 1, "b" => 2))', array("a" => 1, "b" => 2), new Obj(), new Obj(1, 2, null));
+test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), new Obj(), new Obj(null, null, null, array("one" => 1, "two" => 2)));
+
+test('array("a" => 1, "b" => 2, 3))', array("a" => 1, "b" => 2, 3), new Obj(), new Obj(1, 2, 3));
+test('array(3, "a" => 1, "b" => 2))', array(3, "a" => 1, "b" => 2), new Obj(), new Obj(1, 2, 3));
+test('array("a" => 1, 3, "b" => 2))', array("a" => 1, 3, "b" => 2), new Obj(), new Obj(1, 2, 3));
+
+$a = array('foo');
+test('array($a, $a)', array($a, $a), new Obj(), new Obj($a, $a, null));
+test('array(&$a, &$a)', array(&$a, &$a), new Obj(), new Obj($a, $a, null));
+
+test('array(&$a, $a)', array($a, &$a), new Obj(), new Obj($a, $a, null));
+test('array(&$a, $a)', array(&$a, $a), new Obj(), new Obj($a, $a, null));
+
+$a = array(
+    'a' => array(
+        'b' => 'c',
+        'd' => 'e'
+        ),
+    'f' => array(
+        'g' => 'h'
+        )
+    );
+test('array', $a, new Obj(), new Obj(null, null, null, $a));
+
+$o = new Obj(1, 2, 3);
+test('object', $o, new Obj(), new Obj(1, 2, 3));
+
+class Obj2 {
+    public $A;
+    protected $B;
+    private $C;
+
+    function __construct($a, $b, $c) {
+        $this->A = $a;
+        $this->B = $b;
+        $this->C = $c;
+    }
+}
+
+$o = new Obj2(1, 2, 3);
+test('object', $o, new Obj(), new Obj($o));
+
+$o1 = new Obj2(1, 2, 3);
+$o2 = new Obj2(4, 5, 6);
+test('object', array($o1, $o2), new Obj(), new Obj($o1, $o2));
+
+$o = new Obj2(1, 2, 3);
+test('object', array(&$o, &$o), new Obj(), new Obj($o, $o));
+
+$o = new Obj2(1, 2, 3);
+test('object', array(&$o, $o), new Obj(), new Obj($o, $o));
+
+$o = new Obj2(1, 2, 3);
+test('object', array($o, &$o), new Obj(), new Obj($o, $o));
+
+--EXPECTF--
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(true)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(false)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(0)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(100000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-100000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  float(123.456)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(0) ""
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(6) "foobar"
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  ["c:private"]=>
+  array(3) {
+    [0]=>
+    int(7)
+    [1]=>
+    int(8)
+    [2]=>
+    int(9)
+  }
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+  [3]=>
+  int(4)
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(3) "foo"
+  ["b:protected"]=>
+  string(6) "foobar"
+  ["c:private"]=>
+  string(4) "hoge"
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (5) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+  ["one"]=>
+  int(1)
+  ["two"]=>
+  int(2)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+  ["f"]=>
+  array(1) {
+    ["g"]=>
+    string(1) "h"
+  }
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(4)
+    ["B:protected"]=>
+    int(5)
+    ["C:private"]=>
+    int(6)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  &object(Obj2)#%d (3) {
+    ["A"]=>
+    int(1)
+    ["B:protected"]=>
+    int(2)
+    ["C:private"]=>
+    int(3)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
diff --git a/php/tests/101.phpt b/php/tests/101.phpt
new file mode 100644
index 00000000..c6f09d5d
--- /dev/null
+++ b/php/tests/101.phpt
@@ -0,0 +1,451 @@
+--TEST--
+unpack of object converter : class unpacker (string: php_only=0)
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.2.0') < 0) {
+    echo "skip tests in PHP 5.2 or newer";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack'))
+{
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+error_reporting(0);
+ini_set('msgpack.php_only', 0);
+
+function test($type, $variable, $object, $result = null)
+{
+    $msgpack = new MessagePack();
+
+    $serialized = $msgpack->pack($variable);
+
+    $unserialized = null;
+    $unpacker = $msgpack->unpacker();
+    if ($unpacker->execute($serialized))
+    {
+        $unserialized = $unpacker->data($object);
+    }
+
+    var_dump($unserialized);
+    if ($result)
+    {
+        echo $unserialized == $result ? 'OK' : 'ERROR', PHP_EOL;
+    }
+    else
+    {
+        echo 'SKIP', PHP_EOL;
+    }
+}
+
+class Obj
+{
+    public $a;
+    protected $b;
+    private $c;
+
+    public function __construct($a = null, $b = null, $c = null, $d = null)
+    {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+        if (is_array($d))
+        {
+            foreach ($d as $key => $val)
+            {
+                $this->{$key} = $val;
+            }
+        }
+    }
+}
+
+test('null', null, 'Obj', new Obj(null, null, null));
+
+test('bool: true', true, 'Obj', new Obj(true, null, null));
+test('bool: false', false, 'Obj', new Obj(false, null, null));
+
+test('zero: 0', 0, 'Obj', new Obj(0, null, null));
+test('small: 1', 1, 'Obj', new Obj(1, null, null));
+test('small: -1', -1, 'Obj', new Obj(-1, null, null));
+test('medium: 1000', 1000, 'Obj', new Obj(1000, null, null));
+test('medium: -1000', -1000, 'Obj', new Obj(-1000, null, null));
+test('large: 100000', 100000, 'Obj', new Obj(100000, null, null));
+test('large: -100000', -100000, 'Obj', new Obj(-100000, null, null));
+
+test('double: 123.456', 123.456, 'Obj', new Obj(123.456, null, null));
+
+test('empty: ""', "", 'Obj', new Obj("", null, null));
+test('string: "foobar"', "foobar", 'Obj', new Obj("foobar", null, null));
+
+test('array: empty', array(), 'Obj', new Obj(null, null, null));
+test('array(1, 2, 3)', array(1, 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), 'Obj', new Obj(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)));
+test('array(1, 2, 3, 4)', array(1, 2, 3, 4), 'Obj');
+
+test('array("foo", "foobar", "foohoge")', array("foo", "foobar", "hoge"), 'Obj', new Obj("foo", "foobar", "hoge"));
+test('array("a" => 1, "b" => 2))', array("a" => 1, "b" => 2), 'Obj', new Obj(1, 2, null));
+test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), 'Obj', new Obj(null, null, null, array("one" => 1, "two" => 2)));
+test('array("" => "empty")', array("" => "empty"), 'Obj');
+
+test('array("a" => 1, "b" => 2, 3))', array("a" => 1, "b" => 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(3, "a" => 1, "b" => 2))', array(3, "a" => 1, "b" => 2), 'Obj', new Obj(1, 2, 3));
+test('array("a" => 1, 3, "b" => 2))', array("a" => 1, 3, "b" => 2), 'Obj', new Obj(1, 2, 3));
+
+$a = array('foo');
+test('array($a, $a)', array($a, $a), 'Obj', new Obj($a, $a, null));
+
+$a = array(
+    'a' => array(
+        'b' => 'c',
+        'd' => 'e'
+        ),
+    'f' => array(
+        'g' => 'h'
+        )
+    );
+test('array', $a, 'Obj', new Obj(null, null, null, $a));
+
+$o = new Obj(1, 2, 3);
+test('object', $o, 'Obj', new Obj(1, 2, 3));
+
+class Obj2 {
+    public $A;
+    protected $B;
+    private $C;
+
+    function __construct($a, $b, $c) {
+        $this->A = $a;
+        $this->B = $b;
+        $this->C = $c;
+    }
+}
+
+$o = new Obj2(1, 2, 3);
+test('object', $o, 'Obj', new Obj(1, 2, 3));
+
+$o1 = new Obj2(1, 2, 3);
+$o2 = new Obj2(4, 5, 6);
+test('object', array($o1, $o2), 'Obj', new Obj(array(1, 2, 3), array(4, 5, 6)));
+
+--EXPECTF--
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(true)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(false)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(0)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(100000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-100000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  float(123.456)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(0) ""
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(6) "foobar"
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  array(3) {
+    [0]=>
+    int(7)
+    [1]=>
+    int(8)
+    [2]=>
+    int(9)
+  }
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+  [3]=>
+  int(4)
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(3) "foo"
+  [%r"?b"?:protected"?%r]=>
+  string(6) "foobar"
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  string(4) "hoge"
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (5) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  ["one"]=>
+  int(1)
+  ["two"]=>
+  int(2)
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  [""]=>
+  string(5) "empty"
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  ["f"]=>
+  array(1) {
+    ["g"]=>
+    string(1) "h"
+  }
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
diff --git a/php/tests/101b.phpt b/php/tests/101b.phpt
new file mode 100644
index 00000000..d7843b7a
--- /dev/null
+++ b/php/tests/101b.phpt
@@ -0,0 +1,439 @@
+--TEST--
+unpack of object converter : class unpack (string: php_only=0)
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
+    echo "skip tests in PHP 5.1 or older";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack'))
+{
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+error_reporting(0);
+ini_set('msgpack.php_only', 0);
+
+function test($type, $variable, $object, $result = null)
+{
+    $msgpack = new MessagePack();
+
+    $serialized = $msgpack->pack($variable);
+
+    $unserialized = null;
+    $unpacker = $msgpack->unpacker();
+    if ($unpacker->execute($serialized))
+    {
+        $unserialized = $unpacker->data($object);
+    }
+
+    var_dump($unserialized);
+    if ($result)
+    {
+        echo $unserialized == $result ? 'OK' : 'ERROR', PHP_EOL;
+    }
+    else
+    {
+        echo 'SKIP', PHP_EOL;
+    }
+}
+
+class Obj
+{
+    public $a;
+    protected $b;
+    private $c;
+
+    public function __construct($a = null, $b = null, $c = null, $d = null)
+    {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+        if (is_array($d))
+        {
+            foreach ($d as $key => $val)
+            {
+                $this->{$key} = $val;
+            }
+        }
+    }
+}
+
+test('null', null, 'Obj', new Obj(null, null, null));
+
+test('bool: true', true, 'Obj', new Obj(true, null, null));
+test('bool: false', false, 'Obj', new Obj(false, null, null));
+
+test('zero: 0', 0, 'Obj', new Obj(0, null, null));
+test('small: 1', 1, 'Obj', new Obj(1, null, null));
+test('small: -1', -1, 'Obj', new Obj(-1, null, null));
+test('medium: 1000', 1000, 'Obj', new Obj(1000, null, null));
+test('medium: -1000', -1000, 'Obj', new Obj(-1000, null, null));
+test('large: 100000', 100000, 'Obj', new Obj(100000, null, null));
+test('large: -100000', -100000, 'Obj', new Obj(-100000, null, null));
+
+test('double: 123.456', 123.456, 'Obj', new Obj(123.456, null, null));
+
+test('empty: ""', "", 'Obj', new Obj("", null, null));
+test('string: "foobar"', "foobar", 'Obj', new Obj("foobar", null, null));
+
+test('array: empty', array(), 'Obj', new Obj(null, null, null));
+test('array(1, 2, 3)', array(1, 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), 'Obj', new Obj(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)));
+test('array(1, 2, 3, 4)', array(1, 2, 3, 4), 'Obj');
+
+test('array("foo", "foobar", "foohoge")', array("foo", "foobar", "hoge"), 'Obj', new Obj("foo", "foobar", "hoge"));
+test('array("a" => 1, "b" => 2))', array("a" => 1, "b" => 2), 'Obj', new Obj(1, 2, null));
+test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), 'Obj', new Obj(null, null, null, array("one" => 1, "two" => 2)));
+
+test('array("a" => 1, "b" => 2, 3))', array("a" => 1, "b" => 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(3, "a" => 1, "b" => 2))', array(3, "a" => 1, "b" => 2), 'Obj', new Obj(1, 2, 3));
+test('array("a" => 1, 3, "b" => 2))', array("a" => 1, 3, "b" => 2), 'Obj', new Obj(1, 2, 3));
+
+$a = array('foo');
+test('array($a, $a)', array($a, $a), 'Obj', new Obj($a, $a, null));
+
+$a = array(
+    'a' => array(
+        'b' => 'c',
+        'd' => 'e'
+        ),
+    'f' => array(
+        'g' => 'h'
+        )
+    );
+test('array', $a, 'Obj', new Obj(null, null, null, $a));
+
+$o = new Obj(1, 2, 3);
+test('object', $o, 'Obj', new Obj(1, 2, 3));
+
+class Obj2 {
+    public $A;
+    protected $B;
+    private $C;
+
+    function __construct($a, $b, $c) {
+        $this->A = $a;
+        $this->B = $b;
+        $this->C = $c;
+    }
+}
+
+$o = new Obj2(1, 2, 3);
+test('object', $o, 'Obj', new Obj(1, 2, 3));
+
+$o1 = new Obj2(1, 2, 3);
+$o2 = new Obj2(4, 5, 6);
+test('object', array($o1, $o2), 'Obj', new Obj(array(1, 2, 3), array(4, 5, 6)));
+
+--EXPECTF--
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(true)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(false)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(0)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(100000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-100000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  float(123.456)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(0) ""
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(6) "foobar"
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  ["c:private"]=>
+  array(3) {
+    [0]=>
+    int(7)
+    [1]=>
+    int(8)
+    [2]=>
+    int(9)
+  }
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+  [3]=>
+  int(4)
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(3) "foo"
+  ["b:protected"]=>
+  string(6) "foobar"
+  ["c:private"]=>
+  string(4) "hoge"
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (5) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+  ["one"]=>
+  int(1)
+  ["two"]=>
+  int(2)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+  ["f"]=>
+  array(1) {
+    ["g"]=>
+    string(1) "h"
+  }
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
diff --git a/php/tests/102.phpt b/php/tests/102.phpt
new file mode 100644
index 00000000..c91eba58
--- /dev/null
+++ b/php/tests/102.phpt
@@ -0,0 +1,453 @@
+--TEST--
+unpack of object converter : class unpacker (object: php_only=0)
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.2.0') < 0) {
+    echo "skip tests in PHP 5.2 or newer";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack'))
+{
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+error_reporting(0);
+ini_set('msgpack.php_only', 0);
+
+function test($type, $variable, $object, $result = null)
+{
+    $msgpack = new MessagePack();
+
+    $serialized = $msgpack->pack($variable);
+
+    $unserialized = null;
+    $unpacker = $msgpack->unpacker();
+    if ($unpacker->execute($serialized))
+    {
+        $unserialized = $unpacker->data($object);
+    }
+
+    var_dump($unserialized);
+    if ($result)
+    {
+        echo $unserialized == $result ? 'OK' : 'ERROR', PHP_EOL;
+    }
+    else
+    {
+        echo 'SKIP', PHP_EOL;
+    }
+}
+
+class Obj
+{
+    public $a;
+    protected $b;
+    private $c;
+
+    public function __construct($a = null, $b = null, $c = null, $d = null)
+    {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+        if (is_array($d))
+        {
+            foreach ($d as $key => $val)
+            {
+                $this->{$key} = $val;
+            }
+        }
+    }
+}
+
+$object = new Obj();
+
+test('null', null, new Obj(), new Obj(null, null, null));
+
+test('bool: true', true, new Obj(), new Obj(true, null, null));
+test('bool: false', false, new Obj(), new Obj(false, null, null));
+
+test('zero: 0', 0, new Obj(), new Obj(0, null, null));
+test('small: 1', 1, new Obj(), new Obj(1, null, null));
+test('small: -1', -1, new Obj(), new Obj(-1, null, null));
+test('medium: 1000', 1000, new Obj(), new Obj(1000, null, null));
+test('medium: -1000', -1000, new Obj(), new Obj(-1000, null, null));
+test('large: 100000', 100000, new Obj(), new Obj(100000, null, null));
+test('large: -100000', -100000, new Obj(), new Obj(-100000, null, null));
+
+test('double: 123.456', 123.456, new Obj(), new Obj(123.456, null, null));
+
+test('empty: ""', "", new Obj(), new Obj("", null, null));
+test('string: "foobar"', "foobar", new Obj(), new Obj("foobar", null, null));
+
+test('array: empty', array(), new Obj(), new Obj(null, null, null));
+test('array(1, 2, 3)', array(1, 2, 3), new Obj(), new Obj(1, 2, 3));
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), new Obj(), new Obj(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)));
+test('array(1, 2, 3, 4)', array(1, 2, 3, 4), new Obj());
+
+test('array("foo", "foobar", "foohoge")', array("foo", "foobar", "hoge"), new Obj(), new Obj("foo", "foobar", "hoge"));
+test('array("a" => 1, "b" => 2))', array("a" => 1, "b" => 2), new Obj(), new Obj(1, 2, null));
+test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), new Obj(), new Obj(null, null, null, array("one" => 1, "two" => 2)));
+test('array("" => "empty")', array("" => "empty"), new Obj());
+
+test('array("a" => 1, "b" => 2, 3))', array("a" => 1, "b" => 2, 3), new Obj(), new Obj(1, 2, 3));
+test('array(3, "a" => 1, "b" => 2))', array(3, "a" => 1, "b" => 2), new Obj(), new Obj(1, 2, 3));
+test('array("a" => 1, 3, "b" => 2))', array("a" => 1, 3, "b" => 2), new Obj(), new Obj(1, 2, 3));
+
+$a = array('foo');
+test('array($a, $a)', array($a, $a), new Obj(), new Obj($a, $a, null));
+
+$a = array(
+    'a' => array(
+        'b' => 'c',
+        'd' => 'e'
+        ),
+    'f' => array(
+        'g' => 'h'
+        )
+    );
+test('array', $a, new Obj(), new Obj(null, null, null, $a));
+
+$o = new Obj(1, 2, 3);
+test('object', $o, new Obj(), new Obj(1, 2, 3));
+
+class Obj2 {
+    public $A;
+    protected $B;
+    private $C;
+
+    function __construct($a, $b, $c) {
+        $this->A = $a;
+        $this->B = $b;
+        $this->C = $c;
+    }
+}
+
+$o = new Obj2(1, 2, 3);
+test('object', $o, new Obj(), new Obj(1, 2, 3));
+
+$o1 = new Obj2(1, 2, 3);
+$o2 = new Obj2(4, 5, 6);
+test('object', array($o1, $o2), new Obj(), new Obj(array(1, 2, 3), array(4, 5, 6)));
+
+--EXPECTF--
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(true)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(false)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(0)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(100000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-100000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  float(123.456)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(0) ""
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(6) "foobar"
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  array(3) {
+    [0]=>
+    int(7)
+    [1]=>
+    int(8)
+    [2]=>
+    int(9)
+  }
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+  [3]=>
+  int(4)
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(3) "foo"
+  [%r"?b"?:protected"?%r]=>
+  string(6) "foobar"
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  string(4) "hoge"
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (5) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  ["one"]=>
+  int(1)
+  ["two"]=>
+  int(2)
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  [""]=>
+  string(5) "empty"
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  ["f"]=>
+  array(1) {
+    ["g"]=>
+    string(1) "h"
+  }
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
diff --git a/php/tests/102b.phpt b/php/tests/102b.phpt
new file mode 100644
index 00000000..1fec6eef
--- /dev/null
+++ b/php/tests/102b.phpt
@@ -0,0 +1,439 @@
+--TEST--
+unpack of object converter : class unpacker (object: php_only=0)
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
+    echo "skip tests in PHP 5.1 or older";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack'))
+{
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+error_reporting(0);
+ini_set('msgpack.php_only', 0);
+
+function test($type, $variable, $object, $result = null)
+{
+    $msgpack = new MessagePack();
+
+    $serialized = $msgpack->pack($variable);
+
+    $unserialized = null;
+    $unpacker = $msgpack->unpacker();
+    if ($unpacker->execute($serialized))
+    {
+        $unserialized = $unpacker->data($object);
+    }
+
+    var_dump($unserialized);
+    if ($result)
+    {
+        echo $unserialized == $result ? 'OK' : 'ERROR', PHP_EOL;
+    }
+    else
+    {
+        echo 'SKIP', PHP_EOL;
+    }
+}
+
+class Obj
+{
+    public $a;
+    protected $b;
+    private $c;
+
+    public function __construct($a = null, $b = null, $c = null, $d = null)
+    {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+        if (is_array($d))
+        {
+            foreach ($d as $key => $val)
+            {
+                $this->{$key} = $val;
+            }
+        }
+    }
+}
+
+test('null', null, new Obj(), new Obj(null, null, null));
+
+test('bool: true', true, new Obj(), new Obj(true, null, null));
+test('bool: false', false, new Obj(), new Obj(false, null, null));
+
+test('zero: 0', 0, new Obj(), new Obj(0, null, null));
+test('small: 1', 1, new Obj(), new Obj(1, null, null));
+test('small: -1', -1, new Obj(), new Obj(-1, null, null));
+test('medium: 1000', 1000, new Obj(), new Obj(1000, null, null));
+test('medium: -1000', -1000, new Obj(), new Obj(-1000, null, null));
+test('large: 100000', 100000, new Obj(), new Obj(100000, null, null));
+test('large: -100000', -100000, new Obj(), new Obj(-100000, null, null));
+
+test('double: 123.456', 123.456, new Obj(), new Obj(123.456, null, null));
+
+test('empty: ""', "", new Obj(), new Obj("", null, null));
+test('string: "foobar"', "foobar", new Obj(), new Obj("foobar", null, null));
+
+test('array: empty', array(), new Obj(), new Obj(null, null, null));
+test('array(1, 2, 3)', array(1, 2, 3), new Obj(), new Obj(1, 2, 3));
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), new Obj(), new Obj(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)));
+test('array(1, 2, 3, 4)', array(1, 2, 3, 4), new Obj());
+
+test('array("foo", "foobar", "foohoge")', array("foo", "foobar", "hoge"), new Obj(), new Obj("foo", "foobar", "hoge"));
+test('array("a" => 1, "b" => 2))', array("a" => 1, "b" => 2), new Obj(), new Obj(1, 2, null));
+test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), new Obj(), new Obj(null, null, null, array("one" => 1, "two" => 2)));
+
+test('array("a" => 1, "b" => 2, 3))', array("a" => 1, "b" => 2, 3), new Obj(), new Obj(1, 2, 3));
+test('array(3, "a" => 1, "b" => 2))', array(3, "a" => 1, "b" => 2), new Obj(), new Obj(1, 2, 3));
+test('array("a" => 1, 3, "b" => 2))', array("a" => 1, 3, "b" => 2), new Obj(), new Obj(1, 2, 3));
+
+$a = array('foo');
+test('array($a, $a)', array($a, $a), new Obj(), new Obj($a, $a, null));
+
+$a = array(
+    'a' => array(
+        'b' => 'c',
+        'd' => 'e'
+        ),
+    'f' => array(
+        'g' => 'h'
+        )
+    );
+test('array', $a, new Obj(), new Obj(null, null, null, $a));
+
+$o = new Obj(1, 2, 3);
+test('object', $o, new Obj(), new Obj(1, 2, 3));
+
+class Obj2 {
+    public $A;
+    protected $B;
+    private $C;
+
+    function __construct($a, $b, $c) {
+        $this->A = $a;
+        $this->B = $b;
+        $this->C = $c;
+    }
+}
+
+$o = new Obj2(1, 2, 3);
+test('object', $o, new Obj(), new Obj(1, 2, 3));
+
+$o1 = new Obj2(1, 2, 3);
+$o2 = new Obj2(4, 5, 6);
+test('object', array($o1, $o2), new Obj(), new Obj(array(1, 2, 3), array(4, 5, 6)));
+
+--EXPECTF--
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(true)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(false)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(0)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(100000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-100000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  float(123.456)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(0) ""
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(6) "foobar"
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  ["c:private"]=>
+  array(3) {
+    [0]=>
+    int(7)
+    [1]=>
+    int(8)
+    [2]=>
+    int(9)
+  }
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+  [3]=>
+  int(4)
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(3) "foo"
+  ["b:protected"]=>
+  string(6) "foobar"
+  ["c:private"]=>
+  string(4) "hoge"
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (5) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+  ["one"]=>
+  int(1)
+  ["two"]=>
+  int(2)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+  ["f"]=>
+  array(1) {
+    ["g"]=>
+    string(1) "h"
+  }
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
diff --git a/php/tests/103.phpt b/php/tests/103.phpt
new file mode 100644
index 00000000..60b3da15
--- /dev/null
+++ b/php/tests/103.phpt
@@ -0,0 +1,451 @@
+--TEST--
+unpack of object converter: class unpacker (string: OPT_PHPONLY=false)
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.2.0') < 0) {
+    echo "skip tests in PHP 5.2 or newer";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack'))
+{
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+error_reporting(0);
+
+function test($type, $variable, $object, $result = null)
+{
+    $msgpack = new MessagePack();
+    $msgpack->setOption(MessagePack::OPT_PHPONLY, false);
+
+    $serialized = $msgpack->pack($variable);
+
+    $unserialized = null;
+    $unpacker = $msgpack->unpacker();
+    if ($unpacker->execute($serialized))
+    {
+        $unserialized = $unpacker->data($object);
+    }
+
+    var_dump($unserialized);
+    if ($result)
+    {
+        echo $unserialized == $result ? 'OK' : 'ERROR', PHP_EOL;
+    }
+    else
+    {
+        echo 'SKIP', PHP_EOL;
+    }
+}
+
+class Obj
+{
+    public $a;
+    protected $b;
+    private $c;
+
+    public function __construct($a = null, $b = null, $c = null, $d = null)
+    {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+        if (is_array($d))
+        {
+            foreach ($d as $key => $val)
+            {
+                $this->{$key} = $val;
+            }
+        }
+    }
+}
+
+test('null', null, 'Obj', new Obj(null, null, null));
+
+test('bool: true', true, 'Obj', new Obj(true, null, null));
+test('bool: false', false, 'Obj', new Obj(false, null, null));
+
+test('zero: 0', 0, 'Obj', new Obj(0, null, null));
+test('small: 1', 1, 'Obj', new Obj(1, null, null));
+test('small: -1', -1, 'Obj', new Obj(-1, null, null));
+test('medium: 1000', 1000, 'Obj', new Obj(1000, null, null));
+test('medium: -1000', -1000, 'Obj', new Obj(-1000, null, null));
+test('large: 100000', 100000, 'Obj', new Obj(100000, null, null));
+test('large: -100000', -100000, 'Obj', new Obj(-100000, null, null));
+
+test('double: 123.456', 123.456, 'Obj', new Obj(123.456, null, null));
+
+test('empty: ""', "", 'Obj', new Obj("", null, null));
+test('string: "foobar"', "foobar", 'Obj', new Obj("foobar", null, null));
+
+test('array: empty', array(), 'Obj', new Obj(null, null, null));
+test('array(1, 2, 3)', array(1, 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), 'Obj', new Obj(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)));
+test('array(1, 2, 3, 4)', array(1, 2, 3, 4), 'Obj');
+
+test('array("foo", "foobar", "foohoge")', array("foo", "foobar", "hoge"), 'Obj', new Obj("foo", "foobar", "hoge"));
+test('array("a" => 1, "b" => 2))', array("a" => 1, "b" => 2), 'Obj', new Obj(1, 2, null));
+test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), 'Obj', new Obj(null, null, null, array("one" => 1, "two" => 2)));
+test('array("" => "empty")', array("" => "empty"), 'Obj');
+
+test('array("a" => 1, "b" => 2, 3))', array("a" => 1, "b" => 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(3, "a" => 1, "b" => 2))', array(3, "a" => 1, "b" => 2), 'Obj', new Obj(1, 2, 3));
+test('array("a" => 1, 3, "b" => 2))', array("a" => 1, 3, "b" => 2), 'Obj', new Obj(1, 2, 3));
+
+$a = array('foo');
+test('array($a, $a)', array($a, $a), 'Obj', new Obj($a, $a, null));
+
+$a = array(
+    'a' => array(
+        'b' => 'c',
+        'd' => 'e'
+        ),
+    'f' => array(
+        'g' => 'h'
+        )
+    );
+test('array', $a, 'Obj', new Obj(null, null, null, $a));
+
+$o = new Obj(1, 2, 3);
+test('object', $o, 'Obj', new Obj(1, 2, 3));
+
+class Obj2 {
+    public $A;
+    protected $B;
+    private $C;
+
+    function __construct($a, $b, $c) {
+        $this->A = $a;
+        $this->B = $b;
+        $this->C = $c;
+    }
+}
+
+$o = new Obj2(1, 2, 3);
+test('object', $o, 'Obj', new Obj(1, 2, 3));
+
+$o1 = new Obj2(1, 2, 3);
+$o2 = new Obj2(4, 5, 6);
+test('object', array($o1, $o2), 'Obj', new Obj(array(1, 2, 3), array(4, 5, 6)));
+
+--EXPECTF--
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(true)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(false)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(0)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(100000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-100000)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  float(123.456)
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(0) ""
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(6) "foobar"
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  array(3) {
+    [0]=>
+    int(7)
+    [1]=>
+    int(8)
+    [2]=>
+    int(9)
+  }
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+  [3]=>
+  int(4)
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(3) "foo"
+  [%r"?b"?:protected"?%r]=>
+  string(6) "foobar"
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  string(4) "hoge"
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (5) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  ["one"]=>
+  int(1)
+  ["two"]=>
+  int(2)
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  NULL
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  [""]=>
+  string(5) "empty"
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  [%r"?b"?:protected"?%r]=>
+  NULL
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+  ["f"]=>
+  array(1) {
+    ["g"]=>
+    string(1) "h"
+  }
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  [%r"?b"?:protected"?%r]=>
+  int(2)
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  [%r"?b"?:protected"?%r]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  [%r"?c"?:("Obj":)?private"?%r]=>
+  NULL
+}
+OK
diff --git a/php/tests/103b.phpt b/php/tests/103b.phpt
new file mode 100644
index 00000000..fc875b97
--- /dev/null
+++ b/php/tests/103b.phpt
@@ -0,0 +1,446 @@
+--TEST--
+unpack of object converter: class unpacker (string: OPT_PHPONLY=false)
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
+    echo "skip tests in PHP 5.1 or older";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack'))
+{
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+error_reporting(0);
+
+function test($type, $variable, $object, $result = null)
+{
+    $msgpack = new MessagePack();
+    if (version_compare(PHP_VERSION, '5.1.0') <= 0)
+    {
+        $msgpack->setOption(MESSAGEPACK_OPT_PHPONLY, false);
+    }
+    else
+    {
+        $msgpack->setOption(MessagePack::OPT_PHPONLY, false);
+    }
+
+    $serialized = $msgpack->pack($variable);
+
+    $unserialized = null;
+    $unpacker = $msgpack->unpacker();
+    if ($unpacker->execute($serialized))
+    {
+        $unserialized = $unpacker->data($object);
+    }
+
+    var_dump($unserialized);
+    if ($result)
+    {
+        echo $unserialized == $result ? 'OK' : 'ERROR', PHP_EOL;
+    }
+    else
+    {
+        echo 'SKIP', PHP_EOL;
+    }
+}
+
+class Obj
+{
+    public $a;
+    protected $b;
+    private $c;
+
+    public function __construct($a = null, $b = null, $c = null, $d = null)
+    {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+        if (is_array($d))
+        {
+            foreach ($d as $key => $val)
+            {
+                $this->{$key} = $val;
+            }
+        }
+    }
+}
+
+test('null', null, 'Obj', new Obj(null, null, null));
+
+test('bool: true', true, 'Obj', new Obj(true, null, null));
+test('bool: false', false, 'Obj', new Obj(false, null, null));
+
+test('zero: 0', 0, 'Obj', new Obj(0, null, null));
+test('small: 1', 1, 'Obj', new Obj(1, null, null));
+test('small: -1', -1, 'Obj', new Obj(-1, null, null));
+test('medium: 1000', 1000, 'Obj', new Obj(1000, null, null));
+test('medium: -1000', -1000, 'Obj', new Obj(-1000, null, null));
+test('large: 100000', 100000, 'Obj', new Obj(100000, null, null));
+test('large: -100000', -100000, 'Obj', new Obj(-100000, null, null));
+
+test('double: 123.456', 123.456, 'Obj', new Obj(123.456, null, null));
+
+test('empty: ""', "", 'Obj', new Obj("", null, null));
+test('string: "foobar"', "foobar", 'Obj', new Obj("foobar", null, null));
+
+test('array: empty', array(), 'Obj', new Obj(null, null, null));
+test('array(1, 2, 3)', array(1, 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(array(1, 2, 3), arr...', array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)), 'Obj', new Obj(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9)));
+test('array(1, 2, 3, 4)', array(1, 2, 3, 4), 'Obj');
+
+test('array("foo", "foobar", "foohoge")', array("foo", "foobar", "hoge"), 'Obj', new Obj("foo", "foobar", "hoge"));
+test('array("a" => 1, "b" => 2))', array("a" => 1, "b" => 2), 'Obj', new Obj(1, 2, null));
+test('array("one" => 1, "two" => 2))', array("one" => 1, "two" => 2), 'Obj', new Obj(null, null, null, array("one" => 1, "two" => 2)));
+
+test('array("a" => 1, "b" => 2, 3))', array("a" => 1, "b" => 2, 3), 'Obj', new Obj(1, 2, 3));
+test('array(3, "a" => 1, "b" => 2))', array(3, "a" => 1, "b" => 2), 'Obj', new Obj(1, 2, 3));
+test('array("a" => 1, 3, "b" => 2))', array("a" => 1, 3, "b" => 2), 'Obj', new Obj(1, 2, 3));
+
+$a = array('foo');
+test('array($a, $a)', array($a, $a), 'Obj', new Obj($a, $a, null));
+
+$a = array(
+    'a' => array(
+        'b' => 'c',
+        'd' => 'e'
+        ),
+    'f' => array(
+        'g' => 'h'
+        )
+    );
+test('array', $a, 'Obj', new Obj(null, null, null, $a));
+
+$o = new Obj(1, 2, 3);
+test('object', $o, 'Obj', new Obj(1, 2, 3));
+
+class Obj2 {
+    public $A;
+    protected $B;
+    private $C;
+
+    function __construct($a, $b, $c) {
+        $this->A = $a;
+        $this->B = $b;
+        $this->C = $c;
+    }
+}
+
+$o = new Obj2(1, 2, 3);
+test('object', $o, 'Obj', new Obj(1, 2, 3));
+
+$o1 = new Obj2(1, 2, 3);
+$o2 = new Obj2(4, 5, 6);
+test('object', array($o1, $o2), 'Obj', new Obj(array(1, 2, 3), array(4, 5, 6)));
+
+--EXPECTF--
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(true)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  bool(false)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(0)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-1000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(100000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(-100000)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  float(123.456)
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(0) ""
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(6) "foobar"
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  ["c:private"]=>
+  array(3) {
+    [0]=>
+    int(7)
+    [1]=>
+    int(8)
+    [2]=>
+    int(9)
+  }
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+  [3]=>
+  int(4)
+}
+SKIP
+object(Obj)#%d (3) {
+  ["a"]=>
+  string(3) "foo"
+  ["b:protected"]=>
+  string(6) "foobar"
+  ["c:private"]=>
+  string(4) "hoge"
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (5) {
+  ["a"]=>
+  NULL
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+  ["one"]=>
+  int(1)
+  ["two"]=>
+  int(2)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["b:protected"]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  ["c:private"]=>
+  NULL
+}
+OK
+object(Obj)#%d (4) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  ["b:protected"]=>
+  NULL
+  ["c:private"]=>
+  NULL
+  ["f"]=>
+  array(1) {
+    ["g"]=>
+    string(1) "h"
+  }
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  int(1)
+  ["b:protected"]=>
+  int(2)
+  ["c:private"]=>
+  int(3)
+}
+OK
+object(Obj)#%d (3) {
+  ["a"]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    int(3)
+  }
+  ["b:protected"]=>
+  array(3) {
+    [0]=>
+    int(4)
+    [1]=>
+    int(5)
+    [2]=>
+    int(6)
+  }
+  ["c:private"]=>
+  NULL
+}
+OK