Skip to content

Commit b493c51

Browse files
committed
[8.4] Add support for asymmetric visibility modifiers
Represented using new PRIVATE_SET, PROTECTED_SET and PUBLIC_SET bits in Modifiers. RFC: https://wiki.php.net/rfc/asymmetric-visibility-v2
1 parent 54139ca commit b493c51

File tree

16 files changed

+2501
-2060
lines changed

16 files changed

+2501
-2060
lines changed

grammar/php.y

+10-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,10 @@
8181
%token T_USE
8282
%token T_INSTEADOF
8383
%token T_GLOBAL
84-
%right T_STATIC T_ABSTRACT T_FINAL T_PRIVATE T_PROTECTED T_PUBLIC T_READONLY
84+
%token T_STATIC T_ABSTRACT T_FINAL T_PRIVATE T_PROTECTED T_PUBLIC T_READONLY
85+
%token T_PUBLIC_SET
86+
%token T_PROTECTED_SET
87+
%token T_PRIVATE_SET
8588
%token T_VAR
8689
%token T_UNSET
8790
%token T_ISSET
@@ -672,6 +675,9 @@ property_modifier:
672675
T_PUBLIC { $$ = Modifiers::PUBLIC; }
673676
| T_PROTECTED { $$ = Modifiers::PROTECTED; }
674677
| T_PRIVATE { $$ = Modifiers::PRIVATE; }
678+
| T_PUBLIC_SET { $$ = Modifiers::PUBLIC_SET; }
679+
| T_PROTECTED_SET { $$ = Modifiers::PROTECTED_SET; }
680+
| T_PRIVATE_SET { $$ = Modifiers::PRIVATE_SET; }
675681
| T_READONLY { $$ = Modifiers::READONLY; }
676682
;
677683

@@ -906,6 +912,9 @@ member_modifier:
906912
T_PUBLIC { $$ = Modifiers::PUBLIC; }
907913
| T_PROTECTED { $$ = Modifiers::PROTECTED; }
908914
| T_PRIVATE { $$ = Modifiers::PRIVATE; }
915+
| T_PUBLIC_SET { $$ = Modifiers::PUBLIC_SET; }
916+
| T_PROTECTED_SET { $$ = Modifiers::PROTECTED_SET; }
917+
| T_PRIVATE_SET { $$ = Modifiers::PRIVATE_SET; }
909918
| T_STATIC { $$ = Modifiers::STATIC; }
910919
| T_ABSTRACT { $$ = Modifiers::ABSTRACT; }
911920
| T_FINAL { $$ = Modifiers::FINAL; }

lib/PhpParser/Builder/Param.php

+22
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,28 @@ public function makeReadonly() {
122122
return $this;
123123
}
124124

