Browse Source

Implement property_exists()/isset()/empty() on wrapped JavaScript objects.

Fixes issue #32.
C. Scott Ananian 11 năm trước cách đây
mục cha
commit
035e6faa8b
2 tập tin đã thay đổi với 171 bổ sung1 xóa
  1. 107 0
      tests/property_exists.phpt
  2. 64 1
      v8js.cc

+ 107 - 0
tests/property_exists.phpt

@@ -0,0 +1,107 @@
+--TEST--
+Test V8::executeString() : property_exists/isset/empty on wrapped JS objects
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+
+$v8 = new V8Js();
+
+$JS = <<< EOT
+(function(exports) {
+  // begin module code
+  exports.hello = function() { return 'hello'; };
+  exports.isnull = null;
+  exports.isundefined = undefined;
+  exports.isfalse = false;
+  exports.iszero = 0;
+  exports.isquotezero = '0';
+  exports.isemptyarray = [];
+  exports.isemptystring = '';
+  exports.istrue = true;
+  // end module code
+  return exports;
+})({})
+EOT;
+
+$exports = $v8->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===

+ 64 - 1
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<v8::Context> temp_context = v8::Context::New(obj->isolate);
+	v8::Context::Scope temp_scope(temp_context);
+
+	v8::Local<v8::Value> v8obj = v8::Local<v8::Value>::New(obj->isolate, obj->v8obj);
+
+	if (Z_TYPE_P(member) == IS_STRING && v8obj->IsObject() && !v8obj->IsFunction())
+	{
+
+		v8::Local<v8::Object> jsObj = v8obj->ToObject();
+		v8::Local<v8::String> jsKey = V8JS_STRL(Z_STRVAL_P(member), Z_STRLEN_P(member));
+		v8::Local<v8::Value> 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<v8::Array> array = v8::Local<v8::Array>::Cast(jsVal);
+						retval = (array->Length() != 0);
+					}
+					/* for PHP compatibility, '0' should also be empty */
+					if (jsVal->IsString() && retval) {
+						v8::Local<v8::String> 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;