bool TestExtVariable::test_unserialize() { { // this was crashing unserialize_from_string(StringUtil::HexDecode("53203a20224c612072756f74612067697261207065722074757474692220204d203a20227365636f6e646f206d6520736920c3a820696e6361737472617461206461207175616c6368652070617274652122")); } { Variant v = unserialize_from_string("O:8:\"stdClass\":1:{s:4:\"name\";s:5:\"value\";}"); VERIFY(v.is(KindOfObject)); Object obj = v.toObject(); VS(obj->o_getClassName(), "stdClass"); VS(obj.o_get("name"), "value"); } { Variant v = unserialize_from_string(String("O:8:\"stdClass\":1:{s:7:\"\0*\0name\";s:5:\"value\";}", 45, AttachLiteral)); VERIFY(v.is(KindOfObject)); Object obj = v.toObject(); VS(obj->o_getClassName(), "stdClass"); VS(obj.o_get("name"), uninit_null()); } { Variant v1 = CREATE_MAP3("a","apple","b",2,"c",CREATE_VECTOR3(1,"y",3)); Variant v2 = unserialize_from_string("a:3:{s:1:\"a\";s:5:\"apple\";s:1:\"b\";i:2;s:1:\"c\";a:3:{i:0;i:1;i:1;s:1:\"y\";i:2;i:3;}}"); VS(v1, v2); } return Count(true); }
void objectToBSON(const Object& value, const char* key, bson_t* bson) { const String& className = value->o_getClassName(); if (className == s_MongoId) { mongoIdToBSON(value, key, bson); } else if (className == s_MongoDate) { mongoDateToBSON(value, key, bson); } else if (className == s_MongoRegex) { mongoRegexToBSON(value, key, bson); } else if (className == s_MongoTimestamp) { mongoTimestampToBSON(value, key, bson); } else if (className == s_MongoCode) { mongoCodeToBSON(value, key, bson); } else if (className == s_MongoBinData) { mongoBinDataToBSON(value, key, bson); } else if (className == s_MongoInt32) { mongoInt32ToBSON(value, key, bson); } else if (className == s_MongoInt64) { mongoInt64ToBSON(value, key, bson); } else if (className == s_MongoMaxKey) { mongoMaxKeyToBSON(key, bson); } else if (className == s_MongoMinKey) { mongoMinKeyToBSON(key, bson); } else { arrayToBSON(value.toArray(), key, bson); } }
void CmdInterrupt::sendImpl(DebuggerThriftBuffer &thrift) { DebuggerCommand::sendImpl(thrift); thrift.write(m_interrupt); thrift.write(m_program); thrift.write(m_errorMsg); thrift.write(m_threadId); thrift.write(m_pendingJump); if (m_site) { thrift.write(true); thrift.write(m_site->getFile()); thrift.write(m_site->getLine0()); thrift.write(m_site->getChar0()); thrift.write(m_site->getLine1()); thrift.write(m_site->getChar1()); thrift.write(m_site->getNamespace()); thrift.write(m_site->getClass()); thrift.write(m_site->getFunction()); Object e = m_site->getException(); if (e.isNull()) { thrift.write(""); } else { thrift.write(e->o_getClassName()); } thrift.write(DebuggerClient::FormatVariable(e)); } else { thrift.write(false); } BreakPointInfo::SendImpl(m_matched, thrift); }
bool TestExtVariable::test_unserialize() { { Variant v = f_unserialize("O:8:\"stdClass\":1:{s:4:\"name\";s:5:\"value\";}"); VERIFY(v.is(KindOfObject)); Object obj = v.toObject(); VS(obj->o_getClassName(), "stdClass"); VS(obj.o_get("name"), "value"); } { Variant v = f_unserialize(String("O:8:\"stdClass\":1:{s:7:\"\0*\0name\";s:5:\"value\";}", 45, AttachLiteral)); VERIFY(v.is(KindOfObject)); Object obj = v.toObject(); VS(obj->o_getClassName(), "stdClass"); VS(obj.o_get("name"), "value"); } { Variant v1 = CREATE_MAP3("a","apple","b",2,"c",CREATE_VECTOR3(1,"y",3)); Variant v2 = f_unserialize("a:3:{s:1:\"a\";s:5:\"apple\";s:1:\"b\";i:2;s:1:\"c\";a:3:{i:0;i:1;i:1;s:1:\"y\";i:2;i:3;}}"); VS(v1, v2); } return Count(true); }
void Variant::unserialize(VariableUnserializer *uns, Uns::Mode mode /* = Uns::Mode::Value */) { // NOTE: If you make changes to how serialization and unserialization work, // make sure to update the reserialize() method in "runtime/ext/ext_apc.cpp" // and to update test_apc_reserialize() in "test/ext/test_ext_apc.cpp". char type, sep; type = uns->readChar(); sep = uns->readChar(); if (type != 'R') { uns->add(this, mode); } if (type == 'N') { if (sep != ';') throw Exception("Expected ';' but got '%c'", sep); setNull(); // NULL *IS* the value, without we get undefined warnings return; } if (sep != ':') { throw Exception("Expected ':' but got '%c'", sep); } switch (type) { case 'r': { int64_t id = uns->readInt(); Variant *v = uns->getByVal(id); if (v == nullptr) { throw Exception("Id %" PRId64 " out of range", id); } operator=(*v); } break; case 'R': { int64_t id = uns->readInt(); Variant *v = uns->getByRef(id); if (v == nullptr) { throw Exception("Id %" PRId64 " out of range", id); } assignRef(*v); } break; case 'b': { int64_t v = uns->readInt(); operator=((bool)v); } break; case 'i': { int64_t v = uns->readInt(); operator=(v); } break; case 'd': { double v; char ch = uns->peek(); bool negative = false; char buf[4]; if (ch == '-') { negative = true; ch = uns->readChar(); ch = uns->peek(); } if (ch == 'I') { uns->read(buf, 3); buf[3] = '\0'; if (strcmp(buf, "INF")) { throw Exception("Expected 'INF' but got '%s'", buf); } v = atof("inf"); } else if (ch == 'N') { uns->read(buf, 3); buf[3] = '\0'; if (strcmp(buf, "NAN")) { throw Exception("Expected 'NAN' but got '%s'", buf); } v = atof("nan"); } else { v = uns->readDouble(); } operator=(negative ? -v : v); } break; case 's': { String v; v.unserialize(uns); operator=(v); } break; case 'S': if (uns->getType() == VariableUnserializer::Type::APCSerialize) { union { char buf[8]; StringData *sd; } u; uns->read(u.buf, 8); operator=(u.sd); } else { throw Exception("Unknown type '%c'", type); } break; case 'a': { Array v = Array::Create(); v.unserialize(uns); operator=(v); return; // array has '}' terminating } break; case 'L': { int64_t id = uns->readInt(); sep = uns->readChar(); if (sep != ':') { throw Exception("Expected ':' but got '%c'", sep); } String rsrcName; rsrcName.unserialize(uns); sep = uns->readChar(); if (sep != '{') { throw Exception("Expected '{' but got '%c'", sep); } sep = uns->readChar(); if (sep != '}') { throw Exception("Expected '}' but got '%c'", sep); } DummyResource* rsrc = NEWOBJ(DummyResource); rsrc->o_setResourceId(id); rsrc->m_class_name = rsrcName; operator=(rsrc); return; // resource has '}' terminating } break; case 'O': case 'V': case 'K': { String clsName; clsName.unserialize(uns); sep = uns->readChar(); if (sep != ':') { throw Exception("Expected ':' but got '%c'", sep); } int64_t size = uns->readInt(); char sep = uns->readChar(); if (sep != ':') { throw Exception("Expected ':' but got '%c'", sep); } sep = uns->readChar(); if (sep != '{') { throw Exception("Expected '{' but got '%c'", sep); } const bool allowObjectFormatForCollections = true; Class* cls; // If we are potentially dealing with a collection, we need to try to // load the collection class under an alternate name so that we can // deserialize data that was serialized before the migration of // collections to the HH namespace. if (type != 'O') { // Collections are CPP builtins; don't attempt to autoload cls = Unit::getClass(clsName.get(), /* autoload */ false); if (!cls) { cls = tryAlternateCollectionClass(clsName.get()); } } else if (allowObjectFormatForCollections) { // In order to support the legacy {O|V}:{Set|Vector|Map} // serialization, we defer autoloading until we know that there's // no alternate (builtin) collection class. cls = Unit::getClass(clsName.get(), /* autoload */ false); if (!cls) { cls = tryAlternateCollectionClass(clsName.get()); } if (!cls) { cls = Unit::loadClass(clsName.get()); // with autoloading } } else { cls = Unit::loadClass(clsName.get()); // with autoloading } Object obj; if (RuntimeOption::UnserializationWhitelistCheck && (type == 'O') && !uns->isWhitelistedClass(clsName)) { const char* err_msg = "The object being unserialized with class name '%s' " "is not in the given whitelist. " "See http://fburl.com/SafeSerializable for more detail"; if (RuntimeOption::UnserializationWhitelistCheckWarningOnly) { raise_warning(err_msg, clsName.c_str()); } else { raise_error(err_msg, clsName.c_str()); } } if (cls) { // Only unserialize CPP extension types which can actually // support it. Otherwise, we risk creating a CPP object // without having it initialized completely. if (cls->instanceCtor() && !cls->isCppSerializable()) { obj = ObjectData::newInstance( SystemLib::s___PHP_Unserializable_ClassClass); obj->o_set(s_PHP_Unserializable_Class_Name, clsName); } else { obj = ObjectData::newInstance(cls); if (UNLIKELY(cls == c_Pair::classof() && size != 2)) { throw Exception("Pair objects must have exactly 2 elements"); } } } else { obj = ObjectData::newInstance( SystemLib::s___PHP_Incomplete_ClassClass); obj->o_set(s_PHP_Incomplete_Class_Name, clsName); } operator=(obj); if (size > 0) { if (type == 'O') { // Collections are not allowed if (obj->isCollection()) { if (size > 0) { throw Exception("%s does not support the 'O' serialization " "format", clsName.data()); } // Be lax and tolerate the 'O' serialization format for collection // classes if there are 0 properties. raise_warning("%s does not support the 'O' serialization " "format", clsName.data()); } /* Count backwards so that i is the number of properties remaining (to be used as an estimate for the total number of dynamic properties when we see the first dynamic prop). see getVariantPtr */ for (int64_t i = size; i--; ) { String key = uns->unserializeKey().toString(); int ksize = key.size(); const char *kdata = key.data(); int subLen = 0; if (kdata[0] == '\0') { if (UNLIKELY(!ksize)) { throw EmptyObjectPropertyException(); } // private or protected subLen = strlen(kdata + 1) + 2; if (UNLIKELY(subLen >= ksize)) { if (subLen == ksize) { throw EmptyObjectPropertyException(); } else { throw Exception("Mangled private object property"); } } String k(kdata + subLen, ksize - subLen, CopyString); Class* ctx = (Class*)-1; if (kdata[1] != '*') { ctx = Unit::lookupClass( String(kdata + 1, subLen - 2, CopyString).get()); } unserializeProp(uns, obj.get(), k, ctx, key, i + 1); } else { unserializeProp(uns, obj.get(), key, nullptr, key, i + 1); } } } else { assert(type == 'V' || type == 'K'); if (!obj->isCollection()) { throw Exception("%s is not a collection class", clsName.data()); } collectionUnserialize(obj.get(), uns, size, type); } } sep = uns->readChar(); if (sep != '}') { throw Exception("Expected '}' but got '%c'", sep); } obj->invokeWakeup(); return; // object has '}' terminating } break; case 'C': { String clsName; clsName.unserialize(uns); sep = uns->readChar(); if (sep != ':') { throw Exception("Expected ':' but got '%c'", sep); } String serialized; serialized.unserialize(uns, '{', '}'); Object obj; try { obj = create_object_only(clsName); } catch (ClassNotFoundException &e) { if (!uns->allowUnknownSerializableClass()) { throw; } obj = create_object_only(s_PHP_Incomplete_Class); obj->o_set(s_PHP_Incomplete_Class_Name, clsName); obj->o_set("serialized", serialized); } if (!obj->instanceof(SystemLib::s_SerializableClass)) { raise_warning("Class %s has no unserializer", obj->o_getClassName().data()); } else { obj->o_invoke_few_args(s_unserialize, 1, serialized); obj.get()->clearNoDestruct(); } operator=(obj); return; // object has '}' terminating } break; default: throw Exception("Unknown type '%c'", type); } sep = uns->readChar(); if (sep != ';') { throw Exception("Expected ';' but got '%c'", sep); } }
bool WddxPacket::recursiveAddVar(const String& varName, const Variant& varVariant, bool hasVarTag) { bool isArray = varVariant.isArray(); bool isObject = varVariant.isObject(); if (isArray || isObject) { if (hasVarTag) { m_packetString += "<var name='"; m_packetString += varName.data(); m_packetString += "'>"; } Array varAsArray; Object varAsObject = varVariant.toObject(); if (isArray) varAsArray = varVariant.toArray(); if (isObject) varAsArray = varAsObject.toArray(); int length = varAsArray.length(); if (length > 0) { ArrayIter it = ArrayIter(varAsArray); if (it.first().isString()) isObject = true; if (isObject) { m_packetString += "<struct>"; if (!isArray) { m_packetString += "<var name='php_class_name'><string>"; m_packetString += varAsObject->o_getClassName().c_str(); m_packetString += "</string></var>"; } } else { m_packetString += "<array length='"; m_packetString += std::to_string(length); m_packetString += "'>"; } for (ArrayIter it(varAsArray); it; ++it) { Variant key = it.first(); Variant value = it.second(); recursiveAddVar(key.toString(), value, isObject); } if (isObject) { m_packetString += "</struct>"; } else { m_packetString += "</array>"; } } else { //empty object if (isObject) { m_packetString += "<struct>"; if (!isArray) { m_packetString += "<var name='php_class_name'><string>"; m_packetString += varAsObject->o_getClassName().c_str(); m_packetString += "</string></var>"; } m_packetString += "</struct>"; } } if (hasVarTag) { m_packetString += "</var>"; } return true; } std::string varType = getDataTypeString(varVariant.getType()).data(); if (!getWddxEncoded(varType, "", varName, false).empty()) { std::string varValue = varVariant.toString().data(); if (varType.compare("boolean") == 0) { varValue = varVariant.toBoolean() ? "true" : "false"; } m_packetString += getWddxEncoded(varType, varValue, varName, hasVarTag); return true; } return false; }
bool f_is_callable(CVarRef v, bool syntax /* = false */, Variant name /* = null */) { if (v.isString()) { if (!name.isNull()) name = v; if (syntax) return true; String str = v.toString(); int c = str.find("::"); if (c != 0 && c != String::npos && c + 2 < str.size()) { String classname = str.substr(0, c); String methodname = str.substr(c + 2); return f_class_exists(classname) && ClassInfo::HasAccess(classname, methodname, true, false); } return f_function_exists(str); } if (v.is(KindOfArray)) { Array arr = v.toArray(); if (arr.size() == 2 && arr.exists(0LL) && arr.exists(1LL)) { Variant v0 = arr.rvalAt(0LL); Variant v1 = arr.rvalAt(1LL); Object obj; bool staticCall = false; if (v0.is(KindOfObject)) { obj = v0.toObject(); v0 = obj->o_getClassName(); } else if (v0.isString()) { if (!f_class_exists(v0.toString())) { return false; } staticCall = true; } if (v1.isString()) { String str = v1.toString(); int c = str.find("::"); if (c != 0 && c != String::npos && c + 2 < str.size()) { String name1 = v0.toString(); String name2 = str.substr(0, c); ASSERT(name1.get() && name2.get()); if (name1->isame(name2.get()) || ClassInfo::IsSubClass(name1, name2, false)) { staticCall = true; v0 = name2; v1 = str.substr(c + 2); } } } if (v0.isString() && v1.isString()) { if (!name.isNull()) { name = v0.toString() + "::" + v1.toString(); } if (same(v0, s_self) || same(v0, s_parent)) { throw NotImplementedException("augmenting class scope info"); } return ClassInfo::HasAccess(v0, v1, staticCall, !obj.isNull()); } } } if (!name.isNull()) { name = v.toString(); } return false; }
bool f_is_callable(CVarRef v, bool syntax /* = false */, Variant name /* = null */) { if (v.isString()) { if (!name.isNull()) name = v; if (syntax) return true; string lowered = Util::toLower((const char *)v.toString()); size_t c = lowered.find("::"); if (c != 0 && c != string::npos && c+2 < lowered.size()) { string classname = lowered.substr(0, c); string methodname = lowered.substr(c+2); return f_call_user_func(2, "class_exists", Array::Create(String(classname))) && ClassInfo::hasAccess(classname, methodname, true, false); } return f_function_exists(v.toString()); } if (v.is(KindOfArray)) { Array arr = v.toArray(); if (arr.size() == 2 && arr.exists(0LL) && arr.exists(1LL)) { Variant v0 = arr.rvalAt(0LL); Variant v1 = arr.rvalAt(1LL); Object obj; bool staticCall = false; if (v0.is(KindOfObject)) { obj = v0.toObject(); v0 = obj->o_getClassName(); } else if (v0.isString()) { if (!f_call_user_func(2, "class_exists", Array::Create(v0))) { return false; } staticCall = true; } if (v1.isString()) { string lowered = Util::toLower((const char *)v1.toString()); size_t c = lowered.find("::"); if (c != 0 && c != string::npos && c+2 < lowered.size()) { string name1 = Util::toLower((const char *)v0.toString()); string name2 = lowered.substr(0, c); if (name1 == name2 || ClassInfo::isSubClass(name1, name2, false)) { staticCall = true; v0 = name2; v1 = lowered.substr(c+2); } } } if (v0.isString() && v1.isString()) { if (!name.isNull()) { name = v0.toString() + "::" + v1.toString(); } if (same(v0, "self") || same(v0, "parent")) { throw NotImplementedException("augmenting class scope info"); } return ClassInfo::hasAccess(v0, v1, staticCall, !obj.isNull()); } } } if (!name.isNull()) { name = v.toString(); } return false; }