From 2a0a84763479e6eebd30f685e5f64afd7e7cb3bc Mon Sep 17 00:00:00 2001
From: advect <advect@gmail.com>
Date: Wed, 29 Sep 2010 08:47:06 +0900
Subject: [PATCH] php: update 0.3.0

---
 php/ChangeLog                 |   15 +-
 php/config.m4                 |   45 --
 php/config.w32                |    3 -
 php/msgpack.c                 |  139 ++--
 php/msgpack/pack_define.h     |   26 +
 php/msgpack/pack_template.h   |  766 +++++++++++++++++++++
 php/msgpack/sysdep.h          |  118 ++++
 php/msgpack/unpack_define.h   |   93 +++
 php/msgpack/unpack_template.h |  409 +++++++++++
 php/msgpack/version.h         |   40 ++
 php/msgpack_class.c           |  315 +++++++--
 php/msgpack_class.h           |    6 +-
 php/msgpack_pack.c            |  132 ++--
 php/msgpack_pack.h            |    7 +-
 php/msgpack_unpack.c          | 1216 +++++++++++++++------------------
 php/msgpack_unpack.h          |  123 +++-
 php/package.xml               |   85 ++-
 php/php-msgpack.spec          |    4 +-
 php/php_msgpack.h             |    3 +-
 php/tests/009.phpt            |   18 +-
 php/tests/009b.phpt           |  101 +++
 php/tests/014.phpt            |    2 +-
 php/tests/015.phpt            |    2 +-
 php/tests/015b.phpt           |    2 +-
 php/tests/018.phpt            |    2 +-
 php/tests/024.phpt            |    6 +-
 php/tests/024b.phpt           |  165 +++++
 php/tests/026.phpt            |    6 +-
 php/tests/026b.phpt           |  107 +++
 php/tests/027.phpt            |    2 +-
 php/tests/028.phpt            |    6 +-
 php/tests/028b.phpt           |  167 +++++
 php/tests/029.phpt            |   37 +-
 php/tests/030.phpt            |    3 -
 php/tests/060.phpt            |   18 +-
 php/tests/060b.phpt           |  313 +++++++++
 php/tests/061.phpt            |    8 +-
 php/tests/061b.phpt           |  318 +++++++++
 php/tests/070.phpt            |    8 +-
 php/tests/070b.phpt           |  297 ++++++++
 php/tests/071.phpt            |    8 +-
 php/tests/071b.phpt           |  299 ++++++++
 php/tests/072.phpt            |    8 +-
 php/tests/072b.phpt           |  339 +++++++++
 php/tests/073.phpt            |  346 ++++++++++
 php/tests/073b.phpt           |  340 +++++++++
 php/tests/080.phpt            |  301 ++++++++
 php/tests/081.phpt            |  303 ++++++++
 php/tests/082.phpt            |  346 ++++++++++
 php/tests/083.phpt            |  347 ++++++++++
 php/tests/084.phpt            |  301 ++++++++
 php/tests/085.phpt            |  344 ++++++++++
 php/tests/086.phpt            |  345 ++++++++++
 php/tests/087.phpt            |  302 ++++++++
 php/tests/088.phpt            |  345 ++++++++++
 php/tests/089.phpt            |  347 ++++++++++
 56 files changed, 8804 insertions(+), 950 deletions(-)
 create mode 100644 php/msgpack/pack_define.h
 create mode 100644 php/msgpack/pack_template.h
 create mode 100644 php/msgpack/sysdep.h
 create mode 100644 php/msgpack/unpack_define.h
 create mode 100644 php/msgpack/unpack_template.h
 create mode 100644 php/msgpack/version.h
 create mode 100644 php/tests/009b.phpt
 create mode 100644 php/tests/024b.phpt
 create mode 100644 php/tests/026b.phpt
 create mode 100644 php/tests/028b.phpt
 create mode 100644 php/tests/060b.phpt
 create mode 100644 php/tests/061b.phpt
 create mode 100644 php/tests/070b.phpt
 create mode 100644 php/tests/071b.phpt
 create mode 100644 php/tests/072b.phpt
 create mode 100644 php/tests/073.phpt
 create mode 100644 php/tests/073b.phpt
 create mode 100644 php/tests/080.phpt
 create mode 100644 php/tests/081.phpt
 create mode 100644 php/tests/082.phpt
 create mode 100644 php/tests/083.phpt
 create mode 100644 php/tests/084.phpt
 create mode 100644 php/tests/085.phpt
 create mode 100644 php/tests/086.phpt
 create mode 100644 php/tests/087.phpt
 create mode 100644 php/tests/088.phpt
 create mode 100644 php/tests/089.phpt

diff --git a/php/ChangeLog b/php/ChangeLog
index 5481c1fc..3cee3b60 100644
--- a/php/ChangeLog
+++ b/php/ChangeLog
@@ -1,14 +1,21 @@
 msgpack extension changelog
 
+Version 0.3.0
+-------------
+	* Change msgpack_unpack.c (used template)
+	* Add php_only ini option (true / false)
+	* Change class MessagePack and MessagePackUnpacker __construct option.
+	* Add class MessagePack and MessagePackUnpacker setOption method.
+
 Version 0.2.1
 -------------
-	* Fix stream deserializer
+	* Fix stream deserializer.
 
 Version 0.2.0
 -------------
-	* Add stream deserializer / class MessagePackUnpacker interface
-	* Add alias functions
-	* Add class MessagePack interface
+	* Add stream deserializer / class MessagePackUnpacker interface.
+	* Add alias functions.
+	* Add class MessagePack interface.
 
 Version 0.1.5
 -------------
diff --git a/php/config.m4 b/php/config.m4
index d319684f..a78e1f32 100644
--- a/php/config.m4
+++ b/php/config.m4
@@ -21,52 +21,7 @@ PHP_ARG_WITH(msgpack, for msgpack support,
 Make sure that the comment is aligned:
 [  --with-msgpack             Include msgpack support])
 
-dnl PHP_ARG_ENABLE(msgpack, whether to enable msgpack support,
-dnl Make sure that the comment is aligned:
-dnl [  --enable-msgpack           Enable msgpack support])
-
 if test "$PHP_MSGPACK" != "no"; then
-  dnl Write more examples of tests here...
-
-  dnl --with-msgpack -> check with-path
-  SEARCH_PATH="/usr/local /usr"     # you might want to change this
-  SEARCH_FOR="/include/msgpack.h"  # you most likely want to change this
-  if test -r $PHP_MSGPACK/$SEARCH_FOR; then # path given as parameter
-    MSGPACK_DIR=$PHP_MSGPACK
-  else # search default path list
-    AC_MSG_CHECKING([for msgpack files in default path])
-    for i in $SEARCH_PATH ; do
-      if test -r $i/$SEARCH_FOR; then
-        MSGPACK_DIR=$i
-        AC_MSG_RESULT(found in $i)
-      fi
-    done
-  fi
-
-  if test -z "$MSGPACK_DIR"; then
-    AC_MSG_RESULT([not found])
-    AC_MSG_ERROR([Please reinstall the msgpack distribution])
-  fi
-
-  dnl --with-msgpack -> add include path
-  PHP_ADD_INCLUDE($MSGPACK_DIR/include)
-
-  dnl --with-msgpack -> check for lib and symbol presence
-  LIBNAME=msgpack # you may want to change this
-  LIBSYMBOL=msgpack_pack_object # you most likely want to change this 
-
-  PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL,
-  [
-    PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $MSGPACK_DIR/lib, MSGPACK_SHARED_LIBADD)
-    AC_DEFINE(HAVE_MSGPACKLIB,1,[ ])
-  ],[
-    AC_MSG_ERROR([wrong msgpack lib version or lib not found])
-  ],[
-    -L$MSGPACK_DIR/lib -lm
-  ])
-
-  PHP_SUBST(MSGPACK_SHARED_LIBADD)
-
   PHP_NEW_EXTENSION(msgpack, msgpack.c msgpack_pack.c msgpack_unpack.c msgpack_class.c, $ext_shared)
 
   PHP_INSTALL_HEADERS([ext/msgpack], [php_msgpack.h])
diff --git a/php/config.w32 b/php/config.w32
index f2153e6b..726b75f2 100644
--- a/php/config.w32
+++ b/php/config.w32
@@ -4,9 +4,6 @@
 // If your extension references something external, use ARG_WITH
 // ARG_WITH("msgpack", "for msgpack support", "no");
 
-// Otherwise, use ARG_ENABLE
-// ARG_ENABLE("msgpack", "enable msgpack support", "no");
-
 if (PHP_MSGPACK != "no") {
    EXTENSION("msgpack", "msgpack.c msgpack_pack.c msgpack_unpack.c msgpack_class.c");
 }
diff --git a/php/msgpack.c b/php/msgpack.c
index 62fb68d7..5d4f9264 100644
--- a/php/msgpack.c
+++ b/php/msgpack.c
@@ -15,6 +15,7 @@
 #include "msgpack_pack.h"
 #include "msgpack_unpack.h"
 #include "msgpack_class.h"
+#include "msgpack/version.h"
 
 static ZEND_FUNCTION(msgpack_serialize);
 static ZEND_FUNCTION(msgpack_unserialize);
@@ -27,20 +28,46 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_msgpack_unserialize, 0, 0, 1)
     ZEND_ARG_INFO(0, str)
 ZEND_END_ARG_INFO()
 
+PHP_INI_BEGIN()
+STD_PHP_INI_BOOLEAN(
+    "msgpack.error_display", "1", PHP_INI_ALL, OnUpdateBool,
+    error_display, zend_msgpack_globals, msgpack_globals)
+STD_PHP_INI_BOOLEAN(
+    "msgpack.php_only", "1", PHP_INI_ALL, OnUpdateBool,
+    php_only, zend_msgpack_globals, msgpack_globals)
+PHP_INI_END()
+
 PS_SERIALIZER_FUNCS(msgpack);
 
 static const zend_function_entry msgpack_functions[] = {
     ZEND_FE(msgpack_serialize, arginfo_msgpack_serialize)
     ZEND_FE(msgpack_unserialize, arginfo_msgpack_unserialize)
     ZEND_FALIAS(msgpack_pack, msgpack_serialize, arginfo_msgpack_serialize)
-    ZEND_FALIAS(msgpack_unpack, msgpack_unserialize,
-                arginfo_msgpack_unserialize)
+    ZEND_FALIAS(msgpack_unpack, msgpack_unserialize, arginfo_msgpack_unserialize)
     {NULL, NULL, NULL}
 };
 
+static void msgpack_init_globals(zend_msgpack_globals *msgpack_globals)
+{
+    TSRMLS_FETCH();
+
+    if (PG(display_errors))
+    {
+        msgpack_globals->error_display = 1;
+    }
+    else
+    {
+        msgpack_globals->error_display = 0;
+    }
+
+    msgpack_globals->php_only = 1;
+}
+
 static ZEND_MINIT_FUNCTION(msgpack)
 {
-    MSGPACK_G(error_display) = 1;
+    ZEND_INIT_MODULE_GLOBALS(msgpack, msgpack_init_globals, NULL);
+
+    REGISTER_INI_ENTRIES();
 
 #if HAVE_PHP_SESSION
     php_session_register_serializer("msgpack",
@@ -48,7 +75,14 @@ static ZEND_MINIT_FUNCTION(msgpack)
                                     PS_SERIALIZER_DECODE_NAME(msgpack));
 #endif
 
-    msgpack_init_class(TSRMLS_CC);
+    msgpack_init_class();
+
+    return SUCCESS;
+}
+
+static ZEND_MSHUTDOWN_FUNCTION(msgpack)
+{
+    UNREGISTER_INI_ENTRIES();
 
     return SUCCESS;
 }
@@ -56,12 +90,15 @@ static ZEND_MINIT_FUNCTION(msgpack)
 static ZEND_MINFO_FUNCTION(msgpack)
 {
     php_info_print_table_start();
-    php_info_print_table_row(2, "msgpack support", "enabled");
-    php_info_print_table_row(2, "msgpack version", MSGPACK_VERSION);
+    php_info_print_table_row(2, "MessagePack Support", "enabled");
 #if HAVE_PHP_SESSION
-    php_info_print_table_row(2, "msgpack Session Support", "enabled" );
+    php_info_print_table_row(2, "Session Support", "enabled" );
 #endif
+    php_info_print_table_row(2, "extension Version", MSGPACK_EXTENSION_VERSION);
+    php_info_print_table_row(2, "header Version", MSGPACK_VERSION);
     php_info_print_table_end();
+
+    DISPLAY_INI_ENTRIES();
 }
 
 zend_module_entry msgpack_module_entry = {
@@ -71,7 +108,7 @@ zend_module_entry msgpack_module_entry = {
     "msgpack",
     msgpack_functions,
     ZEND_MINIT(msgpack),
-    NULL,
+    ZEND_MSHUTDOWN(msgpack),
     NULL,
     NULL,
     ZEND_MINFO(msgpack),
@@ -109,7 +146,6 @@ PS_SERIALIZER_ENCODE_FUNC(msgpack)
 
 PS_SERIALIZER_DECODE_FUNC(msgpack)
 {
-    php_unserialize_data_t var_hash;
     int ret;
     HashTable *tmp_hash;
     HashPosition tmp_hash_pos;
@@ -117,38 +153,29 @@ PS_SERIALIZER_DECODE_FUNC(msgpack)
     ulong key_long;
     uint key_len;
     zval *tmp;
-    zval **data;
-    msgpack_unserialize_data mpsd;
+    zval **value;
+    size_t off = 0;
+    msgpack_unpack_t mp;
+    php_unserialize_data_t var_hash;
 
-    PHP_VAR_UNSERIALIZE_INIT(var_hash);
+    ALLOC_INIT_ZVAL(tmp);
 
-    MAKE_STD_ZVAL(tmp);
+    template_init(&mp);
 
-    mpsd.data = (unsigned char *)val;;
-    mpsd.length = vallen;
-    mpsd.offset = 0;
+    msgpack_unserialize_var_init(&var_hash);
 
-    ret = msgpack_unserialize_zval(&tmp, &mpsd, &var_hash TSRMLS_CC);
+    (&mp)->user.retval = (zval *)tmp;
+    (&mp)->user.var_hash = (php_unserialize_data_t *)&var_hash;
 
-    switch (ret)
-    {
-        case MSGPACK_UNPACK_EXTRA_BYTES:
-        case MSGPACK_UNPACK_SUCCESS:
-            break;
-        case MSGPACK_UNPACK_PARSE_ERROR:
-        case MSGPACK_UNPACK_CONTINUE:
-        default:
-            zval_ptr_dtor(&tmp);
-            return FAILURE;
-    }
+    ret = template_execute(&mp, (char *)val, (size_t)vallen, &off);
 
-    PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
+    msgpack_unserialize_var_destroy(&var_hash);
 
     tmp_hash = HASH_OF(tmp);
 
     zend_hash_internal_pointer_reset_ex(tmp_hash, &tmp_hash_pos);
     while (zend_hash_get_current_data_ex(
-               tmp_hash, (void *)&data, &tmp_hash_pos) == SUCCESS)
+               tmp_hash, (void *)&value, &tmp_hash_pos) == SUCCESS)
     {
         ret = zend_hash_get_current_key_ex(
             tmp_hash, &key_str, &key_len, &key_long, 0, &tmp_hash_pos);
@@ -158,7 +185,8 @@ PS_SERIALIZER_DECODE_FUNC(msgpack)
                 /* ??? */
                 break;
             case HASH_KEY_IS_STRING:
-                php_set_session_var(key_str, key_len - 1, *data, NULL TSRMLS_CC);
+                php_set_session_var(
+                    key_str, key_len - 1, *value, NULL TSRMLS_CC);
                 php_add_session_var(key_str, key_len - 1 TSRMLS_CC);
                 break;
         }
@@ -185,46 +213,59 @@ PHP_MSGPACK_API void php_msgpack_unserialize(
     zval *return_value, char *str, size_t str_len TSRMLS_DC)
 {
     int ret;
+    size_t off = 0;
+    msgpack_unpack_t mp;
     php_unserialize_data_t var_hash;
-    msgpack_unserialize_data mpsd;
 
     if (str_len <= 0)
     {
         RETURN_NULL();
     }
 
-    PHP_VAR_UNSERIALIZE_INIT(var_hash);
+    template_init(&mp);
 
-    mpsd.data = (unsigned char *)str;
-    mpsd.length = str_len;
-    mpsd.offset = 0;
+    msgpack_unserialize_var_init(&var_hash);
 
-    ret = msgpack_unserialize_zval(&return_value, &mpsd, &var_hash TSRMLS_CC);
+    (&mp)->user.retval = (zval *)return_value;
+    (&mp)->user.var_hash = (php_unserialize_data_t *)&var_hash;
+
+    ret = template_execute(&mp, str, (size_t)str_len, &off);
+
+    msgpack_unserialize_var_destroy(&var_hash);
 
     switch (ret)
     {
         case MSGPACK_UNPACK_PARSE_ERROR:
-            zend_error(E_WARNING,
-                       "[msgpack] (php_msgpack_unserialize) Parse error");
+            if (MSGPACK_G(error_display))
+            {
+                zend_error(E_WARNING,
+                           "[msgpack] (php_msgpack_unserialize) Parse error");
+            }
             break;
         case MSGPACK_UNPACK_CONTINUE:
-            zend_error(E_WARNING,
-                       "[msgpack] (php_msgpack_unserialize) "
-                       "Insufficient data for unserializeng");
+            if (MSGPACK_G(error_display))
+            {
+                zend_error(E_WARNING,
+                           "[msgpack] (php_msgpack_unserialize) "
+                           "Insufficient data for unserializing");
+            }
             break;
         case MSGPACK_UNPACK_EXTRA_BYTES:
-            zend_error(E_WARNING,
-                       "[msgpack] (php_msgpack_unserialize) Extra bytes");
-            break;
         case MSGPACK_UNPACK_SUCCESS:
+            if (off < (size_t)str_len && MSGPACK_G(error_display))
+            {
+                zend_error(E_WARNING,
+                           "[msgpack] (php_msgpack_unserialize) Extra bytes");
+            }
             break;
         default:
-            zend_error(E_WARNING,
-                       "[msgpack] (php_msgpack_unserialize) Unknown result");
+            if (MSGPACK_G(error_display))
+            {
+                zend_error(E_WARNING,
+                           "[msgpack] (php_msgpack_unserialize) Unknown result");
+            }
             break;
     }
-
-    PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
 }
 
 static ZEND_FUNCTION(msgpack_serialize)
diff --git a/php/msgpack/pack_define.h b/php/msgpack/pack_define.h
new file mode 100644
index 00000000..4845d52e
--- /dev/null
+++ b/php/msgpack/pack_define.h
@@ -0,0 +1,26 @@
+/*
+ * MessagePack unpacking routine template
+ *
+ * Copyright (C) 2008-2010 FURUHASHI Sadayuki
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+#ifndef MSGPACK_PACK_DEFINE_H__
+#define MSGPACK_PACK_DEFINE_H__
+
+#include "msgpack/sysdep.h"
+#include <limits.h>
+#include <string.h>
+
+#endif /* msgpack/pack_define.h */
+
diff --git a/php/msgpack/pack_template.h b/php/msgpack/pack_template.h
new file mode 100644
index 00000000..b636967f
--- /dev/null
+++ b/php/msgpack/pack_template.h
@@ -0,0 +1,766 @@
+/*
+ * MessagePack packing routine template
+ *
+ * Copyright (C) 2008-2010 FURUHASHI Sadayuki
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#if defined(__LITTLE_ENDIAN__)
+#define TAKE8_8(d)  ((uint8_t*)&d)[0]
+#define TAKE8_16(d) ((uint8_t*)&d)[0]
+#define TAKE8_32(d) ((uint8_t*)&d)[0]
+#define TAKE8_64(d) ((uint8_t*)&d)[0]
+#elif defined(__BIG_ENDIAN__)
+#define TAKE8_8(d)  ((uint8_t*)&d)[0]
+#define TAKE8_16(d) ((uint8_t*)&d)[1]
+#define TAKE8_32(d) ((uint8_t*)&d)[3]
+#define TAKE8_64(d) ((uint8_t*)&d)[7]
+#endif
+
+#ifndef msgpack_pack_inline_func
+#error msgpack_pack_inline_func template is not defined
+#endif
+
+#ifndef msgpack_pack_user
+#error msgpack_pack_user type is not defined
+#endif
+
+#ifndef msgpack_pack_append_buffer
+#error msgpack_pack_append_buffer callback is not defined
+#endif
+
+
+/*
+ * Integer
+ */
+
+#define msgpack_pack_real_uint8(x, d) \
+do { \
+	if(d < (1<<7)) { \
+		/* fixnum */ \
+		msgpack_pack_append_buffer(x, &TAKE8_8(d), 1); \
+	} else { \
+		/* unsigned 8 */ \
+		unsigned char buf[2] = {0xcc, TAKE8_8(d)}; \
+		msgpack_pack_append_buffer(x, buf, 2); \
+	} \
+} while(0)
+
+#define msgpack_pack_real_uint16(x, d) \
+do { \
+	if(d < (1<<7)) { \
+		/* fixnum */ \
+		msgpack_pack_append_buffer(x, &TAKE8_16(d), 1); \
+	} else if(d < (1<<8)) { \
+		/* unsigned 8 */ \
+		unsigned char buf[2] = {0xcc, TAKE8_16(d)}; \
+		msgpack_pack_append_buffer(x, buf, 2); \
+	} else { \
+		/* unsigned 16 */ \
+		unsigned char buf[3]; \
+		buf[0] = 0xcd; _msgpack_store16(&buf[1], d); \
+		msgpack_pack_append_buffer(x, buf, 3); \
+	} \
+} while(0)
+
+#define msgpack_pack_real_uint32(x, d) \
+do { \
+	if(d < (1<<8)) { \
+		if(d < (1<<7)) { \
+			/* fixnum */ \
+			msgpack_pack_append_buffer(x, &TAKE8_32(d), 1); \
+		} else { \
+			/* unsigned 8 */ \
+			unsigned char buf[2] = {0xcc, TAKE8_32(d)}; \
+			msgpack_pack_append_buffer(x, buf, 2); \
+		} \
+	} else { \
+		if(d < (1<<16)) { \
+			/* unsigned 16 */ \
+			unsigned char buf[3]; \
+			buf[0] = 0xcd; _msgpack_store16(&buf[1], d); \
+			msgpack_pack_append_buffer(x, buf, 3); \
+		} else { \
+			/* unsigned 32 */ \
+			unsigned char buf[5]; \
+			buf[0] = 0xce; _msgpack_store32(&buf[1], d); \
+			msgpack_pack_append_buffer(x, buf, 5); \
+		} \
+	} \
+} while(0)
+
+#define msgpack_pack_real_uint64(x, d) \
+do { \
+	if(d < (1ULL<<8)) { \
+		if(d < (1<<7)) { \
+			/* fixnum */ \
+			msgpack_pack_append_buffer(x, &TAKE8_64(d), 1); \
+		} else { \
+			/* unsigned 8 */ \
+			unsigned char buf[2] = {0xcc, TAKE8_64(d)}; \
+			msgpack_pack_append_buffer(x, buf, 2); \
+		} \
+	} else { \
+		if(d < (1ULL<<16)) { \
+			/* unsigned 16 */ \
+			unsigned char buf[3]; \
+			buf[0] = 0xcd; _msgpack_store16(&buf[1], d); \
+			msgpack_pack_append_buffer(x, buf, 3); \
+		} else if(d < (1ULL<<32)) { \
+			/* unsigned 32 */ \
+			unsigned char buf[5]; \
+			buf[0] = 0xce; _msgpack_store32(&buf[1], d); \
+			msgpack_pack_append_buffer(x, buf, 5); \
+		} else { \
+			/* unsigned 64 */ \
+			unsigned char buf[9]; \
+			buf[0] = 0xcf; _msgpack_store64(&buf[1], d); \
+			msgpack_pack_append_buffer(x, buf, 9); \
+		} \
+	} \
+} while(0)
+
+#define msgpack_pack_real_int8(x, d) \
+do { \
+	if(d < -(1<<5)) { \
+		/* signed 8 */ \
+		unsigned char buf[2] = {0xd0, TAKE8_8(d)}; \
+		msgpack_pack_append_buffer(x, buf, 2); \
+	} else { \
+		/* fixnum */ \
+		msgpack_pack_append_buffer(x, &TAKE8_8(d), 1); \
+	} \
+} while(0)
+
+#define msgpack_pack_real_int16(x, d) \
+do { \
+	if(d < -(1<<5)) { \
+		if(d < -(1<<7)) { \
+			/* signed 16 */ \
+			unsigned char buf[3]; \
+			buf[0] = 0xd1; _msgpack_store16(&buf[1], d); \
+			msgpack_pack_append_buffer(x, buf, 3); \
+		} else { \
+			/* signed 8 */ \
+			unsigned char buf[2] = {0xd0, TAKE8_16(d)}; \
+			msgpack_pack_append_buffer(x, buf, 2); \
+		} \
+	} else if(d < (1<<7)) { \
+		/* fixnum */ \
+		msgpack_pack_append_buffer(x, &TAKE8_16(d), 1); \
+	} else { \
+		if(d < (1<<8)) { \
+			/* unsigned 8 */ \
+			unsigned char buf[2] = {0xcc, TAKE8_16(d)}; \
+			msgpack_pack_append_buffer(x, buf, 2); \
+		} else { \
+			/* unsigned 16 */ \
+			unsigned char buf[3]; \
+			buf[0] = 0xcd; _msgpack_store16(&buf[1], d); \
+			msgpack_pack_append_buffer(x, buf, 3); \
+		} \
+	} \
+} while(0)
+
+#define msgpack_pack_real_int32(x, d) \
+do { \
+	if(d < -(1<<5)) { \
+		if(d < -(1<<15)) { \
+			/* signed 32 */ \
+			unsigned char buf[5]; \
+			buf[0] = 0xd2; _msgpack_store32(&buf[1], d); \
+			msgpack_pack_append_buffer(x, buf, 5); \
+		} else if(d < -(1<<7)) { \
+			/* signed 16 */ \
+			unsigned char buf[3]; \
+			buf[0] = 0xd1; _msgpack_store16(&buf[1], d); \
+			msgpack_pack_append_buffer(x, buf, 3); \
+		} else { \
+			/* signed 8 */ \
+			unsigned char buf[2] = {0xd0, TAKE8_32(d)}; \
+			msgpack_pack_append_buffer(x, buf, 2); \
+		} \
+	} else if(d < (1<<7)) { \
+		/* fixnum */ \
+		msgpack_pack_append_buffer(x, &TAKE8_32(d), 1); \
+	} else { \
+		if(d < (1<<8)) { \
+			/* unsigned 8 */ \
+			unsigned char buf[2] = {0xcc, TAKE8_32(d)}; \
+			msgpack_pack_append_buffer(x, buf, 2); \
+		} else if(d < (1<<16)) { \
+			/* unsigned 16 */ \
+			unsigned char buf[3]; \
+			buf[0] = 0xcd; _msgpack_store16(&buf[1], d); \
+			msgpack_pack_append_buffer(x, buf, 3); \
+		} else { \
+			/* unsigned 32 */ \
+			unsigned char buf[5]; \
+			buf[0] = 0xce; _msgpack_store32(&buf[1], d); \
+			msgpack_pack_append_buffer(x, buf, 5); \
+		} \
+	} \
+} while(0)
+
+#define msgpack_pack_real_int64(x, d) \
+do { \
+	if(d < -(1LL<<5)) { \
+		if(d < -(1LL<<15)) { \
+			if(d < -(1LL<<31)) { \
+				/* signed 64 */ \
+				unsigned char buf[9]; \
+				buf[0] = 0xd3; _msgpack_store64(&buf[1], d); \
+				msgpack_pack_append_buffer(x, buf, 9); \
+			} else { \
+				/* signed 32 */ \
+				unsigned char buf[5]; \
+				buf[0] = 0xd2; _msgpack_store32(&buf[1], d); \
+				msgpack_pack_append_buffer(x, buf, 5); \
+			} \
+		} else { \
+			if(d < -(1<<7)) { \
+				/* signed 16 */ \
+				unsigned char buf[3]; \
+				buf[0] = 0xd1; _msgpack_store16(&buf[1], d); \
+				msgpack_pack_append_buffer(x, buf, 3); \
+			} else { \
+				/* signed 8 */ \
+				unsigned char buf[2] = {0xd0, TAKE8_64(d)}; \
+				msgpack_pack_append_buffer(x, buf, 2); \
+			} \
+		} \
+	} else if(d < (1<<7)) { \
+		/* fixnum */ \
+		msgpack_pack_append_buffer(x, &TAKE8_64(d), 1); \
+	} else { \
+		if(d < (1LL<<16)) { \
+			if(d < (1<<8)) { \
+				/* unsigned 8 */ \
+				unsigned char buf[2] = {0xcc, TAKE8_64(d)}; \
+				msgpack_pack_append_buffer(x, buf, 2); \
+			} else { \
+				/* unsigned 16 */ \
+				unsigned char buf[3]; \
+				buf[0] = 0xcd; _msgpack_store16(&buf[1], d); \
+				msgpack_pack_append_buffer(x, buf, 3); \
+			} \
+		} else { \
+			if(d < (1LL<<32)) { \
+				/* unsigned 32 */ \
+				unsigned char buf[5]; \
+				buf[0] = 0xce; _msgpack_store32(&buf[1], d); \
+				msgpack_pack_append_buffer(x, buf, 5); \
+			} else { \
+				/* unsigned 64 */ \
+				unsigned char buf[9]; \
+				buf[0] = 0xcf; _msgpack_store64(&buf[1], d); \
+				msgpack_pack_append_buffer(x, buf, 9); \
+			} \
+		} \
+	} \
+} while(0)
+
+
+#ifdef msgpack_pack_inline_func_fixint
+
+msgpack_pack_inline_func_fixint(_uint8)(msgpack_pack_user x, uint8_t d)
+{
+	unsigned char buf[2] = {0xcc, TAKE8_8(d)};
+	msgpack_pack_append_buffer(x, buf, 2);
+}
+
+msgpack_pack_inline_func_fixint(_uint16)(msgpack_pack_user x, uint16_t d)
+{
+	unsigned char buf[3];
+	buf[0] = 0xcd; _msgpack_store16(&buf[1], d);
+	msgpack_pack_append_buffer(x, buf, 3);
+}
+
+msgpack_pack_inline_func_fixint(_uint32)(msgpack_pack_user x, uint32_t d)
+{
+	unsigned char buf[5];
+	buf[0] = 0xce; _msgpack_store32(&buf[1], d);
+	msgpack_pack_append_buffer(x, buf, 5);
+}
+
+msgpack_pack_inline_func_fixint(_uint64)(msgpack_pack_user x, uint64_t d)
+{
+	unsigned char buf[9];
+	buf[0] = 0xcf; _msgpack_store64(&buf[1], d);
+	msgpack_pack_append_buffer(x, buf, 9);
+}
+
+msgpack_pack_inline_func_fixint(_int8)(msgpack_pack_user x, int8_t d)
+{
+	unsigned char buf[2] = {0xd0, TAKE8_8(d)};
+	msgpack_pack_append_buffer(x, buf, 2);
+}
+
+msgpack_pack_inline_func_fixint(_int16)(msgpack_pack_user x, int16_t d)
+{
+	unsigned char buf[3];
+	buf[0] = 0xd1; _msgpack_store16(&buf[1], d);
+	msgpack_pack_append_buffer(x, buf, 3);
+}
+
+msgpack_pack_inline_func_fixint(_int32)(msgpack_pack_user x, int32_t d)
+{
+	unsigned char buf[5];
+	buf[0] = 0xd2; _msgpack_store32(&buf[1], d);
+	msgpack_pack_append_buffer(x, buf, 5);
+}
+
+msgpack_pack_inline_func_fixint(_int64)(msgpack_pack_user x, int64_t d)
+{
+	unsigned char buf[9];
+	buf[0] = 0xd3; _msgpack_store64(&buf[1], d);
+	msgpack_pack_append_buffer(x, buf, 9);
+}
+
+#undef msgpack_pack_inline_func_fixint
+#endif
+
+
+msgpack_pack_inline_func(_uint8)(msgpack_pack_user x, uint8_t d)
+{
+	msgpack_pack_real_uint8(x, d);
+}
+
+msgpack_pack_inline_func(_uint16)(msgpack_pack_user x, uint16_t d)
+{
+	msgpack_pack_real_uint16(x, d);
+}
+
+msgpack_pack_inline_func(_uint32)(msgpack_pack_user x, uint32_t d)
+{
+	msgpack_pack_real_uint32(x, d);
+}
+
+msgpack_pack_inline_func(_uint64)(msgpack_pack_user x, uint64_t d)
+{
+	msgpack_pack_real_uint64(x, d);
+}
+
+msgpack_pack_inline_func(_int8)(msgpack_pack_user x, int8_t d)
+{
+	msgpack_pack_real_int8(x, d);
+}
+
+msgpack_pack_inline_func(_int16)(msgpack_pack_user x, int16_t d)
+{
+	msgpack_pack_real_int16(x, d);
+}
+
+msgpack_pack_inline_func(_int32)(msgpack_pack_user x, int32_t d)
+{
+	msgpack_pack_real_int32(x, d);
+}
+
+msgpack_pack_inline_func(_int64)(msgpack_pack_user x, int64_t d)
+{
+	msgpack_pack_real_int64(x, d);
+}
+
+
+#ifdef msgpack_pack_inline_func_cint
+
+msgpack_pack_inline_func_cint(_short)(msgpack_pack_user x, short d)
+{
+#if defined(SIZEOF_SHORT)
+#if SIZEOF_SHORT == 2
+	msgpack_pack_real_int16(x, d);
+#elif SIZEOF_SHORT == 4
+	msgpack_pack_real_int32(x, d);
+#else
+	msgpack_pack_real_int64(x, d);
+#endif
+
+#elif defined(SHRT_MAX)
+#if SHRT_MAX == 0x7fff
+	msgpack_pack_real_int16(x, d);
+#elif SHRT_MAX == 0x7fffffff
+	msgpack_pack_real_int32(x, d);
+#else
+	msgpack_pack_real_int64(x, d);
+#endif
+
+#else
+if(sizeof(short) == 2) {
+	msgpack_pack_real_int16(x, d);
+} else if(sizeof(short) == 4) {
+	msgpack_pack_real_int32(x, d);
+} else {
+	msgpack_pack_real_int64(x, d);
+}
+#endif
+}
+
+msgpack_pack_inline_func_cint(_int)(msgpack_pack_user x, int d)
+{
+#if defined(SIZEOF_INT)
+#if SIZEOF_INT == 2
+	msgpack_pack_real_int16(x, d);
+#elif SIZEOF_INT == 4
+	msgpack_pack_real_int32(x, d);
+#else
+	msgpack_pack_real_int64(x, d);
+#endif
+
+#elif defined(INT_MAX)
+#if INT_MAX == 0x7fff
+	msgpack_pack_real_int16(x, d);
+#elif INT_MAX == 0x7fffffff
+	msgpack_pack_real_int32(x, d);
+#else
+	msgpack_pack_real_int64(x, d);
+#endif
+
+#else
+if(sizeof(int) == 2) {
+	msgpack_pack_real_int16(x, d);
+} else if(sizeof(int) == 4) {
+	msgpack_pack_real_int32(x, d);
+} else {
+	msgpack_pack_real_int64(x, d);
+}
+#endif
+}
+
+msgpack_pack_inline_func_cint(_long)(msgpack_pack_user x, long d)
+{
+#if defined(SIZEOF_LONG)
+#if SIZEOF_LONG == 2
+	msgpack_pack_real_int16(x, d);
+#elif SIZEOF_LONG == 4
+	msgpack_pack_real_int32(x, d);
+#else
+	msgpack_pack_real_int64(x, d);
+#endif
+
+#elif defined(LONG_MAX)
+#if LONG_MAX == 0x7fffL
+	msgpack_pack_real_int16(x, d);
+#elif LONG_MAX == 0x7fffffffL
+	msgpack_pack_real_int32(x, d);
+#else
+	msgpack_pack_real_int64(x, d);
+#endif
+
+#else
+if(sizeof(long) == 2) {
+	msgpack_pack_real_int16(x, d);
+} else if(sizeof(long) == 4) {
+	msgpack_pack_real_int32(x, d);
+} else {
+	msgpack_pack_real_int64(x, d);
+}
+#endif
+}
+
+msgpack_pack_inline_func_cint(_long_long)(msgpack_pack_user x, long long d)
+{
+#if defined(SIZEOF_LONG_LONG)
+#if SIZEOF_LONG_LONG == 2
+	msgpack_pack_real_int16(x, d);
+#elif SIZEOF_LONG_LONG == 4
+	msgpack_pack_real_int32(x, d);
+#else
+	msgpack_pack_real_int64(x, d);
+#endif
+
+#elif defined(LLONG_MAX)
+#if LLONG_MAX == 0x7fffL
+	msgpack_pack_real_int16(x, d);
+#elif LLONG_MAX == 0x7fffffffL
+	msgpack_pack_real_int32(x, d);
+#else
+	msgpack_pack_real_int64(x, d);
+#endif
+
+#else
+if(sizeof(long long) == 2) {
+	msgpack_pack_real_int16(x, d);
+} else if(sizeof(long long) == 4) {
+	msgpack_pack_real_int32(x, d);
+} else {
+	msgpack_pack_real_int64(x, d);
+}
+#endif
+}
+
+msgpack_pack_inline_func_cint(_unsigned_short)(msgpack_pack_user x, unsigned short d)
+{
+#if defined(SIZEOF_SHORT)
+#if SIZEOF_SHORT == 2
+	msgpack_pack_real_uint16(x, d);
+#elif SIZEOF_SHORT == 4
+	msgpack_pack_real_uint32(x, d);
+#else
+	msgpack_pack_real_uint64(x, d);
+#endif
+
+#elif defined(USHRT_MAX)
+#if USHRT_MAX == 0xffffU
+	msgpack_pack_real_uint16(x, d);
+#elif USHRT_MAX == 0xffffffffU
+	msgpack_pack_real_uint32(x, d);
+#else
+	msgpack_pack_real_uint64(x, d);
+#endif
+
+#else
+if(sizeof(unsigned short) == 2) {
+	msgpack_pack_real_uint16(x, d);
+} else if(sizeof(unsigned short) == 4) {
+	msgpack_pack_real_uint32(x, d);
+} else {
+	msgpack_pack_real_uint64(x, d);
+}
+#endif
+}
+
+msgpack_pack_inline_func_cint(_unsigned_int)(msgpack_pack_user x, unsigned int d)
+{
+#if defined(SIZEOF_INT)
+#if SIZEOF_INT == 2
+	msgpack_pack_real_uint16(x, d);
+#elif SIZEOF_INT == 4
+	msgpack_pack_real_uint32(x, d);
+#else
+	msgpack_pack_real_uint64(x, d);
+#endif
+
+#elif defined(UINT_MAX)
+#if UINT_MAX == 0xffffU
+	msgpack_pack_real_uint16(x, d);
+#elif UINT_MAX == 0xffffffffU
+	msgpack_pack_real_uint32(x, d);
+#else
+	msgpack_pack_real_uint64(x, d);
+#endif
+
+#else
+if(sizeof(unsigned int) == 2) {
+	msgpack_pack_real_uint16(x, d);
+} else if(sizeof(unsigned int) == 4) {
+	msgpack_pack_real_uint32(x, d);
+} else {
+	msgpack_pack_real_uint64(x, d);
+}
+#endif
+}
+
+msgpack_pack_inline_func_cint(_unsigned_long)(msgpack_pack_user x, unsigned long d)
+{
+#if defined(SIZEOF_LONG)
+#if SIZEOF_LONG == 2
+	msgpack_pack_real_uint16(x, d);
+#elif SIZEOF_LONG == 4
+	msgpack_pack_real_uint32(x, d);
+#else
+	msgpack_pack_real_uint64(x, d);
+#endif
+
+#elif defined(ULONG_MAX)
+#if ULONG_MAX == 0xffffUL
+	msgpack_pack_real_uint16(x, d);
+#elif ULONG_MAX == 0xffffffffUL
+	msgpack_pack_real_uint32(x, d);
+#else
+	msgpack_pack_real_uint64(x, d);
+#endif
+
+#else
+if(sizeof(unsigned long) == 2) {
+	msgpack_pack_real_uint16(x, d);
+} else if(sizeof(unsigned long) == 4) {
+	msgpack_pack_real_uint32(x, d);
+} else {
+	msgpack_pack_real_uint64(x, d);
+}
+#endif
+}
+
+msgpack_pack_inline_func_cint(_unsigned_long_long)(msgpack_pack_user x, unsigned long long d)
+{
+#if defined(SIZEOF_LONG_LONG)
+#if SIZEOF_LONG_LONG == 2
+	msgpack_pack_real_uint16(x, d);
+#elif SIZEOF_LONG_LONG == 4
+	msgpack_pack_real_uint32(x, d);
+#else
+	msgpack_pack_real_uint64(x, d);
+#endif
+
+#elif defined(ULLONG_MAX)
+#if ULLONG_MAX == 0xffffUL
+	msgpack_pack_real_uint16(x, d);
+#elif ULLONG_MAX == 0xffffffffUL
+	msgpack_pack_real_uint32(x, d);
+#else
+	msgpack_pack_real_uint64(x, d);
+#endif
+
+#else
+if(sizeof(unsigned long long) == 2) {
+	msgpack_pack_real_uint16(x, d);
+} else if(sizeof(unsigned long long) == 4) {
+	msgpack_pack_real_uint32(x, d);
+} else {
+	msgpack_pack_real_uint64(x, d);
+}
+#endif
+}
+
+#undef msgpack_pack_inline_func_cint
+#endif
+
+
+
+/*
+ * Float
+ */
+
+msgpack_pack_inline_func(_float)(msgpack_pack_user x, float d)
+{
+	union { float f; uint32_t i; } mem;
+	mem.f = d;
+	unsigned char buf[5];
+	buf[0] = 0xca; _msgpack_store32(&buf[1], mem.i);
+	msgpack_pack_append_buffer(x, buf, 5);
+}
+
+msgpack_pack_inline_func(_double)(msgpack_pack_user x, double d)
+{
+	union { double f; uint64_t i; } mem;
+	mem.f = d;
+	unsigned char buf[9];
+	buf[0] = 0xcb; _msgpack_store64(&buf[1], mem.i);
+	msgpack_pack_append_buffer(x, buf, 9);
+}
+
+
+/*
+ * Nil
+ */
+
+msgpack_pack_inline_func(_nil)(msgpack_pack_user x)
+{
+	static const unsigned char d = 0xc0;
+	msgpack_pack_append_buffer(x, &d, 1);
+}
+
+
+/*
+ * Boolean
+ */
+
+msgpack_pack_inline_func(_true)(msgpack_pack_user x)
+{
+	static const unsigned char d = 0xc3;
+	msgpack_pack_append_buffer(x, &d, 1);
+}
+
+msgpack_pack_inline_func(_false)(msgpack_pack_user x)
+{
+	static const unsigned char d = 0xc2;
+	msgpack_pack_append_buffer(x, &d, 1);
+}
+
+
+/*
+ * Array
+ */
+
+msgpack_pack_inline_func(_array)(msgpack_pack_user x, unsigned int n)
+{
+	if(n < 16) {
+		unsigned char d = 0x90 | n;
+		msgpack_pack_append_buffer(x, &d, 1);
+	} else if(n < 65536) {
+		unsigned char buf[3];
+		buf[0] = 0xdc; _msgpack_store16(&buf[1], n);
+		msgpack_pack_append_buffer(x, buf, 3);
+	} else {
+		unsigned char buf[5];
+		buf[0] = 0xdd; _msgpack_store32(&buf[1], n);
+		msgpack_pack_append_buffer(x, buf, 5);
+	}
+}
+
+
+/*
+ * Map
+ */
+
+msgpack_pack_inline_func(_map)(msgpack_pack_user x, unsigned int n)
+{
+	if(n < 16) {
+		unsigned char d = 0x80 | n;
+		msgpack_pack_append_buffer(x, &TAKE8_8(d), 1);
+	} else if(n < 65536) {
+		unsigned char buf[3];
+		buf[0] = 0xde; _msgpack_store16(&buf[1], n);
+		msgpack_pack_append_buffer(x, buf, 3);
+	} else {
+		unsigned char buf[5];
+		buf[0] = 0xdf; _msgpack_store32(&buf[1], n);
+		msgpack_pack_append_buffer(x, buf, 5);
+	}
+}
+
+
+/*
+ * Raw
+ */
+
+msgpack_pack_inline_func(_raw)(msgpack_pack_user x, size_t l)
+{
+	if(l < 32) {
+		unsigned char d = 0xa0 | l;
+		msgpack_pack_append_buffer(x, &TAKE8_8(d), 1);
+	} else if(l < 65536) {
+		unsigned char buf[3];
+		buf[0] = 0xda; _msgpack_store16(&buf[1], l);
+		msgpack_pack_append_buffer(x, buf, 3);
+	} else {
+		unsigned char buf[5];
+		buf[0] = 0xdb; _msgpack_store32(&buf[1], l);
+		msgpack_pack_append_buffer(x, buf, 5);
+	}
+}
+
+msgpack_pack_inline_func(_raw_body)(msgpack_pack_user x, const void* b, size_t l)
+{
+	msgpack_pack_append_buffer(x, (const unsigned char*)b, l);
+}
+
+#undef msgpack_pack_inline_func
+#undef msgpack_pack_user
+#undef msgpack_pack_append_buffer
+
+#undef TAKE8_8
+#undef TAKE8_16
+#undef TAKE8_32
+#undef TAKE8_64
+
+#undef msgpack_pack_real_uint8
+#undef msgpack_pack_real_uint16
+#undef msgpack_pack_real_uint32
+#undef msgpack_pack_real_uint64
+#undef msgpack_pack_real_int8
+#undef msgpack_pack_real_int16
+#undef msgpack_pack_real_int32
+#undef msgpack_pack_real_int64
+
diff --git a/php/msgpack/sysdep.h b/php/msgpack/sysdep.h
new file mode 100644
index 00000000..2bc01c9f
--- /dev/null
+++ b/php/msgpack/sysdep.h
@@ -0,0 +1,118 @@
+/*
+ * MessagePack system dependencies
+ *
+ * Copyright (C) 2008-2010 FURUHASHI Sadayuki
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+#ifndef MSGPACK_SYSDEP_H__
+#define MSGPACK_SYSDEP_H__
+
+
+#ifdef _MSC_VER
+typedef __int8 int8_t;
+typedef unsigned __int8 uint8_t;
+typedef __int16 int16_t;
+typedef unsigned __int16 uint16_t;
+typedef __int32 int32_t;
+typedef unsigned __int32 uint32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+#else
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#endif
+
+
+#ifdef _WIN32
+typedef long _msgpack_atomic_counter_t;
+#define _msgpack_sync_decr_and_fetch(ptr) InterlockedDecrement(ptr)
+#define _msgpack_sync_incr_and_fetch(ptr) InterlockedIncrement(ptr)
+#else
+typedef unsigned int _msgpack_atomic_counter_t;
+#define _msgpack_sync_decr_and_fetch(ptr) __sync_sub_and_fetch(ptr, 1)
+#define _msgpack_sync_incr_and_fetch(ptr) __sync_add_and_fetch(ptr, 1)
+#endif
+
+
+#ifdef _WIN32
+#include <winsock2.h>
+
+#ifdef __cplusplus
+/* numeric_limits<T>::min,max */
+#ifdef max
+#undef max
+#endif
+#ifdef min
+#undef min
+#endif
+#endif
+
+#else
+#include <arpa/inet.h>  /* __BYTE_ORDER */
+#endif
+
+#if !defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define __LITTLE_ENDIAN__
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define __BIG_ENDIAN__
+#endif
+#endif
+
+#ifdef __LITTLE_ENDIAN__
+
+#define _msgpack_be16(x) ntohs(x)
+#define _msgpack_be32(x) ntohl(x)
+
+#if defined(_byteswap_uint64)
+#  define _msgpack_be64(x) (_byteswap_uint64(x))
+#elif defined(bswap_64)
+#  define _msgpack_be64(x) bswap_64(x)
+#elif defined(__DARWIN_OSSwapInt64)
+#  define _msgpack_be64(x) __DARWIN_OSSwapInt64(x)
+#else
+#define _msgpack_be64(x) \
+	( ((((uint64_t)x) << 56) & 0xff00000000000000ULL ) | \
+	  ((((uint64_t)x) << 40) & 0x00ff000000000000ULL ) | \
+	  ((((uint64_t)x) << 24) & 0x0000ff0000000000ULL ) | \
+	  ((((uint64_t)x) <<  8) & 0x000000ff00000000ULL ) | \
+	  ((((uint64_t)x) >>  8) & 0x00000000ff000000ULL ) | \
+	  ((((uint64_t)x) >> 24) & 0x0000000000ff0000ULL ) | \
+	  ((((uint64_t)x) >> 40) & 0x000000000000ff00ULL ) | \
+	  ((((uint64_t)x) >> 56) & 0x00000000000000ffULL ) )
+#endif
+
+#else
+#define _msgpack_be16(x) (x)
+#define _msgpack_be32(x) (x)
+#define _msgpack_be64(x) (x)
+#endif
+
+
+#define _msgpack_store16(to, num) \
+	do { uint16_t val = _msgpack_be16(num); memcpy(to, &val, 2); } while(0);
+#define _msgpack_store32(to, num) \
+	do { uint32_t val = _msgpack_be32(num); memcpy(to, &val, 4); } while(0);
+#define _msgpack_store64(to, num) \
+	do { uint64_t val = _msgpack_be64(num); memcpy(to, &val, 8); } while(0);
+
+
+#define _msgpack_load16(cast, from) ((cast)_msgpack_be16(*(uint16_t*)from))
+#define _msgpack_load32(cast, from) ((cast)_msgpack_be32(*(uint32_t*)from))
+#define _msgpack_load64(cast, from) ((cast)_msgpack_be64(*(uint64_t*)from))
+
+
+#endif /* msgpack/sysdep.h */
+
diff --git a/php/msgpack/unpack_define.h b/php/msgpack/unpack_define.h
new file mode 100644
index 00000000..959d3519
--- /dev/null
+++ b/php/msgpack/unpack_define.h
@@ -0,0 +1,93 @@
+/*
+ * MessagePack unpacking routine template
+ *
+ * Copyright (C) 2008-2010 FURUHASHI Sadayuki
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+#ifndef MSGPACK_UNPACK_DEFINE_H__
+#define MSGPACK_UNPACK_DEFINE_H__
+
+#include "msgpack/sysdep.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#ifndef MSGPACK_EMBED_STACK_SIZE
+#define MSGPACK_EMBED_STACK_SIZE 32
+#endif
+
+
+typedef enum {
+	CS_HEADER            = 0x00,  // nil
+
+	//CS_                = 0x01,
+	//CS_                = 0x02,  // false
+	//CS_                = 0x03,  // true
+
+	//CS_                = 0x04,
+	//CS_                = 0x05,
+	//CS_                = 0x06,
+	//CS_                = 0x07,
+
+	//CS_                = 0x08,
+	//CS_                = 0x09,
+	CS_FLOAT             = 0x0a,
+	CS_DOUBLE            = 0x0b,
+	CS_UINT_8            = 0x0c,
+	CS_UINT_16           = 0x0d,
+	CS_UINT_32           = 0x0e,
+	CS_UINT_64           = 0x0f,
+	CS_INT_8             = 0x10,
+	CS_INT_16            = 0x11,
+	CS_INT_32            = 0x12,
+	CS_INT_64            = 0x13,
+
+	//CS_                = 0x14,
+	//CS_                = 0x15,
+	//CS_BIG_INT_16        = 0x16,
+	//CS_BIG_INT_32        = 0x17,
+	//CS_BIG_FLOAT_16      = 0x18,
+	//CS_BIG_FLOAT_32      = 0x19,
+	CS_RAW_16            = 0x1a,
+	CS_RAW_32            = 0x1b,
+	CS_ARRAY_16          = 0x1c,
+	CS_ARRAY_32          = 0x1d,
+	CS_MAP_16            = 0x1e,
+	CS_MAP_32            = 0x1f,
+
+	//ACS_BIG_INT_VALUE,
+	//ACS_BIG_FLOAT_VALUE,
+	ACS_RAW_VALUE,
+} msgpack_unpack_state;
+
+
+typedef enum {
+	CT_ARRAY_ITEM,
+	CT_MAP_KEY,
+	CT_MAP_VALUE,
+} msgpack_container_type;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* msgpack/unpack_define.h */
+
diff --git a/php/msgpack/unpack_template.h b/php/msgpack/unpack_template.h
new file mode 100644
index 00000000..0fbfbb78
--- /dev/null
+++ b/php/msgpack/unpack_template.h
@@ -0,0 +1,409 @@
+/*
+ * MessagePack unpacking routine template
+ *
+ * Copyright (C) 2008-2010 FURUHASHI Sadayuki
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifndef msgpack_unpack_func
+#error msgpack_unpack_func template is not defined
+#endif
+
+#ifndef msgpack_unpack_callback
+#error msgpack_unpack_callback template is not defined
+#endif
+
+#ifndef msgpack_unpack_struct
+#error msgpack_unpack_struct template is not defined
+#endif
+
+#ifndef msgpack_unpack_struct_decl
+#define msgpack_unpack_struct_decl(name) msgpack_unpack_struct(name)
+#endif
+
+#ifndef msgpack_unpack_object
+#error msgpack_unpack_object type is not defined
+#endif
+
+#ifndef msgpack_unpack_user
+#error msgpack_unpack_user type is not defined
+#endif
+
+#ifndef USE_CASE_RANGE
+#if !defined(_MSC_VER)
+#define USE_CASE_RANGE
+#endif
+#endif
+
+msgpack_unpack_struct_decl(_stack) {
+	msgpack_unpack_object obj;
+	size_t count;
+	unsigned int ct;
+	msgpack_unpack_object map_key;
+};
+
+msgpack_unpack_struct_decl(_context) {
+	msgpack_unpack_user user;
+	unsigned int cs;
+	unsigned int trail;
+	unsigned int top;
+	/*
+	msgpack_unpack_struct(_stack)* stack;
+	unsigned int stack_size;
+	msgpack_unpack_struct(_stack) embed_stack[MSGPACK_EMBED_STACK_SIZE];
+	*/
+	msgpack_unpack_struct(_stack) stack[MSGPACK_EMBED_STACK_SIZE];
+};
+
+
+msgpack_unpack_func(void, _init)(msgpack_unpack_struct(_context)* ctx)
+{
+	ctx->cs = CS_HEADER;
+	ctx->trail = 0;
+	ctx->top = 0;
+	/*
+	ctx->stack = ctx->embed_stack;
+	ctx->stack_size = MSGPACK_EMBED_STACK_SIZE;
+	*/
+	ctx->stack[0].obj = msgpack_unpack_callback(_root)(&ctx->user);
+}
+
+/*
+msgpack_unpack_func(void, _destroy)(msgpack_unpack_struct(_context)* ctx)
+{
+	if(ctx->stack_size != MSGPACK_EMBED_STACK_SIZE) {
+		free(ctx->stack);
+	}
+}
+*/
+
+msgpack_unpack_func(msgpack_unpack_object, _data)(msgpack_unpack_struct(_context)* ctx)
+{
+	return (ctx)->stack[0].obj;
+}
+
+
+msgpack_unpack_func(int, _execute)(msgpack_unpack_struct(_context)* ctx, const char* data, size_t len, size_t* off)
+{
+	assert(len >= *off);
+
+	const unsigned char* p = (unsigned char*)data + *off;
+	const unsigned char* const pe = (unsigned char*)data + len;
+	const void* n = NULL;
+
+	unsigned int trail = ctx->trail;
+	unsigned int cs = ctx->cs;
+	unsigned int top = ctx->top;
+	msgpack_unpack_struct(_stack)* stack = ctx->stack;
+	/*
+	unsigned int stack_size = ctx->stack_size;
+	*/
+	msgpack_unpack_user* user = &ctx->user;
+
+	msgpack_unpack_object obj;
+	msgpack_unpack_struct(_stack)* c = NULL;
+
+	int ret;
+
+#define push_simple_value(func) \
+	if(msgpack_unpack_callback(func)(user, &obj) < 0) { goto _failed; } \
+	goto _push
+#define push_fixed_value(func, arg) \
+	if(msgpack_unpack_callback(func)(user, arg, &obj) < 0) { goto _failed; } \
+	goto _push
+#define push_variable_value(func, base, pos, len) \
+	if(msgpack_unpack_callback(func)(user, \
+		(const char*)base, (const char*)pos, len, &obj) < 0) { goto _failed; } \
+	goto _push
+
+#define again_fixed_trail(_cs, trail_len) \
+	trail = trail_len; \
+	cs = _cs; \
+	goto _fixed_trail_again
+#define again_fixed_trail_if_zero(_cs, trail_len, ifzero) \
+	trail = trail_len; \
+	if(trail == 0) { goto ifzero; } \
+	cs = _cs; \
+	goto _fixed_trail_again
+
+#define start_container(func, count_, ct_) \
+	if(top >= MSGPACK_EMBED_STACK_SIZE) { goto _failed; } /* FIXME */ \
+	if(msgpack_unpack_callback(func)(user, count_, &stack[top].obj) < 0) { goto _failed; } \
+	if((count_) == 0) { obj = stack[top].obj; goto _push; } \
+	stack[top].ct = ct_; \
+	stack[top].count = count_; \
+	++top; \
+	/*printf("container %d count %d stack %d\n",stack[top].obj,count_,top);*/ \
+	/*printf("stack push %d\n", top);*/ \
+	/* FIXME \
+	if(top >= stack_size) { \
+		if(stack_size == MSGPACK_EMBED_STACK_SIZE) { \
+			size_t csize = sizeof(msgpack_unpack_struct(_stack)) * MSGPACK_EMBED_STACK_SIZE; \
+			size_t nsize = csize * 2; \
+			msgpack_unpack_struct(_stack)* tmp = (msgpack_unpack_struct(_stack)*)malloc(nsize); \
+			if(tmp == NULL) { goto _failed; } \
+			memcpy(tmp, ctx->stack, csize); \
+			ctx->stack = stack = tmp; \
+			ctx->stack_size = stack_size = MSGPACK_EMBED_STACK_SIZE * 2; \
+		} else { \
+			size_t nsize = sizeof(msgpack_unpack_struct(_stack)) * ctx->stack_size * 2; \
+			msgpack_unpack_struct(_stack)* tmp = (msgpack_unpack_struct(_stack)*)realloc(ctx->stack, nsize); \
+			if(tmp == NULL) { goto _failed; } \
+			ctx->stack = stack = tmp; \
+			ctx->stack_size = stack_size = stack_size * 2; \
+		} \
+	} \
+	*/ \
+	goto _header_again
+
+#define NEXT_CS(p) \
+	((unsigned int)*p & 0x1f)
+
+#ifdef USE_CASE_RANGE
+#define SWITCH_RANGE_BEGIN     switch(*p) {
+#define SWITCH_RANGE(FROM, TO) case FROM ... TO:
+#define SWITCH_RANGE_DEFAULT   default:
+#define SWITCH_RANGE_END       }
+#else
+#define SWITCH_RANGE_BEGIN     { if(0) {
+#define SWITCH_RANGE(FROM, TO) } else if(FROM <= *p && *p <= TO) {
+#define SWITCH_RANGE_DEFAULT   } else {
+#define SWITCH_RANGE_END       } }
+#endif
+
+	if(p == pe) { goto _out; }
+	do {
+		switch(cs) {
+		case CS_HEADER:
+			SWITCH_RANGE_BEGIN
+			SWITCH_RANGE(0x00, 0x7f)  // Positive Fixnum
+				push_fixed_value(_uint8, *(uint8_t*)p);
+			SWITCH_RANGE(0xe0, 0xff)  // Negative Fixnum
+				push_fixed_value(_int8, *(int8_t*)p);
+			SWITCH_RANGE(0xc0, 0xdf)  // Variable
+				switch(*p) {
+				case 0xc0:  // nil
+					push_simple_value(_nil);
+				//case 0xc1:  // string
+				//	again_terminal_trail(NEXT_CS(p), p+1);
+				case 0xc2:  // false
+					push_simple_value(_false);
+				case 0xc3:  // true
+					push_simple_value(_true);
+				//case 0xc4:
+				//case 0xc5:
+				//case 0xc6:
+				//case 0xc7:
+				//case 0xc8:
+				//case 0xc9:
+				case 0xca:  // float
+				case 0xcb:  // double
+				case 0xcc:  // unsigned int  8
+				case 0xcd:  // unsigned int 16
+				case 0xce:  // unsigned int 32
+				case 0xcf:  // unsigned int 64
+				case 0xd0:  // signed int  8
+				case 0xd1:  // signed int 16
+				case 0xd2:  // signed int 32
+				case 0xd3:  // signed int 64
+					again_fixed_trail(NEXT_CS(p), 1 << (((unsigned int)*p) & 0x03));
+				//case 0xd4:
+				//case 0xd5:
+				//case 0xd6:  // big integer 16
+				//case 0xd7:  // big integer 32
+				//case 0xd8:  // big float 16
+				//case 0xd9:  // big float 32
+				case 0xda:  // raw 16
+				case 0xdb:  // raw 32
+				case 0xdc:  // array 16
+				case 0xdd:  // array 32
+				case 0xde:  // map 16
+				case 0xdf:  // map 32
+					again_fixed_trail(NEXT_CS(p), 2 << (((unsigned int)*p) & 0x01));
+				default:
+					goto _failed;
+				}
+			SWITCH_RANGE(0xa0, 0xbf)  // FixRaw
+				again_fixed_trail_if_zero(ACS_RAW_VALUE, ((unsigned int)*p & 0x1f), _raw_zero);
+			SWITCH_RANGE(0x90, 0x9f)  // FixArray
+				start_container(_array, ((unsigned int)*p) & 0x0f, CT_ARRAY_ITEM);
+			SWITCH_RANGE(0x80, 0x8f)  // FixMap
+				start_container(_map, ((unsigned int)*p) & 0x0f, CT_MAP_KEY);
+
+			SWITCH_RANGE_DEFAULT
+				goto _failed;
+			SWITCH_RANGE_END
+			// end CS_HEADER
+
+
+		_fixed_trail_again:
+			++p;
+
+		default:
+			if((size_t)(pe - p) < trail) { goto _out; }
+			n = p;  p += trail - 1;
+			switch(cs) {
+			//case CS_
+			//case CS_
+			case CS_FLOAT: {
+					union { uint32_t i; float f; } mem;
+					mem.i = _msgpack_load32(uint32_t,n);
+					push_fixed_value(_float, mem.f); }
+			case CS_DOUBLE: {
+					union { uint64_t i; double f; } mem;
+					mem.i = _msgpack_load64(uint64_t,n);
+					push_fixed_value(_double, mem.f); }
+			case CS_UINT_8:
+				push_fixed_value(_uint8, *(uint8_t*)n);
+			case CS_UINT_16:
+				push_fixed_value(_uint16, _msgpack_load16(uint16_t,n));
+			case CS_UINT_32:
+				push_fixed_value(_uint32, _msgpack_load32(uint32_t,n));
+			case CS_UINT_64:
+				push_fixed_value(_uint64, _msgpack_load64(uint64_t,n));
+
+			case CS_INT_8:
+				push_fixed_value(_int8, *(int8_t*)n);
+			case CS_INT_16:
+				push_fixed_value(_int16, _msgpack_load16(int16_t,n));
+			case CS_INT_32:
+				push_fixed_value(_int32, _msgpack_load32(int32_t,n));
+			case CS_INT_64:
+				push_fixed_value(_int64, _msgpack_load64(int64_t,n));
+
+			//case CS_
+			//case CS_
+			//case CS_BIG_INT_16:
+			//	again_fixed_trail_if_zero(ACS_BIG_INT_VALUE, _msgpack_load16(uint16_t,n), _big_int_zero);
+			//case CS_BIG_INT_32:
+			//	again_fixed_trail_if_zero(ACS_BIG_INT_VALUE, _msgpack_load32(uint32_t,n), _big_int_zero);
+			//case ACS_BIG_INT_VALUE:
+			//_big_int_zero:
+			//	// FIXME
+			//	push_variable_value(_big_int, data, n, trail);
+
+			//case CS_BIG_FLOAT_16:
+			//	again_fixed_trail_if_zero(ACS_BIG_FLOAT_VALUE, _msgpack_load16(uint16_t,n), _big_float_zero);
+			//case CS_BIG_FLOAT_32:
+			//	again_fixed_trail_if_zero(ACS_BIG_FLOAT_VALUE, _msgpack_load32(uint32_t,n), _big_float_zero);
+			//case ACS_BIG_FLOAT_VALUE:
+			//_big_float_zero:
+			//	// FIXME
+			//	push_variable_value(_big_float, data, n, trail);
+
+			case CS_RAW_16:
+				again_fixed_trail_if_zero(ACS_RAW_VALUE, _msgpack_load16(uint16_t,n), _raw_zero);
+			case CS_RAW_32:
+				again_fixed_trail_if_zero(ACS_RAW_VALUE, _msgpack_load32(uint32_t,n), _raw_zero);
+			case ACS_RAW_VALUE:
+			_raw_zero:
+				push_variable_value(_raw, data, n, trail);
+
+			case CS_ARRAY_16:
+				start_container(_array, _msgpack_load16(uint16_t,n), CT_ARRAY_ITEM);
+			case CS_ARRAY_32:
+				/* FIXME security guard */
+				start_container(_array, _msgpack_load32(uint32_t,n), CT_ARRAY_ITEM);
+
+			case CS_MAP_16:
+				start_container(_map, _msgpack_load16(uint16_t,n), CT_MAP_KEY);
+			case CS_MAP_32:
+				/* FIXME security guard */
+				start_container(_map, _msgpack_load32(uint32_t,n), CT_MAP_KEY);
+
+			default:
+				goto _failed;
+			}
+		}
+
+_push:
+	if(top == 0) { goto _finish; }
+	c = &stack[top-1];
+	switch(c->ct) {
+	case CT_ARRAY_ITEM:
+		if(msgpack_unpack_callback(_array_item)(user, &c->obj, obj) < 0) { goto _failed; }
+		if(--c->count == 0) {
+			obj = c->obj;
+			--top;
+			/*printf("stack pop %d\n", top);*/
+			goto _push;
+		}
+		goto _header_again;
+	case CT_MAP_KEY:
+		c->map_key = obj;
+		c->ct = CT_MAP_VALUE;
+		goto _header_again;
+	case CT_MAP_VALUE:
+		if(msgpack_unpack_callback(_map_item)(user, &c->obj, c->map_key, obj) < 0) { goto _failed; }
+		if(--c->count == 0) {
+			obj = c->obj;
+			--top;
+			/*printf("stack pop %d\n", top);*/
+			goto _push;
+		}
+		c->ct = CT_MAP_KEY;
+		goto _header_again;
+
+	default:
+		goto _failed;
+	}
+
+_header_again:
+		cs = CS_HEADER;
+		++p;
+	} while(p != pe);
+	goto _out;
+
+
+_finish:
+	stack[0].obj = obj;
+	++p;
+	ret = 1;
+	/*printf("-- finish --\n"); */
+	goto _end;
+
+_failed:
+	/*printf("** FAILED **\n"); */
+	ret = -1;
+	goto _end;
+
+_out:
+	ret = 0;
+	goto _end;
+
+_end:
+	ctx->cs = cs;
+	ctx->trail = trail;
+	ctx->top = top;
+	*off = p - (const unsigned char*)data;
+
+	return ret;
+}
+
+
+#undef msgpack_unpack_func
+#undef msgpack_unpack_callback
+#undef msgpack_unpack_struct
+#undef msgpack_unpack_object
+#undef msgpack_unpack_user
+
+#undef push_simple_value
+#undef push_fixed_value
+#undef push_variable_value
+#undef again_fixed_trail
+#undef again_fixed_trail_if_zero
+#undef start_container
+
+#undef NEXT_CS
+
diff --git a/php/msgpack/version.h b/php/msgpack/version.h
new file mode 100644
index 00000000..13671d1a
--- /dev/null
+++ b/php/msgpack/version.h
@@ -0,0 +1,40 @@
+/*
+ * MessagePack for C version information
+ *
+ * Copyright (C) 2008-2009 FURUHASHI Sadayuki
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+#ifndef MSGPACK_VERSION_H__
+#define MSGPACK_VERSION_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+const char* msgpack_version(void);
+int msgpack_version_major(void);
+int msgpack_version_minor(void);
+
+#define MSGPACK_VERSION "0.5.4"
+#define MSGPACK_VERSION_MAJOR 0
+#define MSGPACK_VERSION_MINOR 5
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* msgpack/version.h */
+
diff --git a/php/msgpack_class.c b/php/msgpack_class.c
index b728cb56..5cfff8d8 100644
--- a/php/msgpack_class.c
+++ b/php/msgpack_class.c
@@ -6,33 +6,81 @@
 #include "msgpack_unpack.h"
 #include "msgpack_class.h"
 
