Skip to content

Commit 401ef99

Browse files
committed
Add emitting support for asymmetric visibility
1 parent ae94171 commit 401ef99

File tree

6 files changed

+75
-7
lines changed

6 files changed

+75
-7
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php namespace lang\ast\emit;
2+
3+
use lang\ast\Code;
4+
use lang\ast\nodes\{
5+
Assignment,
6+
Block,
7+
InstanceExpression,
8+
Literal,
9+
OffsetExpression,
10+
ReturnStatement,
11+
Variable
12+
};
13+
14+
trait AsymmetricVisibility {
15+
16+
protected function emitProperty($result, $property) {
17+
$literal= new Literal("'{$property->name}'");
18+
$virtual= new InstanceExpression(new Variable('this'), new OffsetExpression(new Literal('__virtual'), $literal));
19+
20+
if (in_array('private(set)', $property->modifiers)) {
21+
$check= (
22+
'$scope= debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]["class"] ?? null;'.
23+
'if (__CLASS__ !== $scope && \\lang\\VirtualProperty::class !== $scope)'.
24+
'throw new \\Error("Cannot access private property ".__CLASS__."::".$name);'
25+
);
26+
}
27+
28+
$scope= $result->codegen->scope[0];
29+
$scope->virtual[$property->name]= [
30+
new ReturnStatement($virtual),
31+
new Block([new Code($check), new Assignment($virtual, '=', new Variable('value'))]),
32+
];
33+
if (isset($property->expression)) {
34+
$scope->init[sprintf('$this->__virtual["%s"]', $property->name)]= $property->expression;
35+
}
36+
}
37+
}

src/main/php/lang/ast/emit/PHP81.class.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ class PHP81 extends PHP {
2323
RewriteBlockLambdaExpressions,
2424
RewriteDynamicClassConstants,
2525
RewriteStaticVariableInitializations,
26+
RewriteProperties,
2627
ReadonlyClasses,
27-
OmitConstantTypes,
28-
PropertyHooks
28+
OmitConstantTypes
2929
;
3030

3131
/** Sets up type => literal mappings */

src/main/php/lang/ast/emit/PHP82.class.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ class PHP82 extends PHP {
2323
RewriteBlockLambdaExpressions,
2424
RewriteDynamicClassConstants,
2525
RewriteStaticVariableInitializations,
26+
RewriteProperties,
2627
ReadonlyClasses,
27-
OmitConstantTypes,
28-
PropertyHooks
28+
OmitConstantTypes
2929
;
3030

3131
/** Sets up type => literal mappings */

src/main/php/lang/ast/emit/PHP83.class.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
* @see https://wiki.php.net/rfc#php_83
1919
*/
2020
class PHP83 extends PHP {
21-
use RewriteBlockLambdaExpressions, ReadonlyClasses, PropertyHooks;
21+
use RewriteBlockLambdaExpressions, RewriteProperties, ReadonlyClasses;
2222

2323
/** Sets up type => literal mappings */
2424
public function __construct() {

src/main/php/lang/ast/emit/RewriteProperties.class.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
<?php namespace lang\ast\emit;
22

33
trait RewriteProperties {
4-
use PropertyHooks, ReadonlyProperties {
4+
use PropertyHooks, ReadonlyProperties, AsymmetricVisibility {
55
PropertyHooks::emitProperty as emitPropertyHooks;
66
ReadonlyProperties::emitProperty as emitReadonlyProperties;
7+
AsymmetricVisibility::emitProperty as emitAsymmetricVisibility;
78
}
89

910
protected function emitProperty($result, $property) {
1011
if ($property->hooks) {
1112
return $this->emitPropertyHooks($result, $property);
12-
} else if (in_array('readonly', $property->modifiers)) {
13+
} else if (in_array('private(set)', $property->modifiers)) {
14+
return $this->emitAsymmetricVisibility($result, $property);
15+
} else if (PHP_VERSION_ID <= 80100 && in_array('readonly', $property->modifiers)) {
1316
return $this->emitReadonlyProperties($result, $property);
1417
}
1518
parent::emitProperty($result, $property);
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php namespace lang\ast\unittest\emit;
2+
3+
use lang\Error;
4+
use test\{Assert, Expect, Test};
5+
6+
/**
7+
* Asymmetric visibility tests
8+
*
9+
* @see https://wiki.php.net/rfc/asymmetric-visibility-v2
10+
*/
11+
class AsymmetricVisibilityTest extends EmittingTest {
12+
13+
#[Test]
14+
public function reading() {
15+
$t= $this->declare('class %T {
16+
public private(set) $fixture= "Test";
17+
}');
18+
Assert::equals('Test', $t->newInstance()->fixture);
19+
}
20+
21+
#[Test, Expect(class: Error::class, message: '/Cannot access private property T.+::fixture/')]
22+
public function writing() {
23+
$t= $this->declare('class %T {
24+
public private(set) $fixture= "Test";
25+
}');
26+
$t->newInstance()->fixture= 'Changed';
27+
}
28+
}

0 commit comments

Comments
 (0)