Skip to content

Commit 0d5c99e

Browse files
author
Martijn Otto
committed
Also allow returning the original ecmascript object from PHP
1 parent 033a884 commit 0d5c99e

File tree

5 files changed

+90
-29
lines changed

5 files changed

+90
-29
lines changed

jsobject.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,17 @@ Php::Iterator *JSObject::getIterator()
172172
return new Iterator(this, _object);
173173
}
174174

175+
/**
176+
* Retrieve the original ecmascript value
177+
*
178+
* @return original ecmascript value
179+
*/
180+
v8::Local<v8::Object> JSObject::object() const
181+
{
182+
// the stack has the original object
183+
return _object;
184+
}
185+
175186
/**
176187
* End namespace
177188
*/

jsobject.h

+7
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,13 @@ class JSObject :
145145
* @return The iterator
146146
*/
147147
Php::Iterator *getIterator() override;
148+
149+
/**
150+
* Retrieve the original ecmascript value
151+
*
152+
* @return original ecmascript value
153+
*/
154+
v8::Local<v8::Object> object() const;
148155
};
149156

150157
/**

object.cpp

+28-16
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,9 @@ static uint32_t count(const Php::Object &object)
5959
*/
6060
static void callback(const v8::FunctionCallbackInfo<v8::Value> &info)
6161
{
62-
// create a local handle, so properties "fall out of scope"
62+
// create a local handle, so properties "fall out of scope" and retrieve a handle to the original object
6363
v8::HandleScope scope(isolate());
64-
65-
// retrieve handle to the original object
66-
Handle<Php::Value> handle(info.Data());
64+
Handle<Php::Object> handle(info.Holder()->GetInternalField(0));
6765

6866
// an array to hold all the arguments
6967
Php::Array arguments;
@@ -96,7 +94,7 @@ static void indexed_enumerator(const v8::PropertyCallbackInfo<v8::Array> &info)
9694
{
9795
// create a local handle, so properties "fall out of scope" and retrieve the original object
9896
v8::HandleScope scope(isolate());
99-
Handle<Php::Object> handle(info.Data());
97+
Handle<Php::Object> handle(info.Holder()->GetInternalField(0));
10098

10199
// create a new array to store all the properties
102100
v8::Local<v8::Array> properties(v8::Array::New(isolate()));
@@ -128,7 +126,7 @@ static void named_enumerator(const v8::PropertyCallbackInfo<v8::Array> &info)
128126
{
129127
// create a local handle, so properties "fall out of scope" and retrieve the original object
130128
v8::HandleScope scope(isolate());
131-
Handle<Php::Object> handle(info.Data());
129+
Handle<Php::Object> handle(info.Holder()->GetInternalField(0));
132130

133131
// create a new array to store all the properties
134132
v8::Local<v8::Array> properties(v8::Array::New(isolate()));
@@ -161,7 +159,7 @@ static void getter(uint32_t index, const v8::PropertyCallbackInfo<v8::Value> &in
161159
{
162160
// create a local handle, so properties "fall out of scope" and retrieve the original object
163161
v8::HandleScope scope(isolate());
164-
Handle<Php::Object> handle(info.Data());
162+
Handle<Php::Object> handle(info.Holder()->GetInternalField(0));
165163

166164
// check if we have an item at the requested offset
167165
if (handle->call("offsetExists", static_cast<int64_t>(index)))
@@ -188,7 +186,7 @@ static void getter(v8::Local<v8::String> property, const v8::PropertyCallbackInf
188186
v8::HandleScope scope(isolate());
189187

190188
// retrieve handle to the original object and the property name
191-
Handle<Php::Object> handle(info.Data());
189+
Handle<Php::Object> handle(info.Holder()->GetInternalField(0));
192190
v8::String::Utf8Value name(property);
193191

194192
/**
@@ -261,8 +259,9 @@ static void getter(v8::Local<v8::String> property, const v8::PropertyCallbackInf
261259
*/
262260
static void setter(uint32_t index, v8::Local<v8::Value> input, const v8::PropertyCallbackInfo<v8::Value>& info)
263261
{
264-
// retrieve handle to the original object
265-
Handle<Php::Object> handle(info.Data());
262+
// create a local handle, so properties "fall out of scope" and retrieve the original object
263+
v8::HandleScope scope(isolate());
264+
Handle<Php::Object> handle(info.Holder()->GetInternalField(0));
266265

267266
// store the property inside the object
268267
handle->call("offsetSet", static_cast<int64_t>(index), value(input));
@@ -277,8 +276,9 @@ static void setter(uint32_t index, v8::Local<v8::Value> input, const v8::Propert
277276
*/
278277
static void setter(v8::Local<v8::String> property, v8::Local<v8::Value> input, const v8::PropertyCallbackInfo<v8::Value>& info)
279278
{
280-
// retrieve handle to the original object and convert the requested property
281-
Handle<Php::Object> handle(info.Data());
279+
// create a local handle, so properties "fall out of scope" and retrieve the original object
280+
v8::HandleScope scope(isolate());
281+
Handle<Php::Object> handle(info.Holder()->GetInternalField(0));
282282
v8::String::Utf8Value name(property);
283283

284284
// if the object is not implementing ArrayAccess or has the given property as a member
@@ -301,16 +301,22 @@ static void setter(v8::Local<v8::String> property, v8::Local<v8::Value> input, c
301301
* @param object The object to wrap
302302
*/
303303
Object::Object(Php::Object object) :
304-
_template(v8::ObjectTemplate::New())
304+
_template(v8::ObjectTemplate::New()),
305+
_object(object)
305306
{
307+
// TODO: check whether it saves memory and time to re-use object templates
308+
309+
// reserve space to store the handle to the object as an external reference
310+
_template->SetInternalFieldCount(1);
311+
306312
// if the object can be invoked as a function, we register the callback
307313
if (object.isCallable()) _template->SetCallAsFunctionHandler(callback, Handle<Php::Value>(object));
308314

309315
// register the property handlers
310-
_template->SetNamedPropertyHandler(getter, setter, nullptr, nullptr, named_enumerator, Handle<Php::Object>(object));
316+
_template->SetNamedPropertyHandler(getter, setter, nullptr, nullptr, named_enumerator);
311317

312318
// if the object implements the ArrayAccess interface, we should also set the indexed property handler
313-
if (object.instanceOf("ArrayAccess")) _template->SetIndexedPropertyHandler(getter, setter, nullptr, nullptr, indexed_enumerator, Handle<Php::Object>(object));
319+
if (object.instanceOf("ArrayAccess")) _template->SetIndexedPropertyHandler(getter, setter, nullptr, nullptr, indexed_enumerator);
314320
}
315321

316322
/**
@@ -322,7 +328,13 @@ Object::Object(Php::Object object) :
322328
Object::operator v8::Local<v8::Value> ()
323329
{
324330
// create a new object based on the template
325-
return _template->NewInstance();
331+
auto instance = _template->NewInstance();
332+
333+
// attach the object to the instance
334+
instance->SetInternalField(0, Handle<Php::Value>(_object));
335+
336+
// return the instance
337+
return instance;
326338
}
327339

328340
/**

object.h

+6
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ class Object
3737
* @var Stack<v8::ObjectTemplate>
3838
*/
3939
Stack<v8::ObjectTemplate> _template;
40+
41+
/**
42+
* The PHP object we wrap
43+
* @var Php::Object
44+
*/
45+
Php::Object _object;
4046
public:
4147
/**
4248
* Constructor

value.cpp

+38-13
Original file line numberDiff line numberDiff line change
@@ -72,19 +72,28 @@ v8::Handle<v8::Value> value(const Php::Value &input)
7272
// the result value we are assigning
7373
v8::Local<v8::Value> result;
7474

75-
// the value can be of many types
76-
switch (input.type())
75+
// are we dealing with a value originally from ecmascript?
76+
if (input.instanceOf("JS\\Object"))
7777
{
78-
case Php::Type::Null: /* don't set anything, let it be empty */ break;
79-
case Php::Type::Numeric: result = v8::Integer::New(isolate(), input); break;
80-
case Php::Type::Float: result = v8::Number::New(isolate(), input); break;
81-
case Php::Type::Bool: result = v8::Boolean::New(isolate(), input); break;
82-
case Php::Type::String: result = v8::String::NewFromUtf8(isolate(), input); break;
83-
case Php::Type::Object: result = Object(input); break;
84-
case Php::Type::Callable: result = v8::FunctionTemplate::New(isolate(), callback, Handle<Php::Value>(input))->GetFunction(); break;
85-
case Php::Type::Array: result = Array(input); break;
86-
default:
87-
break;
78+
// cast the input to the original object
79+
result = static_cast<JSObject*>(input.implementation())->object();
80+
}
81+
else
82+
{
83+
// the value can be of many types
84+
switch (input.type())
85+
{
86+
case Php::Type::Null: /* don't set anything, let it be empty */ break;
87+
case Php::Type::Numeric: result = v8::Integer::New(isolate(), input); break;
88+
case Php::Type::Float: result = v8::Number::New(isolate(), input); break;
89+
case Php::Type::Bool: result = v8::Boolean::New(isolate(), input); break;
90+
case Php::Type::String: result = v8::String::NewFromUtf8(isolate(), input); break;
91+
case Php::Type::Object: result = Object(input); break;
92+
case Php::Type::Callable: result = v8::FunctionTemplate::New(isolate(), callback, Handle<Php::Value>(input))->GetFunction(); break;
93+
case Php::Type::Array: result = Array(input); break;
94+
default:
95+
break;
96+
}
8897
}
8998

9099
// return the value by "escaping" it
@@ -177,8 +186,24 @@ Php::Value value(v8::Handle<v8::Value> input)
177186
// or perhaps an object
178187
if (input->IsObject())
179188
{
189+
// retrieve the object and the first internal field
190+
auto object = input.As<v8::Object>();
191+
auto field = object->GetInternalField(0);
192+
193+
// does it have an internal field and is it external? we are converting back
194+
// an original PHP object, just retrieve the original thing that came from PHP
195+
if (!field.IsEmpty() && field->IsExternal())
196+
{
197+
// the PHP value is stored in the first internal field,
198+
// retrieve it and create the handle around it
199+
Handle<Php::Object> handle(field);
200+
201+
// dereference and return it
202+
return *handle;
203+
}
204+
180205
// create a new js object and convert it to userspace
181-
return Php::Object("JS\\Object", new JSObject(input.As<v8::Object>()));
206+
return Php::Object("JS\\Object", new JSObject(object));
182207
}
183208

184209
// we sadly don't support this type of value

0 commit comments

Comments
 (0)