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:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
break printThis.php:5
|
||||
run
|
||||
print $this
|
||||
quit
|
||||
@@ -0,0 +1,20 @@
|
||||
[22;32mWelcome to HipHop Debugger![0m
|
||||
[22;32mType "help" or "?" for a complete list of commands.
|
||||
[0m
|
||||
[22;32mProgram %sprintThis.php loaded. Type '[r]un' or '[c]ontinue' to go.[0m
|
||||
hphpd> break printThis.php:5
|
||||
[22;32mBreakpoint 1 set on line 5 of printThis.php[0m
|
||||
hphpd> run
|
||||
[22;32mBreakpoint 1 reached at Foo::method() on line 5 of %sprintThis.php[0m
|
||||
[22;37m 4 [0m [22;36mfunction[0m [22;34mmethod[0m() {[0m
|
||||
[22;31;47m 5 [0m [22;31;47m$[0m[22;31;47mother[0m[22;31;47m [0m[22;31;47m=[0m[22;31;47m [0m[22;31;47m$[0m[22;31;47mthis[0m[22;31;47m;[0m[0m
|
||||
[22;37m 6 [0m }[0m
|
||||
[0m
|
||||
hphpd> print $this
|
||||
[22;36mFoo Object
|
||||
(
|
||||
[prop] => Hello
|
||||
|
||||
)
|
||||
[0m
|
||||
hphpd> quit
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
class Foo {
|
||||
function method() {
|
||||
$other = $this;
|
||||
}
|
||||
}
|
||||
|
||||
$object = new Foo;
|
||||
$object->prop = "Hello\n";
|
||||
|
||||
$object->method();
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário