Skip to content

Commit b527f3b

Browse files
committed
zend_vm: Use the clone_obj_with handler in ZEND_CLONE
1 parent 711020e commit b527f3b

12 files changed

+730
-20
lines changed

Zend/tests/clone/clone_with_001.phpt

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
--TEST--
2+
Clone with basic
3+
--FILE--
4+
<?php
5+
6+
$x = new stdClass();
7+
8+
$foo = 'FOO';
9+
$bar = 'BAR';
10+
$array = [
11+
'baz' => 'BAZ',
12+
'arra' => [1, 2, 3],
13+
];
14+
15+
var_dump(clone $x);
16+
var_dump(clone($x));
17+
var_dump(clone($x, foo: $foo, bar: $bar));
18+
var_dump(clone($x, ...$array));
19+
var_dump(clone($x, ...["obj" => $x]));
20+
21+
var_dump(clone($x, ...[
22+
"abc",
23+
"def",
24+
]));
25+
26+
?>
27+
--EXPECTF--
28+
object(stdClass)#%d (0) {
29+
}
30+
object(stdClass)#%d (0) {
31+
}
32+
object(stdClass)#%d (2) {
33+
["foo"]=>
34+
string(3) "FOO"
35+
["bar"]=>
36+
string(3) "BAR"
37+
}
38+
object(stdClass)#%d (2) {
39+
["baz"]=>
40+
string(3) "BAZ"
41+
["arra"]=>
42+
array(3) {
43+
[0]=>
44+
int(1)
45+
[1]=>
46+
int(2)
47+
[2]=>
48+
int(3)
49+
}
50+
}
51+
object(stdClass)#%d (1) {
52+
["obj"]=>
53+
object(stdClass)#%d (0) {
54+
}
55+
}
56+
object(stdClass)#%d (2) {
57+
["0"]=>
58+
string(3) "abc"
59+
["1"]=>
60+
string(3) "def"
61+
}

Zend/tests/clone/clone_with_002.phpt

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
--TEST--
2+
Clone with respects visiblity
3+
--FILE--
4+
<?php
5+
6+
class P {
7+
public $a = 'default';
8+
protected $b = 'default';
9+
private $c = 'default';
10+
public private(set) string $d = 'default';
11+
12+
public function m1() {
13+
return clone($this, a: 'updated A', b: 'updated B', c: 'updated C', d: 'updated D');
14+
}
15+
}
16+
17+
class C extends P {
18+
public function m2() {
19+
return clone($this, a: 'updated A', b: 'updated B', c: 'dynamic C');
20+
}
21+
22+
public function m3() {
23+
return clone($this, d: 'inaccessible');
24+
}
25+
}
26+
27+
class Unrelated {
28+
public function m3(P $p) {
29+
return clone($p, b: 'inaccessible');
30+
}
31+
}
32+
33+
$p = new P();
34+
35+
var_dump(clone($p, a: 'updated A'));
36+
var_dump($p->m1());
37+
38+
$c = new C();
39+
var_dump($c->m1());
40+
var_dump($c->m2());
41+
try {
42+
var_dump($c->m3());
43+
} catch (Error $e) {
44+
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
45+
}
46+
47+
try {
48+
var_dump(clone($p, b: 'inaccessible'));
49+
} catch (Error $e) {
50+
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
51+
}
52+
53+
try {
54+
var_dump(clone($p, d: 'inaccessible'));
55+
} catch (Error $e) {
56+
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
57+
}
58+
59+
try {
60+
var_dump((new Unrelated())->m3($p));
61+
} catch (Error $e) {
62+
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
63+
}
64+
65+
?>
66+
--EXPECTF--
67+
object(P)#%d (4) {
68+
["a"]=>
69+
string(9) "updated A"
70+
["b":protected]=>
71+
string(7) "default"
72+
["c":"P":private]=>
73+
string(7) "default"
74+
["d"]=>
75+
string(7) "default"
76+
}
77+
object(P)#%d (4) {
78+
["a"]=>
79+
string(9) "updated A"
80+
["b":protected]=>
81+
string(9) "updated B"
82+
["c":"P":private]=>
83+
string(9) "updated C"
84+
["d"]=>
85+
string(9) "updated D"
86+
}
87+
object(C)#%d (4) {
88+
["a"]=>
89+
string(9) "updated A"
90+
["b":protected]=>
91+
string(9) "updated B"
92+
["c":"P":private]=>
93+
string(9) "updated C"
94+
["d"]=>
95+
string(9) "updated D"
96+
}
97+
98+
Deprecated: Creation of dynamic property C::$c is deprecated in %s on line %d
99+
object(C)#%d (5) {
100+
["a"]=>
101+
string(9) "updated A"
102+
["b":protected]=>
103+
string(9) "updated B"
104+
["c":"P":private]=>
105+
string(7) "default"
106+
["d"]=>
107+
string(7) "default"
108+
["c"]=>
109+
string(9) "dynamic C"
110+
}
111+
Error: Cannot modify private(set) property P::$d from scope C
112+
Error: Cannot access protected property P::$b
113+
Error: Cannot modify private(set) property P::$d from global scope
114+
Error: Cannot access protected property P::$b

Zend/tests/clone/clone_with_003.phpt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--TEST--
2+
Clone with supports property hooks
3+
--FILE--
4+
<?php
5+
6+
class Clazz {
7+
public string $hooked = 'default' {
8+
set (string $value) {
9+
$this->hooked = strtoupper($value);
10+
}
11+
}
12+
}
13+
14+
$c = new Clazz();
15+
16+
var_dump(clone($c, hooked: 'updated'));
17+
18+
?>
19+
--EXPECTF--
20+
object(Clazz)#%d (1) {
21+
["hooked"]=>
22+
string(7) "UPDATED"
23+
}

