Skip to content

Commit c083085

Browse files
committed
Implement __invoke
1 parent 1b21216 commit c083085

File tree

3 files changed

+44
-11
lines changed

3 files changed

+44
-11
lines changed

src/node_php_jsobject_class.cc

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -267,30 +267,40 @@ PHP_METHOD(JsObject, __unset) {
267267
TRACE("<");
268268
}
269269

270-
class JsInvokeMethodMsg : public MessageToJs {
270+
class JsInvokeMsg : public MessageToJs {
271271
Value object_;
272272
Value member_;
273273
ulong argc_;
274274
Value *argv_;
275275
public:
276-
JsInvokeMethodMsg(ObjectMapper *m, objid_t objId, zval *member, ulong argc, zval **argv TSRMLS_DC)
276+
JsInvokeMsg(ObjectMapper *m, objid_t objId, zval *member, ulong argc, zval **argv TSRMLS_DC)
277277
: MessageToJs(m), object_(), member_(m, member TSRMLS_CC), argc_(argc), argv_(Value::NewArray(m, argc, argv TSRMLS_CC)) {
278278
object_.SetJsObject(objId);
279279
}
280-
virtual ~JsInvokeMethodMsg() { delete[] argv_; }
280+
virtual ~JsInvokeMsg() { delete[] argv_; }
281281
protected:
282282
virtual void InJs(ObjectMapper *m) {
283-
TRACE("> JsInvokeMethodMsg");
283+
TRACE("> JsInvokeMsg");
284284
Nan::MaybeLocal<v8::Object> jsObj =
285285
Nan::To<v8::Object>(object_.ToJs(m));
286286
if (jsObj.IsEmpty()) {
287287
return Nan::ThrowTypeError("receiver is not an object");
288288
}
289289

290-
Nan::MaybeLocal<v8::Object> method = Nan::To<v8::Object>(
291-
Nan::Get(jsObj.ToLocalChecked(), member_.ToJs(m))
292-
.FromMaybe<v8::Value>(Nan::Undefined())
293-
);
290+
v8::Local<v8::Value> member = member_.ToJs(m);
291+
Nan::MaybeLocal<v8::Object> method;
292+
if (member->IsNull()) {
293+
// invoke function, not method.
294+
method = jsObj;
295+
// should be null, but https://github.com/nodejs/nan/issues/497
296+
// doesn't let us pass null to functions yet.
297+
jsObj = Nan::MakeMaybe(Nan::New<v8::Object>());
298+
} else {
299+
method = Nan::To<v8::Object>(
300+
Nan::Get(jsObj.ToLocalChecked(), member)
301+
.FromMaybe<v8::Value>(Nan::Undefined())
302+
);
303+
}
294304
if (method.IsEmpty()) {
295305
return Nan::ThrowTypeError("method is not an object");
296306
}
@@ -307,7 +317,7 @@ class JsInvokeMethodMsg : public MessageToJs {
307317
if (!result.IsEmpty()) {
308318
retval_.Set(m, result.ToLocalChecked());
309319
}
310-
TRACE("< JsInvokeMethodMsg");
320+
TRACE("< JsInvokeMsg");
311321
}
312322
};
313323

@@ -327,7 +337,7 @@ PHP_METHOD(JsObject, __call) {
327337
argv[i] = *z;
328338
}
329339
}
330-
JsInvokeMethodMsg msg(obj->channel, obj->id, member, argc, argv TSRMLS_CC);
340+
JsInvokeMsg msg(obj->channel, obj->id, member, argc, argv TSRMLS_CC);
331341
obj->channel->Send(&msg);
332342
msg.WaitForResponse();
333343
THROW_IF_EXCEPTION("JS exception thrown during __call of \"%*s\"",
@@ -336,7 +346,26 @@ PHP_METHOD(JsObject, __call) {
336346
TRACE("<");
337347
}
338348

339-
349+
PHP_METHOD(JsObject, __invoke) {
350+
TRACE(">");
351+
node_php_jsobject *obj = (node_php_jsobject *)
352+
zend_object_store_get_object(this_ptr TSRMLS_CC);
353+
zval member; INIT_ZVAL(member);
354+
int argc = ZEND_NUM_ARGS();
355+
zval **argv = (zval**) safe_emalloc(argc, sizeof(*argv), 0);
356+
if (argc > 0 && zend_get_parameters_array(ht, argc, argv) == FAILURE) {
357+
efree(argv);
358+
zend_throw_exception(zend_exception_get_default(TSRMLS_C), "bad args to __invoke", 0 TSRMLS_CC);
359+
return;
360+
}
361+
JsInvokeMsg msg(obj->channel, obj->id, &member, argc, argv TSRMLS_CC);
362+
efree(argv);
363+
obj->channel->Send(&msg);
364+
msg.WaitForResponse();
365+
THROW_IF_EXCEPTION("JS exception thrown during __invoke");
366+
msg.retval_.ToPhp(obj->channel, return_value, return_value_ptr TSRMLS_CC);
367+
TRACE("<");
368+
}
340369

341370

342371
/* Use (slightly thunked) versions of the has/read/write property handlers
@@ -482,6 +511,7 @@ static const zend_function_entry node_php_jsobject_methods[] = {
482511
PHP_ME(JsObject, __set, node_php_jsobject_set_args, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
483512
PHP_ME(JsObject, __unset, node_php_jsobject_unset_args, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
484513
PHP_ME(JsObject, __call, node_php_jsobject_call_args, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
514+
PHP_ME(JsObject, __invoke, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
485515
ZEND_FE_END
486516
};
487517

test/context.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ describe('Passing context object from JS to PHP', function() {
5353
'string(11) "abcdef \uD83D\uDCA9"',
5454
'int(1)',
5555
'string(5) "fname"',
56+
'int(42)',
5657
'object(JsBuffer) (1) {',
5758
' ["value"]=>',
5859
' string(3) "abc"',

test/context.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,7 @@
88
var_dump($c->f);
99
var_dump($c->g->f);
1010
var_dump($c->h->name);
11+
$f = $c->h;
12+
var_dump($f(42));
1113
var_dump($c->i);
1214
?>

0 commit comments

Comments
 (0)