cc858b73db
Replace "collection" with "collections" in various file names since we typically use the plural form in conversation and documentation. Add set() and removeAt() methods needed for collection interfaces. Also add the KeyedIterable and KeyedIterator interfaces. Add __construct() methods for collections
905 linhas
22 KiB
C++
905 linhas
22 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#include <runtime/base/variable_serializer.h>
|
|
#include <runtime/base/execution_context.h>
|
|
#include <runtime/base/complex_types.h>
|
|
#include <util/exception.h>
|
|
#include <runtime/base/zend/zend_printf.h>
|
|
#include <runtime/base/zend/zend_functions.h>
|
|
#include <runtime/base/zend/zend_string.h>
|
|
#include <math.h>
|
|
#include <cmath>
|
|
#include <runtime/base/runtime_option.h>
|
|
#include <runtime/base/array/array_iterator.h>
|
|
#include <runtime/base/util/request_local.h>
|
|
#include <runtime/ext/ext_json.h>
|
|
#include <runtime/ext/ext_collections.h>
|
|
|
|
namespace HPHP {
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// static strings
|
|
|
|
static StaticString s_JsonSerializable("JsonSerializable");
|
|
static StaticString s_jsonSerialize("jsonSerialize");
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
VariableSerializer::VariableSerializer(Type type, int option /* = 0 */,
|
|
int maxRecur /* = 3 */)
|
|
: m_type(type), m_option(option), m_buf(nullptr), m_indent(0),
|
|
m_valueCount(0), m_referenced(false), m_refCount(1), m_maxCount(maxRecur),
|
|
m_levelDebugger(0) {
|
|
m_maxLevelDebugger = g_context->getDebuggerPrintLevel();
|
|
if (type == Serialize || type == APCSerialize || type == DebuggerSerialize) {
|
|
m_arrayIds = new PointerCounterMap();
|
|
} else {
|
|
m_arrayIds = nullptr;
|
|
}
|
|
}
|
|
|
|
void VariableSerializer::setObjectInfo(CStrRef objClass, int objId,
|
|
char objCode) {
|
|
assert(objCode == 'O' || objCode == 'V' || objCode == 'K');
|
|
m_objClass = objClass;
|
|
m_objId = objId;
|
|
m_objCode = objCode;
|
|
}
|
|
|
|
void VariableSerializer::getResourceInfo(String &rsrcName, int &rsrcId) {
|
|
rsrcName = m_rsrcName;
|
|
rsrcId = m_rsrcId;
|
|
}
|
|
|
|
void VariableSerializer::setResourceInfo(CStrRef rsrcName, int rsrcId) {
|
|
m_rsrcName = rsrcName;
|
|
m_rsrcId = rsrcId;
|
|
}
|
|
|
|
String VariableSerializer::serialize(CVarRef v, bool ret) {
|
|
StringBuffer buf;
|
|
m_buf = &buf;
|
|
if (ret) {
|
|
buf.setOutputLimit(RuntimeOption::SerializationSizeLimit);
|
|
} else {
|
|
buf.setOutputLimit(StringData::MaxSize);
|
|
}
|
|
m_valueCount = 1;
|
|
write(v);
|
|
if (ret) {
|
|
return m_buf->detach();
|
|
} else {
|
|
String str = m_buf->detach();
|
|
g_context->write(str);
|
|
}
|
|
return null_string;
|
|
}
|
|
|
|
String VariableSerializer::serializeValue(CVarRef v, bool limit) {
|
|
StringBuffer buf;
|
|
m_buf = &buf;
|
|
if (limit) {
|
|
buf.setOutputLimit(RuntimeOption::SerializationSizeLimit);
|
|
}
|
|
m_valueCount = 1;
|
|
write(v);
|
|
return m_buf->detach();
|
|
}
|
|
|
|
String VariableSerializer::serializeWithLimit(CVarRef v, int limit) {
|
|
if (m_type == Serialize || m_type == JSON || m_type == APCSerialize ||
|
|
m_type == DebuggerSerialize) {
|
|
assert(false);
|
|
return null_string;
|
|
}
|
|
StringBuffer buf;
|
|
m_buf = &buf;
|
|
if (RuntimeOption::SerializationSizeLimit > 0 &&
|
|
(limit <= 0 || limit > RuntimeOption::SerializationSizeLimit)) {
|
|
limit = RuntimeOption::SerializationSizeLimit;
|
|
}
|
|
buf.setOutputLimit(limit);
|
|
//Does not need m_valueCount, which is only useful with the unsupported types
|
|
try {
|
|
write(v);
|
|
} catch (StringBufferLimitException &e) {
|
|
return e.m_result;
|
|
}
|
|
return m_buf->detach();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void VariableSerializer::write(bool v) {
|
|
switch (m_type) {
|
|
case PrintR:
|
|
if (v) m_buf->append(1);
|
|
break;
|
|
case VarExport:
|
|
case PHPOutput:
|
|
case JSON:
|
|
case DebuggerDump:
|
|
m_buf->append(v ? "true" : "false");
|
|
break;
|
|
case VarDump:
|
|
case DebugDump:
|
|
indent();
|
|
m_buf->append(v ? "bool(true)" : "bool(false)");
|
|
writeRefCount();
|
|
m_buf->append('\n');
|
|
break;
|
|
case Serialize:
|
|
case APCSerialize:
|
|
case DebuggerSerialize:
|
|
m_buf->append(v ? "b:1;" : "b:0;");
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void VariableSerializer::write(int64_t v) {
|
|
switch (m_type) {
|
|
case PrintR:
|
|
case VarExport:
|
|
case PHPOutput:
|
|
case JSON:
|
|
case DebuggerDump:
|
|
m_buf->append(v);
|
|
break;
|
|
case VarDump:
|
|
indent();
|
|
m_buf->append("int(");
|
|
m_buf->append(v);
|
|
m_buf->append(")\n");
|
|
break;
|
|
case DebugDump:
|
|
indent();
|
|
m_buf->append("long(");
|
|
m_buf->append(v);
|
|
m_buf->append(')');
|
|
writeRefCount();
|
|
m_buf->append('\n');
|
|
break;
|
|
case Serialize:
|
|
case APCSerialize:
|
|
case DebuggerSerialize:
|
|
m_buf->append("i:");
|
|
m_buf->append(v);
|
|
m_buf->append(';');
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void VariableSerializer::write(double v) {
|
|
switch (m_type) {
|
|
case JSON:
|
|
if (!std::isinf(v) && !std::isnan(v)) {
|
|
char *buf;
|
|
if (v == 0.0) v = 0.0; // so to avoid "-0" output
|
|
vspprintf(&buf, 0, "%.*k", 14, v);
|
|
m_buf->append(buf);
|
|
free(buf);
|
|
} else {
|
|
// PHP issues a warning: double INF/NAN does not conform to the
|
|
// JSON spec, encoded as 0.
|
|
m_buf->append('0');
|
|
}
|
|
break;
|
|
case VarExport:
|
|
case PHPOutput:
|
|
case PrintR:
|
|
case DebuggerDump:
|
|
{
|
|
char *buf;
|
|
if (v == 0.0) v = 0.0; // so to avoid "-0" output
|
|
bool isExport = m_type == VarExport || m_type == PHPOutput;
|
|
vspprintf(&buf, 0, isExport ? "%.*H" : "%.*G", 14, v);
|
|
m_buf->append(buf);
|
|
// In PHPOutput mode, we always want doubles to parse as
|
|
// doubles, so make sure there's a decimal point.
|
|
if (m_type == PHPOutput && strpbrk(buf, ".E") == nullptr) {
|
|
m_buf->append(".0");
|
|
}
|
|
free(buf);
|
|
}
|
|
break;
|
|
case VarDump:
|
|
case DebugDump:
|
|
{
|
|
char *buf;
|
|
if (v == 0.0) v = 0.0; // so to avoid "-0" output
|
|
vspprintf(&buf, 0, "float(%.*G)", 14, v);
|
|
indent();
|
|
m_buf->append(buf);
|
|
free(buf);
|
|
writeRefCount();
|
|
m_buf->append('\n');
|
|
}
|
|
break;
|
|
case Serialize:
|
|
case APCSerialize:
|
|
case DebuggerSerialize:
|
|
m_buf->append("d:");
|
|
if (std::isnan(v)) {
|
|
m_buf->append("NAN");
|
|
} else if (std::isinf(v)) {
|
|
if (v < 0) m_buf->append('-');
|
|
m_buf->append("INF");
|
|
} else {
|
|
char *buf;
|
|
if (v == 0.0) v = 0.0; // so to avoid "-0" output
|
|
vspprintf(&buf, 0, "%.*H", 14, v);
|
|
m_buf->append(buf);
|
|
free(buf);
|
|
}
|
|
m_buf->append(';');
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void VariableSerializer::write(const char *v, int len /* = -1 */,
|
|
bool isArrayKey /* = false */) {
|
|
if (v == nullptr) v = "";
|
|
if (len < 0) len = strlen(v);
|
|
|
|
switch (m_type) {
|
|
case PrintR: {
|
|
m_buf->append(v, len);
|
|
break;
|
|
}
|
|
case DebuggerDump:
|
|
case VarExport: {
|
|
m_buf->append('\'');
|
|
const char *p = v;
|
|
for (int i = 0; i < len; i++, p++) {
|
|
const char c = *p;
|
|
// adapted from Zend php_var_export and php_addcslashes
|
|
if (c == '\0') {
|
|
m_buf->append("' . \"\\0\" . '");
|
|
continue;
|
|
} else if (c == '\'' || c == '\\') {
|
|
m_buf->append('\\');
|
|
}
|
|
m_buf->append(c);
|
|
}
|
|
m_buf->append('\'');
|
|
break;
|
|
}
|
|
case VarDump:
|
|
case DebugDump: {
|
|
indent();
|
|
m_buf->append("string(");
|
|
m_buf->append(len);
|
|
m_buf->append(") \"");
|
|
m_buf->append(v, len);
|
|
m_buf->append('"');
|
|
writeRefCount();
|
|
m_buf->append('\n');
|
|
break;
|
|
}
|
|
case Serialize:
|
|
case APCSerialize:
|
|
case DebuggerSerialize:
|
|
m_buf->append("s:");
|
|
m_buf->append(len);
|
|
m_buf->append(":\"");
|
|
m_buf->append(v, len);
|
|
m_buf->append("\";");
|
|
break;
|
|
case JSON: {
|
|
if (m_option & k_JSON_NUMERIC_CHECK) {
|
|
int64_t lval; double dval;
|
|
switch (is_numeric_string(v, len, &lval, &dval, 0)) {
|
|
case KindOfInt64:
|
|
write(lval);
|
|
return;
|
|
case KindOfDouble:
|
|
write(dval);
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_buf->appendJsonEscape(v, len, m_option);
|
|
break;
|
|
}
|
|
case PHPOutput: {
|
|
m_buf->append('"');
|
|
for (int i = 0; i < len; ++i) {
|
|
const unsigned char c = v[i];
|
|
switch (c) {
|
|
case '\n': m_buf->append("\\n"); break;
|
|
case '\r': m_buf->append("\\r"); break;
|
|
case '\t': m_buf->append("\\t"); break;
|
|
case '\\': m_buf->append("\\\\"); break;
|
|
case '$': m_buf->append("\\$"); break;
|
|
case '"': m_buf->append("\\\""); break;
|
|
default: {
|
|
if (c >= ' ' && c <= '~') {
|
|
// The range [' ', '~'] contains only printable characters
|
|
// and we've already handled special cases above
|
|
m_buf->append(c);
|
|
} else {
|
|
char buf[5];
|
|
snprintf(buf, sizeof(buf), "\\%03o", c);
|
|
m_buf->append(buf);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
m_buf->append('"');
|
|
break;
|
|
}
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void VariableSerializer::write(CStrRef v) {
|
|
if (m_type == APCSerialize && !v.isNull() && v->isStatic()) {
|
|
union {
|
|
char buf[8];
|
|
StringData *sd;
|
|
} u;
|
|
u.sd = v.get();
|
|
m_buf->append("S:");
|
|
m_buf->append(u.buf, 8);
|
|
m_buf->append(';');
|
|
} else {
|
|
v.serialize(this);
|
|
}
|
|
}
|
|
|
|
void VariableSerializer::write(CObjRef v) {
|
|
if (!v.isNull() && m_type == JSON) {
|
|
|
|
if (v.instanceof(SystemLib::s_JsonSerializableClass)) {
|
|
assert(!v->isCollection());
|
|
Variant ret = v->o_invoke(s_jsonSerialize, null_array, -1);
|
|
// for non objects or when $this is returned
|
|
if (!ret.isObject() || (ret.isObject() && !ret.same(v))) {
|
|
write(ret);
|
|
return;
|
|
}
|
|
}
|
|
if (incNestedLevel(v.get(), true)) {
|
|
writeOverflow(v.get(), true);
|
|
} else {
|
|
if (v->isCollection()) {
|
|
collectionSerialize(v.get(), this);
|
|
} else {
|
|
Array props(ArrayData::Create());
|
|
ClassInfo::GetArray(v.get(), props, ClassInfo::GetArrayPublic);
|
|
setObjectInfo(v->o_getClassName(), v->o_getId(), 'O');
|
|
props.serialize(this);
|
|
}
|
|
}
|
|
decNestedLevel(v.get());
|
|
} else {
|
|
v.serialize(this);
|
|
}
|
|
}
|
|
|
|
void VariableSerializer::write(CVarRef v, bool isArrayKey /* = false */) {
|
|
setReferenced(v.isReferenced());
|
|
setRefCount(v.getRefCount());
|
|
if (!isArrayKey && v.isObject()) {
|
|
write(v.toObject());
|
|
return;
|
|
}
|
|
v.serialize(this, isArrayKey);
|
|
}
|
|
|
|
void VariableSerializer::writeNull() {
|
|
switch (m_type) {
|
|
case PrintR:
|
|
// do nothing
|
|
break;
|
|
case VarExport:
|
|
case PHPOutput:
|
|
m_buf->append("NULL");
|
|
break;
|
|
case VarDump:
|
|
case DebugDump:
|
|
indent();
|
|
m_buf->append("NULL");
|
|
writeRefCount();
|
|
m_buf->append('\n');
|
|
break;
|
|
case Serialize:
|
|
case APCSerialize:
|
|
case DebuggerSerialize:
|
|
m_buf->append("N;");
|
|
break;
|
|
case JSON:
|
|
case DebuggerDump:
|
|
m_buf->append("null");
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void VariableSerializer::writeOverflow(void* ptr, bool isObject /* = false */) {
|
|
bool wasRef = m_referenced;
|
|
setReferenced(false);
|
|
switch (m_type) {
|
|
case PrintR:
|
|
if (!m_objClass.empty()) {
|
|
m_buf->append(m_objClass);
|
|
m_buf->append(" Object\n");
|
|
} else {
|
|
m_buf->append("Array\n");
|
|
}
|
|
m_buf->append(" *RECURSION*");
|
|
break;
|
|
case VarExport:
|
|
case PHPOutput:
|
|
throw NestingLevelTooDeepException();
|
|
case VarDump:
|
|
case DebugDump:
|
|
case DebuggerDump:
|
|
indent();
|
|
m_buf->append("*RECURSION*\n");
|
|
break;
|
|
case DebuggerSerialize:
|
|
if (m_maxLevelDebugger > 0 && m_levelDebugger > m_maxLevelDebugger) {
|
|
// Not recursion, just cut short of print
|
|
m_buf->append("s:12:\"...(omitted)\";", 20);
|
|
break;
|
|
}
|
|
// fall through
|
|
case Serialize:
|
|
case APCSerialize:
|
|
{
|
|
assert(m_arrayIds);
|
|
PointerCounterMap::const_iterator iter = m_arrayIds->find(ptr);
|
|
assert(iter != m_arrayIds->end());
|
|
int id = iter->second;
|
|
if (isObject) {
|
|
m_buf->append("r:");
|
|
m_buf->append(id);
|
|
m_buf->append(';');
|
|
} else if (wasRef) {
|
|
m_buf->append("R:");
|
|
m_buf->append(id);
|
|
m_buf->append(';');
|
|
} else {
|
|
m_buf->append("N;");
|
|
}
|
|
}
|
|
break;
|
|
case JSON:
|
|
raise_warning("json_encode(): recursion detected");
|
|
m_buf->append("null");
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void VariableSerializer::writeRefCount() {
|
|
if (m_type == DebugDump) {
|
|
m_buf->append(" refcount(");
|
|
m_buf->append(m_refCount);
|
|
m_buf->append(')');
|
|
m_refCount = 1;
|
|
}
|
|
}
|
|
|
|
void VariableSerializer::writeArrayHeader(int size, bool isVectorData) {
|
|
m_arrayInfos.push_back(ArrayInfo());
|
|
ArrayInfo &info = m_arrayInfos.back();
|
|
info.first_element = true;
|
|
info.indent_delta = 0;
|
|
|
|
switch (m_type) {
|
|
case PrintR:
|
|
if (!m_rsrcName.empty()) {
|
|
m_buf->append("Resource id #");
|
|
m_buf->append(m_rsrcId);
|
|
break;
|
|
} else if (!m_objClass.empty()) {
|
|
m_buf->append(m_objClass);
|
|
m_buf->append(" Object\n");
|
|
} else {
|
|
m_buf->append("Array\n");
|
|
}
|
|
if (m_indent > 0) {
|
|
m_indent += 4;
|
|
indent();
|
|
}
|
|
m_buf->append("(\n");
|
|
m_indent += (info.indent_delta = 4);
|
|
break;
|
|
case VarExport:
|
|
case PHPOutput:
|
|
if (m_indent > 0) {
|
|
m_buf->append('\n');
|
|
indent();
|
|
}
|
|
if (!m_objClass.empty()) {
|
|
m_buf->append(m_objClass);
|
|
m_buf->append("::__set_state(array(\n");
|
|
} else {
|
|
m_buf->append("array (\n");
|
|
}
|
|
m_indent += (info.indent_delta = 2);
|
|
break;
|
|
case VarDump:
|
|
case DebugDump:
|
|
indent();
|
|
if (!m_rsrcName.empty()) {
|
|
m_buf->append("resource(");
|
|
m_buf->append(m_rsrcId);
|
|
m_buf->append(") of type (");
|
|
m_buf->append(m_rsrcName);
|
|
m_buf->append(")\n");
|
|
break;
|
|
} else if (!m_objClass.empty()) {
|
|
m_buf->append("object(");
|
|
m_buf->append(m_objClass);
|
|
m_buf->append(")#");
|
|
m_buf->append(m_objId);
|
|
m_buf->append(' ');
|
|
} else {
|
|
m_buf->append("array");
|
|
}
|
|
m_buf->append('(');
|
|
m_buf->append(size);
|
|
m_buf->append(')');
|
|
|
|
// ...so to strictly follow PHP's output
|
|
if (m_type == VarDump) {
|
|
m_buf->append(' ');
|
|
} else {
|
|
writeRefCount();
|
|
}
|
|
|
|
m_buf->append("{\n");
|
|
m_indent += (info.indent_delta = 2);
|
|
break;
|
|
case Serialize:
|
|
case APCSerialize:
|
|
case DebuggerSerialize:
|
|
if (!m_objClass.empty()) {
|
|
m_buf->append(m_objCode);
|
|
m_buf->append(":");
|
|
m_buf->append((int)m_objClass.size());
|
|
m_buf->append(":\"");
|
|
m_buf->append(m_objClass);
|
|
m_buf->append("\":");
|
|
m_buf->append(size);
|
|
m_buf->append(":{");
|
|
} else {
|
|
m_buf->append("a:");
|
|
m_buf->append(size);
|
|
m_buf->append(":{");
|
|
}
|
|
break;
|
|
case JSON:
|
|
case DebuggerDump:
|
|
info.is_vector =
|
|
(m_objClass.empty() || m_objCode == 'V' || m_objCode == 'K') &&
|
|
isVectorData;
|
|
if (info.is_vector && m_type == JSON) {
|
|
info.is_vector = (m_option & k_JSON_FORCE_OBJECT)
|
|
? false : info.is_vector;
|
|
}
|
|
|
|
if (info.is_vector) {
|
|
m_buf->append('[');
|
|
} else {
|
|
m_buf->append('{');
|
|
}
|
|
|
|
if (m_type == JSON && m_option & k_JSON_PRETTY_PRINT) {
|
|
m_indent += (info.indent_delta = 4);
|
|
}
|
|
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
// ...so we don't mess up next array output
|
|
if (!m_objClass.empty() || !m_rsrcName.empty()) {
|
|
m_objClass.clear();
|
|
info.is_object = true;
|
|
} else {
|
|
info.is_object = false;
|
|
}
|
|
}
|
|
|
|
void VariableSerializer::writePropertyKey(CStrRef prop) {
|
|
const char *key = prop.data();
|
|
int kl = prop.size();
|
|
if (!*key && kl) {
|
|
const char *cls = key + 1;
|
|
if (*cls == '*') {
|
|
assert(key[2] == 0);
|
|
m_buf->append(key + 3, kl - 3);
|
|
const char prot[] = "\":protected";
|
|
int o = m_type == PrintR ? 1 : 0;
|
|
m_buf->append(prot + o, sizeof(prot) - 1 - o);
|
|
} else {
|
|
int l = strlen(cls);
|
|
m_buf->append(cls + l + 1, kl - l - 2);
|
|
int o = m_type == PrintR ? 1 : 0;
|
|
m_buf->append("\":\"" + o, 3 - 2*o);
|
|
m_buf->append(cls, l);
|
|
const char priv[] = "\":private";
|
|
m_buf->append(priv + o, sizeof(priv) - 1 - o);
|
|
}
|
|
} else {
|
|
m_buf->append(prop);
|
|
if (m_type != PrintR) m_buf->append('"');
|
|
}
|
|
}
|
|
|
|
/* key MUST be a non-reference string or int */
|
|
void VariableSerializer::writeArrayKey(Variant key) {
|
|
Variant::TypedValueAccessor tva = key.getTypedAccessor();
|
|
bool skey = Variant::IsString(tva);
|
|
if (skey && m_type == APCSerialize) {
|
|
write(Variant::GetAsString(tva));
|
|
return;
|
|
}
|
|
ArrayInfo &info = m_arrayInfos.back();
|
|
switch (m_type) {
|
|
case PrintR: {
|
|
indent();
|
|
m_buf->append('[');
|
|
if (info.is_object && skey) {
|
|
writePropertyKey(Variant::GetAsString(tva));
|
|
} else {
|
|
m_buf->append(key);
|
|
}
|
|
m_buf->append("] => ");
|
|
break;
|
|
}
|
|
case VarExport:
|
|
case PHPOutput:
|
|
indent();
|
|
write(key, true);
|
|
m_buf->append(" => ");
|
|
break;
|
|
case VarDump:
|
|
case DebugDump:
|
|
indent();
|
|
m_buf->append('[');
|
|
if (!skey) {
|
|
m_buf->append(Variant::GetInt64(tva));
|
|
} else {
|
|
m_buf->append('"');
|
|
if (info.is_object) {
|
|
writePropertyKey(Variant::GetAsString(tva));
|
|
} else {
|
|
m_buf->append(Variant::GetAsString(tva));
|
|
m_buf->append('"');
|
|
}
|
|
}
|
|
m_buf->append("]=>\n");
|
|
break;
|
|
case APCSerialize:
|
|
assert(!info.is_object);
|
|
case Serialize:
|
|
case DebuggerSerialize:
|
|
write(key);
|
|
break;
|
|
case JSON:
|
|
case DebuggerDump:
|
|
if (!info.first_element) {
|
|
m_buf->append(',');
|
|
}
|
|
if (m_type == JSON && m_option & k_JSON_PRETTY_PRINT) {
|
|
m_buf->append("\n");
|
|
indent();
|
|
}
|
|
if (!info.is_vector) {
|
|
if (skey) {
|
|
CStrRef s = Variant::GetAsString(tva);
|
|
const char *k = s.data();
|
|
int len = s.size();
|
|
if (info.is_object && !*k && len) {
|
|
while (*++k) len--;
|
|
k++;
|
|
len -= 2;
|
|
}
|
|
write(k, len);
|
|
} else {
|
|
m_buf->append('"');
|
|
m_buf->append(Variant::GetInt64(tva));
|
|
m_buf->append('"');
|
|
}
|
|
m_buf->append(':');
|
|
if (m_type == JSON && m_option & k_JSON_PRETTY_PRINT) {
|
|
m_buf->append(' ');
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void VariableSerializer::writeArrayValue(CVarRef value) {
|
|
// Do not count referenced values after the first
|
|
if ((m_type == Serialize || m_type == APCSerialize ||
|
|
m_type == DebuggerSerialize) &&
|
|
!(value.isReferenced() &&
|
|
m_arrayIds->find(value.getRefData()) != m_arrayIds->end())) {
|
|
m_valueCount++;
|
|
}
|
|
|
|
write(value);
|
|
switch (m_type) {
|
|
case PrintR:
|
|
m_buf->append('\n');
|
|
break;
|
|
case VarExport:
|
|
case PHPOutput:
|
|
m_buf->append(",\n");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
ArrayInfo &info = m_arrayInfos.back();
|
|
info.first_element = false;
|
|
}
|
|
|
|
void VariableSerializer::writeArrayFooter() {
|
|
ArrayInfo &info = m_arrayInfos.back();
|
|
|
|
m_indent -= info.indent_delta;
|
|
switch (m_type) {
|
|
case PrintR:
|
|
if (m_rsrcName.empty()) {
|
|
indent();
|
|
m_buf->append(")\n");
|
|
if (m_indent > 0) {
|
|
m_indent -= 4;
|
|
}
|
|
}
|
|
break;
|
|
case VarExport:
|
|
case PHPOutput:
|
|
indent();
|
|
if (info.is_object) {
|
|
m_buf->append("))");
|
|
} else {
|
|
m_buf->append(')');
|
|
}
|
|
break;
|
|
case VarDump:
|
|
case DebugDump:
|
|
if (m_rsrcName.empty()) {
|
|
indent();
|
|
m_buf->append("}\n");
|
|
}
|
|
break;
|
|
case Serialize:
|
|
case APCSerialize:
|
|
case DebuggerSerialize:
|
|
m_buf->append('}');
|
|
break;
|
|
case JSON:
|
|
case DebuggerDump:
|
|
if (m_type == JSON && m_option & k_JSON_PRETTY_PRINT) {
|
|
m_buf->append("\n");
|
|
indent();
|
|
}
|
|
if (info.is_vector) {
|
|
m_buf->append(']');
|
|
} else {
|
|
m_buf->append('}');
|
|
}
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
m_arrayInfos.pop_back();
|
|
}
|
|
|
|
void VariableSerializer::writeSerializableObject(CStrRef clsname,
|
|
CStrRef serialized) {
|
|
m_buf->append("C:");
|
|
m_buf->append(clsname.size());
|
|
m_buf->append(":\"");
|
|
m_buf->append(clsname.data(), clsname.size());
|
|
m_buf->append("\":");
|
|
m_buf->append(serialized.size());
|
|
m_buf->append(":{");
|
|
m_buf->append(serialized.data(), serialized.size());
|
|
m_buf->append('}');
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void VariableSerializer::indent() {
|
|
for (int i = 0; i < m_indent; i++) {
|
|
m_buf->append(' ');
|
|
}
|
|
if (m_referenced) {
|
|
if (m_indent > 0 && m_type == VarDump) m_buf->append('&');
|
|
m_referenced = false;
|
|
}
|
|
}
|
|
|
|
bool VariableSerializer::incNestedLevel(void *ptr,
|
|
bool isObject /* = false */) {
|
|
switch (m_type) {
|
|
case VarExport:
|
|
case PHPOutput:
|
|
case PrintR:
|
|
case VarDump:
|
|
case DebugDump:
|
|
case JSON:
|
|
case DebuggerDump:
|
|
return ++m_counts[ptr] >= m_maxCount;
|
|
case DebuggerSerialize:
|
|
if (m_maxLevelDebugger > 0 && ++m_levelDebugger > m_maxLevelDebugger) {
|
|
return true;
|
|
}
|
|
// fall through
|
|
case Serialize:
|
|
case APCSerialize:
|
|
{
|
|
assert(m_arrayIds);
|
|
int ct = ++m_counts[ptr];
|
|
if (m_arrayIds->find(ptr) != m_arrayIds->end() &&
|
|
(m_referenced || isObject)) {
|
|
return true;
|
|
} else {
|
|
(*m_arrayIds)[ptr] = m_valueCount;
|
|
}
|
|
return ct >= (m_maxCount - 1);
|
|
}
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void VariableSerializer::decNestedLevel(void *ptr) {
|
|
--m_counts[ptr];
|
|
if (m_type == DebuggerSerialize && m_maxLevelDebugger > 0) {
|
|
--m_levelDebugger;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}
|