Zend/tests/clone/clone_with_004.phpt

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
--TEST--
2+
Clone with evaluation order
3+
--FILE--
4+
<?php
5+
6+
class Clazz {
7+
public string $hooked = 'default' {
8+
set (string $value) {
9+
echo __FUNCTION__, PHP_EOL;
10+
11+
$this->hooked = strtoupper($value);
12+
}
13+
}
14+
15+
public string $maxLength {
16+
set (string $value) {
17+
echo __FUNCTION__, PHP_EOL;
18+
19+
if (strlen($value) > 5) {
20+
throw new \Exception('Length exceeded');
21+
}
22+
23+
$this->maxLength = $value;
24+
}
25+
}
26+
27+
public string $minLength {
28+
set (string $value) {
29+
echo __FUNCTION__, PHP_EOL;
30+
31+
if (strlen($value) < 5) {
32+
throw new \Exception('Length unsufficient');
33+
}
34+
35+
$this->minLength = $value;
36+
}
37+
}
38+
}
39+
40+
$c = new Clazz();
41+
42+
var_dump(clone($c, hooked: 'updated'));
43+
echo PHP_EOL;
44+
var_dump(clone($c, hooked: 'updated', maxLength: 'abc', minLength: 'abcdef'));
45+
echo PHP_EOL;
46+
var_dump(clone($c, minLength: 'abcdef', hooked: 'updated', maxLength: 'abc'));
47+
48+
?>
49+
--EXPECTF--
50+
$hooked::set
51+
object(Clazz)#%d (1) {
52+
["hooked"]=>
53+
string(7) "UPDATED"
54+
["maxLength"]=>
55+
uninitialized(string)
56+
["minLength"]=>
57+
uninitialized(string)
58+
}
59+
60+
$hooked::set
61+
$maxLength::set
62+
$minLength::set
63+
object(Clazz)#%d (3) {
64+
["hooked"]=>
65+
string(7) "UPDATED"
66+
["maxLength"]=>
67+
string(3) "abc"
68+
["minLength"]=>
69+
string(6) "abcdef"
70+
}
71+
72+
$minLength::set
73+
$hooked::set
74+
$maxLength::set
75+
object(Clazz)#%d (3) {
76+
["hooked"]=>
77+
string(7) "UPDATED"
78+
["maxLength"]=>
79+
string(3) "abc"
80+
["minLength"]=>
81+
string(6) "abcdef"
82+
}

Zend/tests/clone/clone_with_005.phpt

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
--TEST--
2+
Clone with error handling
3+
--FILE--
4+
<?php
5+
6+
class Clazz {
7+
public string $hooked = 'default' {
8+
set (string $value) {
9+
echo __FUNCTION__, PHP_EOL;
10+
11+
$this->hooked = strtoupper($value);
12+
}
13+
}
14+
15+
public string $maxLength {
16+
set (string $value) {
17+
echo __FUNCTION__, PHP_EOL;
18+
19+
if (strlen($value) > 5) {
20+
throw new \Exception('Length exceeded');
21+
}
22+
23+
$this->maxLength = $value;
24+
}
25+
}
26+
27+
public string $minLength {
28+
set (string $value) {
29+
echo __FUNCTION__, PHP_EOL;
30+
31+
if (strlen($value) < 5) {
32+
throw new \Exception('Length insufficient');
33+
}
34+
35+
$this->minLength = $value;
36+
}
37+
}
38+
}
39+
40+
$c = new Clazz();
41+
42+
try {
43+
var_dump(clone($c, hooked: 'updated', maxLength: 'abcdef', minLength: 'abc'));
44+
} catch (Throwable $e) {
45+
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
46+
}
47+
48+
echo PHP_EOL;
49+
50+
try {
51+
var_dump(clone($c, hooked: 'updated', minLength: 'abc', maxLength: 'abcdef'));
52+
} catch (Throwable $e) {
53+
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
54+
}
55+
56+
?>
57+
--EXPECT--
58+
$hooked::set
59+
$maxLength::set
60+
Exception: Length exceeded
61+
62+
$hooked::set
63+
$minLength::set
64+
Exception: Length insufficient

Zend/tests/clone/clone_with_006.phpt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
--TEST--
2+
Clone with error cases
3+
--FILE--
4+
<?php
5+
6+
$x = new stdClass();
7+
8+
try {
9+
var_dump(clone($x, ...1));
10+
} catch (Throwable $e) {
11+
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
12+
}
13+
14+
?>
15+
--EXPECT--
16+
TypeError: Only arrays can be unpacked for clone, int given

Zend/tests/clone/clone_with_007.phpt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
--TEST--
2+
Clone with supports __clone
3+
--FILE--
4+
<?php
5+
6+
class Clazz {
7+
public function __construct(
8+
public string $foo,
9+
public string $bar,
10+
) { }
11+
12+
public function __clone() {
13+
$this->foo = 'foo updated in __clone';
14+
$this->bar = 'bar updated in __clone';
15+
}
16+
}
17+
18+
$c = new Clazz('foo', 'bar');
19+
20+
var_dump(clone($c, foo: 'foo updated in clone-with'));
21+
22+
?>
23+
--EXPECTF--
24+
object(Clazz)#%d (2) {
25+
["foo"]=>
26+
string(25) "foo updated in clone-with"
27+
["bar"]=>
28+
string(22) "bar updated in __clone"
29+
}

0 commit comments

Comments
 (0)