Browse Source

Handle property visibility and __get, refs #79

Protected and private properties should not be available
to JS context.  Instead call __get function, if the
property is not accessible.
Stefan Siegl 11 years ago
parent
commit
170b1ff94c
3 changed files with 122 additions and 8 deletions
  1. 33 0
      tests/property_visibility.phpt
  2. 44 0
      tests/property_visibility__get.phpt
  3. 45 8
      v8js_convert.cc

+ 33 - 0
tests/property_visibility.phpt

@@ -0,0 +1,33 @@
+--TEST--
+Test V8::executeString() : Property visibility
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+
+class Foo {
+	private $privBar = "privBar";
+	protected $protBar = "protBar";
+	public $pubBar = "pubBar";
+}
+
+$js = new V8Js();
+
+$js->foo = new Foo();
+
+$script = <<<END
+
+var_dump(PHP.foo.privBr);
+var_dump(PHP.foo.protBar);
+var_dump(PHP.foo.pubBar);
+
+END;
+
+$js->executeString($script);
+?>
+===EOF===
+--EXPECT--
+NULL
+NULL
+string(6) "pubBar"
+===EOF===

+ 44 - 0
tests/property_visibility__get.phpt

@@ -0,0 +1,44 @@
+--TEST--
+Test V8::executeString() : Property visibility __get
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--FILE--
+<?php
+
+class Foo {
+	private $privBar = "privBar";
+	protected $protBar = "protBar";
+	public $pubBar = "pubBar";
+
+	public function __get($key)
+	{
+		var_dump($key);
+		return 42;
+	}
+}
+
+$js = new V8Js();
+
+$js->foo = new Foo();
+
+$script = <<<END
+
+var_dump(PHP.foo.unknownBar);
+var_dump(PHP.foo.privBar);
+var_dump(PHP.foo.protBar);
+var_dump(PHP.foo.pubBar);
+
+END;
+
+$js->executeString($script);
+?>
+===EOF===
+--EXPECT--
+string(10) "unknownBar"
+int(42)
+string(7) "privBar"
+int(42)
+string(7) "protBar"
+int(42)
+string(6) "pubBar"
+===EOF===

+ 45 - 8
v8js_convert.cc

@@ -604,15 +604,52 @@ static inline v8::Local<v8::Value> php_v8js_named_property_callback(v8::Local<v8
 		}
 		if (callback_type == V8JS_PROP_GETTER) {
 			/* Nope, not a method -- must be a (case-sensitive) property */
-			php_value = zend_read_property(scope, object, V8JS_CONST name, name_len, true TSRMLS_CC);
-			// special case uninitialized_zval_ptr and return an empty value
-			// (indicating that we don't intercept this property) if the
-			// property doesn't exist.
-			if (php_value == EG(uninitialized_zval_ptr)) {
-				ret_value = v8::Handle<v8::Value>();
-			} else {
-				// wrap it
+			zval zname;
+			ZVAL_STRINGL(&zname, name, name_len, 0);
+			zend_property_info *property_info = zend_get_property_info(ce, &zname, 1 TSRMLS_CC);
+
+			if(property_info && property_info->flags & ZEND_ACC_PUBLIC) {
+				php_value = zend_read_property(NULL, object, V8JS_CONST name, name_len, true TSRMLS_CC);
+				// special case uninitialized_zval_ptr and return an empty value
+				// (indicating that we don't intercept this property) if the
+				// property doesn't exist.
+				if (php_value == EG(uninitialized_zval_ptr)) {
+					ret_value = v8::Handle<v8::Value>();
+				} else {
+					// wrap it
+					ret_value = zval_to_v8js(php_value, isolate TSRMLS_CC);
+					/* We don't own the reference to php_value... unless the
+					 * returned refcount was 0, in which case the below code
+					 * will free it. */
+					zval_add_ref(&php_value);
+					zval_ptr_dtor(&php_value);
+				}
+			}
+			else if (zend_hash_find(&ce->function_table, "__get", 6, (void**)&method_ptr) == SUCCESS
+					 /* Allow only public methods */
+					 && ((method_ptr->common.fn_flags & ZEND_ACC_PUBLIC) != 0)) {
+				/* Okay, let's call __get. */
+				zend_fcall_info fci;
+
+				zval fmember;
+				ZVAL_STRING(&fmember, "__get", 0);
+
+				fci.size = sizeof(fci);
+				fci.function_table = &ce->function_table;
+				fci.function_name = &fmember;
+				fci.symbol_table = NULL;
+				fci.object_ptr = object;
+				fci.retval_ptr_ptr = &php_value;
+				fci.param_count = 1;
+
+				zval *zname_ptr = &zname;
+				zval **zname_ptr_ptr = &zname_ptr;
+				fci.params = &zname_ptr_ptr;
+
+				zend_call_function(&fci, NULL TSRMLS_CC);
+
 				ret_value = zval_to_v8js(php_value, isolate TSRMLS_CC);
+
 				/* We don't own the reference to php_value... unless the
 				 * returned refcount was 0, in which case the below code
 				 * will free it. */