+typedef struct {
+    zend_object object;
+    long php_only;
+} php_msgpack_base_t;
+
 typedef struct {
     zend_object object;
     smart_str buffer;
     zval *retval;
     long offset;
+    msgpack_unpack_t mp;
+    php_unserialize_data_t var_hash;
+    long php_only;
 } php_msgpack_unpacker_t;
 
 #if ZEND_MODULE_API_NO >= 20060613
-#define MAGPACK_METHOD_BASE(classname, name) zim_##classname##_##name
+#   define MSGPACK_METHOD_BASE(classname, name) zim_##classname##_##name
 #else
-#define MSGPACK_METHOD_BASE(classname, name) zif_##classname##_##name
+#   define MSGPACK_METHOD_BASE(classname, name) zif_##classname##_##name
 #endif
 
+#if ZEND_MODULE_API_NO >= 20090115
+#   define PUSH_PARAM(arg) zend_vm_stack_push(arg TSRMLS_CC)
+#   define POP_PARAM() (void)zend_vm_stack_pop(TSRMLS_C)
+#   define PUSH_EO_PARAM()
+#   define POP_EO_PARAM()
+#else
+#   define PUSH_PARAM(arg) zend_ptr_stack_push(&EG(argument_stack), arg)
+#   define POP_PARAM() (void)zend_ptr_stack_pop(&EG(argument_stack))
+#   define PUSH_EO_PARAM() zend_ptr_stack_push(&EG(argument_stack), NULL)
+#   define POP_EO_PARAM() (void)zend_ptr_stack_pop(&EG(argument_stack))
+#endif
+
+#define MSGPACK_METHOD_HELPER(classname, name, retval, thisptr, num, param) \
+    PUSH_PARAM(param); PUSH_PARAM((void*)num);                              \
+    PUSH_EO_PARAM();                                                        \
+    MSGPACK_METHOD_BASE(classname, name)(num, retval, NULL, thisptr, 0 TSRMLS_CC); \
+    POP_EO_PARAM();                                                         \
+    POP_PARAM();                                                            \
+    POP_PARAM();
+
 #define MSGPACK_METHOD(classname, name, retval, thisptr) \
-    MAGPACK_METHOD_BASE(classname, name)(0, retval, NULL, thisptr, 0 TSRMLS_CC)
+    MSGPACK_METHOD_BASE(classname, name)(0, retval, NULL, thisptr, 0 TSRMLS_CC)
+
+#define MSGPACK_METHOD1(classname, name, retval, thisptr, param1) \
+    MSGPACK_METHOD_HELPER(classname, name, retval, thisptr, 1, param1);
+
+#define MSGPACK_BASE_OBJECT   \
+    php_msgpack_base_t *base; \
+    base = (php_msgpack_base_t *)zend_object_store_get_object(getThis() TSRMLS_CC);
 
 #define MSGPACK_UNPACKER_OBJECT       \
     php_msgpack_unpacker_t *unpacker; \
-    unpacker =(php_msgpack_unpacker_t *)zend_object_store_get_object(getThis() TSRMLS_CC);
+    unpacker = (php_msgpack_unpacker_t *)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+#define MSGPACK_CLASS_OPT_PHPONLY -1001
 
 /* MessagePack */
 static zend_class_entry *msgpack_ce = NULL;
 
+static ZEND_METHOD(msgpack, __construct);
+static ZEND_METHOD(msgpack, setOption);
 static ZEND_METHOD(msgpack, pack);
 static ZEND_METHOD(msgpack, unpack);
 static ZEND_METHOD(msgpack, unpacker);
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_msgpack_base___construct, 0, 0, 0)
+    ZEND_ARG_INFO(0, opt)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_msgpack_base_setOption, 0, 0, 2)
+    ZEND_ARG_INFO(0, option)
+    ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_msgpack_base_pack, 0, 0, 1)
     ZEND_ARG_INFO(0, value)
 ZEND_END_ARG_INFO()
@@ -45,6 +93,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_msgpack_base_unpacker, 0, 0, 0)
 ZEND_END_ARG_INFO()
 
 static const zend_function_entry msgpack_base_methods[] = {
+    ZEND_ME(msgpack, __construct,
+            arginfo_msgpack_base___construct, ZEND_ACC_PUBLIC)
+    ZEND_ME(msgpack, setOption, arginfo_msgpack_base_setOption, ZEND_ACC_PUBLIC)
     ZEND_ME(msgpack, pack, arginfo_msgpack_base_pack, ZEND_ACC_PUBLIC)
     ZEND_ME(msgpack, unpack, arginfo_msgpack_base_unpack, ZEND_ACC_PUBLIC)
     ZEND_ME(msgpack, unpacker, arginfo_msgpack_base_unpacker, ZEND_ACC_PUBLIC)
@@ -54,19 +105,26 @@ static const zend_function_entry msgpack_base_methods[] = {
 /* MessagePackUnpacker */
 static zend_class_entry *msgpack_unpacker_ce = NULL;
 
-static ZEND_METHOD(msgpack, __construct);
-static ZEND_METHOD(msgpack, __destruct);
-static ZEND_METHOD(msgpack, feed);
-static ZEND_METHOD(msgpack, execute);
-static ZEND_METHOD(msgpack, data);
-static ZEND_METHOD(msgpack, reset);
+static ZEND_METHOD(msgpack_unpacker, __construct);
+static ZEND_METHOD(msgpack_unpacker, __destruct);
+static ZEND_METHOD(msgpack_unpacker, setOption);
+static ZEND_METHOD(msgpack_unpacker, feed);
+static ZEND_METHOD(msgpack_unpacker, execute);
+static ZEND_METHOD(msgpack_unpacker, data);
+static ZEND_METHOD(msgpack_unpacker, reset);
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_msgpack_unpacker___construct, 0, 0, 0)
+    ZEND_ARG_INFO(0, opt)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_msgpack_unpacker___destruct, 0, 0, 0)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_msgpack_unpacker_setOption, 0, 0, 2)
+    ZEND_ARG_INFO(0, option)
+    ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_msgpack_unpacker_feed, 0, 0, 1)
     ZEND_ARG_INFO(0, str)
 ZEND_END_ARG_INFO()
