Skip to content

Commit 7aba1b9

Browse files
committed
Support clone with unpack on PHP 7.4
1 parent 8feae1c commit 7aba1b9

File tree

2 files changed

+27
-14
lines changed

2 files changed

+27
-14
lines changed
Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,35 @@
11
<?php namespace lang\ast\emit;
22

3-
use lang\ast\nodes\{Signature, Parameter, UnpackExpression};
3+
use lang\ast\nodes\{ArrayLiteral, UnpackExpression};
44

55
/** @see https://wiki.php.net/rfc/clone_with_v2 */
66
trait RewriteCloneWith {
77

88
protected function emitClone($result, $clone) {
9+
static $wrapper= '(function($c, array $w) { foreach ($w as $p=>$v) { $c->$p=$v; } return $c;})';
10+
911
$expr= $clone->arguments['object'] ?? $clone->arguments[0] ?? null;
1012
$with= $clone->arguments['withProperties'] ?? $clone->arguments[1] ?? null;
1113

12-
// Wrap clone with, e.g. clone($x, ['id' => 6100]), inside an IIFE which
13-
/// iterates over the property-value pairs, assigning them to the clone.
14-
if ($with) {
15-
$result->out->write('(function($object, array $withProperties) {');
16-
$result->out->write('foreach ($withProperties as $p=>$v) { $object->$p=$v; } return $object;})(clone ');
14+
// Built ontop of a wrapper function which iterates over the property-value pairs,
15+
// assigning them to the clone. Unwind unpack statements, e.g. `clone(...$args)`,
16+
// into an array, manually unpacking it for invocation.
17+
if ($expr instanceof UnpackExpression || $with instanceof UnpackExpression) {
18+
$t= $result->temp();
19+
$result->out->write('('.$t.'=');
20+
$this->emitOne($result, new ArrayLiteral($with ? [[null, $expr], [null, $with]] : [[null, $expr]], $clone->line));
21+
$result->out->write(')?');
22+
$result->out->write($wrapper.'(clone ('.$t.'["object"] ?? '.$t.'[0]), '.$t.'["withProperties"] ?? '.$t.'[1] ?? [])');
23+
$result->out->write(':null');
24+
} else if ($with) {
25+
$result->out->write($wrapper.'(clone ');
1726
$this->emitOne($result, $expr);
1827
$result->out->write(',');
1928
$this->emitOne($result, $with);
2029
$result->out->write(')');
21-
} else if (isset($clone->arguments['object'])) {
30+
} else {
2231
$result->out->write('clone ');
2332
$this->emitOne($result, $expr);
24-
} else if ($expr instanceof UnpackExpression) {
25-
$result->out->write('(function($u) { $c= clone $u["object"] ?? $u[0];');
26-
$result->out->write('foreach ($u["withProperties"] ?? $u[1] ?? [] as $p=>$v) { $c->$p=$v; } return $c;})(');
27-
$this->emitOne($result, $expr->expression);
28-
$result->out->write(')');
29-
} else {
30-
return parent::emitClone($result, $clone);
3133
}
3234
}
3335
}

src/test/php/lang/ast/unittest/emit/CloningTest.class.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,17 @@ public function run($in) {
108108
Assert::equals('<id: 2, name: Changed>', $clone->toString());
109109
}
110110

111+
#[Test]
112+
public function clone_unpack_object_and_properties() {
113+
$clone= $this->run('class %T {
114+
public function run($in) {
115+
return clone(...["object" => $in], ...["withProperties" => ["name" => "Changed"]]);
116+
}
117+
}', $this->fixture);
118+
119+
Assert::equals('<id: 2, name: Changed>', $clone->toString());
120+
}
121+
111122
#[Test]
112123
public function clone_unpack_only_properties() {
113124
$clone= $this->run('class %T {

0 commit comments

Comments
 (0)