125+
/**
126+
* Gives the promoted property private(set) visibility.
127+
*
128+
* @return $this The builder instance (for fluid interface)
129+
*/
130+
public function makePrivateSet() {
131+
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE_SET);
132+
133+
return $this;
134+
}
135+
136+
/**
137+
* Gives the promoted property protected(set) visibility.
138+
*
139+
* @return $this The builder instance (for fluid interface)
140+
*/
141+
public function makeProtectedSet() {
142+
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED_SET);
143+
144+
return $this;
145+
}
146+
125147
/**
126148
* Adds an attribute group.
127149
*

lib/PhpParser/Builder/Property.php

+22
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,28 @@ public function makeFinal() {
112112
return $this;
113113
}
114114

115+
/**
116+
* Gives the property private(set) visibility.
117+
*
118+
* @return $this The builder instance (for fluid interface)
119+
*/
120+
public function makePrivateSet() {
121+
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE_SET);
122+
123+
return $this;
124+
}
125+
126+
/**
127+
* Gives the property protected(set) visibility.
128+
*
129+
* @return $this The builder instance (for fluid interface)
130+
*/
131+
public function makeProtectedSet() {
132+
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED_SET);
133+
134+
return $this;
135+
}
136+
115137
/**
116138
* Sets default value for the property.
117139
*

lib/PhpParser/Modifiers.php

+25-12
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,13 @@ final class Modifiers {
1414
public const ABSTRACT = 16;
1515
public const FINAL = 32;
1616
public const READONLY = 64;
17+
public const PUBLIC_SET = 128;
18+
public const PROTECTED_SET = 256;
19+
public const PRIVATE_SET = 512;
1720

18-
public const VISIBILITY_MASK = 1 | 2 | 4;
21+
public const VISIBILITY_MASK = self::PUBLIC | self::PROTECTED | self::PRIVATE;
22+
23+
public const VISIBILITY_SET_MASK = self::PUBLIC_SET | self::PROTECTED_SET | self::PRIVATE_SET;
1924

2025
private const TO_STRING_MAP = [
2126
self::PUBLIC => 'public',
@@ -25,6 +30,9 @@ final class Modifiers {
2530
self::ABSTRACT => 'abstract',
2631
self::FINAL => 'final',
2732
self::READONLY => 'readonly',
33+
self::PUBLIC_SET => 'public(set)',
34+
self::PROTECTED_SET => 'protected(set)',
35+
self::PRIVATE_SET => 'private(set)',
2836
];
2937

3038
public static function toString(int $modifier): string {
@@ -34,15 +42,19 @@ public static function toString(int $modifier): string {
3442
return self::TO_STRING_MAP[$modifier];
3543
}
3644

45+
private static function isValidModifier(int $modifier): bool {
46+
$isPow2 = ($modifier & ($modifier - 1)) == 0 && $modifier != 0;
47+
return $isPow2 && $modifier <= self::PRIVATE_SET;
48+
}
49+
3750
/**
3851
* @internal
3952
*/
4053
public static function verifyClassModifier(int $a, int $b): void {
41-
foreach ([Modifiers::ABSTRACT, Modifiers::FINAL, Modifiers::READONLY] as $modifier) {
42-
if ($a & $modifier && $b & $modifier) {
43-
throw new Error(
44-
'Multiple ' . self::toString($modifier) . ' modifiers are not allowed');
45-
}
54+
assert(self::isValidModifier($b));
55+
if (($a & $b) != 0) {
56+
throw new Error(
57+
'Multiple ' . self::toString($b) . ' modifiers are not allowed');
4658
}
4759

4860
if ($a & 48 && $b & 48) {
@@ -54,15 +66,16 @@ public static function verifyClassModifier(int $a, int $b): void {
5466
* @internal
5567
*/
5668
public static function verifyModifier(int $a, int $b): void {
57-
if ($a & Modifiers::VISIBILITY_MASK && $b & Modifiers::VISIBILITY_MASK) {
69+
assert(self::isValidModifier($b));
70+
if (($a & Modifiers::VISIBILITY_MASK && $b & Modifiers::VISIBILITY_MASK) ||
71+
($a & Modifiers::VISIBILITY_SET_MASK && $b & Modifiers::VISIBILITY_SET_MASK)
72+
) {
5873
throw new Error('Multiple access type modifiers are not allowed');
5974
}
6075

61-
foreach ([Modifiers::ABSTRACT, Modifiers::STATIC, Modifiers::FINAL, Modifiers::READONLY] as $modifier) {
62-
if ($a & $modifier && $b & $modifier) {
63-
throw new Error(
64-
'Multiple ' . self::toString($modifier) . ' modifiers are not allowed');
65-
}
76+
if (($a & $b) != 0) {
77+
throw new Error(
78+
'Multiple ' . self::toString($b) . ' modifiers are not allowed');
6679
}
6780

6881
if ($a & 48 && $b & 48) {

lib/PhpParser/Node/Param.php

+21
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,25 @@ public function isPrivate(): bool {
8686
public function isReadonly(): bool {
8787
return (bool) ($this->flags & Modifiers::READONLY);
8888
}
89+
90+
/**
91+
* Whether the promoted property has explicit public(set) visibility.
92+
*/
93+
public function isPublicSet(): bool {
94+
return (bool) ($this->flags & Modifiers::PUBLIC_SET);
95+
}
96+
97+
/**
98+
* Whether the promoted property has explicit protected(set) visibility.
99+
*/
100+
public function isProtectedSet(): bool {
101+
return (bool) ($this->flags & Modifiers::PROTECTED_SET);
102+
}
103+
104+
/**
105+
* Whether the promoted property has explicit private(set) visibility.
106+
*/
107+
public function isPrivateSet(): bool {
108+
return (bool) ($this->flags & Modifiers::PRIVATE_SET);
109+
}
89110
}

lib/PhpParser/Node/Stmt/Property.php

+21
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,27 @@ public function isReadonly(): bool {
8080
return (bool) ($this->flags & Modifiers::READONLY);
8181
}
8282

83+
/**
84+
* Whether the property has explicit public(set) visibility.
85+
*/
86+
public function isPublicSet(): bool {
87+
return (bool) ($this->flags & Modifiers::PUBLIC_SET);
88+
}
89+
90+
/**
91+
* Whether the property has explicit protected(set) visibility.
92+
*/
93+
public function isProtectedSet(): bool {
94+
return (bool) ($this->flags & Modifiers::PROTECTED_SET);
95+
}
96+
97+
/**
98+
* Whether the property has explicit private(set) visibility.
99+
*/
100+
public function isPrivateSet(): bool {
101+
return (bool) ($this->flags & Modifiers::PRIVATE_SET);
102+
}
103+
83104
public function getType(): string {
84105
return 'Stmt_Property';
85106
}

lib/PhpParser/NodeDumper.php

+9
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,15 @@ protected function dumpFlags(int $flags): string {
185185
if ($flags & Modifiers::READONLY) {
186186
$strs[] = 'READONLY';
187187
}
188+
if ($flags & Modifiers::PUBLIC_SET) {
189+
$strs[] = 'PUBLIC_SET';
190+
}
191+
if ($flags & Modifiers::PROTECTED_SET) {
192+
$strs[] = 'PROTECTED_SET';
193+
}
194+
if ($flags & Modifiers::PRIVATE_SET) {
195+
$strs[] = 'PRIVATE_SET';
196+
}
188197

189198
if ($strs) {
190199
return implode(' | ', $strs) . ' (' . $flags . ')';

0 commit comments

Comments
 (0)