Skip to content

Commit 1b21216

Browse files
committed
Add JsBuffer class to allow passing node Buffer objects from PHP to JS
1 parent 0820332 commit 1b21216

File tree

11 files changed

+347
-68
lines changed

11 files changed

+347
-68
lines changed

binding.gyp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
'sources': [
3131
'src/node_php_embed.cc',
3232
'src/node_php_jsobject_class.cc',
33+
'src/node_php_jsbuffer_class.cc',
3334
],
3435
},
3536
{

src/macros.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@
7474
Nan::Get((source), NEW_STR(property)) \
7575
.FromMaybe((v8::Local<v8::Value>)Nan::Undefined())
7676

77+
#if 0
78+
#include <nan.h>
7779
static NAN_INLINE int32_t CAST_INT(v8::Local<v8::Value> v, int32_t defaultValue) {
7880
Nan::HandleScope scope;
7981
return v->IsNumber() ? Nan::To<int32_t>(v).FromMaybe(defaultValue) :
@@ -90,8 +92,10 @@ static NAN_INLINE v8::Local<v8::String> CAST_STRING(v8::Local<v8::Value> v, v8::
9092
Nan::EscapableHandleScope scope;
9193
return scope.Escape(v->IsString() ? Nan::To<v8::String>(v).FromMaybe(defaultValue) : defaultValue);
9294
}
95+
#endif
9396

9497
/* Zend helpers */
98+
#include <Zend/zend_modules.h>
9599
#if ZEND_MODULE_API_NO >= 20100409
96100
# define ZEND_HASH_KEY_DC , const zend_literal *key
97101
# define ZEND_HASH_KEY_CC , key

src/messages.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ class PhpGetPropertyMsg : public MessageToPhp {
109109
retval_.SetEmpty();
110110
return;
111111
} else {
112-
retval_.Set(m, r);
112+
retval_.Set(m, r TSRMLS_CC);
113113
/* We don't own the reference to php_value... unless the
114114
* returned refcount was 0, in which case the below code
115115
* will free it. */

src/node_php_embed.cc

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ extern "C" {
88

99
#include "node_php_embed.h"
1010
#include "node_php_jsobject_class.h"
11+
#include "node_php_jsbuffer_class.h"
1112

1213
#include "asyncmessageworker.h"
1314
#include "messages.h"
@@ -118,15 +119,16 @@ static int node_php_embed_ub_write(const char *str, unsigned int str_length TSRM
118119
AsyncMessageWorker::MessageChannel *messageChannel = NODE_PHP_EMBED_G(messageChannel);
119120
PhpRequestWorker *worker = (PhpRequestWorker *)
120121
(messageChannel->GetWorker());
121-
ZVal stream{ZEND_FILE_LINE_C}, buf{ZEND_FILE_LINE_C},
122-
retval{ZEND_FILE_LINE_C};
122+
ZVal stream{ZEND_FILE_LINE_C}, retval{ZEND_FILE_LINE_C};
123123
worker->GetStream().ToPhp(messageChannel, stream TSRMLS_CC);
124124
// use plain zval to avoid allocating copy of method name
125125
zval method; ZVAL_STRINGL(&method, "write", 5, 0);
126-
// XXX need to pass 'str' as buffer, not a string, and avoid copying.
127-
buf.SetString(str, str_length, 1);
126+
// special buffer type to pass `str` as a node buffer and avoid copying
127+
zval buffer, *args[] = { &buffer }; INIT_ZVAL(buffer);
128+
node_php_jsbuffer_create(&buffer, str, str_length, 0 TSRMLS_CC);
128129
call_user_function(EG(function_table), stream.PtrPtr(), &method,
129-
retval.Ptr(), 1, buf.PtrPtr() TSRMLS_CC);
130+
retval.Ptr(), 1, args TSRMLS_CC);
131+
zval_dtor(&buffer);
130132
TRACE("<");
131133
return str_length;
132134
}
@@ -208,6 +210,7 @@ static void node_php_embed_globals_dtor(zend_node_php_embed_globals *node_php_em
208210
PHP_MINIT_FUNCTION(node_php_embed) {
209211
TRACE("> PHP_MINIT_FUNCTION");
210212
PHP_MINIT(node_php_jsobject_class)(INIT_FUNC_ARGS_PASSTHRU);
213+
PHP_MINIT(node_php_jsbuffer_class)(INIT_FUNC_ARGS_PASSTHRU);
211214
TRACE("< PHP_MINIT_FUNCTION");
212215
return SUCCESS;
213216
}

src/node_php_jsbuffer_class.cc

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
extern "C" {
2+
#include <main/php.h>
3+
#include <Zend/zend.h>
4+
#include <Zend/zend_exceptions.h>
5+
}
6+
7+
#include "node_php_jsbuffer_class.h"
8+
9+
#include "macros.h"
10+
11+
using namespace node_php_embed;
12+
13+
/* Class entries */
14+
zend_class_entry *php_ce_jsbuffer;
15+
16+
/*Object handlers */
17+
static zend_object_handlers node_php_jsbuffer_handlers;
18+
19+
/* Constructors and destructors */
20+
static void node_php_jsbuffer_free_storage(void *object, zend_object_handle handle TSRMLS_DC) {
21+
TRACE(">");
22+
node_php_jsbuffer *c = (node_php_jsbuffer *) object;
23+
TRACEX("- dealloc %p", c);
24+
25+
zend_object_std_dtor(&c->std TSRMLS_CC);
26+
if (c->z) {
27+
TRACEX("- freeing zval refcount %d", Z_REFCOUNT_P(c->z));
28+
zval_ptr_dtor(&(c->z));
29+
}
30+
if (c->owner == 1) {
31+
TRACE("- freeing PHP owned data");
32+
efree((void*)c->data);
33+
} else if (c->owner == 2) {
34+
TRACE("- freeing C++ owned data");
35+
delete[] c->data;
36+
}
37+
38+
efree(object);
39+
TRACE("<");
40+
}
41+
42+
static zend_object_value node_php_jsbuffer_new(zend_class_entry *ce TSRMLS_DC) {
43+
TRACE(">");
44+
zend_object_value retval;
45+
node_php_jsbuffer *c;
46+
47+
c = (node_php_jsbuffer *) ecalloc(1, sizeof(*c));
48+
TRACEX("- alloc %p", c);
49+
50+
zend_object_std_init(&c->std, ce TSRMLS_CC);
51+
52+
retval.handle = zend_objects_store_put(c, NULL, (zend_objects_free_object_storage_t) node_php_jsbuffer_free_storage, NULL TSRMLS_CC);
53+
retval.handlers = &node_php_jsbuffer_handlers;
54+
55+
TRACE("<");
56+
return retval;
57+
}
58+
59+
void node_php_embed::node_php_jsbuffer_create(zval *res, const char *data, ulong length, int owner TSRMLS_DC) {
60+
TRACE(">");
61+
node_php_jsbuffer *c;
62+
63+
object_init_ex(res, php_ce_jsbuffer);
64+
65+
c = (node_php_jsbuffer *) zend_object_store_get_object(res TSRMLS_CC);
66+
67+
if (owner) {
68+
char *tmp = (owner==1) ? (char*)ecalloc(length, 1) : new char[length];
69+
memcpy(tmp, data, length);
70+
data = tmp;
71+
}
72+
c->data = data;
73+
c->length = length;
74+
c->owner = owner;
75+
c->z = NULL;
76+
77+
TRACE("<");
78+
}
79+
80+
/* Methods */
81+
#define PARSE_PARAMS(method, ...) \
82+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, __VA_ARGS__) == FAILURE) { \
83+
zend_throw_exception(zend_exception_get_default(TSRMLS_C), "bad args to " #method, 0 TSRMLS_CC); \
84+
return; \
85+
} \
86+
87+
ZEND_BEGIN_ARG_INFO_EX(node_php_jsbuffer_construct_args, 0, 0, 1)
88+
ZEND_ARG_INFO(0, string)
89+
ZEND_END_ARG_INFO()
90+
PHP_METHOD(JsBuffer, __construct) {
91+
TRACE(">");
92+
node_php_jsbuffer *obj = (node_php_jsbuffer *)
93+
zend_object_store_get_object(this_ptr TSRMLS_CC);
94+
zval *str;
95+
PARSE_PARAMS(__construct, "z/", &str);
96+
convert_to_string(str);
97+
obj->z = str; Z_ADDREF_P(str);
98+
obj->data = Z_STRVAL_P(str);
99+
obj->length = Z_STRLEN_P(str);
100+
obj->owner = 0; // not owned directly; the ref to str will take care of it.
101+
TRACE("<");
102+
}
103+
104+
ZEND_BEGIN_ARG_INFO_EX(node_php_jsbuffer_toString_args, 0, 0, 0)
105+
ZEND_END_ARG_INFO()
106+
PHP_METHOD(JsBuffer, __toString) {
107+
node_php_jsbuffer *obj = (node_php_jsbuffer *)
108+
zend_object_store_get_object(this_ptr TSRMLS_CC);
109+
RETURN_STRINGL(obj->data, obj->length, 1);
110+
}
111+
112+
ZEND_BEGIN_ARG_INFO_EX(node_php_jsbuffer_debugInfo_args, 0, 0, 0)
113+
ZEND_END_ARG_INFO()
114+
PHP_METHOD(JsBuffer, __debugInfo) {
115+
node_php_jsbuffer *obj = (node_php_jsbuffer *)
116+
zend_object_store_get_object(this_ptr TSRMLS_CC);
117+
array_init_size(return_value, 1);
118+
add_assoc_stringl_ex(return_value, "value", 6, (char*)obj->data, obj->length, 1);
119+
}
120+
121+
#define STUB_METHOD(name) \
122+
PHP_METHOD(JsBuffer, name) { \
123+
TRACE(">"); \
124+
zend_throw_exception( \
125+
zend_exception_get_default(TSRMLS_C), \
126+
"Can't directly serialize or unserialize JsBuffer.", \
127+
0 TSRMLS_CC \
128+
); \
129+
TRACE("<"); \
130+
RETURN_FALSE; \
131+
}
132+
133+
/* NOTE: We could also override node_php_jsbuffer_handlers.get_constructor
134+
* to throw an exception when invoked, but doing so causes the
135+
* half-constructed object to leak -- this seems to be a PHP bug. So
136+
* we'll define magic __construct methods instead. */
137+
STUB_METHOD(__sleep)
138+
STUB_METHOD(__wakeup)
139+
140+
static const zend_function_entry node_php_jsbuffer_methods[] = {
141+
PHP_ME(JsBuffer, __construct, node_php_jsbuffer_construct_args, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
142+
PHP_ME(JsBuffer, __sleep, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
143+
PHP_ME(JsBuffer, __wakeup, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
144+
PHP_ME(JsBuffer, __toString, node_php_jsbuffer_toString_args, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
145+
PHP_ME(JsBuffer, __debugInfo, node_php_jsbuffer_debugInfo_args, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
146+
ZEND_FE_END
147+
};
148+
149+
PHP_MINIT_FUNCTION(node_php_jsbuffer_class) {
150+
TRACE("> PHP_MINIT_FUNCTION");
151+
zend_class_entry ce;
152+
/* JsBuffer class */
153+
INIT_CLASS_ENTRY(ce, "JsBuffer", node_php_jsbuffer_methods);
154+
php_ce_jsbuffer = zend_register_internal_class(&ce TSRMLS_CC);
155+
php_ce_jsbuffer->ce_flags |= ZEND_ACC_FINAL;
156+
php_ce_jsbuffer->create_object = node_php_jsbuffer_new;
157+
158+
/* JsBuffer handlers */
159+
memcpy(&node_php_jsbuffer_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
160+
node_php_jsbuffer_handlers.clone_obj = NULL;
161+
node_php_jsbuffer_handlers.cast_object = NULL;
162+
node_php_jsbuffer_handlers.get_property_ptr_ptr = NULL;
163+
164+
TRACE("< PHP_MINIT_FUNCTION");
165+
return SUCCESS;
166+
}

src/node_php_jsbuffer_class.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#ifndef NODE_PHP_JSBUFFER_CLASS_H
2+
#define NODE_PHP_JSBUFFER_CLASS_H
3+
4+
extern "C" {
5+
#include <main/php.h>
6+
#include <Zend/zend.h>
7+
}
8+
9+
namespace node_php_embed {
10+
11+
struct node_php_jsbuffer {
12+
zend_object std;
13+
const char *data;
14+
ulong length;
15+
int owner; /* 0 = not owned, 1 = php owned, 2 = c++ owned */
16+
zval *z; /* can hold a reference to a PHP string */
17+
};
18+
19+
/* Create a PHP version of a JS Buffer.
20+
* owner=0 - not owned
21+
* owner=1 - PHP owned
22+
* owner=2 - C++ owned
23+
*/
24+
void node_php_jsbuffer_create(zval *res, const char *data, ulong length, int owner TSRMLS_DC);
25+
26+
}
27+
28+
extern zend_class_entry *php_ce_jsbuffer;
29+
30+
PHP_MINIT_FUNCTION(node_php_jsbuffer_class);
31+
32+
#endif

0 commit comments

Comments
 (0)