Skip to content
Merged
3 changes: 3 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
?? ??? ????, PHP 8.5.0alpha3

- Core:
. Add clone-with support to the clone() function. (timwolla, edorian)

- Curl:
. Add support for CURLINFO_CONN_ID in curl_getinfo() (thecaliskan)
. Add support for CURLINFO_QUEUE_TIME_T in curl_getinfo() (thecaliskan)
Expand Down
3 changes: 2 additions & 1 deletion UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,8 @@ PHP 8.5 UPGRADE NOTES
. get_exception_handler() allows retrieving the current user-defined exception
handler function.
RFC: https://wiki.php.net/rfc/get-error-exception-handler
. The clone language construct is now a function.
. The clone language construct is now a function and supports reassigning
(readonly) properties during cloning via the new $withProperties parameter.
RFC: https://wiki.php.net/rfc/clone_with_v2

- Curl:
Expand Down
71 changes: 71 additions & 0 deletions Zend/tests/clone/clone_with_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
--TEST--
Clone with basic
--FILE--
<?php

class Dummy { }

$x = new stdClass();

$foo = 'FOO';
$bar = new Dummy();
$array = [
'baz' => 'BAZ',
'array' => [1, 2, 3],
];

var_dump(clone $x);
var_dump(clone($x));
var_dump(clone($x, [ 'foo' => $foo, 'bar' => $bar ]));
var_dump(clone($x, $array));
var_dump(clone($x, [ 'obj' => $x ]));

var_dump(clone($x, [
'abc',
'def',
new Dummy(),
'named' => 'value',
]));

?>
--EXPECTF--
object(stdClass)#%d (0) {
}
object(stdClass)#%d (0) {
}
object(stdClass)#%d (2) {
["foo"]=>
string(3) "FOO"
["bar"]=>
object(Dummy)#%d (0) {
}
}
object(stdClass)#%d (2) {
["baz"]=>
string(3) "BAZ"
["array"]=>
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}
}
object(stdClass)#%d (1) {
["obj"]=>
object(stdClass)#%d (0) {
}
}
object(stdClass)#%d (4) {
["0"]=>
string(3) "abc"
["1"]=>
string(3) "def"
["2"]=>
object(Dummy)#%d (0) {
}
["named"]=>
string(5) "value"
}
114 changes: 114 additions & 0 deletions Zend/tests/clone/clone_with_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
--TEST--
Clone with respects visiblity
--FILE--
<?php

class P {
public $a = 'default';
protected $b = 'default';
private $c = 'default';
public private(set) string $d = 'default';

public function m1() {
return clone($this, [ 'a' => 'updated A', 'b' => 'updated B', 'c' => 'updated C', 'd' => 'updated D' ]);
}
}

class C extends P {
public function m2() {
return clone($this, [ 'a' => 'updated A', 'b' => 'updated B', 'c' => 'dynamic C' ]);
}

public function m3() {
return clone($this, [ 'd' => 'inaccessible' ]);
}
}

class Unrelated {
public function m3(P $p) {
return clone($p, [ 'b' => 'inaccessible' ]);
}
}

$p = new P();

var_dump(clone($p, [ 'a' => 'updated A' ]));
var_dump($p->m1());

$c = new C();
var_dump($c->m1());
var_dump($c->m2());
try {
var_dump($c->m3());
} catch (Error $e) {
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
}

try {
var_dump(clone($p, [ 'b' => 'inaccessible' ]));
} catch (Error $e) {
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
}

try {
var_dump(clone($p, [ 'd' => 'inaccessible' ]));
} catch (Error $e) {
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
}

try {
var_dump((new Unrelated())->m3($p));
} catch (Error $e) {
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
}

?>
--EXPECTF--
object(P)#%d (4) {
["a"]=>
string(9) "updated A"
["b":protected]=>
string(7) "default"
["c":"P":private]=>
string(7) "default"
["d"]=>
string(7) "default"
}
object(P)#%d (4) {
["a"]=>
string(9) "updated A"
["b":protected]=>
string(9) "updated B"
["c":"P":private]=>
string(9) "updated C"
["d"]=>
string(9) "updated D"
}
object(C)#%d (4) {
["a"]=>
string(9) "updated A"
["b":protected]=>
string(9) "updated B"
["c":"P":private]=>
string(9) "updated C"
["d"]=>
string(9) "updated D"
}

