Skip to content

Commit

Permalink
Also allow returning the original ecmascript object from PHP
Browse files Browse the repository at this point in the history
  • Loading branch information
Martijn Otto committed May 12, 2015
1 parent 033a884 commit 0d5c99e
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 29 deletions.
11 changes: 11 additions & 0 deletions jsobject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,17 @@ Php::Iterator *JSObject::getIterator()
return new Iterator(this, _object);
}

/**
* Retrieve the original ecmascript value
*
* @return original ecmascript value
*/
v8::Local<v8::Object> JSObject::object() const
{
// the stack has the original object
return _object;
}

/**
* End namespace
*/
Expand Down
7 changes: 7 additions & 0 deletions jsobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@ class JSObject :
* @return The iterator
*/
Php::Iterator *getIterator() override;

/**
* Retrieve the original ecmascript value
*
* @return original ecmascript value
*/
v8::Local<v8::Object> object() const;
};

/**
Expand Down
44 changes: 28 additions & 16 deletions object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,9 @@ static uint32_t count(const Php::Object &object)
*/
static void callback(const v8::FunctionCallbackInfo<v8::Value> &info)
{
// create a local handle, so properties "fall out of scope"
// create a local handle, so properties "fall out of scope" and retrieve a handle to the original object
v8::HandleScope scope(isolate());

// retrieve handle to the original object
Handle<Php::Value> handle(info.Data());
Handle<Php::Object> handle(info.Holder()->GetInternalField(0));

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

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

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

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

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

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

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

// if the object is not implementing ArrayAccess or has the given property as a member
Expand All @@ -301,16 +301,22 @@ static void setter(v8::Local<v8::String> property, v8::Local<v8::Value> input, c
* @param object The object to wrap
*/
Object::Object(Php::Object object) :
_template(v8::ObjectTemplate::New())
_template(v8::ObjectTemplate::New()),
_object(object)
{
// TODO: check whether it saves memory and time to re-use object templates

// reserve space to store the handle to the object as an external reference
_template->SetInternalFieldCount(1);

// if the object can be invoked as a function, we register the callback
if (object.isCallable()) _template->SetCallAsFunctionHandler(callback, Handle<Php::Value>(object));

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

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

/**
Expand All @@ -322,7 +328,13 @@ Object::Object(Php::Object object) :
Object::operator v8::Local<v8::Value> ()
{
// create a new object based on the template
return _template->NewInstance();
auto instance = _template->NewInstance();

// attach the object to the instance
instance->SetInternalField(0, Handle<Php::Value>(_object));

// return the instance
return instance;
}

/**
Expand Down
6 changes: 6 additions & 0 deletions object.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ class Object
* @var Stack<v8::ObjectTemplate>
*/
Stack<v8::ObjectTemplate> _template;

/**
* The PHP object we wrap
* @var Php::Object
*/
Php::Object _object;
public:
/**
* Constructor
Expand Down
51 changes: 38 additions & 13 deletions value.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,19 +72,28 @@ v8::Handle<v8::Value> value(const Php::Value &input)
// the result value we are assigning
v8::Local<v8::Value> result;

// the value can be of many types
switch (input.type())
// are we dealing with a value originally from ecmascript?
if (input.instanceOf("JS\\Object"))
{
case Php::Type::Null: /* don't set anything, let it be empty */ break;
case Php::Type::Numeric: result = v8::Integer::New(isolate(), input); break;
case Php::Type::Float: result = v8::Number::New(isolate(), input); break;
case Php::Type::Bool: result = v8::Boolean::New(isolate(), input); break;
case Php::Type::String: result = v8::String::NewFromUtf8(isolate(), input); break;
case Php::Type::Object: result = Object(input); break;
case Php::Type::Callable: result = v8::FunctionTemplate::New(isolate(), callback, Handle<Php::Value>(input))->GetFunction(); break;
case Php::Type::Array: result = Array(input); break;
default:
break;
// cast the input to the original object
result = static_cast<JSObject*>(input.implementation())->object();
}
else
{
// the value can be of many types
switch (input.type())
{
case Php::Type::Null: /* don't set anything, let it be empty */ break;
case Php::Type::Numeric: result = v8::Integer::New(isolate(), input); break;
case Php::Type::Float: result = v8::Number::New(isolate(), input); break;
case Php::Type::Bool: result = v8::Boolean::New(isolate(), input); break;
case Php::Type::String: result = v8::String::NewFromUtf8(isolate(), input); break;
case Php::Type::Object: result = Object(input); break;
case Php::Type::Callable: result = v8::FunctionTemplate::New(isolate(), callback, Handle<Php::Value>(input))->GetFunction(); break;
case Php::Type::Array: result = Array(input); break;
default:
break;
}
}

// return the value by "escaping" it
Expand Down Expand Up @@ -177,8 +186,24 @@ Php::Value value(v8::Handle<v8::Value> input)
// or perhaps an object
if (input->IsObject())
{
// retrieve the object and the first internal field
auto object = input.As<v8::Object>();
auto field = object->GetInternalField(0);

// does it have an internal field and is it external? we are converting back
// an original PHP object, just retrieve the original thing that came from PHP
if (!field.IsEmpty() && field->IsExternal())
{
// the PHP value is stored in the first internal field,
// retrieve it and create the handle around it
Handle<Php::Object> handle(field);

// dereference and return it
return *handle;
}

// create a new js object and convert it to userspace
return Php::Object("JS\\Object", new JSObject(input.As<v8::Object>()));
return Php::Object("JS\\Object", new JSObject(object));
}

// we sadly don't support this type of value
Expand Down

0 comments on commit 0d5c99e

Please sign in to comment.