Просмотр исходного кода

Merge pull request #510 from chrisbckr/php8-tojson

Implements toJSON function that redirects to jsonSerialize
Stefan Siegl 2 лет назад
Родитель
Сommit
21ed0f8ba9
2 измененных файлов с 48 добавлено и 0 удалено
  1. 20 0
      tests/tojson_001.phpt
  2. 28 0
      v8js_object_export.cc

+ 20 - 0
tests/tojson_001.phpt

@@ -0,0 +1,20 @@
+--TEST--
+Test V8::executeString() : redirects toJSON() to jsonSerialize
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+class Foo implements JsonSerializable {
+    public function jsonSerialize(): mixed {
+    	return ['foo', 'bar'];
+    }
+}
+
+$v8 = new V8Js;
+$v8->foo = new Foo;
+$v8->executeString('var_dump(JSON.stringify(PHP.foo));');
+?>
+===EOF===
+--EXPECTF--
+string(13) "["foo","bar"]"
+===EOF===

+ 28 - 0
v8js_object_export.cc

@@ -1007,6 +1007,34 @@ static v8::MaybeLocal<v8::Object> v8js_wrap_object(v8::Isolate *isolate, zend_cl
 
 	v8::MaybeLocal<v8::Object> newobj = constr->NewInstance(v8_context, 1, &external);
 
+	bool has_json_serializable = false;
+	for (unsigned int i = 0; i < ce->num_interfaces; i ++) {
+		if (strcmp (ZSTR_VAL(ce->interfaces[i]->name), "JsonSerializable") == 0) {
+			has_json_serializable = true;
+			break;
+		}
+	}
+
+	if (has_json_serializable) {
+		zend_string *jsonserialize_str = zend_string_init
+			("jsonSerialize", sizeof("jsonSerialize") - 1, 0);
+		zend_function *jsonserialize_method_ptr = reinterpret_cast<zend_function *>
+			(zend_hash_find_ptr_lc(&ce->function_table, jsonserialize_str));
+		if (jsonserialize_method_ptr &&
+			jsonserialize_method_ptr->common.fn_flags & ZEND_ACC_PUBLIC) {
+			v8::Local<v8::String> method_name = V8JS_SYM("toJSON");
+			v8::Local<v8::FunctionTemplate> ft;
+
+			ft = v8::FunctionTemplate::New(isolate, v8js_php_callback,
+					v8::External::New((isolate), jsonserialize_method_ptr));
+
+			v8js_function_tmpl_t *persistent_ft = &ctx->method_tmpls[std::make_pair(ce, jsonserialize_method_ptr)];
+			persistent_ft->Reset(isolate, ft);
+
+			newobj.ToLocalChecked()->CreateDataProperty(v8_context, method_name, ft->GetFunction(v8_context).ToLocalChecked());
+		}
+	}
+
 	if (ce == zend_ce_closure && !newobj.IsEmpty()) {
 		// free uncached function template when object is freed
 		ctx->weak_closures[persist_tpl_].Reset(isolate, newobj.ToLocalChecked());