Skip to content

Commit

Permalink
[8.4] Add support for asymmetric visibility modifiers
Browse files Browse the repository at this point in the history
Represented using new PRIVATE_SET, PROTECTED_SET and PUBLIC_SET
bits in Modifiers.

RFC: https://wiki.php.net/rfc/asymmetric-visibility-v2
  • Loading branch information
nikic committed Sep 5, 2024
1 parent 54139ca commit b493c51
Show file tree
Hide file tree
Showing 16 changed files with 2,501 additions and 2,060 deletions.
11 changes: 10 additions & 1 deletion grammar/php.y
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,10 @@
%token T_USE
%token T_INSTEADOF
%token T_GLOBAL
%right T_STATIC T_ABSTRACT T_FINAL T_PRIVATE T_PROTECTED T_PUBLIC T_READONLY
%token T_STATIC T_ABSTRACT T_FINAL T_PRIVATE T_PROTECTED T_PUBLIC T_READONLY
%token T_PUBLIC_SET
%token T_PROTECTED_SET
%token T_PRIVATE_SET
%token T_VAR
%token T_UNSET
%token T_ISSET
Expand Down Expand Up @@ -672,6 +675,9 @@ property_modifier:
T_PUBLIC { $$ = Modifiers::PUBLIC; }
| T_PROTECTED { $$ = Modifiers::PROTECTED; }
| T_PRIVATE { $$ = Modifiers::PRIVATE; }
| T_PUBLIC_SET { $$ = Modifiers::PUBLIC_SET; }
| T_PROTECTED_SET { $$ = Modifiers::PROTECTED_SET; }
| T_PRIVATE_SET { $$ = Modifiers::PRIVATE_SET; }
| T_READONLY { $$ = Modifiers::READONLY; }
;

Expand Down Expand Up @@ -906,6 +912,9 @@ member_modifier:
T_PUBLIC { $$ = Modifiers::PUBLIC; }
| T_PROTECTED { $$ = Modifiers::PROTECTED; }
| T_PRIVATE { $$ = Modifiers::PRIVATE; }
| T_PUBLIC_SET { $$ = Modifiers::PUBLIC_SET; }
| T_PROTECTED_SET { $$ = Modifiers::PROTECTED_SET; }
| T_PRIVATE_SET { $$ = Modifiers::PRIVATE_SET; }
| T_STATIC { $$ = Modifiers::STATIC; }
| T_ABSTRACT { $$ = Modifiers::ABSTRACT; }
| T_FINAL { $$ = Modifiers::FINAL; }
Expand Down
22 changes: 22 additions & 0 deletions lib/PhpParser/Builder/Param.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,28 @@ public function makeReadonly() {
return $this;
}

/**
* Gives the promoted property private(set) visibility.
*
* @return $this The builder instance (for fluid interface)
*/
public function makePrivateSet() {
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE_SET);

return $this;
}

/**
* Gives the promoted property protected(set) visibility.
*
* @return $this The builder instance (for fluid interface)
*/
public function makeProtectedSet() {
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED_SET);

return $this;
}

/**
* Adds an attribute group.
*
Expand Down
22 changes: 22 additions & 0 deletions lib/PhpParser/Builder/Property.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,28 @@ public function makeFinal() {
return $this;
}

/**
* Gives the property private(set) visibility.
*
* @return $this The builder instance (for fluid interface)
*/
public function makePrivateSet() {
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE_SET);

return $this;
}

/**
* Gives the property protected(set) visibility.
*
* @return $this The builder instance (for fluid interface)
*/
public function makeProtectedSet() {
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED_SET);

return $this;
}