@@ -83,17 +141,52 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_msgpack_unpacker_reset, 0, 0, 0)
 ZEND_END_ARG_INFO()
 
 static const zend_function_entry msgpack_unpacker_methods[] = {
-    ZEND_ME(msgpack, __construct,
+    ZEND_ME(msgpack_unpacker, __construct,
             arginfo_msgpack_unpacker___construct, ZEND_ACC_PUBLIC)
-    ZEND_ME(msgpack, __destruct,
+    ZEND_ME(msgpack_unpacker, __destruct,
             arginfo_msgpack_unpacker___destruct, ZEND_ACC_PUBLIC)
-    ZEND_ME(msgpack, feed, arginfo_msgpack_unpacker_feed, ZEND_ACC_PUBLIC)
-    ZEND_ME(msgpack, execute, arginfo_msgpack_unpacker_execute, ZEND_ACC_PUBLIC)
-    ZEND_ME(msgpack, data, arginfo_msgpack_unpacker_data, ZEND_ACC_PUBLIC)
-    ZEND_ME(msgpack, reset, arginfo_msgpack_unpacker_reset, ZEND_ACC_PUBLIC)
+    ZEND_ME(msgpack_unpacker, setOption,
+            arginfo_msgpack_unpacker_setOption, ZEND_ACC_PUBLIC)
+    ZEND_ME(msgpack_unpacker, feed,
+            arginfo_msgpack_unpacker_feed, ZEND_ACC_PUBLIC)
+    ZEND_ME(msgpack_unpacker, execute,
+            arginfo_msgpack_unpacker_execute, ZEND_ACC_PUBLIC)
+    ZEND_ME(msgpack_unpacker, data,
+            arginfo_msgpack_unpacker_data, ZEND_ACC_PUBLIC)
+    ZEND_ME(msgpack_unpacker, reset,
+            arginfo_msgpack_unpacker_reset, ZEND_ACC_PUBLIC)
     {NULL, NULL, NULL}
 };
 
+static void php_msgpack_base_free(php_msgpack_base_t *base TSRMLS_DC)
+{
+    zend_object_std_dtor(&base->object TSRMLS_CC);
+    efree(base);
+}
+
+static zend_object_value php_msgpack_base_new(zend_class_entry *ce TSRMLS_DC)
+{
+    zend_object_value retval;
+    zval *tmp;
+    php_msgpack_base_t *base;
+
+    base = emalloc(sizeof(php_msgpack_base_t));
+
+    zend_object_std_init(&base->object, ce TSRMLS_CC);
+
+    zend_hash_copy(
+        base->object.properties, &ce->default_properties,
+        (copy_ctor_func_t)zval_add_ref, (void *)&tmp, sizeof(zval *));
+
+    retval.handle = zend_objects_store_put(
+        base, (zend_objects_store_dtor_t)zend_objects_destroy_object,
+        (zend_objects_free_object_storage_t)php_msgpack_base_free,
+        NULL TSRMLS_CC);
+    retval.handlers = zend_get_std_object_handlers();
+
+    return retval;
+}
+
 static void php_msgpack_unpacker_free(
     php_msgpack_unpacker_t *unpacker TSRMLS_DC)
 {
@@ -126,10 +219,58 @@ static zend_object_value php_msgpack_unpacker_new(
 }
 
 /* MessagePack */
+static ZEND_METHOD(msgpack, __construct)
+{
+    bool php_only = MSGPACK_G(php_only);
+    MSGPACK_BASE_OBJECT;
+
+    if (zend_parse_parameters(
+            ZEND_NUM_ARGS() TSRMLS_CC, "|b", &php_only) == FAILURE)
+    {
+        return;
+    }
+
+    base->php_only = php_only;
+}
+
+static ZEND_METHOD(msgpack, setOption)
+{
+    long option;
+    zval *value;
+    MSGPACK_BASE_OBJECT;
+
+    if (zend_parse_parameters(
+            ZEND_NUM_ARGS() TSRMLS_CC, "lz", &option, &value) == FAILURE)
+    {
+        return;
+    }
+
+    switch (option)
+    {
+        case MSGPACK_CLASS_OPT_PHPONLY:
+            convert_to_boolean(value);
+            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");
+            }
+            RETURN_FALSE;
+            break;
+    }
+
+    RETURN_TRUE;
+}
+
 static ZEND_METHOD(msgpack, pack)
 {
     zval *parameter;
     smart_str buf = {0};
+    int php_only = MSGPACK_G(php_only);
+    MSGPACK_BASE_OBJECT;
 
     if (zend_parse_parameters(
             ZEND_NUM_ARGS() TSRMLS_CC, "z", &parameter) == FAILURE)
@@ -137,8 +278,12 @@ static ZEND_METHOD(msgpack, pack)
         return;
     }
 
+    MSGPACK_G(php_only) = base->php_only;
+
     php_msgpack_serialize(&buf, parameter TSRMLS_CC);
 
+    MSGPACK_G(php_only) = php_only;
+
     ZVAL_STRINGL(return_value, buf.c, buf.len, 1);
 
     smart_str_free(&buf);
@@ -148,6 +293,8 @@ static ZEND_METHOD(msgpack, unpack)
 {
     char *str;
     int str_len;
+    int php_only = MSGPACK_G(php_only);
+    MSGPACK_BASE_OBJECT;
 
     if (zend_parse_parameters(
             ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE)
@@ -160,31 +307,57 @@ static ZEND_METHOD(msgpack, unpack)
         RETURN_NULL();
     }
 
+    MSGPACK_G(php_only) = base->php_only;
+
     php_msgpack_unserialize(return_value, str, str_len TSRMLS_CC);
+
+    MSGPACK_G(php_only) = php_only;
 }
 
 static ZEND_METHOD(msgpack, unpacker)
 {
-    zval temp;
+    zval temp, *opt;
+    MSGPACK_BASE_OBJECT;
+
+    ALLOC_INIT_ZVAL(opt);
+    ZVAL_BOOL(opt, base->php_only);
 
     object_init_ex(return_value, msgpack_unpacker_ce);
 
-    MSGPACK_METHOD(msgpack, __construct, &temp, return_value);
+    MSGPACK_METHOD1(msgpack_unpacker, __construct, &temp, return_value, opt);
+
+    zval_ptr_dtor(&opt);
 }
 
 /* MessagePackUnpacker */
-static ZEND_METHOD(msgpack, __construct)
+static ZEND_METHOD(msgpack_unpacker, __construct)
 {
+    bool php_only = MSGPACK_G(php_only);
     MSGPACK_UNPACKER_OBJECT;
 
+    if (zend_parse_parameters(
+            ZEND_NUM_ARGS() TSRMLS_CC, "|b", &php_only) == FAILURE)
+    {
+        return;
+    }
+
+    unpacker->php_only = php_only;
+
     unpacker->buffer.c = NULL;
     unpacker->buffer.len = 0;
     unpacker->buffer.a = 0;
     unpacker->retval = NULL;
     unpacker->offset = 0;
+
+    template_init(&unpacker->mp);
+
+    msgpack_unserialize_var_init(&unpacker->var_hash);
+
+    (&unpacker->mp)->user.var_hash =
+        (php_unserialize_data_t *)&unpacker->var_hash;
 }
 
-static ZEND_METHOD(msgpack, __destruct)
+static ZEND_METHOD(msgpack_unpacker, __destruct)
 {
     MSGPACK_UNPACKER_OBJECT;
 
@@ -194,9 +367,43 @@ static ZEND_METHOD(msgpack, __destruct)
     {
         zval_ptr_dtor(&unpacker->retval);
     }
+
+    msgpack_unserialize_var_destroy(&unpacker->var_hash);
 }
 
-static ZEND_METHOD(msgpack, feed)
+static ZEND_METHOD(msgpack_unpacker, setOption)
+{
+    long option;
+    zval *value;
+    MSGPACK_UNPACKER_OBJECT;
+
+    if (zend_parse_parameters(
+            ZEND_NUM_ARGS() TSRMLS_CC, "lz", &option, &value) == FAILURE)
+    {
+        return;
+    }
+
+    switch (option)
+    {
+        case MSGPACK_CLASS_OPT_PHPONLY:
+            convert_to_boolean(value);
+            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");
+            }
+            RETURN_FALSE;
+            break;
+    }
+
+    RETURN_TRUE;
+}
+
+static ZEND_METHOD(msgpack_unpacker, feed)
 {
     char *str;
     int str_len;
@@ -218,14 +425,15 @@ static ZEND_METHOD(msgpack, feed)
     RETURN_TRUE;
 }
 
-static ZEND_METHOD(msgpack, execute)
+static ZEND_METHOD(msgpack_unpacker, execute)
 {
-    char *str = NULL;
+    char *str = NULL, *data;
     long str_len = 0;
     zval *offset;
     int ret;
-    php_unserialize_data_t var_hash;
-    msgpack_unserialize_data mpsd;
+    size_t len, off;
+    int error_display = MSGPACK_G(error_display);
+    int php_only = MSGPACK_G(php_only);
     MSGPACK_UNPACKER_OBJECT;
 
     if (zend_parse_parameters(
@@ -237,45 +445,38 @@ static ZEND_METHOD(msgpack, execute)
 
     if (str != NULL)
     {
-        mpsd.data = (unsigned char *)str;
-        mpsd.length = str_len;
-        mpsd.offset = Z_LVAL_P(offset);
+        data = (char *)str;
+        len = (size_t)str_len;
+        off = Z_LVAL_P(offset);
     }
     else
     {
-        mpsd.data = (unsigned char *)unpacker->buffer.c;
-        mpsd.length = unpacker->buffer.len;
-        mpsd.offset = unpacker->offset;
+        data = (char *)unpacker->buffer.c;
+        len = unpacker->buffer.len;
+        off = unpacker->offset;
     }
 
-    if (mpsd.length <= 0 || mpsd.length == mpsd.offset)
-    {
-        RETURN_FALSE;
-    }
-
-    PHP_VAR_UNSERIALIZE_INIT(var_hash);
-
     if (unpacker->retval == NULL)
     {
         ALLOC_INIT_ZVAL(unpacker->retval);
     }
+    (&unpacker->mp)->user.retval = (zval *)unpacker->retval;
 
     MSGPACK_G(error_display) = 0;
+    MSGPACK_G(php_only) = unpacker->php_only;
 
-    ret = msgpack_unserialize_zval(
-        &unpacker->retval, &mpsd, &var_hash TSRMLS_CC);
+    ret = template_execute(&unpacker->mp, data, len, &off);
 
-    MSGPACK_G(error_display) = 1;
-
-    PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
+    MSGPACK_G(error_display) = error_display;
+    MSGPACK_G(php_only) = php_only;
 
     if (str != NULL)
     {
-        ZVAL_LONG(offset, mpsd.offset);
+        ZVAL_LONG(offset, off);
     }
     else
     {
-        unpacker->offset = mpsd.offset;
+        unpacker->offset = off;
     }
 
     switch (ret)
@@ -288,14 +489,14 @@ static ZEND_METHOD(msgpack, execute)
     }
 }
 
-static ZEND_METHOD(msgpack, data)
+static ZEND_METHOD(msgpack_unpacker, data)
 {
     MSGPACK_UNPACKER_OBJECT;
 
     RETURN_ZVAL(unpacker->retval, 1, 1);
 }
 
-static ZEND_METHOD(msgpack, reset)
+static ZEND_METHOD(msgpack_unpacker, reset)
 {
     smart_str buffer = {0};
     MSGPACK_UNPACKER_OBJECT;
@@ -325,15 +526,33 @@ static ZEND_METHOD(msgpack, reset)
         zval_ptr_dtor(&unpacker->retval);
         unpacker->retval = NULL;
     }
+
+    msgpack_unserialize_var_destroy(&unpacker->var_hash);
+
+
+    msgpack_unserialize_var_init(&unpacker->var_hash);
+
+    (&unpacker->mp)->user.var_hash =
+        (php_unserialize_data_t *)&unpacker->var_hash;
+
+    msgpack_unserialize_init(&((&unpacker->mp)->user));
 }
 
-void msgpack_init_class(TSRMLS_DC)
+void msgpack_init_class()
 {
     zend_class_entry ce;
+    TSRMLS_FETCH();
 
+    /* base */
     INIT_CLASS_ENTRY(ce, "MessagePack", msgpack_base_methods);
     msgpack_ce = zend_register_internal_class(&ce TSRMLS_CC);
+    msgpack_ce->create_object = php_msgpack_base_new;
 
+    zend_declare_class_constant_long(
+        msgpack_ce, ZEND_STRS("OPT_PHPONLY") - 1,
+        MSGPACK_CLASS_OPT_PHPONLY TSRMLS_CC);
+
+    /* unpacker */
     INIT_CLASS_ENTRY(ce, "MessagePackUnpacker", msgpack_unpacker_methods);
     msgpack_unpacker_ce = zend_register_internal_class(&ce TSRMLS_CC);
     msgpack_unpacker_ce->create_object = php_msgpack_unpacker_new;
diff --git a/php/msgpack_class.h b/php/msgpack_class.h
index 00ac9e7b..fbebaf4d 100644
--- a/php/msgpack_class.h
+++ b/php/msgpack_class.h
@@ -1,7 +1,7 @@
 
-#ifndef MSGPACL_CLASS_H
-#define MSGPACL_CLASS_H
+#ifndef MSGPACK_CLASS_H
+#define MSGPACK_CLASS_H
 
-void msgpack_init_class(TSRMLS_DC);
+void msgpack_init_class();
 
 #endif
diff --git a/php/msgpack_pack.c b/php/msgpack_pack.c
index 91a3d396..d2d4ba3b 100644
--- a/php/msgpack_pack.c
+++ b/php/msgpack_pack.c
@@ -119,13 +119,14 @@ inline static void msgpack_serialize_class(
 
             if (Z_TYPE_PP(name) != IS_STRING)
             {
-                zend_error(E_NOTICE,
-                           "[msgpack] (msgpack_serialize_class) "
-                           "__sleep should return an array only "
-                           "containing the names of "
-                           "instance-variables to serialize.");
-
-                msgpack_pack_nil(buf);
+                if (MSGPACK_G(error_display))
+                {
+                    zend_error(E_NOTICE,
+                               "[msgpack] (msgpack_serialize_class) "
+                               "__sleep should return an array only "
+                               "containing the names of "
+                               "instance-variables to serialize.");
+                }
                 continue;
             }
 
@@ -195,11 +196,14 @@ inline static void msgpack_serialize_class(
 
                         pefree(prot_name, ce->type & ZEND_INTERNAL_CLASS);
 
-                        zend_error(E_NOTICE,
-                                   "[msgpack] (msgpack_serialize_class) "
-                                   "\"%s\" returned as member variable from "
-                                   "__sleep() but does not exist",
-                                   Z_STRVAL_PP(name));
+                        if (MSGPACK_G(error_display))
+                        {
+                            zend_error(E_NOTICE,
+                                       "[msgpack] (msgpack_serialize_class) "
+                                       "\"%s\" returned as member variable from "
+                                       "__sleep() but does not exist",
+                                       Z_STRVAL_PP(name));
+                        }
 
                         msgpack_serialize_string(
                             buf, Z_STRVAL_PP(name), Z_STRLEN_PP(name));
@@ -227,6 +231,7 @@ inline static void msgpack_serialize_array(
 {
     HashTable *ht;
     size_t n;
+    bool hash = true;
 
     if (object)
     {
@@ -253,19 +258,46 @@ inline static void msgpack_serialize_array(
 
     if (object)
     {
-        msgpack_pack_map(buf, n + 1);
+        if (MSGPACK_G(php_only))
+        {
+            if (Z_ISREF_P(val))
+            {
+                msgpack_pack_map(buf, n + 2);
+                msgpack_pack_nil(buf);
+                msgpack_pack_long(buf, MSGPACK_SERIALIZE_TYPE_REFERENCE);
+            }
+            else
+            {
+                msgpack_pack_map(buf, n + 1);
+            }
 
-        msgpack_pack_nil(buf);
+            msgpack_pack_nil(buf);
 
-        msgpack_serialize_string(buf, class_name, name_len);
+            msgpack_serialize_string(buf, class_name, name_len);
+        }
+        else
+        {
+            msgpack_pack_array(buf, n);
+            hash = false;
+        }
     }
     else if (n == 0)
     {
+        hash = false;
         msgpack_pack_array(buf, n);
     }
     else
     {
-        msgpack_pack_map(buf, n);
+        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);
+        }
     }
 
     if (n > 0)
@@ -292,20 +324,26 @@ inline static void msgpack_serialize_array(
                 continue;
             }
 
-            switch (key_type)
+            if (hash)
             {
-                case HASH_KEY_IS_LONG:
-                    msgpack_pack_long(buf, key_index);
-                    break;
-                case HASH_KEY_IS_STRING:
-                    msgpack_serialize_string(buf, key, key_len - 1);
-                    break;
-                default:
-                    msgpack_serialize_string(buf, "", sizeof(""));
-
-                    zend_error(E_WARNING, "[msgpack] (msgpack_serialize_array) "
-                               "key is not string nor array");
-                    break;
+                switch (key_type)
+                {
+                    case HASH_KEY_IS_LONG:
+                        msgpack_pack_long(buf, key_index);
+                        break;
+                    case HASH_KEY_IS_STRING:
+                        msgpack_serialize_string(buf, key, key_len - 1);
+                        break;
+                    default:
+                        msgpack_serialize_string(buf, "", sizeof(""));
+                        if (MSGPACK_G(error_display))
+                        {
+                            zend_error(E_WARNING,
+                                       "[msgpack] (msgpack_serialize_array) "
+                                       "key is not string nor array");
+                        }
+                        break;
+                }
             }
 
             if (zend_hash_get_current_data_ex(
@@ -365,7 +403,6 @@ inline static void msgpack_serialize_object(
             msgpack_pack_long(buf, MSGPACK_SERIALIZE_TYPE_CUSTOM_OBJECT);
 
             msgpack_serialize_string(buf, ce->name, ce->name_length);
-
             msgpack_pack_raw(buf, serialized_length);
             msgpack_pack_raw_body(buf, serialized_data, serialized_length);
         }
@@ -401,11 +438,14 @@ inline static void msgpack_serialize_object(
                 }
                 else
                 {
-                    zend_error(E_NOTICE,
-                               "[msgpack] (msgpack_serialize_object) "
-                               "__sleep should return an array only "
-                               "containing the names of instance-variables "
-                               "to serialize");
+                    if (MSGPACK_G(error_display))
+                    {
+                        zend_error(E_NOTICE,
+                                   "[msgpack] (msgpack_serialize_object) "
+                                   "__sleep should return an array only "
+                                   "containing the names of instance-variables "
+                                   "to serialize");
+                    }
                     msgpack_pack_nil(buf);
                 }
                 zval_ptr_dtor(&retval_ptr);
@@ -429,18 +469,19 @@ void msgpack_serialize_zval(
 {
     ulong *var_already;
 
-    if (var_hash &&
+    if (MSGPACK_G(php_only) &&
+        var_hash &&
         msgpack_var_add(
             var_hash, val, (void *)&var_already TSRMLS_CC) == FAILURE)
     {
-        if (Z_ISREF_P(val))
+        if (Z_ISREF_P(val) && Z_TYPE_P(val) == IS_ARRAY)
         {
             msgpack_pack_map(buf, 2);
 
             msgpack_pack_nil(buf);
-            msgpack_pack_long(buf, MSGPACK_SERIALIZE_TYPE_REFERENCE);
+            msgpack_pack_long(buf, MSGPACK_SERIALIZE_TYPE_RECURSIVE);
 
-            msgpack_pack_nil(buf);
+            msgpack_pack_long(buf, 0);
             msgpack_pack_long(buf, *var_already);
 
             return;
@@ -450,9 +491,9 @@ void msgpack_serialize_zval(
             msgpack_pack_map(buf, 2);
 
             msgpack_pack_nil(buf);
-            msgpack_pack_long(buf, MSGPACK_SERIALIZE_TYPE_OBJECT);
+            msgpack_pack_long(buf, MSGPACK_SERIALIZE_TYPE_RECURSIVE);
 
-            msgpack_pack_nil(buf);
+            msgpack_pack_long(buf, 0);
             msgpack_pack_long(buf, *var_already);
 
             return;
@@ -504,9 +545,12 @@ void msgpack_serialize_zval(
             }
             break;
         default:
-            zend_error(E_WARNING,
-                       "[msgpack] (php_msgpack_serialize) "
-                       "type is unsupported, encoded as null");
+            if (MSGPACK_G(error_display))
+            {
+                zend_error(E_WARNING,
+                           "[msgpack] (php_msgpack_serialize) "
+                           "type is unsupported, encoded as null");
+            }
             msgpack_pack_nil(buf);
             break;
     }
diff --git a/php/msgpack_pack.h b/php/msgpack_pack.h
index 96cfd4e8..16f3e7bc 100644
--- a/php/msgpack_pack.h
+++ b/php/msgpack_pack.h
@@ -1,13 +1,14 @@
 
-#ifndef MSGPACL_PACK_H
-#define MSGPACL_PACK_H
+#ifndef MSGPACK_PACK_H
+#define MSGPACK_PACK_H
 
 #include "ext/standard/php_smart_str.h"
 
 enum msgpack_serialize_type
 {
+    MSGPACK_SERIALIZE_TYPE_NONE =  0,
     MSGPACK_SERIALIZE_TYPE_REFERENCE =  1,
-    MSGPACK_SERIALIZE_TYPE_OBJECT,
+    MSGPACK_SERIALIZE_TYPE_RECURSIVE,
     MSGPACK_SERIALIZE_TYPE_CUSTOM_OBJECT,
 };
 
diff --git a/php/msgpack_unpack.c b/php/msgpack_unpack.c
index c6b9b3f9..f3b610d2 100644
--- a/php/msgpack_unpack.c
+++ b/php/msgpack_unpack.c
@@ -2,31 +2,69 @@
 #include "php.h"
 #include "php_ini.h"
 #include "ext/standard/php_incomplete_class.h"
-#include "ext/standard/php_var.h"
 
 #include "php_msgpack.h"
 #include "msgpack_pack.h"
 #include "msgpack_unpack.h"
 
-#include "msgpack/unpack_define.h"
-
-#define VAR_ENTRIES_MAX 1024
-
 #if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 3)
 #   define Z_ADDREF_PP(ppz)      ZVAL_ADDREF(*(ppz))
 #   define Z_SET_ISREF_PP(ppz)   (*(ppz))->is_ref = 1
 #   define Z_UNSET_ISREF_PP(ppz) (*(ppz))->is_ref = 0
 #endif
 
+#define VAR_ENTRIES_MAX 1024
+
 typedef struct
 {
     zval *data[VAR_ENTRIES_MAX];
     long used_slots;
+    long value_slots;
+    long access_slots[VAR_ENTRIES_MAX];
+    bool alloc_slots[VAR_ENTRIES_MAX];
     void *next;
 } var_entries;
 
+#define MSGPACK_UNSERIALIZE_ALLOC(_unpack)                     \
+    if (_unpack->deps <= 0) {                                  \
+        *obj = _unpack->retval;                                \
+        msgpack_var_push(_unpack->var_hash, obj, true, false); \
+    } else {                                                   \
+        ALLOC_INIT_ZVAL(*obj);                                 \
+        msgpack_var_push(_unpack->var_hash, obj, false, true); \
+    }
+
+#define MSGPACK_UNSERIALIZE_ALLOC_VALUE(_unpack)               \
+    if (_unpack->deps <= 0) {                                  \
+        *obj = _unpack->retval;                                \
+        msgpack_var_push(_unpack->var_hash, obj, true, false); \
+    } else {                                                   \
+        ALLOC_INIT_ZVAL(*obj);                                 \
+        msgpack_var_push(_unpack->var_hash, obj, true, true);  \
+    }
+
+#define MSGPACK_UNSERIALIZE_PUSH_ITEM(_unpack, _count, _val)         \
+    msgpack_var_alloc(_unpack->var_hash, _count);                    \
+    if (Z_TYPE_P(_val) != IS_ARRAY && Z_TYPE_P(_val) != IS_OBJECT) { \
+        msgpack_var_push(_unpack->var_hash, &_val, true, false);     \
+    }
+
+#define MSGPACK_UNSERIALIZE_FINISH_ITEM(_unpack) \
+    long deps = _unpack->deps - 1;               \
+    _unpack->stack[deps]--;                      \
+    if (_unpack->stack[deps] == 0) {             \
+        _unpack->deps--;                         \
+    }
+
+#define MSGPACK_UNSERIALIZE_FINISH_MAP_ITEM(_unpack, _key, _val) \
+    zval_ptr_dtor(&_key);                                        \
+    zval_ptr_dtor(&_val);                                        \
+    msgpack_var_alloc(_unpack->var_hash, 2);                     \
+    MSGPACK_UNSERIALIZE_FINISH_ITEM(_unpack);
+
+
 inline static void msgpack_var_push(
-    php_unserialize_data_t *var_hashx, zval **rval)
+    php_unserialize_data_t *var_hashx, zval **rval, bool value, bool alloc)
 {
     var_entries *var_hash, *prev = NULL;
 
@@ -47,6 +85,7 @@ inline static void msgpack_var_push(
     {
         var_hash = emalloc(sizeof(var_entries));
         var_hash->used_slots = 0;
+        var_hash->value_slots = 0;
         var_hash->next = 0;
 
         if (!var_hashx->first)
@@ -59,9 +98,45 @@ inline static void msgpack_var_push(
         }
     }
 
+    var_hash->alloc_slots[var_hash->used_slots] = alloc;
+
+    if (value)
+    {
+        var_hash->access_slots[var_hash->value_slots++] = var_hash->used_slots;
+    }
+
     var_hash->data[var_hash->used_slots++] = *rval;
 }
 
+inline static void msgpack_var_alloc(
+    php_unserialize_data_t *var_hashx, long count)
+{
+    long i;
+    var_entries *var_hash = var_hashx->first;
+
+    while (var_hash && var_hash->used_slots == VAR_ENTRIES_MAX)
+    {
+        var_hash = var_hash->next;
+    }
+
+    if (!var_hash || count <= 0)
+    {
+        return;
+    }
+
+    for (i = var_hash->used_slots - 1; i >= 0; i--)
+    {
+        if (var_hash->alloc_slots[i])
+        {
+            var_hash->alloc_slots[i] = false;
+            if (--count <= 0)
+            {
+                break;
+            }
+        }
+    }
+}
+
 inline static int msgpack_var_access(
     php_unserialize_data_t *var_hashx, long id, zval ***store)
 {
@@ -79,6 +154,13 @@ inline static int msgpack_var_access(
         return !SUCCESS;
     }
 
+    if (id < 0 || id >= var_hash->value_slots)
+    {
+        return !SUCCESS;
+    }
+
+    id = var_hash->access_slots[id];
+
     if (id < 0 || id >= var_hash->used_slots)
     {
         return !SUCCESS;
@@ -89,741 +171,533 @@ inline static int msgpack_var_access(
     return SUCCESS;
 }
 
-inline static int msgpack_unserialize_array(
-    zval **return_value, msgpack_unserialize_data *mpsd,
-    ulong ct, php_unserialize_data_t *var_hash TSRMLS_DC)
+inline static zend_class_entry* msgpack_unserialize_class(
+    zval **container, char *class_name, size_t name_len)
 {
-    ulong i;
-    HashTable *ht;
-
-    msgpack_var_push(var_hash, return_value);
-
-    if (Z_TYPE_PP(return_value) != IS_ARRAY)
-    {
-        array_init(*return_value);
-    }
-
-    ht = HASH_OF(*return_value);
-
-    for (i = 0; i < ct; i++)
-    {
-        zval *val;
-
-        /* value */
-        ALLOC_INIT_ZVAL(val);
-
-        if (msgpack_unserialize_zval(&val, mpsd, var_hash TSRMLS_CC) <= 0)
-        {
-            if (MSGPACK_G(error_display))
-            {
-                zend_error(E_WARNING,
-                           "[msgpack] (msgpack_unserialize_array) "
-                           "Invalid value");
-            }
-            zval_ptr_dtor(&val);
-
-            return MSGPACK_UNPACK_PARSE_ERROR;
-        }
-
-        /* update */
-        zend_hash_index_update(ht, i, &val, sizeof(zval *), NULL);
-    }
-
-    return MSGPACK_UNPACK_SUCCESS;
-}
-
-inline static int msgpack_unserialize_object_type(
-    zval **return_value, msgpack_unserialize_data *mpsd,
-    php_unserialize_data_t *var_hash, zend_bool is_ref TSRMLS_DC)
-{
-    int ret = MSGPACK_UNPACK_SUCCESS;
-    zval *key, *val, **rval;
-
-    ALLOC_INIT_ZVAL(key);
-
-    if (msgpack_unserialize_zval(&key, mpsd, NULL TSRMLS_CC) <= 0)
-    {
-        zval_ptr_dtor(&key);
-
-        ZVAL_BOOL(*return_value, 0);
-
-        return MSGPACK_UNPACK_PARSE_ERROR;
-    }
-
-    ALLOC_INIT_ZVAL(val);
-
-    if (is_ref)
-    {
-        ret = msgpack_unserialize_zval(&val, mpsd, NULL TSRMLS_CC);
-    }
-    else
-    {
-        ret = msgpack_unserialize_zval(&val, mpsd, var_hash TSRMLS_CC);
-    }
-
-    if (ret <= 0)
-    {
-        zval_ptr_dtor(&val);
-        zval_ptr_dtor(&key);
-
-        ZVAL_BOOL(*return_value, 0);
-
-        return MSGPACK_UNPACK_PARSE_ERROR;
-    }
-
-    if (!var_hash ||
-        msgpack_var_access(var_hash, Z_LVAL_P(val) - 1, &rval) != SUCCESS)
-    {
-        if (MSGPACK_G(error_display))
-        {
-            zend_error(E_WARNING,
-                       "[msgpack] (msgpack_unserialize_object_key) "
-                       "Invalid references value: %ld",
-                       Z_LVAL_P(val) - 1);
-        }
-        ret = MSGPACK_UNPACK_CONTINUE;
-    }
-    else
-    {
-        if (*return_value != NULL)
-        {
-            zval_ptr_dtor(return_value);
-        }
-
-        *return_value = *rval;
-
-        Z_ADDREF_PP(return_value);
-
-        if (is_ref)
-        {
-            Z_SET_ISREF_PP(return_value);
-        }
-        else
-        {
-            Z_UNSET_ISREF_PP(return_value);
-        }
-    }
-
-    zval_ptr_dtor(&key);
-    zval_ptr_dtor(&val);
-
-    return ret;
-}
-
-inline static int msgpack_unserialize_object(
-    zval **return_value, msgpack_unserialize_data *mpsd,
-    ulong ct, php_unserialize_data_t *var_hash TSRMLS_DC)
-{
-    int ret = MSGPACK_UNPACK_SUCCESS;
-
     zend_class_entry *ce, **pce;
     bool incomplete_class = false;
     zval *user_func, *retval_ptr, **args[1], *arg_func_name;
-    HashTable *ht;
+    TSRMLS_FETCH();
 
-    zval *key, *val;
+    do
+    {
+        /* Try to find class directly */
+        if (zend_lookup_class(class_name, name_len, &pce TSRMLS_CC) == SUCCESS)
+        {
+            ce = *pce;
+            break;
+        }
 
-    int object = 1;
-    int custom_object = 0;
+        /* Check for unserialize callback */
+        if ((PG(unserialize_callback_func) == NULL) ||
+            (PG(unserialize_callback_func)[0] == '\0'))
+        {
+            incomplete_class = 1;
+            ce = PHP_IC_ENTRY;
+            break;
+        }
 
-    /* Get class */
-    ALLOC_INIT_ZVAL(key);
+        /* Call unserialize callback */
+        ALLOC_INIT_ZVAL(user_func);
+        ZVAL_STRING(user_func, PG(unserialize_callback_func), 1);
+        args[0] = &arg_func_name;
+        ALLOC_INIT_ZVAL(arg_func_name);
+        ZVAL_STRING(arg_func_name, class_name, 1);
+        if (call_user_function_ex(
+                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] (msgpack_unserialize_class) "
+                           "defined (%s) but not found", class_name);
+            }
 
-    if (msgpack_unserialize_zval(&key, mpsd, NULL TSRMLS_CC) <= 0)
+            incomplete_class = 1;
+            ce = PHP_IC_ENTRY;
+            zval_ptr_dtor(&user_func);
+            zval_ptr_dtor(&arg_func_name);
+            break;
+        }
+        if (retval_ptr)
+        {
+            zval_ptr_dtor(&retval_ptr);
+        }
+
+        /* The callback function may have defined the class */
+        if (zend_lookup_class(class_name, name_len, &pce TSRMLS_CC) == SUCCESS)
+        {
+            ce = *pce;
+        }
+        else
+        {
+            if (MSGPACK_G(error_display))
+            {
+                zend_error(E_WARNING,
+                           "[msgpack] (msgpack_unserialize_class) "
+                           "Function %s() hasn't defined the class "
+                           "it was called for", class_name);
+            }
+
+            incomplete_class = true;
+            ce = PHP_IC_ENTRY;
+        }
+
+        zval_ptr_dtor(&user_func);
+        zval_ptr_dtor(&arg_func_name);
+    }
+    while(0);
+
+    if (EG(exception))
     {
         if (MSGPACK_G(error_display))
         {
             zend_error(E_WARNING,
-                       "[msgpack] (msgpack_unserialize_object) "
-                       "Invalid sign key");
+                       "[msgpack] (msgpack_unserialize_class) "
+                       "Exception error");
         }
 
-        zval_ptr_dtor(&key);
-
-        ZVAL_BOOL(*return_value, 0);
-
-        return MSGPACK_UNPACK_PARSE_ERROR;
+        return NULL;
     }
 
-    ct--;
+    object_init_ex(*container, ce);
 
-    if (Z_TYPE_P(key) == IS_NULL)
+    /* store incomplete class name */
+    if (incomplete_class)
     {
-        ALLOC_INIT_ZVAL(val);
+        php_store_class_name(*container, class_name, name_len);
+    }
 
-        if (msgpack_unserialize_zval(&val, mpsd, NULL TSRMLS_CC) <= 0)
+    return ce;
+}
+
+void msgpack_unserialize_var_init(php_unserialize_data_t *var_hashx)
+{
+    var_hashx->first = 0;
+    var_hashx->first_dtor = 0;
+}
+
+void msgpack_unserialize_var_destroy(php_unserialize_data_t *var_hashx)
+{
+    void *next;
+    long i;
+    var_entries *var_hash = var_hashx->first;
+
+    while (var_hash)
+    {
+        for (i = 0; i < var_hash->used_slots; i++)
         {
-            if (MSGPACK_G(error_display))
+            if (var_hash->alloc_slots[i] && var_hash->data[i])
             {
-                zend_error(E_WARNING,
-                           "[msgpack] (msgpack_unserialize_object) "
-                           "Invalid sign value");
-            }
-
-            zval_ptr_dtor(&val);
-            zval_ptr_dtor(&key);
-
-            ZVAL_BOOL(*return_value, 0);
-
-            return MSGPACK_UNPACK_PARSE_ERROR;
-        }
-
-        if (Z_TYPE_P(val) == IS_LONG)
-        {
-            switch (Z_LVAL_P(val))
-            {
-                case MSGPACK_SERIALIZE_TYPE_REFERENCE:
-                    ret = msgpack_unserialize_object_type(
-                        return_value, mpsd, var_hash, 1 TSRMLS_CC);
-
-                    zval_ptr_dtor(&key);
-                    zval_ptr_dtor(&val);
-
-                    return ret;
-                case MSGPACK_SERIALIZE_TYPE_OBJECT:
-                    ret = msgpack_unserialize_object_type(
-                        return_value, mpsd, var_hash, 0 TSRMLS_CC);
-
-                    zval_ptr_dtor(&key);
-                    zval_ptr_dtor(&val);
-
-                    return ret;
-                case MSGPACK_SERIALIZE_TYPE_CUSTOM_OBJECT:
-                    custom_object = 1;
-
-                    zval_ptr_dtor(&val);
-
-                    ALLOC_INIT_ZVAL(val);
-
-                    if (msgpack_unserialize_zval(
-                            &val, mpsd, NULL TSRMLS_CC) <= 0)
-                    {
-                        zval_ptr_dtor(&key);
-                        zval_ptr_dtor(&val);
-
-                        ZVAL_BOOL(*return_value, 0);
-
-                        return MSGPACK_UNPACK_PARSE_ERROR;
-                    }
-                    break;
-                default:
-                    zval_ptr_dtor(&key);
-                    zval_ptr_dtor(&val);
-
-                    ZVAL_BOOL(*return_value, 0);
-
-                    return MSGPACK_UNPACK_PARSE_ERROR;
+                zval_ptr_dtor(&var_hash->data[i]);
             }
         }
+
+        next = var_hash->next;
+        efree(var_hash);
+        var_hash = next;
+    }
+
+    /*
+    var_hash = var_hashx->first_dtor;
+
+    while (var_hash)
+    {
+        for (i = 0; i < var_hash->used_slots; i++)
+        {
+            zval_ptr_dtor(&var_hash->data[i]);
+        }
+        next = var_hash->next;
+        efree(var_hash);
+        var_hash = next;
+    }
+    */
+}
+
+void msgpack_unserialize_init(msgpack_unserialize_data *unpack)
+{
+    unpack->deps = 0;
+    unpack->type = MSGPACK_SERIALIZE_TYPE_NONE;
+}
+
+int msgpack_unserialize_uint8(
+    msgpack_unserialize_data *unpack, uint8_t data, zval **obj)
+{
+    MSGPACK_UNSERIALIZE_ALLOC(unpack);
+
+    ZVAL_LONG(*obj, data);
+
+    return 0;
+}
+
+int msgpack_unserialize_uint16(
+    msgpack_unserialize_data *unpack, uint16_t data, zval **obj)
+{
+    MSGPACK_UNSERIALIZE_ALLOC(unpack);
+
+    ZVAL_LONG(*obj, data);
+
+    return 0;
+}
+
+int msgpack_unserialize_uint32(
+    msgpack_unserialize_data *unpack, uint32_t data, zval **obj)
+{
+    MSGPACK_UNSERIALIZE_ALLOC(unpack);
+
+    ZVAL_LONG(*obj, data);
+
+    return 0;
+}
+
+int msgpack_unserialize_uint64(
+    msgpack_unserialize_data *unpack, uint64_t data, zval **obj)
+{
+    MSGPACK_UNSERIALIZE_ALLOC(unpack);
+
+    ZVAL_LONG(*obj, data);
+
+    return 0;
+}
+
+int msgpack_unserialize_int8(
+    msgpack_unserialize_data *unpack, int8_t data, zval **obj)
+{
+    MSGPACK_UNSERIALIZE_ALLOC(unpack);
+
+    ZVAL_LONG(*obj, data);
+
+    return 0;
+}
+
+int msgpack_unserialize_int16(
+    msgpack_unserialize_data *unpack, int16_t data, zval **obj)
+{
+    MSGPACK_UNSERIALIZE_ALLOC(unpack);
+
+    ZVAL_LONG(*obj, data);
+
+    return 0;
+}
+
+int msgpack_unserialize_int32(
+    msgpack_unserialize_data *unpack, int32_t data, zval **obj)
+{
+    MSGPACK_UNSERIALIZE_ALLOC(unpack);
+
+    ZVAL_LONG(*obj, data);
+
+    return 0;
+}
+
+int msgpack_unserialize_int64(
+    msgpack_unserialize_data *unpack, int64_t data, zval **obj)
+{
+    MSGPACK_UNSERIALIZE_ALLOC(unpack);
+
+    ZVAL_LONG(*obj, data);
+
+    return 0;
+}
+
+int msgpack_unserialize_float(
+    msgpack_unserialize_data *unpack, float data, zval **obj)
+{
+    MSGPACK_UNSERIALIZE_ALLOC(unpack);
+
+    ZVAL_DOUBLE(*obj, data);
+
+    return 0;
+}
+
+int msgpack_unserialize_double(
+    msgpack_unserialize_data *unpack, double data, zval **obj)
+{
+    MSGPACK_UNSERIALIZE_ALLOC(unpack);
+
+    ZVAL_DOUBLE(*obj, data);
+
+    return 0;
+}
+
+int msgpack_unserialize_nil(msgpack_unserialize_data *unpack, zval **obj)
+{
+    MSGPACK_UNSERIALIZE_ALLOC(unpack);
+
+    ZVAL_NULL(*obj);
+
+    return 0;
+}
+
+int msgpack_unserialize_true(msgpack_unserialize_data *unpack, zval **obj)
+{
+    MSGPACK_UNSERIALIZE_ALLOC(unpack);
+
+    ZVAL_BOOL(*obj, 1);
+
+    return 0;
+}
+
+int msgpack_unserialize_false(msgpack_unserialize_data *unpack, zval **obj)
+{
+    MSGPACK_UNSERIALIZE_ALLOC(unpack);
+
+    ZVAL_BOOL(*obj, 0);
+
+    return 0;
+}
+
+int msgpack_unserialize_raw(
+    msgpack_unserialize_data *unpack, const char* base,
+    const char* data, unsigned int len, zval **obj)
+{
+    MSGPACK_UNSERIALIZE_ALLOC(unpack);
+
+    if (len == 0)
+    {
+        ZVAL_STRINGL(*obj, "", 0, 1);
     }
     else
     {
-        object = 0;
-
-        msgpack_var_push(var_hash, return_value);
-
-        if (Z_TYPE_PP(return_value) != IS_ARRAY)
-        {
-            array_init(*return_value);
-        }
-
-        ht = HASH_OF(*return_value);
-
-        ALLOC_INIT_ZVAL(val);
-
-        if (msgpack_unserialize_zval(&val, mpsd, var_hash TSRMLS_CC) <= 0)
-        {
-            if (MSGPACK_G(error_display))
-            {
-                zend_error(E_WARNING,
-                           "[msgpack] (msgpack_unserialize_object) "
-                           "Invalid sign value");
-            }
-
-            zval_ptr_dtor(&val);
-            zval_ptr_dtor(&key);
-
-            return MSGPACK_UNPACK_PARSE_ERROR;
-        }
-
-        /* update */
-        switch (Z_TYPE_P(key))
-        {
-            case IS_LONG:
-                zend_hash_index_update(
-                    ht, Z_LVAL_P(key), &val, sizeof(zval *), NULL);
-                break;
-            case IS_STRING:
-                zend_symtable_update(
-                    ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1,
-                    &val, sizeof(zval *), NULL);
-                break;
-            default:
-                zval_ptr_dtor(&key);
-                zval_ptr_dtor(&val);
-
-                return MSGPACK_UNPACK_PARSE_ERROR;
-        }
+        ZVAL_STRINGL(*obj, data, len, 1);
     }
 
-    zval_ptr_dtor(&key);
+    return 0;
+}
 
-    if (object)
+int msgpack_unserialize_array(
+    msgpack_unserialize_data *unpack, unsigned int count, zval **obj)
+{
+    MSGPACK_UNSERIALIZE_ALLOC_VALUE(unpack);
+
+    array_init(*obj);
+
+    unpack->stack[unpack->deps++] = count;
+
+    return 0;
+}
+
+int msgpack_unserialize_array_item(
+    msgpack_unserialize_data *unpack, zval **container, zval *obj)
+{
+    MSGPACK_UNSERIALIZE_PUSH_ITEM(unpack, 1, obj);
+
+    add_next_index_zval(*container, obj);
+
+    MSGPACK_UNSERIALIZE_FINISH_ITEM(unpack);
+
+    return 0;
+}
+
+int msgpack_unserialize_map(
+    msgpack_unserialize_data *unpack, unsigned int count, zval **obj)
+{
+    MSGPACK_UNSERIALIZE_ALLOC_VALUE(unpack);
+
+    unpack->stack[unpack->deps++] = count;
+
+    unpack->type = MSGPACK_SERIALIZE_TYPE_NONE;
+
+    return 0;
+}
+
+int msgpack_unserialize_map_item(
+    msgpack_unserialize_data *unpack, zval **container, zval *key, zval *val)
+{
+    TSRMLS_FETCH();
+
+    if (MSGPACK_G(php_only))
     {
-        convert_to_string(val);
+        if (Z_TYPE_P(key) == IS_NULL)
+        {
+            unpack->type = MSGPACK_SERIALIZE_TYPE_NONE;
 
-        do {
-            /* Try to find class directly */
-            if (zend_lookup_class(
-                    Z_STRVAL_P(val), Z_STRLEN_P(val), &pce TSRMLS_CC) == SUCCESS)
+            if (Z_TYPE_P(val) == IS_LONG)
             {
-                ce = *pce;
-                break;
-            }
-
-            /* Check for unserialize callback */
-            if ((PG(unserialize_callback_func) == NULL) ||
-                (PG(unserialize_callback_func)[0] == '\0'))
-            {
-                incomplete_class = 1;
-                ce = PHP_IC_ENTRY;
-                break;
-            }
-
-            /* Call unserialize callback */
-            ALLOC_INIT_ZVAL(user_func);
-            ZVAL_STRING(user_func, PG(unserialize_callback_func), 1);
-            args[0] = &arg_func_name;
-            ALLOC_INIT_ZVAL(arg_func_name);
-            ZVAL_STRING(arg_func_name, Z_STRVAL_P(val), 1);
-            if (call_user_function_ex(
-                    CG(function_table), NULL, user_func, &retval_ptr,
-                    1, args, 0, NULL TSRMLS_CC) != SUCCESS)
-            {
-                if (MSGPACK_G(error_display))
+                switch (Z_LVAL_P(val))
                 {
-                    zend_error(E_WARNING,
-                               "[msgpack] (msgpack_unserialize_object) "
-                               "defined (%s) but not found", Z_STRVAL_P(val));
+                    case MSGPACK_SERIALIZE_TYPE_REFERENCE:
+                        Z_SET_ISREF_PP(container);
+                        break;
+                    case MSGPACK_SERIALIZE_TYPE_RECURSIVE:
+                        unpack->type = MSGPACK_SERIALIZE_TYPE_RECURSIVE;
+                        break;
+                    case MSGPACK_SERIALIZE_TYPE_CUSTOM_OBJECT:
+                        unpack->type = MSGPACK_SERIALIZE_TYPE_CUSTOM_OBJECT;
+                        break;
+                    default:
+                        break;
                 }
+            }
+            else if (Z_TYPE_P(val) == IS_STRING)
+            {
+                zend_class_entry *ce = msgpack_unserialize_class(
+                    container, Z_STRVAL_P(val), Z_STRLEN_P(val));
 
-                incomplete_class = 1;
-                ce = PHP_IC_ENTRY;
-                zval_ptr_dtor(&user_func);
-                zval_ptr_dtor(&arg_func_name);
-                break;
-            }
-            if (retval_ptr)
-            {
-                zval_ptr_dtor(&retval_ptr);
-            }
-
-            /* The callback function may have defined the class */
-            if (zend_lookup_class(
-                    Z_STRVAL_P(val), Z_STRLEN_P(val), &pce TSRMLS_CC) == SUCCESS)
-            {
-                ce = *pce;
-            }
-            else
-            {
-                if (MSGPACK_G(error_display))
+                if (ce == NULL)
                 {
-                    zend_error(E_WARNING,
-                               "[msgpack] (msgpack_unserialize_object) "
-                               "Function %s() hasn't defined the class"
-                               "it was called for", Z_STRVAL_P(val));
+                    MSGPACK_UNSERIALIZE_FINISH_MAP_ITEM(unpack, key, val);
+
+                    return 0;
                 }
-
-                incomplete_class = true;
-                ce = PHP_IC_ENTRY;
             }
 
-            zval_ptr_dtor(&user_func);
-            zval_ptr_dtor(&arg_func_name);
-        } while(0);
+            MSGPACK_UNSERIALIZE_FINISH_MAP_ITEM(unpack, key, val);
 
-        if (EG(exception))
+            return 0;
+        }
+        else if (unpack->type == MSGPACK_SERIALIZE_TYPE_CUSTOM_OBJECT)
         {
-            if (MSGPACK_G(error_display))
+            unpack->type = MSGPACK_SERIALIZE_TYPE_NONE;
+
+            zend_class_entry *ce = msgpack_unserialize_class(
+                container, Z_STRVAL_P(key), Z_STRLEN_P(key));
+
+            if (ce == NULL)
             {
-                zend_error(E_WARNING,
-                           "[msgpack] (msgpack_unserialize_object) "
-                           "Exception error");
+                MSGPACK_UNSERIALIZE_FINISH_MAP_ITEM(unpack, key, val);
+
+                return 0;
             }
 
-            zval_ptr_dtor(&val);
-
-            ZVAL_BOOL(*return_value, 0);
-
-            return MSGPACK_UNPACK_PARSE_ERROR;
-        }
-
-        msgpack_var_push(var_hash, return_value);
-
-        object_init_ex(*return_value, ce);
-
-        /* store incomplete class name */
-        if (incomplete_class)
-        {
-            php_store_class_name(
-                *return_value, Z_STRVAL_P(val), Z_STRLEN_P(val));
-        }
-
-        zval_ptr_dtor(&val);
-
-        /* implementing Serializable */
-        if (custom_object)
-        {
-            zval *rval;
-
+            /* implementing Serializable */
             if (ce->unserialize == NULL)
             {
                 if (MSGPACK_G(error_display))
                 {
                     zend_error(E_WARNING,
-                               "[msgpack] (msgpack_unserialize_object) "
+                               "[msgpack] (msgpack_unserialize_map_item) "
                                "Class %s has no unserializer", ce->name);
                 }
 
-                return MSGPACK_UNPACK_PARSE_ERROR;
-            }
+                MSGPACK_UNSERIALIZE_FINISH_MAP_ITEM(unpack, key, val);
 
-            /* value */
-            ALLOC_INIT_ZVAL(rval);
-
-            if (msgpack_unserialize_zval(&rval, mpsd, var_hash TSRMLS_CC) <= 0)
-            {
-                zval_ptr_dtor(&rval);
-
-                return MSGPACK_UNPACK_PARSE_ERROR;
+                return 0;
             }
 
             ce->unserialize(
-                return_value, ce,
-                (const unsigned char *)Z_STRVAL_P(rval), Z_STRLEN_P(rval) + 1,
-                (zend_unserialize_data *)var_hash TSRMLS_CC);
+                container, ce,
+                (const unsigned char *)Z_STRVAL_P(val), Z_STRLEN_P(val) + 1,
+                NULL TSRMLS_CC);
 
-            zval_ptr_dtor(&key);
-            zval_ptr_dtor(&rval);
+            MSGPACK_UNSERIALIZE_FINISH_MAP_ITEM(unpack, key, val);
 
-            return ret;
+            return 0;
         }
+        else if (unpack->type == MSGPACK_SERIALIZE_TYPE_RECURSIVE)
+        {
+            zval **rval;
 
-        ht = HASH_OF(*return_value);
+            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))
+                {
+                    zend_error(E_WARNING,
+                               "[msgpack] (msgpack_unserialize_map_item) "
+                               "Invalid references value: %ld",
+                               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);
+
+            MSGPACK_UNSERIALIZE_FINISH_MAP_ITEM(unpack, key, val);
+
+            return 0;
+        }
     }
 
-    /* object property */
-    while (ct-- > 0)
+    MSGPACK_UNSERIALIZE_PUSH_ITEM(unpack, 2, val);
+
+    if (Z_TYPE_PP(container) != IS_ARRAY && Z_TYPE_PP(container) != IS_OBJECT)
     {
-        zval *rval;
-
-        /* key */
-        ALLOC_INIT_ZVAL(key);
-
-        if (msgpack_unserialize_zval(&key, mpsd, NULL TSRMLS_CC) <= 0)
-        {
-            zval_ptr_dtor(&key);
-
-            return MSGPACK_UNPACK_PARSE_ERROR;
-        }
-
-        /* value */
-        ALLOC_INIT_ZVAL(rval);
-
-        if (msgpack_unserialize_zval(&rval, mpsd, var_hash TSRMLS_CC) <= 0)
-        {
-            zval_ptr_dtor(&rval);
-            zval_ptr_dtor(&key);
-
-            return MSGPACK_UNPACK_PARSE_ERROR;
-
-        }
-
-        /* update */
-        switch (Z_TYPE_P(key))
-        {
-            case IS_LONG:
-                zend_hash_index_update(
-                    ht, Z_LVAL_P(key), &rval, sizeof(zval *), NULL);
-                break;
-            case IS_STRING:
-                zend_symtable_update(
-                    ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1,
-                    &rval, sizeof(zval *), NULL);
-                break;
-            default:
-                zval_ptr_dtor(&key);
-                zval_ptr_dtor(&rval);
-
-                return MSGPACK_UNPACK_PARSE_ERROR;
-        }
-
-        zval_ptr_dtor(&key);
+        array_init(*container);
     }
 
-    /* wakeup */
-    if (object && Z_OBJCE_PP(return_value) != PHP_IC_ENTRY &&
-        zend_hash_exists(&Z_OBJCE_PP(return_value)->function_table,
-                         "__wakeup", sizeof("__wakeup")))
+    switch (Z_TYPE_P(key))
     {
-        zval f, *h = NULL;
+        case IS_LONG:
+            if (zend_hash_index_update(
+                    HASH_OF(*container), Z_LVAL_P(key), &val,
+                    sizeof(val), NULL) == FAILURE)
+            {
+                zval_ptr_dtor(&val);
+                if (MSGPACK_G(error_display))
+                {
+                    zend_error(E_WARNING,
+                               "[msgpack] (msgpack_unserialize_map_item) "
+                               "illegal offset type, skip this decoding");
+                }
+            }
+            break;
+        case IS_STRING:
+            if (zend_symtable_update(
+                    HASH_OF(*container), Z_STRVAL_P(key), Z_STRLEN_P(key) + 1,
+                    &val, sizeof(val), NULL) == FAILURE)
+            {
+                zval_ptr_dtor(&val);
+                if (MSGPACK_G(error_display))
+                {
+                    zend_error(E_WARNING,
+                               "[msgpack] (msgpack_unserialize_map_item) "
+                               "illegal offset type, skip this decoding");
+                }
+            }
+            break;
+        default:
+            zval_ptr_dtor(&val);
+            if (MSGPACK_G(error_display))
+            {
+                zend_error(E_WARNING,
+                           "[msgpack] (msgpack_unserialize_map_item) "
+                           "illegal offset type, skip this decoding");
+            }
+            break;
+    }
 
-        INIT_PZVAL(&f);
-        ZVAL_STRINGL(&f, "__wakeup", sizeof("__wakeup") - 1, 0);
-        call_user_function_ex(
-            CG(function_table), return_value, &f, &h, 0, 0, 1, NULL TSRMLS_CC);
-        if (h)
-        {
-            zval_ptr_dtor(&h);
-        }
+    zval_ptr_dtor(&key);
 
-        if (EG(exception))
+    long deps = unpack->deps - 1;
+    unpack->stack[deps]--;
+    if (unpack->stack[deps] == 0)
+    {
+        unpack->deps--;
+
+        /* wakeup */
+        if (MSGPACK_G(php_only) &&
+            Z_TYPE_PP(container) == IS_OBJECT &&
+            Z_OBJCE_PP(container) != PHP_IC_ENTRY &&
+            zend_hash_exists(
+                &Z_OBJCE_PP(container)->function_table,
+                "__wakeup", sizeof("__wakeup")))
         {
-            ret = MSGPACK_UNPACK_PARSE_ERROR;
+            zval f, *h = NULL;
+
+            INIT_PZVAL(&f);
+            ZVAL_STRINGL(&f, "__wakeup", sizeof("__wakeup") - 1, 0);
+            call_user_function_ex(
+                CG(function_table), container, &f, &h, 0, 0, 1, NULL TSRMLS_CC);
+            if (h)
+            {
+                zval_ptr_dtor(&h);
+            }
         }
     }
 
-    return ret;
+    return 0;
 }
-
-int msgpack_unserialize_zval(
-    zval **return_value, msgpack_unserialize_data *mpsd,
-    php_unserialize_data_t *var_hash TSRMLS_DC)
-{
-    const unsigned char* data = mpsd->data;
-    const unsigned char* const pe = mpsd->data + mpsd->length;
-    const void* n = NULL;
-
-    unsigned int trail = 0;
-    unsigned int cs = CS_HEADER;
-
-    int ret;
-    unsigned int ct;
-
-#define next_cs(p) ((unsigned int)*p & 0x1f)
-
-#define finish_zval_long(val_) \
-    msgpack_var_push(var_hash, return_value); \
-    ZVAL_LONG(*return_value, val_); \
-    goto _finish
-
-#define finish_zval_null() \
-    msgpack_var_push(var_hash, return_value); \
-    ZVAL_NULL(*return_value); \
-    goto _finish
-
-#define finish_zval_bool(val_) \
-    msgpack_var_push(var_hash, return_value); \
-    ZVAL_BOOL(*return_value, val_); \
-    goto _finish
-
-#define finish_zval_double(val_) \
-    msgpack_var_push(var_hash, return_value); \
-    ZVAL_DOUBLE(*return_value, val_); \
-    goto _finish
-
-#define finish_zval_string(val_, len_) \
-    msgpack_var_push(var_hash, return_value); \
-    if (len_ == 0) { ZVAL_STRINGL(*return_value, "", 0, 1); } \
-    else { ZVAL_STRINGL(*return_value, val_, len_, 1); } \
-    goto _finish
-
-#define finish_zval_array(count_) \
-    ct = count_; \
-    cs = CS_HEADER; \
-    ++(mpsd->data); \
-    mpsd->length = (pe - mpsd->data); \
-    if (msgpack_unserialize_array(return_value, mpsd, ct, var_hash TSRMLS_CC) <= 0) { goto _failed; } \
-    goto _finish_end
-
-#define finish_zval_object(count_) \
-    ct = count_; \
-    cs = CS_HEADER; \
-    ++(mpsd->data); \
-    mpsd->length = (pe - mpsd->data); \
-    if (msgpack_unserialize_object(return_value, mpsd, ct, var_hash TSRMLS_CC) <= 0) { goto _failed; } \
-    goto _finish_end
-
-#define again_fixed_trail(_cs, trail_len) \
-    trail = trail_len; \
-    cs = _cs; \
-    goto _fixed_trail_again
-
-#define again_fixed_trail_if_zero(_cs, trail_len, ifzero) \
-    trail = trail_len; \
-    if(trail == 0) { goto ifzero; } \
-    cs = _cs; \
-    goto _fixed_trail_again
-
-    if (mpsd->length <= 0)
-    {
-        goto _failed;
-    }
-
-    if (mpsd->data == pe)
-    {
-        goto _out;
-    }
-    do
-    {
-        switch (cs)
-        {
-            case CS_HEADER:
-                switch (*(mpsd->data))
-                {
-                    case 0x00 ... 0x7f: /* Positive Fixnum */
-                        finish_zval_long(*(uint8_t*)mpsd->data);
-                    case 0xe0 ... 0xff: /* Negative Fixnum */
-                        finish_zval_long(*(int8_t*)mpsd->data);
-                    case 0xc0 ... 0xdf: /* Variable */
-                        switch (*(mpsd->data))
-                        {
-                            case 0xc0:  /* nil */
-                                finish_zval_null();
-                            case 0xc2:  /* false */
-                                finish_zval_bool(0);
-                            case 0xc3:  /* true */
-                                finish_zval_bool(1);
-                            case 0xca:  /* float */
-                            case 0xcb:  /* double */
-                            case 0xcc:  /* unsigned int  8 */
-                            case 0xcd:  /* unsigned int 16 */
-                            case 0xce:  /* unsigned int 32 */
-                            case 0xcf:  /* unsigned int 64 */
-                            case 0xd0:  /* signed int  8 */
-                            case 0xd1:  /* signed int 16 */
-                            case 0xd2:  /* signed int 32 */
-                            case 0xd3:  /* signed int 64 */
-                                again_fixed_trail(
-                                    next_cs(mpsd->data),
-                                    1 << (((unsigned int)*(mpsd->data)) & 0x03));
-                            case 0xda:  /* raw 16 */
-                            case 0xdb:  /* raw 32 */
-                            case 0xdc:  /* array 16 */
-                            case 0xdd:  /* array 32 */
-                            case 0xde:  /* map 16 */
-                            case 0xdf:  /* map 32 */
-                                again_fixed_trail(
-                                    next_cs(mpsd->data),
-                                    2 << (((unsigned int)*(mpsd->data)) & 0x01));
-                            default:
-                                goto _failed;
-                        }
-                    case 0xa0 ... 0xbf: /* FixRaw */
-                        again_fixed_trail_if_zero(
-                            ACS_RAW_VALUE, (unsigned int)*(mpsd->data) & 0x1f,
-                            _raw_zero);
-                    case 0x90 ... 0x9f: /* FixArray */
-                        finish_zval_array(((unsigned int)*(mpsd->data)) & 0x0f);
-                    case 0x80 ... 0x8f: /* FixMap */
-                        finish_zval_object(((unsigned int)*(mpsd->data)) & 0x0f);
-                    default:
-                        goto _failed;
-                }
-_fixed_trail_again:
-                ++(mpsd->data);
-            default:
-                if ((size_t)(pe - (mpsd->data)) < trail)
-                {
-                    goto _out;
-                }
-                n = (mpsd->data);
-                mpsd->data += trail - 1;
-                switch (cs)
-                {
-                    case CS_FLOAT:
-                    {
-                        union { uint32_t i; float f; } mem;
-                        mem.i = _msgpack_load32(uint32_t, n);
-                        finish_zval_double(mem.f);
-                    }
-                    case CS_DOUBLE:
-                    {
-                        union { uint64_t i; double f; } mem;
-                        mem.i = _msgpack_load64(uint64_t, n);
-                        finish_zval_double(mem.f);
-                    }
-                    case CS_UINT_8:
-                        finish_zval_long(*(uint8_t*)n);
-                    case CS_UINT_16:
-                        finish_zval_long(_msgpack_load16(uint16_t, n));
-                    case CS_UINT_32:
-                        finish_zval_long(_msgpack_load32(uint32_t, n));
-                    case CS_UINT_64:
-                        finish_zval_long(_msgpack_load64(uint64_t, n));
-                    case CS_INT_8:
-                        finish_zval_long(*(int8_t*)n);
-                    case CS_INT_16:
-                        finish_zval_long(_msgpack_load16(int16_t, n));
-                    case CS_INT_32:
-                        finish_zval_long(_msgpack_load32(int32_t, n));
-                    case CS_INT_64:
-                        finish_zval_long(_msgpack_load64(int64_t, n));
-                    case CS_RAW_16:
-                        again_fixed_trail_if_zero(
-                            ACS_RAW_VALUE, _msgpack_load16(uint16_t, n),
-                            _raw_zero);
-                    case CS_RAW_32:
-                        again_fixed_trail_if_zero(
-                            ACS_RAW_VALUE, _msgpack_load32(uint32_t, n),
-                            _raw_zero);
-                    case ACS_RAW_VALUE:
-_raw_zero:
-                        finish_zval_string(n, trail);
-                    case CS_ARRAY_16:
-                        finish_zval_array(_msgpack_load16(uint16_t, n));
-                    case CS_ARRAY_32:
-                        finish_zval_array(_msgpack_load32(uint32_t, n));
-                    /* FIXME security guard */
-                    case CS_MAP_16:
-                        finish_zval_object(_msgpack_load16(uint16_t, n));
-                    case CS_MAP_32:
-                        finish_zval_object(_msgpack_load32(uint32_t, n));
-                    /* FIXME security guard */
-                    default:
-                        goto _failed;
-                }
-        }
-        cs = CS_HEADER;
-        ++(mpsd->data);
-    }
-    while (mpsd->data != pe);
-    goto _out;
-
-_finish:
-    ++(mpsd->data);
-_finish_end:
-    ret = MSGPACK_UNPACK_EXTRA_BYTES;
-    goto _end;
-
-_failed:
-    ret = MSGPACK_UNPACK_PARSE_ERROR;
-    goto _end;
-
-_out:
-    ret = MSGPACK_UNPACK_CONTINUE;
-    goto _end;
-
-_end:
-    mpsd->offset = mpsd->data - data;
-    mpsd->length = pe - mpsd->data;
-
-    if (ret == MSGPACK_UNPACK_EXTRA_BYTES && mpsd->length == 0)
-    {
-        ret = MSGPACK_UNPACK_SUCCESS;
-    }
-
-    return ret;
-}
-
-#undef finish_zval_long
-#undef finish_zval_null
-#undef finish_zval_bool
-#undef finish_zval_double
-#undef finish_zval_string
-#undef finish_zval_array
-#undef finish_zval_object
-#undef again_fixed_trail
-#undef again_fixed_trail_if_zero
-
-#undef next_cs
diff --git a/php/msgpack_unpack.h b/php/msgpack_unpack.h
index 6c85d73f..da963ebf 100644
--- a/php/msgpack_unpack.h
+++ b/php/msgpack_unpack.h
@@ -1,9 +1,13 @@
 
-#ifndef MSGPACL_UNPACK_H
-#define MSGPACL_UNPACK_H
+#ifndef MSGPACK_UNPACK_H
+#define MSGPACK_UNPACK_H
 
 #include "ext/standard/php_var.h"
 
+#define MSGPACK_EMBED_STACK_SIZE 1024
+
+#include "msgpack/unpack_define.h"
+
 typedef enum
 {
     MSGPACK_UNPACK_SUCCESS     =  2,
@@ -12,15 +16,114 @@ typedef enum
     MSGPACK_UNPACK_PARSE_ERROR = -1,
 } msgpack_unpack_return;
 
-typedef struct
-{
-    unsigned char *data;
-    size_t length;
-    size_t offset;
+typedef struct {
+    zval *retval;
+    long deps;
+    php_unserialize_data_t *var_hash;
+    long stack[MSGPACK_EMBED_STACK_SIZE];
+    int type;
 } msgpack_unserialize_data;
 
-int msgpack_unserialize_zval(
-    zval **return_value, msgpack_unserialize_data *mpsd,
-    php_unserialize_data_t *var_hash TSRMLS_DC);
+void msgpack_unserialize_var_init(php_unserialize_data_t *var_hashx);
+void msgpack_unserialize_var_destroy(php_unserialize_data_t *var_hashx);
+
+void msgpack_unserialize_init(msgpack_unserialize_data *unpack);
+
+int msgpack_unserialize_uint8(
+    msgpack_unserialize_data *unpack, uint8_t data, zval **obj);
+int msgpack_unserialize_uint16(
+    msgpack_unserialize_data *unpack, uint16_t data, zval **obj);
+int msgpack_unserialize_uint32(
+    msgpack_unserialize_data *unpack, uint32_t data, zval **obj);
+int msgpack_unserialize_uint64(
+    msgpack_unserialize_data *unpack, uint64_t data, zval **obj);
+int msgpack_unserialize_int8(
+    msgpack_unserialize_data *unpack, int8_t data, zval **obj);
+int msgpack_unserialize_int16(
+    msgpack_unserialize_data *unpack, int16_t data, zval **obj);
+int msgpack_unserialize_int32(
+    msgpack_unserialize_data *unpack, int32_t data, zval **obj);
+int msgpack_unserialize_int64(
+    msgpack_unserialize_data *unpack, int64_t data, zval **obj);
+int msgpack_unserialize_float(
+    msgpack_unserialize_data *unpack, float data, zval **obj);
+int msgpack_unserialize_double(
+    msgpack_unserialize_data *unpack, double data, zval **obj);
+int msgpack_unserialize_nil(msgpack_unserialize_data *unpack, zval **obj);
+int msgpack_unserialize_true(msgpack_unserialize_data *unpack, zval **obj);
+int msgpack_unserialize_false(msgpack_unserialize_data *unpack, zval **obj);
+int msgpack_unserialize_raw(
+    msgpack_unserialize_data *unpack, const char* base, const char* data,
+    unsigned int len, zval **obj);
+int msgpack_unserialize_array(
+    msgpack_unserialize_data *unpack, unsigned int count, zval **obj);
+int msgpack_unserialize_array_item(
+    msgpack_unserialize_data *unpack, zval **container, zval *obj);
+int msgpack_unserialize_map(
+    msgpack_unserialize_data *unpack, unsigned int count, zval **obj);
+int msgpack_unserialize_map_item(
+    msgpack_unserialize_data *unpack, zval **container, zval *key, zval *val);
+
+/* template functions */
+#define msgpack_unpack_struct(name)    struct template ## name
+#define msgpack_unpack_func(ret, name) ret template ## name
+#define msgpack_unpack_callback(name)  template_callback ## name
+
+#define msgpack_unpack_object zval*
+#define unpack_user           msgpack_unserialize_data
+#define msgpack_unpack_user   msgpack_unserialize_data
+
+struct template_context;
+typedef struct template_context msgpack_unpack_t;
+
+static void template_init(msgpack_unpack_t* unpack);
+static msgpack_unpack_object template_data(msgpack_unpack_t* unpack);
+static int template_execute(
+    msgpack_unpack_t* unpack, const char* data, size_t len, size_t* off);
+
+static inline msgpack_unpack_object template_callback_root(unpack_user* user)
+{
+    msgpack_unserialize_init(user);
+    return NULL;
+}
+
+#define template_callback_uint8(user, data, obj) \
+    msgpack_unserialize_uint8(user, data, obj)
+#define template_callback_uint16(user, data, obj) \
+    msgpack_unserialize_uint16(user, data, obj)
+#define template_callback_uint32(user, data, obj) \
+    msgpack_unserialize_uint32(user, data, obj)
+#define template_callback_uint64(user, data, obj) \
+    msgpack_unserialize_uint64(user, data, obj)
+#define template_callback_int8(user, data, obj) \
+    msgpack_unserialize_int8(user, data, obj)
+#define template_callback_int16(user, data, obj) \
+    msgpack_unserialize_int16(user, data, obj)
+#define template_callback_int32(user, data, obj) \
+    msgpack_unserialize_int32(user, data, obj)
+#define template_callback_int64(user, data, obj) \
+    msgpack_unserialize_int64(user, data, obj)
+#define template_callback_float(user, data, obj) \
+    msgpack_unserialize_float(user, data, obj)
+#define template_callback_double(user, data, obj) \
+    msgpack_unserialize_double(user, data, obj)
+#define template_callback_nil(user, obj) \
+    msgpack_unserialize_nil(user, obj)
+#define template_callback_true(user, obj) \
+    msgpack_unserialize_true(user, obj)
+#define template_callback_false(user, obj) \
+    msgpack_unserialize_false(user, obj)
+#define template_callback_raw(user, base, data, len, obj) \
+    msgpack_unserialize_raw(user, base, data, len, obj)
+#define template_callback_array(user, count, obj) \
+    msgpack_unserialize_array(user, count, obj)
+#define template_callback_array_item(user, container, obj) \
+    msgpack_unserialize_array_item(user, container, obj)
+#define template_callback_map(user, count, obj) \
+    msgpack_unserialize_map(user, count, obj)
+#define template_callback_map_item(user, container, key, val) \
+    msgpack_unserialize_map_item(user, container, key, val)
+
+#include "msgpack/unpack_template.h"
 
 #endif
diff --git a/php/package.xml b/php/package.xml
index 48697704..6172a4c5 100644
--- a/php/package.xml
+++ b/php/package.xml
@@ -10,11 +10,11 @@
     <email>advect@gmail.com</email>
     <active>yes</active>
   </lead>
-  <date>2010-07-17</date>
-  <time>18:38:51</time>
+  <date>2010-09-28</date>
+  <time>17:40:09</time>
   <version>
-    <release>0.2.1</release>
-    <api>0.2.1</api>
+    <release>0.3.0</release>
+    <api>0.3.0</api>
   </version>
   <stability>
     <release>beta</release>
@@ -26,15 +26,21 @@
     <dir name="/">
       <file md5sum="12b9d8867e5fde4af426c134283e2082" name="LICENSE" role="doc" />
       <file md5sum="8daeb22744f11b57da9d0966608e2fc1" name="README" role="doc" />
-      <file md5sum="ba9ebe7c9dcae2ac053d9f789827f6f5" name="config.m4" role="src" />
-      <file md5sum="4223c6e2438bc363b6e13998c1e545b3" name="msgpack.c" role="src" />
-      <file md5sum="4c1dfbe6537a9189ee798c2647b0bc1d" name="msgpack_class.c" role="src" />
-      <file md5sum="3f2ae07a305b8353b4a574a4f1cea1c5" name="msgpack_class.h" role="src" />
-      <file md5sum="2b1a8e1a3e9e7a9893c4a0b3e4ac774f" name="msgpack_pack.c" role="src" />
-      <file md5sum="aedc1294b727bc8c06048104a7788f01" name="msgpack_pack.h" role="src" />
-      <file md5sum="0ef4bc4b5178fbf3099a5f53c06da861" name="msgpack_unpack.c" role="src" />
-      <file md5sum="99d4fa550181a3ba1267e473a4ad727b" name="msgpack_unpack.h" role="src" />
-      <file md5sum="8886e0071a2387c52ac6f2eff5b0045c" name="php_msgpack.h" role="src" />
+      <file md5sum="52329b794aab67446540c4c2b5537076" name="config.m4" role="src" />
+      <file md5sum="4f27f70df327b81e9392cdbb9ddcb03b" name="msgpack.c" role="src" />
+      <file md5sum="f6c6a226db5fb12064e89626220fed62" name="msgpack_class.c" role="src" />
+      <file md5sum="f58d228c0b7ace35fc85178f1aa2fb22" name="msgpack_class.h" role="src" />
+      <file md5sum="177fdfa077b5d23bc22553ad6e051ec2" name="msgpack_pack.c" role="src" />
+      <file md5sum="eae6797621ca53f8a7b0c2d9cb8a4d21" name="msgpack_pack.h" role="src" />
+      <file md5sum="1a832b3cbc1d41a2ec1a68584339c868" name="msgpack_unpack.c" role="src" />
+      <file md5sum="ed466f04f0608165701a5273a587de1c" name="msgpack_unpack.h" role="src" />
+      <file md5sum="896b8986bbdfe2af7f6898efd2b82f90" name="php_msgpack.h" role="src" />
+      <file md5sum="82079e9a298ecdda2122757ddfbf576e" name="msgpack/pack_define.h" role="src" />
+      <file md5sum="3f77e3df310b5c40a11ce7fee9cd6424" name="msgpack/pack_template.h" role="src" />
+      <file md5sum="09510085da29090ea0f3919c2e708f46" name="msgpack/unpack_define.h" role="src" />
+      <file md5sum="cf387b1971d55b529593fcbc7972ced4" name="msgpack/unpack_template.h" role="src" />
+      <file md5sum="7cd2e6f11b405521454e1092854cbaab" name="msgpack/sysdep.h" role="src" />
+      <file md5sum="2e346ebe243e8b770ed01f56d9f886ba" name="msgpack/version.h" role="src" />
       <file md5sum="237eda031928a5546bdb92a34815830f" name="tests/001.phpt" role="test" />
       <file md5sum="11d3a17e404ec4938eff1d0b0e724e5f" name="tests/002.phpt" role="test" />
       <file md5sum="1006ba0ae8b22e3d090a200f3eefea82" name="tests/003.phpt" role="test" />
@@ -43,28 +49,32 @@
       <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="84cd4eda863350bf148d9517451c440c" name="tests/009.phpt" role="test" />
+      <file md5sum="7804070722539804a91d0bd2c831d802" name="tests/009.phpt" role="test" />
+      <file md5sum="2621e92078703654fc414fb084816c9d" name="tests/009b.phpt" role="test" />
       <file md5sum="ddb421cd083baaca3ce3a5263f731dcd" name="tests/010.phpt" role="test" />
       <file md5sum="6a24b3b12778e2935722c97c95c9e393" name="tests/012.phpt" role="test" />
       <file md5sum="377f261d48b4f7fc78689aae481ac952" name="tests/013.phpt" role="test" />
-      <file md5sum="4407921d4b5c2a5aba21eb78dc651a5f" name="tests/014.phpt" role="test" />
-      <file md5sum="171194418824d91c9d52c511662cffa7" name="tests/015.phpt" role="test" />
-      <file md5sum="39693db7f0aafe07ed06b6569c2885b0" name="tests/015b.phpt" role="test" />
+      <file md5sum="31758388a32df170cc497291d1e4385d" name="tests/014.phpt" role="test" />
+      <file md5sum="6c26963e7a3d5723f585d672e2e09bc1" name="tests/015.phpt" role="test" />
+      <file md5sum="a61e2fe43719dc9d3fafe26b170cdce2" name="tests/015b.phpt" role="test" />
       <file md5sum="f35a9f6ce6c68c9ce06276fcb5aeb177" name="tests/016.phpt" role="test" />
       <file md5sum="c143aa78b84c8d7a7189d754592613e0" name="tests/017.phpt" role="test" />
-      <file md5sum="0f2600e66084af7989a5facbbf5de990" name="tests/018.phpt" role="test" />
+      <file md5sum="527491d7441baa77188026e21a0af80b" name="tests/018.phpt" role="test" />
       <file md5sum="375d39f1da5efb3067c326a636fa1d88" name="tests/019.phpt" role="test" />
       <file md5sum="3d9e08a86a9c1f53af0a86cce5c42d6d" name="tests/020.phpt" role="test" />
       <file md5sum="40e429c52cbff763df7a7cdc27c2df2c" name="tests/021.phpt" role="test" />
       <file md5sum="23a30dd1eca67eaaf5b640d2153c8c5f" name="tests/022.phpt" role="test" />
       <file md5sum="2ccb3302060dbd9395879e215c9b2072" name="tests/023.phpt" role="test" />
-      <file md5sum="4ce5630191f968701c4e845fdf5ad365" name="tests/024.phpt" role="test" />
+      <file md5sum="ff8767e0976fcfee9c885d5a737d0f7c" name="tests/024.phpt" role="test" />
+      <file md5sum="b2705eb1bbbe1c08e20f4b7217756248" name="tests/024b.phpt" role="test" />
       <file md5sum="5c2921e615f38d922cff04689aa61b7d" name="tests/025.phpt" role="test" />
-      <file md5sum="a61a627b3847788dab2072c1eb3600f5" name="tests/026.phpt" role="test" />
-      <file md5sum="170f20b9a3a9638817f01095fea241dc" name="tests/027.phpt" role="test" />
-      <file md5sum="0abf41b966db58c72b3b96286ea07b01" name="tests/028.phpt" role="test" />
-      <file md5sum="e77b91dc9e8ba3d9a49752afed44a99c" name="tests/029.phpt" role="test" />
-      <file md5sum="731d1a7bec6e3827f50d55df5d1e3ebf" name="tests/030.phpt" role="test" />
+      <file md5sum="7dd1ec82dfe82ee44f60ec520c7fd0e1" name="tests/026.phpt" role="test" />
+      <file md5sum="225e04ca20c5db3becdb0f7577bcfd18" name="tests/026b.phpt" role="test" />
+      <file md5sum="445c65386d71ed17e1180a3fd6e608d7" name="tests/027.phpt" role="test" />
+      <file md5sum="c10653ad3dcf8256d3c10fe717b2ad04" name="tests/028.phpt" role="test" />
+      <file md5sum="b090a261b6539d4ba2d4275d0ba57c3f" name="tests/028b.phpt" role="test" />
+      <file md5sum="f380a42cf429a8fb72c50f9d38228109" name="tests/029.phpt" role="test" />
+      <file md5sum="7de90bedec2c170da12144bb3399dfe5" name="tests/030.phpt" role="test" />
       <file md5sum="7ca67b854f69837658cc741ce42b8ed8" name="tests/031.phpt" role="test" />
       <file md5sum="170dfcb82fd1e097fffc6b57a7e9764a" name="tests/032.phpt" role="test" />
       <file md5sum="0e96b2ebf8c37c1c4c13de4d2d5dae95" name="tests/033.phpt" role="test" />
@@ -74,13 +84,30 @@
       <file md5sum="011672f35987ed8e52b7c9afde98bb8d" name="tests/041.phpt" role="test" />
       <file md5sum="ac28799e22762ff27e331aa060a792ad" name="tests/042.phpt" role="test" />
       <file md5sum="57478f4e95bd000bf73a8331e9e19cba" name="tests/050.phpt" role="test" />
-      <file md5sum="7f4047750c52009c7ef369150d2907d6" name="tests/060.phpt" role="test" />
-      <file md5sum="9ce6adb0492b97c8d7803090b4a08711" name="tests/061.phpt" role="test" />
+      <file md5sum="c34e0f1af0f9b79dc045d9839d36603c" name="tests/060.phpt" role="test" />
+      <file md5sum="591715d0a8437e44e02af2ec55a82a34" name="tests/060b.phpt" role="test" />
+      <file md5sum="e42dba2b4d02b74dad7f776a44c8e2fe" name="tests/061.phpt" role="test" />
+      <file md5sum="75ca37fe17423cddac6ddde324d6b1ec" name="tests/061b.phpt" role="test" />
       <file md5sum="187689f3d1d8fe3636ea7d6188e7e559" name="tests/062.phpt" role="test" />
       <file md5sum="9c91266595d01080b813fb2a056fca82" name="tests/063.phpt" role="test" />
-      <file md5sum="29d00ba15f7017c35d08ba83187aaa69" name="tests/070.phpt" role="test" />
-      <file md5sum="d3759148a10c87bdb5e7377ae7af5211" name="tests/071.phpt" role="test" />
-      <file md5sum="9a3908df5a67606814dfa6651dfddcc8" name="tests/072.phpt" role="test" />
+      <file md5sum="18c088d2ee24af0caa758ffbe92321c9" name="tests/070.phpt" role="test" />
+      <file md5sum="2f89e6a4efe59bc9491aa77af31e65fd" name="tests/070b.phpt" role="test" />
+      <file md5sum="c82b42c39192fd9dcc6ddcf61e7c6c63" name="tests/071.phpt" role="test" />
+      <file md5sum="a2f836fa16af5f01819fd2529a783803" name="tests/071b.phpt" role="test" />
+      <file md5sum="ca1f561229e54efd2bebc99378d1dac1" name="tests/072.phpt" role="test" />
+      <file md5sum="02c2644ba84bb1d78fa66e7706460edc" name="tests/072b.phpt" role="test" />
+      <file md5sum="e725faa855f9760fce4815944d439c0b" name="tests/073.phpt" role="test" />
+      <file md5sum="96b234f889dd6d391db24a21eac55cf6" name="tests/073b.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="c7963f41a1d3bad67febe2a706abae5e" name="tests/087.phpt" role="test" />
+      <file md5sum="f7c4c471a332f66d058f53dae99e7304" name="tests/088.phpt" role="test" />
+      <file md5sum="82f9a41ec36be738c9824b7d70c09e4d" name="tests/089.phpt" role="test" />
     </dir>
   </contents>
   <dependencies>
diff --git a/php/php-msgpack.spec b/php/php-msgpack.spec
index 5726c434..6a98288b 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.2.1
+Version: 0.3.0
 Release: 1%{?dist}
 Source: php-msgpack-%{version}.tar.gz
 License: New BSD License
@@ -11,7 +11,7 @@ Group: Development/Libraries
 Packager: advect <advect@gmail.com>
 Provides: php-pecl-msgpack
 BuildRoot: %{_tmppath}/%{name}-%{version}-root
-BuildRequires: php-devel, msgpack-devel
+BuildRequires: php-devel
 Requires: msgpack
 %if 0%{?php_zend_api}
 Requires: php(zend-abi) = %{php_zend_api}
diff --git a/php/php_msgpack.h b/php/php_msgpack.h
index 91b2d30f..61badea7 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_VERSION "0.2.1"
+#define MSGPACK_EXTENSION_VERSION "0.3.0"
 
 #include "ext/standard/php_smart_str.h"
 
@@ -23,6 +23,7 @@ extern zend_module_entry msgpack_module_entry;
 
 ZEND_BEGIN_MODULE_GLOBALS(msgpack)
     zend_bool error_display;
+    zend_bool php_only;
 ZEND_END_MODULE_GLOBALS(msgpack)
 
 ZEND_DECLARE_MODULE_GLOBALS(msgpack)
diff --git a/php/tests/009.phpt b/php/tests/009.phpt
index 6baad5ee..9992988a 100644
--- a/php/tests/009.phpt
+++ b/php/tests/009.phpt
@@ -1,6 +1,10 @@
 --TEST--
-Check for reference serialisation
+Check for reference serialization
 --SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.3.3') >= 0) {
+    echo "skip tests in PHP 5.3.2 and lower";
+}
 --FILE--
 <?php
 if(!extension_loaded('msgpack')) {
@@ -48,7 +52,7 @@ array(2) {
 }
 OK
 array(&$a, &$a)
-82008100a3666f6f0182c001c002
+820082c00100a3666f6f0182c0020002
 array(2) {
   [0]=>
   &array(1) {
@@ -63,16 +67,16 @@ array(2) {
 }
 OK
 cyclic
-81008100810082c001c002
+810082c0010082c0010082c0020002
 array(1) {
   [0]=>
   &array(1) {
     [0]=>
-    array(1) {
+    &array(1) {
       [0]=>
       &array(1) {
         [0]=>
-        array(1) {
+        &array(1) {
           [0]=>
           *RECURSION*
         }
@@ -101,11 +105,11 @@ array(1) {
   [0]=>
   &array(1) {
     [0]=>
-    array(1) {
+    &array(1) {
       [0]=>
       &array(1) {
         [0]=>
-        array(1) {
+        &array(1) {
           [0]=>
           *RECURSION*
         }
diff --git a/php/tests/009b.phpt b/php/tests/009b.phpt
new file mode 100644
index 00000000..7765d62a
--- /dev/null
+++ b/php/tests/009b.phpt
@@ -0,0 +1,101 @@
+--TEST--
+Check for reference serialization
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.3.2') <= 0) {
+    echo "skip tests in PHP 5.3.3";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack')) {
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+function test($type, $variable, $test) {
+    $serialized = msgpack_serialize($variable);
+    $unserialized = msgpack_unserialize($serialized);
+
+    echo $type, PHP_EOL;
+    echo bin2hex($serialized), PHP_EOL;
+    var_dump($unserialized);
+    echo $test || $unserialized == $variable ? 'OK' : 'ERROR', PHP_EOL;
+}
+
+$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);
+
+var_dump($a);
+var_dump(msgpack_unserialize(msgpack_serialize($a)));
+
+--EXPECT--
+array($a, $a)
+82008100a3666f6f018100a3666f6f
+array(2) {
+  [0]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [1]=>
+  array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+}
+OK
+array(&$a, &$a)
+820082c00100a3666f6f0182c0020002
+array(2) {
+  [0]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+  [1]=>
+  &array(1) {
+    [0]=>
+    string(3) "foo"
+  }
+}
+OK
+cyclic
+810082c0010082c0010082c0020002
+array(1) {
+  [0]=>
+  &array(1) {
+    [0]=>
+    &array(1) {
+      [0]=>
+      *RECURSION*
+    }
+  }
+}
+OK
+array(1) {
+  [0]=>
+  &array(1) {
+    [0]=>
+    &array(1) {
+      [0]=>
+      *RECURSION*
+    }
+  }
+}
+array(1) {
+  [0]=>
+  &array(1) {
+    [0]=>
+    &array(1) {
+      [0]=>
+      *RECURSION*
+    }
+  }
+}
diff --git a/php/tests/014.phpt b/php/tests/014.phpt
index c663579d..b9c7c671 100644
--- a/php/tests/014.phpt
+++ b/php/tests/014.phpt
@@ -34,7 +34,7 @@ test('object', $a, false);
 ?>
 --EXPECTF--
 object
-820083c0a34f626aa16101a162020182c001c002
+820084c001c0a34f626aa16101a162020182c0020002
 array(2) {
   [0]=>
   &object(Obj)#%d (2) {
diff --git a/php/tests/015.phpt b/php/tests/015.phpt
index 828800a2..634a8b1a 100644
--- a/php/tests/015.phpt
+++ b/php/tests/015.phpt
@@ -51,7 +51,7 @@ var_dump($_SESSION);
 ?>
 --EXPECT--
 2
-81a3666f6f02
+82c001a3666f6f02
 array(1) {
   ["foo"]=>
   int(2)
diff --git a/php/tests/015b.phpt b/php/tests/015b.phpt
index 829584b7..7ced3126 100644
--- a/php/tests/015b.phpt
+++ b/php/tests/015b.phpt
@@ -51,7 +51,7 @@ var_dump($_SESSION);
 ?>
 --EXPECT--
 2
-81a3666f6f02
+82c001a3666f6f02
 array(1) {
   ["foo"]=>
   int(2)
diff --git a/php/tests/018.phpt b/php/tests/018.phpt
index 84ccafa2..f5ff4b72 100644
--- a/php/tests/018.phpt
+++ b/php/tests/018.phpt
@@ -74,7 +74,7 @@ object(Obj)#%d (3) {
 }
 OK
 wrong
-82c0a34f706ac0
+82c0a34f706a
 object(Opj)#%d (2) {
   ["a"]=>
   NULL
diff --git a/php/tests/024.phpt b/php/tests/024.phpt
index 30fab759..9b185f7c 100644
--- a/php/tests/024.phpt
+++ b/php/tests/024.phpt
@@ -1,6 +1,10 @@
 --TEST--
 Recursive objects
 --SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.3.3') >= 0) {
+    echo "skip tests in PHP 5.3.2 and lower";
+}
 --FILE--
 <?php
 if(!extension_loaded('msgpack')) {
@@ -151,7 +155,7 @@ object(Obj3)#%d (1) {
 }
 OK
 objectselfrec
-83c0a44f626a34a7004f626a34006164a9004f626a34006f626a82c002c001
+83c0a44f626a34a7004f626a34006164a9004f626a34006f626a82c0020001
 object(Obj4)#%d (2) {
   [%r"?a"?:("Obj4":)?private"?%r]=>
   int(100)
diff --git a/php/tests/024b.phpt b/php/tests/024b.phpt
new file mode 100644
index 00000000..7c691bec
--- /dev/null
+++ b/php/tests/024b.phpt
@@ -0,0 +1,165 @@
+--TEST--
+Recursive objects
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.3.2') <= 0) {
+    echo "skip tests in PHP 5.3.3";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack')) {
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+function test($type, $variable, $test) {
+    $serialized = msgpack_serialize($variable);
+    $unserialized = msgpack_unserialize($serialized);
+
+    echo $type, PHP_EOL;
+    echo bin2hex($serialized), PHP_EOL;
+    var_dump($unserialized);
+    echo $test || $unserialized == $variable ? 'OK' : 'ERROR', PHP_EOL;
+}
+
+class Obj {
+    public $a;
+    protected $b;
+    private $c;
+
+    function __construct($a, $b, $c) {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+    }
+}
+
+class Obj2 {
+    public $aa;
+    protected $bb;
+    private $cc;
+    private $obj;
+
+    function __construct($a, $b, $c) {
+        $this->a = $a;
+        $this->b = $b;
+        $this->c = $c;
+
+        $this->obj = new Obj($a, $b, $c);
+    }
+}
+
+class Obj3 {
+    private $objs;
+
+    function __construct($a, $b, $c) {
+        $this->objs = array();
+
+        for ($i = $a; $i < $c; $i += $b) {
+            $this->objs[] = new Obj($a, $i, $c);
+        }
+    }
+}
+
+class Obj4 {
+    private $a;
+    private $obj;
+
+    function __construct($a) {
+        $this->a = $a;
+    }
+
+    public function set($obj) {
+        $this->obj = $obj;
+    }
+}
+
+$o2 = new Obj2(1, 2, 3);
+test('objectrec', $o2, false);
+
+$o3 = new Obj3(0, 1, 4);
+test('objectrecarr', $o3, false);
+
+$o4 = new Obj4(100);
+$o4->set($o4);
+test('objectselfrec', $o4, true);
+?>
+--EXPECTF--
+objectrec
+88c0a44f626a32a26161c0a5002a006262c0a8004f626a32006363c0a9004f626a32006f626a84c0a34f626aa16101a4002a006202a6004f626a006303a16101a16202a16303
+object(Obj2)#%d (7) {
+  ["aa"]=>
+  NULL
+  [%r"?bb"?:protected"?%r]=>
+  NULL
+  [%r"?cc"?:("Obj2":)?private"?%r]=>
+  NULL
+  [%r"?obj"?:("Obj2":)?private"?%r]=>
+  object(Obj)#%d (3) {
+    ["a"]=>
+    int(1)
+    [%r"?b"?:protected"?%r]=>
+    int(2)
+    [%r"?c"?:("Obj":)?private"?%r]=>
+    int(3)
+  }
+  ["a"]=>
+  int(1)
+  ["b"]=>
+  int(2)
+  ["c"]=>
+  int(3)
+}
+OK
+objectrecarr
+82c0a44f626a33aa004f626a33006f626a73840084c0a34f626aa16100a4002a006200a6004f626a0063040184c0a34f626aa16100a4002a006201a6004f626a0063040284c0a34f626aa16100a4002a006202a6004f626a0063040384c0a34f626aa16100a4002a006203a6004f626a006304
+object(Obj3)#%d (1) {
+  [%r"?objs"?:("Obj3":)?private"?%r]=>
+  array(4) {
+    [0]=>
+    object(Obj)#%d (3) {
+      ["a"]=>
+      int(0)
+      [%r"?b"?:protected"?%r]=>
+      int(0)
+      [%r"?c"?:("Obj":)?private"?%r]=>
+      int(4)
+    }
+    [1]=>
+    object(Obj)#%d (3) {
+      ["a"]=>
+      int(0)
+      [%r"?b"?:protected"?%r]=>
+      int(1)
+      [%r"?c"?:("Obj":)?private"?%r]=>
+      int(4)
+    }
+    [2]=>
+    object(Obj)#%d (3) {
+      ["a"]=>
+      int(0)
+      [%r"?b"?:protected"?%r]=>
+      int(2)
+      [%r"?c"?:("Obj":)?private"?%r]=>
+      int(4)
+    }
+    [3]=>
+    object(Obj)#%d (3) {
+      ["a"]=>
+      int(0)
+      [%r"?b"?:protected"?%r]=>
+      int(3)
+      [%r"?c"?:("Obj":)?private"?%r]=>
+      int(4)
+    }
+  }
+}
+OK
+objectselfrec
+83c0a44f626a34a7004f626a34006164a9004f626a34006f626a82c0020001
+object(Obj4)#%d (2) {
+  [%r"?a"?:("Obj4":)?private"?%r]=>
+  int(100)
+  [%r"?obj"?:("Obj4":)?private"?%r]=>
+  *RECURSION*
+}
+OK
diff --git a/php/tests/026.phpt b/php/tests/026.phpt
index 2d3bb734..c243ef51 100644
--- a/php/tests/026.phpt
+++ b/php/tests/026.phpt
@@ -2,6 +2,10 @@
 Cyclic array test
 --INI--
 --SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.3.3') >= 0) {
+    echo "skip tests in PHP 5.3.2 and lower";
+}
 --FILE--
 <?php
 if(!extension_loaded('msgpack')) {
@@ -39,7 +43,7 @@ var_dump($k);
 ?>
 --EXPECT--
 array
-82a16182a162a163a164a165a16682a16182a162a163a164a165a16682c001c005
+82a16182a162a163a164a165a16683c001a16182a162a163a164a165a16682c0020005
 array(2) {
   ["a"]=>
   array(2) {
diff --git a/php/tests/026b.phpt b/php/tests/026b.phpt
new file mode 100644
index 00000000..f7a3381f
--- /dev/null
+++ b/php/tests/026b.phpt
@@ -0,0 +1,107 @@
+--TEST--
+Cyclic array test
+--INI--
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.3.2') <= 0) {
+    echo "skip tests in PHP 5.3.3";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack')) {
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+function test($type, $variable, $test) {
+    $serialized = msgpack_serialize($variable);
+    $unserialized = msgpack_unserialize($serialized);
+
+    echo $type, PHP_EOL;
+    echo bin2hex($serialized), PHP_EOL;
+    var_dump($unserialized);
+    echo $test || $unserialized == $variable ? 'OK' : 'ERROR', PHP_EOL;
+}
+
+$a = array(
+    'a' => array(
+        'b' => 'c',
+        'd' => 'e'
+    ),
+);
+
+$a['f'] = &$a;
+
+test('array', $a, true);
+
+$a = array("foo" => &$b);
+$b = array(1, 2, $a);
+var_dump($a);
+var_dump($k = msgpack_unserialize(msgpack_serialize($a)));
+
+$k["foo"][1] = "b";
+var_dump($k);
+?>
+--EXPECT--
+array
+82a16182a162a163a164a165a16683c001a16182a162a163a164a165a16682c0020005
+array(2) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  ["f"]=>
+  &array(2) {
+    ["a"]=>
+    array(2) {
+      ["b"]=>
+      string(1) "c"
+      ["d"]=>
+      string(1) "e"
+    }
+    ["f"]=>
+    *RECURSION*
+  }
+}
+OK
+array(1) {
+  ["foo"]=>
+  &array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    *RECURSION*
+  }
+}
+array(1) {
+  ["foo"]=>
+  &array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(2)
+    [2]=>
+    array(1) {
+      ["foo"]=>
+      *RECURSION*
+    }
+  }
+}
+array(1) {
+  ["foo"]=>
+  &array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    string(1) "b"
+    [2]=>
+    array(1) {
+      ["foo"]=>
+      *RECURSION*
+    }
+  }
+}
diff --git a/php/tests/027.phpt b/php/tests/027.phpt
index ecb87841..c9c7cbd5 100644
--- a/php/tests/027.phpt
+++ b/php/tests/027.phpt
@@ -64,7 +64,7 @@ var_dump($_SESSION);
 --EXPECT--
 bool(true)
 read
-wrote: 82a3666f6f01a474657374a6666f6f626172
+wrote: 83c001a3666f6f01a474657374a6666f6f626172
 array(2) {
   ["foo"]=>
   int(1)
diff --git a/php/tests/028.phpt b/php/tests/028.phpt
index 56805f9e..00db6751 100644
--- a/php/tests/028.phpt
+++ b/php/tests/028.phpt
@@ -1,6 +1,10 @@
 --TEST--
 Serialize object into session, full set
 --SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.3.3') >= 0) {
+    echo "skip tests in PHP 5.3.2 and lower";
+}
 --FILE--
 <?php
 if(!extension_loaded('msgpack')) {
@@ -96,7 +100,7 @@ var_dump($_SESSION);
 ?>
 --EXPECTF--
 read
-write: 83a36f6c6484c0a3466f6fa700466f6f00643184c0a3426172a2643182c002c002a70042617200643282c002c002a5002a00643382c002c002a5002a00643282c002c003a2643382c002c003a474657374a6666f6f626172a36e657784c0a3426172a2643184c0a3466f6fa700466f6f00643182c002c00aa5002a00643282c002c00aa2643382c002c00aa70042617200643282c002c00ba5002a00643382c002c00b
+write: 84c001a36f6c6484c0a3466f6fa700466f6f00643184c0a3426172a2643182c0020002a70042617200643282c0020002a5002a00643382c0020002a5002a00643282c0020003a2643382c0020003a474657374a6666f6f626172a36e657784c0a3426172a2643184c0a3466f6fa700466f6f00643182c002000aa5002a00643282c002000aa2643382c002000aa70042617200643282c002000ba5002a00643382c002000b
 array(3) {
   ["old"]=>
   object(Foo)#3 (3) {
diff --git a/php/tests/028b.phpt b/php/tests/028b.phpt
new file mode 100644
index 00000000..7331a576
--- /dev/null
+++ b/php/tests/028b.phpt
@@ -0,0 +1,167 @@
+--TEST--
+Serialize object into session, full set
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.3.2') <= 0) {
+    echo "skip tests in PHP 5.3.3";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack')) {
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+class Foo {
+    private static $s1 = array();
+    protected static $s2 = array();
+    public static $s3 = array();
+
+    private $d1;
+    protected $d2;
+    public $d3;
+
+    public function __construct($foo) {
+        $this->d1 = $foo;
+        $this->d2 = $foo;
+        $this->d3 = $foo;
+    }
+}
+
+class Bar {
+    private static $s1 = array();
+    protected static $s2 = array();
+    public static $s3 = array();
+
+    public $d1;
+    private $d2;
+    protected $d3;
+
+    public function __construct() {
+    }
+
+    public function set($foo) {
+        $this->d1 = $foo;
+        $this->d2 = $foo;
+        $this->d3 = $foo;
+    }
+}
+
+$output = '';
+
+function open($path, $name) {
+    return true;
+}
+
+function close() {
+    return true;
+}
+
+function read($id) {
+    global $output;
+    $output .= "read" . PHP_EOL;
+    $a = new Bar();
+    $b = new Foo($a);
+    $a->set($b);
+    $session = array('old' => $b);
+    return msgpack_serialize($session);
+}
+
+function write($id, $data) {
+    global $output;
+    $output .= "write: ";
+    $output .= bin2hex($data) . PHP_EOL;
+    return true;
+}
+
+function destroy($id) {
+    return true;
+}
+
+function gc($time) {
+    return true;
+}
+
+ini_set('session.serialize_handler', 'msgpack');
+
+session_set_save_handler('open', 'close', 'read', 'write', 'destroy', 'gc');
+
+session_start();
+
+$_SESSION['test'] = "foobar";
+$a = new Bar();
+$b = new Foo($a);
+$a->set($b);
+$_SESSION['new'] = $a;
+
+session_write_close();
+
+echo $output;
+var_dump($_SESSION);
+?>
+--EXPECTF--
+read
+write: 84c001a36f6c6484c0a3466f6fa700466f6f00643184c0a3426172a2643182c0020002a70042617200643282c0020002a5002a00643382c0020002a5002a00643282c0020003a2643382c0020003a474657374a6666f6f626172a36e657784c0a3426172a2643184c0a3466f6fa700466f6f00643182c002000aa5002a00643282c002000aa2643382c002000aa70042617200643282c002000ba5002a00643382c002000b
+array(3) {
+  ["old"]=>
+  object(Foo)#3 (3) {
+    [%r"?d1"?:("Foo":)?private"?%r]=>
+    object(Bar)#4 (3) {
+      ["d1"]=>
+      *RECURSION*
+      [%r"?d2"?:("Bar":)?private"?%r]=>
+      *RECURSION*
+      [%r"?d3"?:protected"?%r]=>
+      *RECURSION*
+    }
+    [%r"?d2"?:protected"?%r]=>
+    object(Bar)#4 (3) {
+      ["d1"]=>
+      *RECURSION*
+      [%r"?d2"?:("Bar":)?private"?%r]=>
+      *RECURSION*
+      [%r"?d3"?:protected"?%r]=>
+      *RECURSION*
+    }
+    ["d3"]=>
+    object(Bar)#4 (3) {
+      ["d1"]=>
+      *RECURSION*
+      [%r"?d2"?:("Bar":)?private"?%r]=>
+      *RECURSION*
+      [%r"?d3"?:protected"?%r]=>
+      *RECURSION*
+    }
+  }
+  ["test"]=>
+  string(6) "foobar"
+  ["new"]=>
+  object(Bar)#5 (3) {
+    ["d1"]=>
+    object(Foo)#6 (3) {
+      [%r"?d1"?:("Foo":)?private"?%r]=>
+      *RECURSION*
+      [%r"?d2"?:protected"?%r]=>
+      *RECURSION*
+      ["d3"]=>
+      *RECURSION*
+    }
+    [%r"?d2"?:("Bar":)?private"?%r]=>
+    object(Foo)#6 (3) {
+      [%r"?d1"?:("Foo":)?private"?%r]=>
+      *RECURSION*
+      [%r"?d2"?:protected"?%r]=>
+      *RECURSION*
+      ["d3"]=>
+      *RECURSION*
+    }
+    [%r"?d3"?:protected"?%r]=>
+    object(Foo)#6 (3) {
+      [%r"?d1"?:("Foo":)?private"?%r]=>
+      *RECURSION*
+      [%r"?d2"?:protected"?%r]=>
+      *RECURSION*
+      ["d3"]=>
+      *RECURSION*
+    }
+  }
+}
diff --git a/php/tests/029.phpt b/php/tests/029.phpt
index 3bd1e9dc..686abb92 100644
--- a/php/tests/029.phpt
+++ b/php/tests/029.phpt
@@ -9,12 +9,39 @@ phpinfo(INFO_MODULES);
 $str = ob_get_clean();
 
 $array = explode("\n", $str);
-$array = preg_grep('/^msgpack/', $array);
 
-echo implode("\n", $array), PHP_EOL;
+$section = false;
+$blank = 0;
+foreach ($array as $key => $val)
+{
+    if (strcmp($val, 'msgpack') == 0 || $section)
+    {
+        $section = true;
+    }
+    else
+    {
+        continue;
+    }
 
+    if (empty($val))
+    {
+        $blank++;
+        if ($blank == 3)
+        {
+            $section = false;
+        }
+    }
+
+    echo $val, PHP_EOL;
+}
 --EXPECTF--
 msgpack
-msgpack support => enabled
-msgpack version => %s
-msgpack Session Support => enabled
+
+MessagePack Support => enabled
+Session Support => enabled
+extension Version => %s
+header Version => %s
+
+Directive => Local Value => Master Value
+msgpack.error_display => On => On
+msgpack.php_only => On => On
diff --git a/php/tests/030.phpt b/php/tests/030.phpt
index ec8622dc..3b8d986e 100644
--- a/php/tests/030.phpt
+++ b/php/tests/030.phpt
@@ -56,9 +56,6 @@ foreach ($datas as $data) {
 }
 ?>
 --EXPECTF--
-output at 2:
-array(0) {
-}
 output at 3:
 array(1) {
   [0]=>
diff --git a/php/tests/060.phpt b/php/tests/060.phpt
index c0fda4c2..649d7886 100644
--- a/php/tests/060.phpt
+++ b/php/tests/060.phpt
@@ -1,6 +1,10 @@
 --TEST--
 Check for buffered streaming unserialization
 --SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.3.3') >= 0) {
+    echo "skip tests in PHP 5.3.2 and lower";
+}
 --FILE--
 <?php
 if(!extension_loaded('msgpack')) {
@@ -41,8 +45,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);
@@ -57,9 +61,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', 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);
@@ -234,11 +238,11 @@ array(1) {
   [0]=>
   &array(1) {
     [0]=>
-    array(1) {
+    &array(1) {
       [0]=>
       &array(1) {
         [0]=>
-        array(1) {
+        &array(1) {
           [0]=>
           *RECURSION*
         }
diff --git a/php/tests/060b.phpt b/php/tests/060b.phpt
new file mode 100644
index 00000000..0b947c8e
--- /dev/null
+++ b/php/tests/060b.phpt
@@ -0,0 +1,313 @@
+--TEST--
+Check for buffered streaming unserialization
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.3.2') <= 0) {
+    echo "skip tests in PHP 5.3.3";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack')) {
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+function test($type, $variable, $test = null) {
+    $serialized = msgpack_serialize($variable);
+
+    $unpacker = new MessagePackUnpacker();
+
+    $length = strlen($serialized);
+
+    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;
+    }
+
+    if (!is_bool($test))
+    {
+        echo $unserialized === $variable ? 'OK' : 'ERROR', PHP_EOL;
+    }
+    else
+    {
+        echo $test || $unserialized == $variable ? 'OK' : 'ERROR', PHP_EOL;
+    }
+}
+
+test('null', null);
+
+test('bool: true', true);
+test('bool: false', 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('array', 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);
+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), false);
+
+test('object', array(new Obj(1, 2, 3), new Obj(4, 5, 6)), false);
+
+$o = new Obj(1, 2, 3);
+
+test('object', array(&$o, &$o), false);
+--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]=>
+      *RECURSION*
+    }
+  }
+}
+OK
+array(2) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  ["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
+array(2) {
+  [0]=>
+  object(Obj)#%d (3) {
+    ["a"]=>
+    int(1)
+    [%r"?b"?:protected"?%r]=>
+    int(2)
+    [%r"?c"?:("Obj":)?private"?%r]=>
+    int(3)
+  }
+  [1]=>
+  object(Obj)#%d (3) {
+    ["a"]=>
+    int(4)
+    [%r"?b"?:protected"?%r]=>
+    int(5)
+    [%r"?c"?:("Obj":)?private"?%r]=>
+    int(6)
+  }
+}
+OK
+array(2) {
+  [0]=>
+  &object(Obj)#%d (3) {
+    ["a"]=>
+    int(1)
+    [%r"?b"?:protected"?%r]=>
+    int(2)
+    [%r"?c"?:("Obj":)?private"?%r]=>
+    int(3)
+  }
+  [1]=>
+  &object(Obj)#%d (3) {
+    ["a"]=>
+    int(1)
+    [%r"?b"?:protected"?%r]=>
+    int(2)
+    [%r"?c"?:("Obj":)?private"?%r]=>
+    int(3)
+  }
+}
+OK
diff --git a/php/tests/061.phpt b/php/tests/061.phpt
index ded494e0..7ecc0c9f 100644
--- a/php/tests/061.phpt
+++ b/php/tests/061.phpt
@@ -1,6 +1,10 @@
 --TEST--
 Check for unbuffered streaming unserialization
 --SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.3.3') >= 0) {
+    echo "skip tests in PHP 5.3.2 and lower";
+}
 --FILE--
 <?php
 if(!extension_loaded('msgpack')) {
@@ -239,11 +243,11 @@ array(1) {
   [0]=>
   &array(1) {
     [0]=>
-    array(1) {
+    &array(1) {
       [0]=>
       &array(1) {
         [0]=>
-        array(1) {
+        &array(1) {
           [0]=>
           *RECURSION*
         }
diff --git a/php/tests/061b.phpt b/php/tests/061b.phpt
new file mode 100644
index 00000000..efb3a9bf
--- /dev/null
+++ b/php/tests/061b.phpt
@@ -0,0 +1,318 @@
+--TEST--
+Check for unbuffered streaming unserialization
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.3.2') <= 0) {
+    echo "skip tests in PHP 5.3.3";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack')) {
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+function test($type, $variable, $test = null) {
+    $serialized = msgpack_serialize($variable);
+
+    $unpacker = new MessagePackUnpacker();
+
+    $length = strlen($serialized);
+
+    $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), false);
+
+test('object', array(new Obj(1, 2, 3), new Obj(4, 5, 6)), false);
+
+$o = new Obj(1, 2, 3);
+
+test('object', array(&$o, &$o), false);
+--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]=>
+      *RECURSION*
+    }
+  }
+}
+OK
+array(2) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  ["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
+array(2) {
+  [0]=>
+  object(Obj)#%d (3) {
+    ["a"]=>
+    int(1)
+    [%r"?b"?:protected"?%r]=>
+    int(2)
+    [%r"?c"?:("Obj":)?private"?%r]=>
+    int(3)
+  }
+  [1]=>
+  object(Obj)#%d (3) {
+    ["a"]=>
+    int(4)
+    [%r"?b"?:protected"?%r]=>
+    int(5)
+    [%r"?c"?:("Obj":)?private"?%r]=>
+    int(6)
+  }
+}
+OK
+array(2) {
+  [0]=>
+  &object(Obj)#%d (3) {
+    ["a"]=>
+    int(1)
+    [%r"?b"?:protected"?%r]=>
+    int(2)
+    [%r"?c"?:("Obj":)?private"?%r]=>
+    int(3)
+  }
+  [1]=>
+  &object(Obj)#%d (3) {
+    ["a"]=>
+    int(1)
+    [%r"?b"?:protected"?%r]=>
+    int(2)
+    [%r"?c"?:("Obj":)?private"?%r]=>
+    int(3)
+  }
+}
+OK
diff --git a/php/tests/070.phpt b/php/tests/070.phpt
index 4a00ddf5..893023b5 100644
--- a/php/tests/070.phpt
+++ b/php/tests/070.phpt
@@ -1,6 +1,10 @@
 --TEST--
 Check for alias functions
 --SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.3.3') >= 0) {
+    echo "skip tests in PHP 5.3.2 and lower";
+}
 --FILE--
 <?php
 if(!extension_loaded('msgpack')) {
@@ -218,11 +222,11 @@ array(1) {
   [0]=>
   &array(1) {
     [0]=>
-    array(1) {
+    &array(1) {
       [0]=>
       &array(1) {
         [0]=>
-        array(1) {
+        &array(1) {
           [0]=>
           *RECURSION*
         }
diff --git a/php/tests/070b.phpt b/php/tests/070b.phpt
new file mode 100644
index 00000000..109ddc1a
--- /dev/null
+++ b/php/tests/070b.phpt
@@ -0,0 +1,297 @@
+--TEST--
+Check for alias functions
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.3.2') <= 0) {
+    echo "skip tests in PHP 5.3.3";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack')) {
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+function test($type, $variable, $test = null) {
+    $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), false);
+
+test('object', array(new Obj(1, 2, 3), new Obj(4, 5, 6)), false);
+
+$o = new Obj(1, 2, 3);
+
+test('object', array(&$o, &$o), false);
+--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]=>
+      *RECURSION*
+    }
+  }
+}
+OK
+array(2) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  ["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
+array(2) {
+  [0]=>
+  object(Obj)#%d (3) {
+    ["a"]=>
+    int(1)
+    [%r"?b"?:protected"?%r]=>
+    int(2)
+    [%r"?c"?:("Obj":)?private"?%r]=>
+    int(3)
+  }
+  [1]=>
+  object(Obj)#%d (3) {
+    ["a"]=>
+    int(4)
+    [%r"?b"?:protected"?%r]=>
+    int(5)
+    [%r"?c"?:("Obj":)?private"?%r]=>
+    int(6)
+  }
+}
+OK
+array(2) {
+  [0]=>
+  &object(Obj)#%d (3) {
+    ["a"]=>
+    int(1)
+    [%r"?b"?:protected"?%r]=>
+    int(2)
+    [%r"?c"?:("Obj":)?private"?%r]=>
+    int(3)
+  }
+  [1]=>
+  &object(Obj)#%d (3) {
+    ["a"]=>
+    int(1)
+    [%r"?b"?:protected"?%r]=>
+    int(2)
+    [%r"?c"?:("Obj":)?private"?%r]=>
+    int(3)
+  }
+}
+OK
diff --git a/php/tests/071.phpt b/php/tests/071.phpt
index cca351aa..431303b6 100644
--- a/php/tests/071.phpt
+++ b/php/tests/071.phpt
@@ -1,6 +1,10 @@
 --TEST--
 Check for class methods
 --SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.3.3') >= 0) {
+    echo "skip tests in PHP 5.3.2 and lower";
+}
 --FILE--
 <?php
 if(!extension_loaded('msgpack')) {
@@ -220,11 +224,11 @@ array(1) {
   [0]=>
   &array(1) {
     [0]=>
-    array(1) {
+    &array(1) {
       [0]=>
       &array(1) {
         [0]=>
-        array(1) {
+        &array(1) {
           [0]=>
           *RECURSION*
         }
diff --git a/php/tests/071b.phpt b/php/tests/071b.phpt
new file mode 100644
index 00000000..770d06ee
--- /dev/null
+++ b/php/tests/071b.phpt
@@ -0,0 +1,299 @@
+--TEST--
+Check for class methods
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.3.2') <= 0) {
+    echo "skip tests in PHP 5.3.3";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack')) {
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+function test($type, $variable, $test = null) {
+    $msgpack = new MessagePack();
+
+    $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), false);
+
+test('object', array(new Obj(1, 2, 3), new Obj(4, 5, 6)), false);
+
+$o = new Obj(1, 2, 3);
+
+test('object', array(&$o, &$o), false);
+--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]=>
+      *RECURSION*
+    }
+  }
+}
+OK
+array(2) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  ["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
+array(2) {
+  [0]=>
+  object(Obj)#%d (3) {
+    ["a"]=>
+    int(1)
+    [%r"?b"?:protected"?%r]=>
+    int(2)
+    [%r"?c"?:("Obj":)?private"?%r]=>
+    int(3)
+  }
+  [1]=>
+  object(Obj)#%d (3) {
+    ["a"]=>
+    int(4)
+    [%r"?b"?:protected"?%r]=>
+    int(5)
+    [%r"?c"?:("Obj":)?private"?%r]=>
+    int(6)
+  }
+}
+OK
+array(2) {
+  [0]=>
+  &object(Obj)#%d (3) {
+    ["a"]=>
+    int(1)
+    [%r"?b"?:protected"?%r]=>
+    int(2)
+    [%r"?c"?:("Obj":)?private"?%r]=>
+    int(3)
+  }
+  [1]=>
+  &object(Obj)#%d (3) {
+    ["a"]=>
+    int(1)
+    [%r"?b"?:protected"?%r]=>
+    int(2)
+    [%r"?c"?:("Obj":)?private"?%r]=>
+    int(3)
+  }
+}
+OK
diff --git a/php/tests/072.phpt b/php/tests/072.phpt
index d8cb1b99..ea79a56d 100644
--- a/php/tests/072.phpt
+++ b/php/tests/072.phpt
@@ -1,6 +1,10 @@
 --TEST--
 Check for class methods unpacker
 --SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.3.3') >= 0) {
+    echo "skip tests in PHP 5.3.2 and lower";
+}
 --FILE--
 <?php
 if(!extension_loaded('msgpack')) {
@@ -260,11 +264,11 @@ array(1) {
   [0]=>
   &array(1) {
     [0]=>
-    array(1) {
+    &array(1) {
       [0]=>
       &array(1) {
         [0]=>
-        array(1) {
+        &array(1) {
           [0]=>
           *RECURSION*
         }
diff --git a/php/tests/072b.phpt b/php/tests/072b.phpt
new file mode 100644
index 00000000..c7b0c3ce
--- /dev/null
+++ b/php/tests/072b.phpt
@@ -0,0 +1,339 @@
+--TEST--
+Check for class methods unpacker
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.3.2') <= 0) {
+    echo "skip tests in PHP 5.3.3";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack')) {
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+function test($type, $variable, $test = null) {
+    $msgpack = new MessagePack();
+
+    $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), false);
+
+test('object', array(new Obj(1, 2, 3), new Obj(4, 5, 6)), false);
+
+$o = new Obj(1, 2, 3);
+
+test('object', array(&$o, &$o), false);
+--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]=>
+      *RECURSION*
+    }
+  }
+}
+OK
+array(2) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  ["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
+array(2) {
+  [0]=>
+  object(Obj)#%d (3) {
+    ["a"]=>
+    int(1)
+    [%r"?b"?:protected"?%r]=>
+    int(2)
+    [%r"?c"?:("Obj":)?private"?%r]=>
+    int(3)
+  }
+  [1]=>
+  object(Obj)#%d (3) {
+    ["a"]=>
+    int(4)
+    [%r"?b"?:protected"?%r]=>
+    int(5)
+    [%r"?c"?:("Obj":)?private"?%r]=>
+    int(6)
+  }
+}
+OK
+array(2) {
+  [0]=>
+  &object(Obj)#%d (3) {
+    ["a"]=>
+    int(1)
+    [%r"?b"?:protected"?%r]=>
+    int(2)
+    [%r"?c"?:("Obj":)?private"?%r]=>
+    int(3)
+  }
+  [1]=>
+  &object(Obj)#%d (3) {
+    ["a"]=>
+    int(1)
+    [%r"?b"?:protected"?%r]=>
+    int(2)
+    [%r"?c"?:("Obj":)?private"?%r]=>
+    int(3)
+  }
+}
+OK
diff --git a/php/tests/073.phpt b/php/tests/073.phpt
new file mode 100644
index 00000000..d3a76379
--- /dev/null
+++ b/php/tests/073.phpt
@@ -0,0 +1,346 @@
+--TEST--
+Check for class unpacker
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.3.3') >= 0) {
+    echo "skip tests in PHP 5.3.2 and lower";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack')) {
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+function test($type, $variable, $test = null) {
+    $msgpack = new MessagePack();
+
+    $serialized = $msgpack->pack($variable);
+
+    $unpacker = new MessagePackUnpacker();
+
+    $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), false);
+
+test('object', array(new Obj(1, 2, 3), new Obj(4, 5, 6)), false);
+
+$o = new Obj(1, 2, 3);
+
+test('object', array(&$o, &$o), false);
+--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]=>
+          *RECURSION*
+        }
+      }
+    }
+  }
+}
+OK
+array(2) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  ["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
+array(2) {
+  [0]=>
+  object(Obj)#%d (3) {
+    ["a"]=>
+    int(1)
+    [%r"?b"?:protected"?%r]=>
+    int(2)
+    [%r"?c"?:("Obj":)?private"?%r]=>
+    int(3)
+  }
+  [1]=>
+  object(Obj)#%d (3) {
+    ["a"]=>
+    int(4)
+    [%r"?b"?:protected"?%r]=>
+    int(5)
+    [%r"?c"?:("Obj":)?private"?%r]=>
+    int(6)
+  }
+}
+OK
+array(2) {
+  [0]=>
+  &object(Obj)#%d (3) {
+    ["a"]=>
+    int(1)
+    [%r"?b"?:protected"?%r]=>
+    int(2)
+    [%r"?c"?:("Obj":)?private"?%r]=>
+    int(3)
+  }
+  [1]=>
+  &object(Obj)#%d (3) {
+    ["a"]=>
+    int(1)
+    [%r"?b"?:protected"?%r]=>
+    int(2)
+    [%r"?c"?:("Obj":)?private"?%r]=>
+    int(3)
+  }
+}
+OK
diff --git a/php/tests/073b.phpt b/php/tests/073b.phpt
new file mode 100644
index 00000000..131a5346
--- /dev/null
+++ b/php/tests/073b.phpt
@@ -0,0 +1,340 @@
+--TEST--
+Check for class unpacker
+--SKIPIF--
+<?php
+if (version_compare(PHP_VERSION, '5.3.2') <= 0) {
+    echo "skip tests in PHP 5.3.3";
+}
+--FILE--
+<?php
+if(!extension_loaded('msgpack')) {
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+function test($type, $variable, $test = null) {
+    $msgpack = new MessagePack();
+
+    $serialized = $msgpack->pack($variable);
+
+    $unpacker = new MessagePackUnpacker();
+
+    $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), false);
+
+test('object', array(new Obj(1, 2, 3), new Obj(4, 5, 6)), false);
+
+$o = new Obj(1, 2, 3);
+
+test('object', array(&$o, &$o), false);
+--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]=>
+      *RECURSION*
+    }
+  }
+}
+OK
+array(2) {
+  ["a"]=>
+  array(2) {
+    ["b"]=>
+    string(1) "c"
+    ["d"]=>
+    string(1) "e"
+  }
+  ["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
+array(2) {
+  [0]=>
+  object(Obj)#%d (3) {
+    ["a"]=>
+    int(1)
+    [%r"?b"?:protected"?%r]=>
+    int(2)
+    [%r"?c"?:("Obj":)?private"?%r]=>
+    int(3)
+  }
+  [1]=>
+  object(Obj)#%d (3) {
+    ["a"]=>
+    int(4)
+    [%r"?b"?:protected"?%r]=>
+    int(5)
+    [%r"?c"?:("Obj":)?private"?%r]=>
+    int(6)
+  }
+}
+OK
+array(2) {
+  [0]=>
+  &object(Obj)#%d (3) {
+    ["a"]=>
+    int(1)
+    [%r"?b"?:protected"?%r]=>
+    int(2)
+    [%r"?c"?:("Obj":)?private"?%r]=>
+    int(3)
+  }
+  [1]=>
+  &object(Obj)#%d (3) {
+    ["a"]=>
+    int(1)
+    [%r"?b"?:protected"?%r]=>
+    int(2)
+    [%r"?c"?:("Obj":)?private"?%r]=>
+    int(3)
+  }
+}
+OK
diff --git a/php/tests/080.phpt b/php/tests/080.phpt
new file mode 100644
index 00000000..aba1cb63
--- /dev/null
+++ b/php/tests/080.phpt
@@ -0,0 +1,301 @@
+--TEST--
+disabled php only (ini_set)
+--SKIPIF--
+--FILE--
+<?php
+if(!extension_loaded('msgpack')) {
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+ini_set('msgpack.php_only', 0);
+
+function test($type, $variable, $test = null) {
+    $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/081.phpt b/php/tests/081.phpt
new file mode 100644
index 00000000..d22daaa0
--- /dev/null
+++ b/php/tests/081.phpt
@@ -0,0 +1,303 @@
+--TEST--
+disabled php only for class methods (ini_set)
+--SKIPIF--
+--FILE--
+<?php
+if(!extension_loaded('msgpack')) {
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+ini_set('msgpack.php_only', 0);
+
+function test($type, $variable, $test = null) {
+    $msgpack = new MessagePack();
+
+    $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/082.phpt b/php/tests/082.phpt
new file mode 100644
index 00000000..45a9d938
--- /dev/null
+++ b/php/tests/082.phpt
@@ -0,0 +1,346 @@
+--TEST--
+disabled php only for class methods unpacker (ini_set)
+--SKIPIF--
+--FILE--
+<?php
+if(!extension_loaded('msgpack')) {
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+ini_set('msgpack.php_only', 0);
+
+function test($type, $variable, $test = null)
+{
+    $msgpack = new MessagePack();
+
+    $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/083.phpt b/php/tests/083.phpt
new file mode 100644
index 00000000..203d8299
--- /dev/null
+++ b/php/tests/083.phpt
@@ -0,0 +1,347 @@
+--TEST--
+disabled php only for class unpacker (ini_set)
+--SKIPIF--
+--FILE--
+<?php
+if(!extension_loaded('msgpack')) {
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+ini_set('msgpack.php_only', 0);
+
+function test($type, $variable, $test = null)
+{
+    $msgpack = new MessagePack();
+
+    $serialized = $msgpack->pack($variable);
+
+    $unpacker = new MessagePackUnpacker();
+
+    $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/084.phpt b/php/tests/084.phpt
new file mode 100644
index 00000000..74d061b5
--- /dev/null
+++ b/php/tests/084.phpt
@@ -0,0 +1,301 @@
+--TEST--
+disabled php only for class methods (constract)
+--SKIPIF--
+--FILE--
+<?php
+if(!extension_loaded('msgpack')) {
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+function test($type, $variable, $test = null) {
+    $msgpack = new MessagePack(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/085.phpt b/php/tests/085.phpt
new file mode 100644
index 00000000..72aacc8b
--- /dev/null
+++ b/php/tests/085.phpt
@@ -0,0 +1,344 @@
+--TEST--
+disabled php only for class methods unpacker (constract)
+--SKIPIF--
+--FILE--
+<?php
+if(!extension_loaded('msgpack')) {
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+function test($type, $variable, $test = null)
+{
+    $msgpack = new MessagePack(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/086.phpt b/php/tests/086.phpt
new file mode 100644
index 00000000..aeaa3328
--- /dev/null
+++ b/php/tests/086.phpt
@@ -0,0 +1,345 @@
+--TEST--
+disabled php only for class unpacker (constract)
+--SKIPIF--
+--FILE--
+<?php
+if(!extension_loaded('msgpack')) {
+    dl('msgpack.' . PHP_SHLIB_SUFFIX);
+}
+
+function test($type, $variable, $test = null)
+{
+    $msgpack = new MessagePack(false);
+
+    $serialized = $msgpack->pack($variable);
+
+    $unpacker = new MessagePackUnpacker(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/087.phpt b/php/tests/087.phpt
new file mode 100644
index 00000000..9bb4e49a
--- /dev/null
+++ b/php/tests/087.phpt
@@ -0,0 +1,302 @@
+--TEST--
+disabled php only for class methods (set option)
+--SKIPIF--
+--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
new file mode 100644
index 00000000..7cbabb91
--- /dev/null
+++ b/php/tests/088.phpt
@@ -0,0 +1,345 @@
+--TEST--
+disabled php only for class methods unpacker (set option)
+--SKIPIF--
+--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
new file mode 100644
index 00000000..f3a05378
--- /dev/null
+++ b/php/tests/089.phpt
@@ -0,0 +1,347 @@
+--TEST--
+disabled php only for class unpacker (set option)
+--SKIPIF--
+--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