Serialize partial class instances using correct class name

When hphpd does a print instruction for a value that is an object, the object is serialized in the proxy (server) and then deserialized in the client. If the client VM does not have the class loaded, then the deserializer creates an instance of __PHP_Incomplete_Class and adds the class name to it as the value of a property called __PHP_Incomplete_Class_Name. This then shows up in the textual output of the print command, which is a bit confusing to say the least. Ironically the printed output is obtained by serializing the object instance.

The basic design seems OK to me: The proxy should send the client the raw data and then leave it up to the client to figure out how to format it and what elide. And in this case, if the client is accessed via the API, the caller gets the raw data. So, it does not seem like a great idea to get the proxy to format objects so that the client just has to print the string as received from the client.

Instead, this diff changes the object serializer so that when the PrintR option (debug output format) is specified, incomplete class instances are serialized with the class name obtained from the value of the __PHP_Incomplete_Class_Name (if present) and the property itself is elided from the output.
Esse commit está contido em:
Herman Venter
2013-05-23 10:41:26 -07:00
commit de Sara Golemon
commit 58760b99ae
7 arquivos alterados com 95 adições e 7 exclusões
+17 -2
Ver Arquivo
@@ -556,6 +556,9 @@ void ObjectData::serialize(VariableSerializer *serializer) const {
serializer->decNestedLevel((void*)this);
}
static StaticString s_PHP_Incomplete_Class("__PHP_Incomplete_Class");
static StaticString s_PHP_Incomplete_Class_Name("__PHP_Incomplete_Class_Name");
void ObjectData::serializeImpl(VariableSerializer *serializer) const {
bool handleSleep = false;
Variant ret;
@@ -668,8 +671,20 @@ void ObjectData::serializeImpl(VariableSerializer *serializer) const {
if (isCollection()) {
collectionSerialize(const_cast<ObjectData*>(this), serializer);
} else {
serializer->setObjectInfo(o_getClassName(), o_getId(), 'O');
o_toArray().serialize(serializer, true);
CStrRef className = o_getClassName();
Array properties = o_toArray();
if (serializer->getType() != VariableSerializer::VarDump &&
className == s_PHP_Incomplete_Class) {
Variant* cname = o_realProp(s_PHP_Incomplete_Class_Name, 0);
if (cname && cname->isString()) {
serializer->setObjectInfo(cname->toCStrRef(), o_getId(), 'O');
properties.remove(s_PHP_Incomplete_Class_Name, true);
properties.serialize(serializer, true);
return;
}
}
serializer->setObjectInfo(className, o_getId(), 'O');
properties.serialize(serializer, true);
}
}
}
+4
Ver Arquivo
@@ -0,0 +1,4 @@
break printThis.php:5
run
print $this
quit
+20
Ver Arquivo
@@ -0,0 +1,20 @@
Welcome to HipHop Debugger!
Type "help" or "?" for a complete list of commands.

Program %sprintThis.php loaded. Type '[r]un' or '[c]ontinue' to go.
hphpd> break printThis.php:5
Breakpoint 1 set on line 5 of printThis.php
hphpd> run
Breakpoint 1 reached at Foo::method() on line 5 of %sprintThis.php
 4  function method() {
 5  $other = $this;
 6  }

hphpd> print $this
Foo Object
(
[prop] => Hello
)

hphpd> quit
+12
Ver Arquivo
@@ -0,0 +1,12 @@
<?php
class Foo {
function method() {
$other = $this;
}
}
$object = new Foo;
$object->prop = "Hello\n";
$object->method();
+6 -1
Ver Arquivo
@@ -628,7 +628,12 @@ bool TestCppBase::TestArray() {
bool TestCppBase::TestObject() {
{
String s = "O:1:\"B\":1:{s:3:\"obj\";O:1:\"A\":1:{s:1:\"a\";i:10;}}";
VS(f_serialize(unserialize_from_string(s)), "O:22:\"__PHP_Incomplete_Class\":2:{s:27:\"__PHP_Incomplete_Class_Name\";s:1:\"B\";s:3:\"obj\";O:22:\"__PHP_Incomplete_Class\":2:{s:27:\"__PHP_Incomplete_Class_Name\";s:1:\"A\";s:1:\"a\";i:10;}}");
Variant v = unserialize_from_string(s);
VERIFY(v.isObject());
auto o = v.toObject();
VS(o->o_getClassName(), "__PHP_Incomplete_Class");
auto os = f_serialize(o);
VS(os, "O:1:\"B\":1:{s:3:\"obj\";O:1:\"A\":1:{s:1:\"a\";i:10;}}");
}
VERIFY(!equal(Object(new TestResource()), Object(new TestResource()) ));
return Count(true);
+34
Ver Arquivo
@@ -17,6 +17,7 @@
#include "hphp/test/test_debugger.h"
#include "hphp/test/test_server.h"
#include "hphp/runtime/ext/ext_curl.h"
#include "hphp/runtime/ext/ext_file.h"
#include "hphp/runtime/ext/ext_preg.h"
#include "hphp/runtime/ext/ext_options.h"
#include "hphp/runtime/base/zend/zend_math.h"
@@ -49,6 +50,8 @@ bool TestDebugger::RunTests(const std::string &which) {
bool ret = true;
unlink("/tmp/hphpd_test_error.log");
RUN_TEST(TestCommandLine);
AsyncFunc<TestDebugger> func(this, &TestDebugger::runServer);
func.start();
@@ -77,6 +80,8 @@ bool TestDebugger::RunTests(const std::string &which) {
return ret;
}
bool TestDebugger::getResponse(const string& path, string& result,
int port /* = -1 */,
const string& host /* = "" */) {
@@ -105,6 +110,35 @@ bool TestDebugger::getResponse(const string& path, string& result,
///////////////////////////////////////////////////////////////////////////////
bool TestDebugger::runCommandLineTestCase(string testName) {
string path = Process::GetCurrentDirectory()+"/test/debugger_tests/";
string pathAndName = path+testName;
String s = f_file_get_contents((pathAndName+".expectf").c_str());
string expected = string(s.data(), s.size());
string::size_type pos = 0;
while ((pos = expected.find("%s")) != string::npos) {
expected.replace(pos, 2, path);
}
s = f_file_get_contents((pathAndName+".cmds").c_str());
string cmds = string(s.data(), s.size());
string actual, err;
string filearg = "--file="+pathAndName+".php";
const char *argv[] = {"", "-mdebug", filearg.c_str(), nullptr};
bool ret = Process::Exec(HHVM_PATH, argv, cmds.c_str(),
actual, &err, false);
if (ret && expected != actual) {
f_file_put_contents(pathAndName+".expected", expected);
f_file_put_contents(pathAndName+".out", actual);
ret = false;
}
return Count(ret);
}
bool TestDebugger::TestCommandLine() {
if (!runCommandLineTestCase("printThis")) return false;
return true;
}
bool TestDebugger::TestSanity() {
// first test, server might not be ready yet
bool ret = false;
+2 -4
Ver Arquivo
@@ -22,16 +22,13 @@
///////////////////////////////////////////////////////////////////////////////
/**
* Testing HTTP server.
*/
class TestDebugger : public TestBase {
public:
TestDebugger();
virtual bool RunTests(const std::string &which);
// test test harness
bool TestCommandLine();
bool TestSanity();
bool TestBasic();
bool TestBreak();
@@ -52,6 +49,7 @@ private:
void runServer();
void stopServer();
bool runCommandLineTestCase(string testName);
bool getResponse(const std::string& path, std::string& result, int port = -1,
const std::string& host = "");
bool recvFromTests(char& c);