/**
* Sets default value for the property.
*
Expand Down
37 changes: 25 additions & 12 deletions lib/PhpParser/Modifiers.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,13 @@ final class Modifiers {
public const ABSTRACT = 16;
public const FINAL = 32;
public const READONLY = 64;
public const PUBLIC_SET = 128;
public const PROTECTED_SET = 256;
public const PRIVATE_SET = 512;

public const VISIBILITY_MASK = 1 | 2 | 4;
public const VISIBILITY_MASK = self::PUBLIC | self::PROTECTED | self::PRIVATE;

public const VISIBILITY_SET_MASK = self::PUBLIC_SET | self::PROTECTED_SET | self::PRIVATE_SET;

private const TO_STRING_MAP = [
self::PUBLIC => 'public',
Expand All @@ -25,6 +30,9 @@ final class Modifiers {
self::ABSTRACT => 'abstract',
self::FINAL => 'final',
self::READONLY => 'readonly',
self::PUBLIC_SET => 'public(set)',
self::PROTECTED_SET => 'protected(set)',
self::PRIVATE_SET => 'private(set)',
];

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

private static function isValidModifier(int $modifier): bool {
$isPow2 = ($modifier & ($modifier - 1)) == 0 && $modifier != 0;
return $isPow2 && $modifier <= self::PRIVATE_SET;
}

/**
* @internal
*/
public static function verifyClassModifier(int $a, int $b): void {
foreach ([Modifiers::ABSTRACT, Modifiers::FINAL, Modifiers::READONLY] as $modifier) {
if ($a & $modifier && $b & $modifier) {
throw new Error(
'Multiple ' . self::toString($modifier) . ' modifiers are not allowed');
}
assert(self::isValidModifier($b));
if (($a & $b) != 0) {
throw new Error(
'Multiple ' . self::toString($b) . ' modifiers are not allowed');
}

if ($a & 48 && $b & 48) {
Expand All @@ -54,15 +66,16 @@ public static function verifyClassModifier(int $a, int $b): void {
* @internal
*/
public static function verifyModifier(int $a, int $b): void {
if ($a & Modifiers::VISIBILITY_MASK && $b & Modifiers::VISIBILITY_MASK) {
assert(self::isValidModifier($b));
if (($a & Modifiers::VISIBILITY_MASK && $b & Modifiers::VISIBILITY_MASK) ||
($a & Modifiers::VISIBILITY_SET_MASK && $b & Modifiers::VISIBILITY_SET_MASK)
) {
throw new Error('Multiple access type modifiers are not allowed');
}

foreach ([Modifiers::ABSTRACT, Modifiers::STATIC, Modifiers::FINAL, Modifiers::READONLY] as $modifier) {
if ($a & $modifier && $b & $modifier) {
throw new Error(
'Multiple ' . self::toString($modifier) . ' modifiers are not allowed');
}
if (($a & $b) != 0) {
throw new Error(
'Multiple ' . self::toString($b) . ' modifiers are not allowed');
}

if ($a & 48 && $b & 48) {
Expand Down
21 changes: 21 additions & 0 deletions lib/PhpParser/Node/Param.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,25 @@ public function isPrivate(): bool {
public function isReadonly(): bool {
return (bool) ($this->flags & Modifiers::READONLY);
}

/**
* Whether the promoted property has explicit public(set) visibility.
*/
public function isPublicSet(): bool {
return (bool) ($this->flags & Modifiers::PUBLIC_SET);
}

/**
* Whether the promoted property has explicit protected(set) visibility.
*/
public function isProtectedSet(): bool {
return (bool) ($this->flags & Modifiers::PROTECTED_SET);
}

/**
* Whether the promoted property has explicit private(set) visibility.
*/
public function isPrivateSet(): bool {
return (bool) ($this->flags & Modifiers::PRIVATE_SET);
}
}
21 changes: 21 additions & 0 deletions lib/PhpParser/Node/Stmt/Property.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,27 @@ public function isReadonly(): bool {
return (bool) ($this->flags & Modifiers::READONLY);
}

/**
* Whether the property has explicit public(set) visibility.
*/
public function isPublicSet(): bool {
return (bool) ($this->flags & Modifiers::PUBLIC_SET);
}

/**
* Whether the property has explicit protected(set) visibility.
*/
public function isProtectedSet(): bool {
return (bool) ($this->flags & Modifiers::PROTECTED_SET);
}

/**
* Whether the property has explicit private(set) visibility.
*/
public function isPrivateSet(): bool {
return (bool) ($this->flags & Modifiers::PRIVATE_SET);
}

public function getType(): string {
return 'Stmt_Property';
}
Expand Down
9 changes: 9 additions & 0 deletions lib/PhpParser/NodeDumper.php
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,15 @@ protected function dumpFlags(int $flags): string {
if ($flags & Modifiers::READONLY) {
$strs[] = 'READONLY';
}
if ($flags & Modifiers::PUBLIC_SET) {
$strs[] = 'PUBLIC_SET';
}
if ($flags & Modifiers::PROTECTED_SET) {
$strs[] = 'PROTECTED_SET';
}
if ($flags & Modifiers::PRIVATE_SET) {
$strs[] = 'PRIVATE_SET';
}

if ($strs) {
return implode(' | ', $strs) . ' (' . $flags . ')';
Expand Down
Loading

0 comments on commit b493c51

Please sign in to comment.