Skip to content

Commit f6a6d1e

Browse files
committed
Rework JavaScript wrapper for PHP objects.
Use the NamedPropertyHandler feature of v8 to wrap accesses to PHP properties and methods from JavaScript. This enables us to support property set/delete/query. The `in` and `delete` operators in JavaScript work like the `isset()` and `unset()` functions in PHP. In particular, a PHP property with a null value will not be `in` the JavaScript object. (This holds when enumerating all properties of an object as well.) Because JavaScript has a single namespace for both properties and methods, we allow the use of the `__call` method on a PHP object (even if the PHP class does not natively define the `__call` magic function) in order to unambiguously invoke a method. Similarly, you can prefix a property name with `$` to unambiguously access the property. (When enumerating all properties, properties are `$`-prefixed in order to ensure they are not conflated with method names.)
1 parent 0adefa5 commit f6a6d1e

File tree

6 files changed

+444
-189
lines changed

6 files changed

+444
-189
lines changed

README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,3 +154,45 @@ Javascript API
154154
// This makes use of the PHP module loader provided via V8Js::setModuleLoader (see PHP API above).
155155
require("path/to/module");
156156

157+
The JavaScript `in` operator, when applied to a wrapped PHP object,
158+
works the same as the PHP `isset()` function. Similarly, when applied
159+
to a wrapped PHP object, JavaScript `delete` works like PHP `unset`.
160+
161+
```php
162+
<?php
163+
class Foo {
164+
var $bar = null;
165+
}
166+
$v8 = new V8Js();
167+
$v8->foo = new Foo;
168+
// This prints "no"
169+
$v8->executeString('print( "bar" in PHP.foo ? "yes" : "no" );');
170+
?>
171+
```
172+
173+
PHP has separate namespaces for properties and methods, while JavaScript
174+
has just one. Usually this isn't an issue, but if you need to you can use
175+
a leading `$` to specify a property, or `__call` to specifically invoke a
176+
method.
177+
178+
```php
179+
<?php
180+
class Foo {
181+
var $bar = "bar";
182+
function bar($what) { echo "I'm a ", $what, "!\n"; }
183+
}
184+
185+
$foo = new Foo;
186+
// This prints 'bar'
187+
echo $foo->bar, "\n";
188+
// This prints "I'm a function!"
189+
$foo->bar("function");
190+
191+
$v8 = new V8Js();
192+
$v8->foo = new Foo;
193+
// This prints 'bar'
194+
$v8->executeString('print(PHP.foo.$bar, "\n");');
195+
// This prints "I'm a function!"
196+
$v8->executeString('PHP.foo.__call("bar", ["function"]);');
197+
?>
198+
```

TODO

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
- Feature: Extension registering from php.ini
22
- Feature: Thread safety
33
- Missing: Indexed property handlers
4+
- Missing: static properties of PHP objects (on instance.constructor?)
45
- Bug: exception propagation fails when property getter is set
56
- Bug: method_exists() leaks when used with V8 objects

tests/object.phpt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ var_dump($a->myobj->foo);
4343
===EOF===
4444
--EXPECT--
4545
mytest => function () { [native code] }
46-
foo => ORIGINAL
46+
$foo => ORIGINAL
4747
Here be monsters..
48-
ORIGINAL
49-
string(8) "ORIGINAL"
48+
CHANGED
49+
string(7) "CHANGED"
5050
===EOF===

tests/object_prototype.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ Test V8::executeString() : Prototype with PHP callbacks
77
$js = <<<'EOT'
88
99
String.prototype.test = function(){ return PHP.test(this.toString(), arguments); };
10-
String.prototype.test_two = function(){ return PHP.test_two.func(this.toString(), arguments); };
10+
String.prototype.test_two = function(){ return PHP.test_two.__call('func', [this.toString(), arguments]); };
1111
Array.prototype.test = function(){ return PHP.test(this.toString(), arguments); };
12-
Array.prototype.test_two = function(){ return PHP.test_two.func(this.toString(), arguments); };
12+
Array.prototype.test_two = function(){ return PHP.test_two.__call('func', [this.toString(), arguments]); };
1313
1414
"Foobar".test("foo", "bar");
1515
"Foobar".test_two("foo", "bar");

tests/var_dump.phpt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -198,11 +198,11 @@ array (11) {
198198
object(Closure)#%d {
199199
function () { [native code] }
200200
}
201-
["date"] =>
201+
["$date"] =>
202202
string(19) "1976-09-27 09:00:00"
203-
["timezone_type"] =>
203+
["$timezone_type"] =>
204204
int(3)
205-
["timezone"] =>
205+
["$timezone"] =>
206206
string(3) "UTC"
207207
}
208208
["array"] =>
@@ -224,7 +224,7 @@ array (11) {
224224
}
225225
["phpobject"] =>
226226
object(Foo)#%d (1) {
227-
["field"] =>
227+
["$field"] =>
228228
string(3) "php"
229229
}
230230
}
@@ -266,7 +266,7 @@ object(Object)#%d (12) {
266266
}
267267
["phpobject"] =>
268268
object(Foo)#%d (1) {
269-
["field"] =>
269+
["$field"] =>
270270
string(3) "php"
271271
}
272272
}

0 commit comments

Comments
 (0)