From 035e6faa8ba2c95088d4ddf9624cf8909cf1415a Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Sat, 5 Oct 2013 11:55:59 -0400 Subject: [PATCH] Implement property_exists()/isset()/empty() on wrapped JavaScript objects. Fixes issue #32. --- tests/property_exists.phpt | 107 +++++++++++++++++++++++++++++++++++++ v8js.cc | 65 +++++++++++++++++++++- 2 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 tests/property_exists.phpt diff --git a/tests/property_exists.phpt b/tests/property_exists.phpt new file mode 100644 index 0000000..3475125 --- /dev/null +++ b/tests/property_exists.phpt @@ -0,0 +1,107 @@ +--TEST-- +Test V8::executeString() : property_exists/isset/empty on wrapped JS objects +--SKIPIF-- + +--FILE-- +executeString($JS, 'basic.js'); + +echo "= isset() =\n"; +echo "bogus: "; var_dump(isset( $exports->bogus )); +echo "hello: "; var_dump(isset( $exports->hello )); +echo "isnull: "; var_dump(isset( $exports->isnull )); +echo "isundefined: "; var_dump(isset( $exports->isundefined )); +echo "isfalse: "; var_dump(isset( $exports->isfalse )); +echo "iszero: "; var_dump(isset( $exports->iszero )); +echo "isquotezero: "; var_dump(isset( $exports->isquotezero )); +echo "isemptyarray: "; var_dump(isset( $exports->isemptyarray )); +echo "isemptystring: "; var_dump(isset( $exports->isemptystring )); +echo "istrue: "; var_dump(isset( $exports->istrue )); +echo "\n"; + +echo "= empty() =\n"; +echo "bogus: "; var_dump(empty( $exports->bogus )); +echo "hello: "; var_dump(empty( $exports->hello )); +echo "isnull: "; var_dump(empty( $exports->isnull )); +echo "isundefined: "; var_dump(empty( $exports->isundefined )); +echo "isfalse: "; var_dump(empty( $exports->isfalse )); +echo "iszero: "; var_dump(empty( $exports->iszero )); +echo "isquotezero: "; var_dump(empty( $exports->isquotezero )); +echo "isemptyarray: "; var_dump(empty( $exports->isemptyarray )); +echo "isemptystring: "; var_dump(empty( $exports->isemptystring )); +echo "istrue: "; var_dump(empty( $exports->istrue )); +echo "\n"; + +echo "= property_exists() =\n"; +echo "bogus: "; var_dump(property_exists( $exports, 'bogus' )); +echo "hello: "; var_dump(property_exists( $exports, 'hello' )); +echo "isnull: "; var_dump(property_exists( $exports, 'isnull' )); +echo "isundefined: "; var_dump(property_exists( $exports, 'isundefined' )); +echo "isfalse: "; var_dump(property_exists( $exports, 'isfalse' )); +echo "iszero: "; var_dump(property_exists( $exports, 'iszero' )); +echo "isquotezero: "; var_dump(property_exists( $exports, 'isquotezero' )); +echo "isemptyarray: "; var_dump(property_exists( $exports, 'isemptyarray' )); +echo "isemptystring: "; var_dump(property_exists( $exports, 'isemptystring' )); +echo "istrue: "; var_dump(property_exists( $exports, 'istrue' )); +echo "\n"; + +?> +===EOF=== +--EXPECT-- += isset() = +bogus: bool(false) +hello: bool(true) +isnull: bool(false) +isundefined: bool(false) +isfalse: bool(true) +iszero: bool(true) +isquotezero: bool(true) +isemptyarray: bool(true) +isemptystring: bool(true) +istrue: bool(true) + += empty() = +bogus: bool(true) +hello: bool(false) +isnull: bool(true) +isundefined: bool(true) +isfalse: bool(true) +iszero: bool(true) +isquotezero: bool(true) +isemptyarray: bool(true) +isemptystring: bool(true) +istrue: bool(false) + += property_exists() = +bogus: bool(false) +hello: bool(true) +isnull: bool(true) +isundefined: bool(true) +isfalse: bool(true) +iszero: bool(true) +isquotezero: bool(true) +isemptyarray: bool(true) +isemptystring: bool(true) +istrue: bool(true) + +===EOF=== diff --git a/v8js.cc b/v8js.cc index 57bb9c5..0204c5f 100644 --- a/v8js.cc +++ b/v8js.cc @@ -111,6 +111,69 @@ ZEND_GET_MODULE(v8js) /* V8 Object handlers */ +static int php_v8js_v8_has_property(zval *object, zval *member, int has_set_exists ZEND_HASH_KEY_DC TSRMLS_DC) /* {{{ */ +{ + /* param has_set_exists: + * 0 (has) whether property exists and is not NULL - isset() + * 1 (set) whether property exists and is true-ish - empty() + * 2 (exists) whether property exists - property_exists() + */ + int retval = false; + php_v8js_object *obj = (php_v8js_object *) zend_object_store_get_object(object TSRMLS_CC); + + v8::Locker locker(obj->isolate); + v8::Isolate::Scope isolate_scope(obj->isolate); + v8::HandleScope local_scope(obj->isolate); + v8::Local temp_context = v8::Context::New(obj->isolate); + v8::Context::Scope temp_scope(temp_context); + + v8::Local v8obj = v8::Local::New(obj->isolate, obj->v8obj); + + if (Z_TYPE_P(member) == IS_STRING && v8obj->IsObject() && !v8obj->IsFunction()) + { + + v8::Local jsObj = v8obj->ToObject(); + v8::Local jsKey = V8JS_STRL(Z_STRVAL_P(member), Z_STRLEN_P(member)); + v8::Local jsVal; + + /* Skip any prototype properties */ + if (jsObj->HasRealNamedProperty(jsKey) || jsObj->HasRealNamedCallbackProperty(jsKey)) { + if (has_set_exists == 2) { + /* property_exists(), that's enough! */ + retval = true; + } else { + /* We need to look at the value. */ + jsVal = jsObj->Get(jsKey); + if (has_set_exists == 0 ) { + /* isset(): We make 'undefined' equivalent to 'null' */ + retval = !( jsVal->IsNull() || jsVal->IsUndefined() ); + } else { + /* empty() */ + retval = jsVal->BooleanValue(); + /* for PHP compatibility, [] should also be empty */ + if (jsVal->IsArray() && retval) { + v8::Local array = v8::Local::Cast(jsVal); + retval = (array->Length() != 0); + } + /* for PHP compatibility, '0' should also be empty */ + if (jsVal->IsString() && retval) { + v8::Local str = jsVal->ToString(); + if (str->Length() == 1) { + uint16_t c = 0; + str->Write(&c, 0, 1); + if (c == '0') { + retval = false; + } + } + } + } + } + } + } + return retval; +} +/* }}} */ + static zval *php_v8js_v8_read_property(zval *object, zval *member, int type ZEND_HASH_KEY_DC TSRMLS_DC) /* {{{ */ { zval *retval = NULL; @@ -1300,7 +1363,7 @@ static PHP_MINIT_FUNCTION(v8js) v8_object_handlers.cast_object = NULL; v8_object_handlers.get_constructor = NULL; v8_object_handlers.get_property_ptr_ptr = NULL; -// v8_object_handlers.has_property = php_v8js_v8_has_property; // Not implemented yet + v8_object_handlers.has_property = php_v8js_v8_has_property; v8_object_handlers.read_property = php_v8js_v8_read_property; v8_object_handlers.write_property = php_v8js_v8_write_property; v8_object_handlers.unset_property = php_v8js_v8_unset_property;