Skip to content

Commit 394a06e

Browse files
author
Martijn Otto
committed
Convert PHP arrays to an ecmascript object that stays in sync
1 parent 2d553fa commit 394a06e

File tree

4 files changed

+349
-34
lines changed

4 files changed

+349
-34
lines changed

array.cpp

+202
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
/**
2+
* array.cpp
3+
*
4+
* Class that wraps around a PHP array to make it available
5+
* in ecmascript. As in PHP, properties added to the array,
6+
* both from ecmascript and PHP, will become visible on the
7+
* other side.
8+
*
9+
* @copyright 2015 Copernica B.V.
10+
*/
11+
12+
/**
13+
* Dependencies
14+
*/
15+
#include "array.h"
16+
#include "handle.h"
17+
#include "value.h"
18+
#include <cstring>
19+
20+
/**
21+
* Start namespace
22+
*/
23+
namespace JS {
24+
25+
/**
26+
* Find the highest existing numerical index in the object
27+
*
28+
* @param array The array to count
29+
* @return the number of numeric, sequential keys
30+
*/
31+
static uint32_t count(const Php::Array &array)
32+
{
33+
// the variable to store count
34+
int64_t result = 0;
35+
36+
// loop over all the properties
37+
for (auto &property : array)
38+
{
39+
// is it numeric and greater than what we've seen before?
40+
if (property.first.isNumeric() && property.first >= result)
41+
{
42+
// store it
43+
result = property.first;
44+
45+
// add another one
46+
++result;
47+
}
48+
}
49+
50+
// return the number of keys in the object
51+
return static_cast<uint32_t>(result);
52+
}
53+
54+
/**
55+
* Retrieve a list of properties for enumeration
56+
*
57+
* @param info callback info
58+
*/
59+
static void enumerator(const v8::PropertyCallbackInfo<v8::Array> &info)
60+
{
61+
// create a local handle, so properties "fall out of scope" and retrieve the original object
62+
v8::HandleScope scope(isolate());
63+
Handle<Php::Array> handle(info.Data());
64+
65+
// create a new array to store all the properties
66+
v8::Local<v8::Array> properties(v8::Array::New(isolate()));
67+
68+
// there is no 'push' method on v8::Array, so we simply have
69+
// to 'Set' the property with the correct index, declared here
70+
uint32_t index = 0;
71+
72+
// iterate over the properties in the object
73+
for (auto &property : *handle)
74+
{
75+
// add the property to the list
76+
properties->Set(index++, value(property.first));
77+
}
78+
79+
// set the value as the 'return' parameter
80+
info.GetReturnValue().Set(properties);
81+
}
82+
83+
/**
84+
* Retrieve a property or function from the object
85+
*
86+
* @param index The index to find the property
87+
* @param info callback info
88+
*/
89+
static void getter(uint32_t index, const v8::PropertyCallbackInfo<v8::Value> &info)
90+
{
91+
// create a local handle, so properties "fall out of scope" and retrieve the original object
92+
v8::HandleScope scope(isolate());
93+
Handle<Php::Array> handle(info.Data());
94+
95+
// check if we have an item at the requested offset
96+
if (handle->contains(index))
97+
{
98+
// retrieve the variable and store it as the result variable
99+
info.GetReturnValue().Set(value(handle->get(index)));
100+
}
101+
else
102+
{
103+
// in javascript, retrieving an unset array offset returns undefined
104+
info.GetReturnValue().SetUndefined();
105+
}
106+
}
107+
108+
/**
109+
* Retrieve a property or function from the object
110+
*
111+
* @param property the property to retrieve
112+
* @param info callback info
113+
*/
114+
static void getter(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value> &info)
115+
{
116+
// create a local handle, so properties "fall out of scope"
117+
v8::HandleScope scope(isolate());
118+
119+
// retrieve handle to the original object and the property name
120+
Handle<Php::Array> handle(info.Data());
121+
v8::String::Utf8Value name(property);
122+
123+
// check if the property exists
124+
if (handle->contains(*name, name.length()))
125+
{
126+
// retrieve the variable and store it as the result variable
127+
info.GetReturnValue().Set(value(handle->get(*name, name.length())));
128+
}
129+
else if (std::strcmp(*name, "length") == 0)
130+
{
131+
// return the count from this array
132+
info.GetReturnValue().Set(count(*handle));
133+
}
134+
else
135+
{
136+
// in javascript, retrieving an unset array offset returns undefined
137+
info.GetReturnValue().SetUndefined();
138+
}
139+
}
140+
141+
/**
142+
* Set a property or function on the object
143+
*
144+
* @param index the index to find the property
145+
* @param input the new property value
146+
* @param info callback info
147+
*/
148+
static void setter(uint32_t index, v8::Local<v8::Value> input, const v8::PropertyCallbackInfo<v8::Value>& info)
149+
{
150+
// retrieve handle to the original object
151+
Handle<Php::Array> handle(info.Data());
152+
153+
// store the property inside the array
154+
handle->set(index, value(input));
155+
}
156+
157+
/**
158+
* Set a property or function on the object
159+
*
160+
* @param property the property to update
161+
* @param input the new property value
162+
* @param info callback info
163+
*/
164+
static void setter(v8::Local<v8::String> property, v8::Local<v8::Value> input, const v8::PropertyCallbackInfo<v8::Value>& info)
165+
{
166+
// retrieve handle to the original object and convert the requested property
167+
Handle<Php::Array> handle(info.Data());
168+
v8::String::Utf8Value name(property);
169+
170+
// store the property inside the array
171+
handle->set(*name, name.length(), value(input));
172+
}
173+
174+
/**
175+
* Constructor
176+
*
177+
* @param array The array to wrap
178+
*/
179+
Array::Array(Php::Array array) :
180+
_template(v8::ObjectTemplate::New())
181+
{
182+
// register the property handlers
183+
_template->SetNamedPropertyHandler(getter, setter, nullptr, nullptr, enumerator, Handle<Php::Array>(array));
184+
_template->SetIndexedPropertyHandler(getter, setter, nullptr, nullptr, nullptr, Handle<Php::Array>(array));
185+
}
186+
187+
/**
188+
* Retrieve the ecmascript object handle
189+
* that can be assigned directly to v8
190+
*
191+
* @return v8::Local<v8::Value>
192+
*/
193+
Array::operator v8::Local<v8::Value> ()
194+
{
195+
// create a new object based on the template
196+
return _template->NewInstance();
197+
}
198+
199+
/**
200+
* End namespace
201+
*/
202+
}

