From 05018b8ebaaba6c96a772b257f0f365c7b52079e Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Sun, 15 May 2022 21:05:50 +0200 Subject: [PATCH] add checkClassModifier() to separate from validation of class members --- grammar/php7.y | 2 +- lib/PhpParser/Node/Stmt/Class_.php | 21 +++++++++++++++++++++ lib/PhpParser/Parser/Php7.php | 2 +- lib/PhpParser/ParserAbstract.php | 9 +++++++++ test/code/parser/stmt/class/modifier.test | 2 +- 5 files changed, 33 insertions(+), 3 deletions(-) diff --git a/grammar/php7.y b/grammar/php7.y index d20e069af5..17db19f226 100644 --- a/grammar/php7.y +++ b/grammar/php7.y @@ -387,7 +387,7 @@ class_entry_type: class_modifiers: class_modifier { $$ = $1; } - | class_modifiers class_modifier { $this->checkModifier($1, $2, #2); $$ = $1 | $2; } + | class_modifiers class_modifier { $this->checkClassModifier($1, $2, #2); $$ = $1 | $2; } ; class_modifier: diff --git a/lib/PhpParser/Node/Stmt/Class_.php b/lib/PhpParser/Node/Stmt/Class_.php index d5b39b5489..52ed6c6cd6 100644 --- a/lib/PhpParser/Node/Stmt/Class_.php +++ b/lib/PhpParser/Node/Stmt/Class_.php @@ -81,6 +81,27 @@ public function isAnonymous() : bool { return null === $this->name; } + /** + * @internal + */ + public static function verifyClassModifier($a, $b) { + if ($a & self::MODIFIER_ABSTRACT && $b & self::MODIFIER_ABSTRACT) { + throw new Error('Multiple abstract modifiers are not allowed'); + } + + if ($a & self::MODIFIER_FINAL && $b & self::MODIFIER_FINAL) { + throw new Error('Multiple final modifiers are not allowed'); + } + + if ($a & self::MODIFIER_READONLY && $b & self::MODIFIER_READONLY) { + throw new Error('Multiple readonly modifiers are not allowed'); + } + + if ($a & 48 && $b & 48) { + throw new Error('Cannot use the final modifier on an abstract class'); + } + } + /** * @internal */ diff --git a/lib/PhpParser/Parser/Php7.php b/lib/PhpParser/Parser/Php7.php index 8bac43c624..3dfcaa744d 100644 --- a/lib/PhpParser/Parser/Php7.php +++ b/lib/PhpParser/Parser/Php7.php @@ -1653,7 +1653,7 @@ protected function initReduceCallbacks() { $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 215 => function ($stackPos) { - $this->checkModifier($this->semStack[$stackPos-(2-1)], $this->semStack[$stackPos-(2-2)], $stackPos-(2-2)); $this->semValue = $this->semStack[$stackPos-(2-1)] | $this->semStack[$stackPos-(2-2)]; + $this->checkClassModifier($this->semStack[$stackPos-(2-1)], $this->semStack[$stackPos-(2-2)], $stackPos-(2-2)); $this->semValue = $this->semStack[$stackPos-(2-1)] | $this->semStack[$stackPos-(2-2)]; }, 216 => function ($stackPos) { $this->semValue = Stmt\Class_::MODIFIER_ABSTRACT; diff --git a/lib/PhpParser/ParserAbstract.php b/lib/PhpParser/ParserAbstract.php index 5ee5a64bbd..d485d78de4 100644 --- a/lib/PhpParser/ParserAbstract.php +++ b/lib/PhpParser/ParserAbstract.php @@ -875,6 +875,15 @@ protected function createCommentNopAttributes(array $comments) { return $attributes; } + protected function checkClassModifier($a, $b, $modifierPos) { + try { + Class_::verifyClassModifier($a, $b); + } catch (Error $error) { + $error->setAttributes($this->getAttributesAt($modifierPos)); + $this->emitError($error); + } + } + protected function checkModifier($a, $b, $modifierPos) { // Jumping through some hoops here because verifyModifier() is also used elsewhere try { diff --git a/test/code/parser/stmt/class/modifier.test b/test/code/parser/stmt/class/modifier.test index 93d3537f05..0d26cb1674 100644 --- a/test/code/parser/stmt/class/modifier.test +++ b/test/code/parser/stmt/class/modifier.test @@ -233,7 +233,7 @@ array(