Deprecated: Creation of dynamic property C::$c is deprecated in %s on line %d
object(C)#%d (5) {
["a"]=>
string(9) "updated A"
["b":protected]=>
string(9) "updated B"
["c":"P":private]=>
string(7) "default"
["d"]=>
string(7) "default"
["c"]=>
string(9) "dynamic C"
}
Error: Cannot modify private(set) property P::$d from scope C
Error: Cannot access protected property P::$b
Error: Cannot modify private(set) property P::$d from global scope
Error: Cannot access protected property P::$b
23 changes: 23 additions & 0 deletions Zend/tests/clone/clone_with_003.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
--TEST--
Clone with supports property hooks
--FILE--
<?php

class Clazz {
public string $hooked = 'default' {
set {
$this->hooked = strtoupper($value);
}
}
}

$c = new Clazz();

var_dump(clone($c, [ 'hooked' => 'updated' ]));

?>
--EXPECTF--
object(Clazz)#%d (1) {
["hooked"]=>
string(7) "UPDATED"
}
82 changes: 82 additions & 0 deletions Zend/tests/clone/clone_with_004.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
--TEST--
Clone with evaluation order
--FILE--
<?php

class Clazz {
public string $hooked = 'default' {
set {
echo __FUNCTION__, PHP_EOL;

$this->hooked = strtoupper($value);
}
}

public string $maxLength {
set {
echo __FUNCTION__, PHP_EOL;

if (strlen($value) > 5) {
throw new \Exception('Length exceeded');
}

$this->maxLength = $value;
}
}

public string $minLength {
set {
echo __FUNCTION__, PHP_EOL;

if (strlen($value) < 5) {
throw new \Exception('Length unsufficient');
}

$this->minLength = $value;
}
}
}

$c = new Clazz();

var_dump(clone($c, [ 'hooked' => 'updated' ]));
echo PHP_EOL;
var_dump(clone($c, [ 'hooked' => 'updated', 'maxLength' => 'abc', 'minLength' => 'abcdef' ]));
echo PHP_EOL;
var_dump(clone($c, [ 'minLength' => 'abcdef', 'hooked' => 'updated', 'maxLength' => 'abc' ]));

?>
--EXPECTF--
$hooked::set
object(Clazz)#%d (1) {
["hooked"]=>
string(7) "UPDATED"
["maxLength"]=>
uninitialized(string)
["minLength"]=>
uninitialized(string)
}

$hooked::set
$maxLength::set
$minLength::set
object(Clazz)#%d (3) {
["hooked"]=>
string(7) "UPDATED"
["maxLength"]=>
string(3) "abc"
["minLength"]=>
string(6) "abcdef"
}

$minLength::set
$hooked::set
$maxLength::set
object(Clazz)#%d (3) {
["hooked"]=>
string(7) "UPDATED"
["maxLength"]=>
string(3) "abc"
["minLength"]=>
string(6) "abcdef"
}
64 changes: 64 additions & 0 deletions Zend/tests/clone/clone_with_005.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
--TEST--
Clone with error handling
--FILE--
<?php

class Clazz {
public string $hooked = 'default' {
set {
echo __FUNCTION__, PHP_EOL;

$this->hooked = strtoupper($value);
}
}

public string $maxLength {
set {
echo __FUNCTION__, PHP_EOL;

if (strlen($value) > 5) {
throw new \Exception('Length exceeded');
}

$this->maxLength = $value;
}
}

public string $minLength {
set {
echo __FUNCTION__, PHP_EOL;

if (strlen($value) < 5) {
throw new \Exception('Length insufficient');
}

$this->minLength = $value;
}
}
}

$c = new Clazz();

try {
var_dump(clone($c, [ 'hooked' => 'updated', 'maxLength' => 'abcdef', 'minLength' => 'abc' ]));
} catch (Throwable $e) {
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
}

echo PHP_EOL;

try {
var_dump(clone($c, [ 'hooked' => 'updated', 'minLength' => 'abc', 'maxLength' => 'abcdef' ]));
} catch (Throwable $e) {
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
}

?>
--EXPECT--
$hooked::set
$maxLength::set
Exception: Length exceeded

$hooked::set
$minLength::set
Exception: Length insufficient
16 changes: 16 additions & 0 deletions Zend/tests/clone/clone_with_006.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
--TEST--
Clone with error cases
--FILE--
<?php

$x = new stdClass();

try {
var_dump(clone($x, 1));
} catch (Throwable $e) {
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
}

?>
--EXPECT--
TypeError: clone(): Argument #2 ($withProperties) must be of type array, int given
Loading