THRIFT-5757: finish PHP cross-test integration
diff --git a/lib/php/Makefile.am b/lib/php/Makefile.am
index ece8d24..07c23ba 100644
--- a/lib/php/Makefile.am
+++ b/lib/php/Makefile.am
@@ -64,8 +64,8 @@
 	lib/Factory/TJSONProtocolFactory.php \
 	lib/Factory/TProtocolFactory.php \
 	lib/Factory/TStringFuncFactory.php \
-	lib/Factory/TTransportFactoryInterface.php
-	lib/Factory/TTransportFactory.php
+	lib/Factory/TTransportFactoryInterface.php \
+	lib/Factory/TTransportFactory.php \
 	lib/Factory/TFramedTransportFactory.php
 
 phpprotocoldir = $(phpdir)/Protocol
@@ -158,4 +158,3 @@
 
 MAINTAINERCLEANFILES = \
 	Makefile.in
-
diff --git a/lib/php/lib/Protocol/TJSONProtocol.php b/lib/php/lib/Protocol/TJSONProtocol.php
index 1e8c391..28b7615 100644
--- a/lib/php/lib/Protocol/TJSONProtocol.php
+++ b/lib/php/lib/Protocol/TJSONProtocol.php
@@ -210,7 +210,7 @@
             $this->trans_->write(self::QUOTE);
         }
 
-        $this->trans_->write(json_encode($b, JSON_UNESCAPED_UNICODE));
+        $this->trans_->write(json_encode($b, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
 
         if (is_numeric($b) && $this->context_->escapeNum()) {
             $this->trans_->write(self::QUOTE);
diff --git a/lib/php/lib/Protocol/TSimpleJSONProtocol.php b/lib/php/lib/Protocol/TSimpleJSONProtocol.php
index 982bfd3..1927aad 100644
--- a/lib/php/lib/Protocol/TSimpleJSONProtocol.php
+++ b/lib/php/lib/Protocol/TSimpleJSONProtocol.php
@@ -85,7 +85,7 @@
     {
         $this->writeContext_->write();
 
-        $this->trans_->write(json_encode((string)$b));
+        $this->trans_->write(json_encode((string)$b, JSON_UNESCAPED_SLASHES));
     }
 
     private function writeJSONInteger($num)
diff --git a/lib/php/src/ext/thrift_protocol/php_thrift_protocol.cpp b/lib/php/src/ext/thrift_protocol/php_thrift_protocol.cpp
index c016b07..04dc1d2 100644
--- a/lib/php/src/ext/thrift_protocol/php_thrift_protocol.cpp
+++ b/lib/php/src/ext/thrift_protocol/php_thrift_protocol.cpp
@@ -72,6 +72,7 @@
   T_MAP        = 13,
   T_SET        = 14,
   T_LIST       = 15,
+  T_UUID       = 16,
   T_UTF8       = 16,
   T_UTF16      = 17
 };
@@ -85,6 +86,61 @@
 const int INVALID_DATA = 1;
 const int BAD_VERSION = 4;
 
+static inline int uuid_hex_nibble(char c) {
+  if (c >= '0' && c <= '9') {
+    return c - '0';
+  }
+  if (c >= 'a' && c <= 'f') {
+    return 10 + (c - 'a');
+  }
+  if (c >= 'A' && c <= 'F') {
+    return 10 + (c - 'A');
+  }
+  return -1;
+}
+
+static bool parse_uuid_string(const char* value, size_t len, uint8_t (&uuid)[16]) {
+  size_t nibble_index = 0;
+  int high_nibble = -1;
+
+  for (size_t i = 0; i < len; ++i) {
+    const char c = value[i];
+    if (c == '-' || c == '{' || c == '}') {
+      continue;
+    }
+
+    const int nibble = uuid_hex_nibble(c);
+    if (nibble < 0) {
+      return false;
+    }
+
+    if (high_nibble < 0) {
+      high_nibble = nibble;
+    } else {
+      if (nibble_index >= sizeof(uuid)) {
+        return false;
+      }
+      uuid[nibble_index++] = static_cast<uint8_t>((high_nibble << 4) | nibble);
+      high_nibble = -1;
+    }
+  }
+
+  return nibble_index == sizeof(uuid) && high_nibble < 0;
+}
+
+static void format_uuid_string(const uint8_t (&uuid)[16], char (&buffer)[37]) {
+  static const char hex[] = "0123456789abcdef";
+  size_t out = 0;
+  for (size_t i = 0; i < sizeof(uuid); ++i) {
+    if (i == 4 || i == 6 || i == 8 || i == 10) {
+      buffer[out++] = '-';
+    }
+    buffer[out++] = hex[(uuid[i] >> 4) & 0x0f];
+    buffer[out++] = hex[uuid[i] & 0x0f];
+  }
+  buffer[out] = '\0';
+}
+
 zend_module_entry thrift_protocol_module_entry = {
   STANDARD_MODULE_HEADER,
   "thrift_protocol",
@@ -467,8 +523,10 @@
     case T_DOUBLE:
       transport.skip(8);
       return;
+    case T_UUID:
+      transport.skip(16);
+      return;
     //case T_UTF7: // aliases T_STRING
-    case T_UTF8:
     case T_UTF16:
     case T_STRING: {
       uint32_t len = transport.readU32();
@@ -582,7 +640,6 @@
       RETURN_DOUBLE(a.d);
     }
     //case T_UTF7: // aliases T_STRING
-    case T_UTF8:
     case T_UTF16:
     case T_STRING: {
       uint32_t size = transport.readU32();
@@ -596,6 +653,14 @@
       }
       return;
     }
+    case T_UUID: {
+      uint8_t uuid[16];
+      char strbuf[37];
+      transport.readBytes(uuid, sizeof(uuid));
+      format_uuid_string(uuid, strbuf);
+      ZVAL_STRINGL(return_value, strbuf, sizeof(strbuf) - 1);
+      return;
+    }
     case T_MAP: { // array of key -> value
       uint8_t types[2];
       transport.readBytes(types, 2);
@@ -673,7 +738,7 @@
 
 static
 void binary_serialize_hashtable_key(int8_t keytype, PHPOutputTransport& transport, HashTable* ht, HashPosition& ht_pos, HashTable* spec) {
-  bool keytype_is_numeric = (!((keytype == T_STRING) || (keytype == T_UTF8) || (keytype == T_UTF16)));
+  bool keytype_is_numeric = (!((keytype == T_STRING) || (keytype == T_UUID) || (keytype == T_UTF16)));
 
   zend_string* key;
   uint key_len;
@@ -751,7 +816,15 @@
       a.d = Z_DVAL_P(value);
       transport.writeI64(a.c);
     } return;
-    case T_UTF8:
+    case T_UUID: {
+      if (Z_TYPE_P(value) != IS_STRING) convert_to_string(value);
+      uint8_t uuid[16];
+      if (!parse_uuid_string(Z_STRVAL_P(value), Z_STRLEN_P(value), uuid)) {
+        throw_tprotocolexception("Attempt to send an invalid UUID string", INVALID_DATA);
+      }
+      transport.write(reinterpret_cast<const char*>(uuid), sizeof(uuid));
+      return;
+    }
     case T_UTF16:
     case T_STRING:
       if (Z_TYPE_P(value) != IS_STRING) convert_to_string(value);