diff --git a/composer.json b/composer.json index f98e301e..0aeb3090 100755 --- a/composer.json +++ b/composer.json @@ -8,7 +8,7 @@ "require" : { "xp-framework/core": "^12.0 | ^11.6 | ^10.16", "xp-framework/reflection": "^3.2 | ^2.15", - "xp-framework/ast": "^11.5", + "xp-framework/ast": "dev-feature/clone-with as 11.6.0", "php" : ">=7.4.0" }, "require-dev" : { diff --git a/src/main/php/lang/ast/emit/PHP.class.php b/src/main/php/lang/ast/emit/PHP.class.php index 63c5fed8..d51abede 100755 --- a/src/main/php/lang/ast/emit/PHP.class.php +++ b/src/main/php/lang/ast/emit/PHP.class.php @@ -1082,6 +1082,19 @@ protected function emitNewClass($result, $new) { $result->codegen->leave(); } + protected function emitClone($result, $clone) { + $result->out->write('clone '); + if (empty($clone->with)) { + $this->emitOne($result, $clone->expression); + } else { + $result->out->write('('); + $this->emitOne($result, $clone->expression); + $result->out->write(','); + $this->emitArguments($result, $clone->with); + $result->out->write(')'); + } + } + protected function emitCallable($result, $callable) { // Disambiguate the following: diff --git a/src/main/php/lang/ast/emit/PHP74.class.php b/src/main/php/lang/ast/emit/PHP74.class.php index 6f97959c..7e2ee689 100755 --- a/src/main/php/lang/ast/emit/PHP74.class.php +++ b/src/main/php/lang/ast/emit/PHP74.class.php @@ -21,6 +21,7 @@ class PHP74 extends PHP { OmitConstantTypes, ReadonlyClasses, RewriteBlockLambdaExpressions, + RewriteCloneWith, RewriteEnums, RewriteExplicitOctals, RewriteProperties, diff --git a/src/main/php/lang/ast/emit/PHP80.class.php b/src/main/php/lang/ast/emit/PHP80.class.php index db83e745..8f5464c3 100755 --- a/src/main/php/lang/ast/emit/PHP80.class.php +++ b/src/main/php/lang/ast/emit/PHP80.class.php @@ -24,6 +24,7 @@ class PHP80 extends PHP { OmitConstantTypes, ReadonlyClasses, RewriteBlockLambdaExpressions, + RewriteCloneWith, RewriteDynamicClassConstants, RewriteEnums, RewriteExplicitOctals, diff --git a/src/main/php/lang/ast/emit/PHP81.class.php b/src/main/php/lang/ast/emit/PHP81.class.php index f93b4969..02207ac8 100755 --- a/src/main/php/lang/ast/emit/PHP81.class.php +++ b/src/main/php/lang/ast/emit/PHP81.class.php @@ -21,6 +21,7 @@ class PHP81 extends PHP { use RewriteBlockLambdaExpressions, + RewriteCloneWith, RewriteDynamicClassConstants, RewriteStaticVariableInitializations, RewriteProperties, diff --git a/src/main/php/lang/ast/emit/PHP82.class.php b/src/main/php/lang/ast/emit/PHP82.class.php index 89fa9cbd..12d4aec2 100755 --- a/src/main/php/lang/ast/emit/PHP82.class.php +++ b/src/main/php/lang/ast/emit/PHP82.class.php @@ -21,6 +21,7 @@ class PHP82 extends PHP { use RewriteBlockLambdaExpressions, + RewriteCloneWith, RewriteDynamicClassConstants, RewriteStaticVariableInitializations, RewriteProperties, diff --git a/src/main/php/lang/ast/emit/PHP83.class.php b/src/main/php/lang/ast/emit/PHP83.class.php index 7c7293b2..b73af417 100755 --- a/src/main/php/lang/ast/emit/PHP83.class.php +++ b/src/main/php/lang/ast/emit/PHP83.class.php @@ -18,7 +18,7 @@ * @see https://wiki.php.net/rfc#php_83 */ class PHP83 extends PHP { - use RewriteBlockLambdaExpressions, RewriteProperties; + use RewriteBlockLambdaExpressions, RewriteCloneWith, RewriteProperties; public $targetVersion= 80300; diff --git a/src/main/php/lang/ast/emit/PHP84.class.php b/src/main/php/lang/ast/emit/PHP84.class.php index 71379f30..a0e42ac8 100755 --- a/src/main/php/lang/ast/emit/PHP84.class.php +++ b/src/main/php/lang/ast/emit/PHP84.class.php @@ -18,7 +18,7 @@ * @see https://wiki.php.net/rfc#php_84 */ class PHP84 extends PHP { - use RewriteBlockLambdaExpressions; + use RewriteBlockLambdaExpressions, RewriteCloneWith; public $targetVersion= 80400; diff --git a/src/main/php/lang/ast/emit/RewriteCloneWith.class.php b/src/main/php/lang/ast/emit/RewriteCloneWith.class.php new file mode 100755 index 00000000..382302b1 --- /dev/null +++ b/src/main/php/lang/ast/emit/RewriteCloneWith.class.php @@ -0,0 +1,32 @@ +with)) return parent::emitClone($result, $clone); + + // Wrap clone with, e.g. clone($x, id: 6100), inside an IIFE as follows: + // `function($args) { $this->id= $args['id']; return $this; }`, then bind + // this closure to the cloned instance before invoking it with the named + // arguments so we can access non-public members. + $t= $result->temp(); + $result->out->write('('.$t.'=clone '); + $this->emitOne($result, $clone->expression); + + $result->out->write(')?(function($a) {'); + foreach ($clone->with as $name => $argument) { + $result->out->write('$this->'.$name.'=$a["'.$name.'"];'); + } + + $result->out->write('return $this;})->bindTo('.$t.','.$t.')(['); + foreach ($clone->with as $name => $argument) { + $result->out->write('"'.$name.'"=>'); + $this->emitOne($result, $argument); + $result->out->write(','); + } + $result->out->write(']):null'); + } +} \ No newline at end of file diff --git a/src/test/php/lang/ast/unittest/emit/CloningTest.class.php b/src/test/php/lang/ast/unittest/emit/CloningTest.class.php index 96292fb8..d0e10d38 100755 --- a/src/test/php/lang/ast/unittest/emit/CloningTest.class.php +++ b/src/test/php/lang/ast/unittest/emit/CloningTest.class.php @@ -9,7 +9,12 @@ class CloningTest extends EmittingTest { #[Before] public function fixture() { $this->fixture= new class() { - public $id= 1; + private $id= 1; + private $name= 'Test'; + + public function toString() { + return "id}, name: {$this->name}>"; + } public function with($id) { $this->id= $id; @@ -50,8 +55,26 @@ public function clone_interceptor_called() { public function run($in) { return clone $in; } - }', $this->fixture->with(id: 1)); + }', $this->fixture->with(1)); + + Assert::equals( + ['', ''], + [$this->fixture->toString(), $clone->toString()] + ); + } + + #[Test] + public function clone_with() { + $clone= $this->run('class %T { + private $id= 6100; + public function run($in) { + return clone($in, id: $this->id, name: "Changed"); + } + }', $this->fixture->with(1)); - Assert::equals([1, 2], [$this->fixture->id, $clone->id]); + Assert::equals( + ['', ''], + [$this->fixture->toString(), $clone->toString()] + ); } } \ No newline at end of file