1
+ <?php namespace lang \ast \emit ;
2
+
3
+ use lang \ast \nodes \{
4
+ Assignment ,
5
+ Block ,
6
+ InstanceExpression ,
7
+ Literal ,
8
+ OffsetExpression ,
9
+ ReturnStatement ,
10
+ Variable
11
+ };
12
+
13
+ /**
14
+ * Asymmetric Visibility
15
+ *
16
+ * @see https://wiki.php.net/rfc/asymmetric-visibility-v2
17
+ * @test lang.ast.unittest.emit.AsymmetricVisibilityTest
18
+ */
19
+ trait AsymmetricVisibility {
20
+ use VisibilityChecks;
21
+
22
+ protected function emitProperty ($ result , $ property ) {
23
+ static $ lookup = [
24
+ 'public ' => MODIFIER_PUBLIC ,
25
+ 'protected ' => MODIFIER_PROTECTED ,
26
+ 'private ' => MODIFIER_PRIVATE ,
27
+ 'static ' => MODIFIER_STATIC ,
28
+ 'final ' => MODIFIER_FINAL ,
29
+ 'abstract ' => MODIFIER_ABSTRACT ,
30
+ 'readonly ' => MODIFIER_READONLY ,
31
+ 'public(set) ' => 0x1000000 ,
32
+ 'protected(set) ' => 0x0000800 ,
33
+ 'private(set) ' => 0x0001000 ,
34
+ ];
35
+
36
+ $ scope = $ result ->codegen ->scope [0 ];
37
+ $ modifiers = 0 ;
38
+ foreach ($ property ->modifiers as $ name ) {
39
+ $ modifiers |= $ lookup [$ name ];
40
+ }
41
+
42
+ // Declare checks for private(set) and protected(set), folding declarations
43
+ // like `[visibility] [visibility](set)` to just the visibility itself.
44
+ if ($ modifiers & 0x1000000 ) {
45
+ $ checks = [];
46
+ $ modifiers &= ~0x1000000 ;
47
+ } else if ($ modifiers & 0x0000800 ) {
48
+ $ checks = [$ this ->protected ($ property ->name , 'modify protected(set) ' )];
49
+ $ modifiers & MODIFIER_PROTECTED && $ modifiers &= ~0x0000800 ;
50
+ } else if ($ modifiers & 0x0001000 ) {
51
+ $ checks = [$ this ->private ($ property ->name , 'modify private(set) ' )];
52
+ $ modifiers & MODIFIER_PRIVATE && $ modifiers &= ~0x0001000 ;
53
+ }
54
+
55
+ // Emit XP meta information for the reflection API
56
+ $ scope ->meta [self ::PROPERTY ][$ property ->name ]= [
57
+ DETAIL_RETURNS => $ property ->type ? $ property ->type ->name () : 'var ' ,
58
+ DETAIL_ANNOTATIONS => $ property ->annotations ,
59
+ DETAIL_COMMENT => $ property ->comment ,
60
+ DETAIL_TARGET_ANNO => [],
61
+ DETAIL_ARGUMENTS => [$ modifiers ]
62
+ ];
63
+
64
+ // The readonly flag is really two flags in one: write-once and restricted(set)
65
+ if (in_array ('readonly ' , $ property ->modifiers )) {
66
+ $ checks []= $ this ->initonce ($ property ->name );
67
+ }
68
+
69
+ $ virtual = new InstanceExpression (new Variable ('this ' ), new OffsetExpression (
70
+ new Literal ('__virtual ' ),
71
+ new Literal ("' {$ property ->name }' " ))
72
+ );
73
+ $ scope ->virtual [$ property ->name ]= [
74
+ new ReturnStatement ($ virtual ),
75
+ new Block ([...$ checks , new Assignment ($ virtual , '= ' , new Variable ('value ' ))]),
76
+ ];
77
+ if (isset ($ property ->expression )) {
78
+ $ scope ->init [sprintf ('$this->__virtual["%s"] ' , $ property ->name )]= $ property ->expression ;
79
+ }
80
+ }
81
+ }
0 commit comments