array.h

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/**
2+
* array.h
3+
*
4+
* Class that wraps around a PHP array to make it available
5+
* in ecmascript. As in PHP, properties added to the array,
6+
* both from ecmascript and PHP, will become visible on the
7+
* other side.
8+
*
9+
* @copyright 2015 Copernica B.V.
10+
*/
11+
12+
/**
13+
* Include guard
14+
*/
15+
#pragma once
16+
17+
/**
18+
* Dependencies
19+
*/
20+
#include <phpcpp.h>
21+
#include <v8.h>
22+
#include "stack.h"
23+
24+
/**
25+
* Start namespace
26+
*/
27+
namespace JS {
28+
29+
/**
30+
* Class definition
31+
*/
32+
class Array
33+
{
34+
private:
35+
/**
36+
* The object template
37+
* @var Stack<v8::ObjectTemplate>
38+
*/
39+
Stack<v8::ObjectTemplate> _template;
40+
public:
41+
/**
42+
* Constructor
43+
*
44+
* @param array The array to wrap
45+
*/
46+
Array(Php::Array array);
47+
48+
/**
49+
* Retrieve the ecmascript object handle
50+
* that can be assigned directly to v8
51+
*
52+
* @return v8::Local<v8::Value>
53+
*/
54+
operator v8::Local<v8::Value> ();
55+
};
56+
57+
/**
58+
* End namespace
59+
*/
60+
}

0 commit comments

Comments
 (0)