diff --git a/src/main/php/lang/ast/syntax/PHP.class.php b/src/main/php/lang/ast/syntax/PHP.class.php index a318d19..979e34f 100755 --- a/src/main/php/lang/ast/syntax/PHP.class.php +++ b/src/main/php/lang/ast/syntax/PHP.class.php @@ -1344,6 +1344,77 @@ public function type0($parse, $optional= false) { return isset($literal[$type]) ? new IsLiteral($type) : new IsValue($type); } + /** Parses property hooks block */ + private function hooks($parse) { + $hooks= []; + while ('}' !== $parse->token->value) { + if ('final' === $parse->token->value) { + $modifiers= ['final']; + $parse->forward(); + } else { + $modifiers= []; + } + + if ('&' === $parse->token->value) { + $byref= true; + $parse->forward(); + } else { + $byref= false; + } + + $hook= $parse->token->value; + $parse->forward(); + + if ('(' === $parse->token->value) { + $parse->forward(); + + if ('#[' === $parse->token->value) { + $parse->forward(); + $annotations= $this->annotations($parse, 'parameter annotations'); + } else { + $annotations= null; + } + + $line= $parse->token->line; + $type= $this->type($parse); + + $parse->forward(); + $param= $parse->token->value; + $parse->forward(); + + if ('=' === $parse->token->value) { + $parse->forward(); + $default= $this->expression($parse, 0); + } else { + $default= null; + } + + $parameter= new Parameter($param, $type, $default, false, false, null, $annotations, null, $line); + $parse->expecting(')', 'property hook parameters'); + } else { + $parameter= null; + } + + $line= $parse->token->line; + if ('=>' === $parse->token->value) { + $parse->forward(); + $expr= $this->expression($parse, 0); + $parse->expecting(';', 'property hook'); + } else if ('{' === $parse->token->value) { + $parse->forward(); + $expr= new Block($this->statements($parse), $line); + $parse->expecting('}', 'property hook'); + } else { + $parse->expecting(';', 'property hook'); + $expr= null; + } + + $hooks[$hook]= new Hook($modifiers, $hook, $expr, $byref, $parameter, $line); + } + + return $hooks; + } + private function properties($parse, &$body, $meta, $modifiers, $type) { $comment= $parse->comment; $parse->comment= null; @@ -1374,7 +1445,6 @@ private function properties($parse, &$body, $meta, $modifiers, $type) { $body[$lookup]= new Property($modifiers, $name, $type, $expr, $annotations, $comment, $line); - // Check for property hooks if (';' === $parse->token->value) { $parse->forward(); return; @@ -1385,78 +1455,12 @@ private function properties($parse, &$body, $meta, $modifiers, $type) { $parse->forward(); $expr= $this->expression($parse, 0); $parse->expecting(';', 'property hook'); - - $body[$lookup]->hooks['get']= new Hook([], 'get', $expr, false, null, $line); + $body[$lookup]->hooks= ['get' => new Hook([], 'get', $expr, false, null, $line)]; return; } else if ('{' === $parse->token->value) { $parse->forward(); - - while ('}' !== $parse->token->value) { - if ('final' === $parse->token->value) { - $modifiers= ['final']; - $parse->forward(); - } else { - $modifiers= []; - } - - if ('&' === $parse->token->value) { - $byref= true; - $parse->forward(); - } else { - $byref= false; - } - - $hook= $parse->token->value; - $parse->forward(); - - if ('(' === $parse->token->value) { - $parse->forward(); - - if ('#[' === $parse->token->value) { - $parse->forward(); - $annotations= $this->annotations($parse, 'parameter annotations'); - } else { - $annotations= null; - } - - $line= $parse->token->line; - $type= $this->type($parse); - - $parse->forward(); - $param= $parse->token->value; - $parse->forward(); - - if ('=' === $parse->token->value) { - $parse->forward(); - $default= $this->expression($parse, 0); - } else { - $default= null; - } - - $parameter= new Parameter($param, $type, $default, false, false, null, $annotations, null, $line); - $parse->expecting(')', 'property hook parameters'); - } else { - $parameter= null; - } - - $line= $parse->token->line; - if ('=>' === $parse->token->value) { - $parse->forward(); - $expr= $this->expression($parse, 0); - $parse->expecting(';', 'property hook'); - } else if ('{' === $parse->token->value) { - $parse->forward(); - $expr= new Block($this->statements($parse), $line); - $parse->expecting('}', 'property hook'); - } else if (';' === $parse->token->value) { - $parse->forward(); - $expr= null; - } - - $body[$lookup]->hooks[$hook]= new Hook($modifiers, $hook, $expr, $byref, $parameter, $line); - } - - $parse->forward(); + $body[$lookup]->hooks= $this->hooks($parse); + $parse->expecting('}', 'property hook'); return; } else { $parse->expecting(';, , or {', 'property'); @@ -1573,7 +1577,30 @@ private function parameters($parse) { $parse->forward(); $default= $this->expression($parse, 0); } - $parameters[]= new Parameter($name, $type, $default, $byref, $variadic, $promote, $annotations, null, $line); + + // Only promoted parameters can have hooks + if ($promote) { + $property= new Property($promote, $name, $type, null, null, null, $line); + if ('{' === $parse->token->value) { + $parse->forward(); + $property->hooks= $this->hooks($parse); + $parse->expecting('}', 'property hook'); + } + } else { + $property= null; + } + + $parameters[]= new Parameter( + $name, + $type, + $default, + $byref, + $variadic, + $property, + $annotations, + null, + $line + ); if (')' === $parse->token->value) { break; diff --git a/src/test/php/lang/ast/unittest/parse/MembersTest.class.php b/src/test/php/lang/ast/unittest/parse/MembersTest.class.php index da3926f..a57b331 100755 --- a/src/test/php/lang/ast/unittest/parse/MembersTest.class.php +++ b/src/test/php/lang/ast/unittest/parse/MembersTest.class.php @@ -230,12 +230,24 @@ public function abstract_property_with_hook() { #[Test] public function promoted_parameter() { $class= new ClassDeclaration([], new IsValue('\\A'), null, [], [], null, null, self::LINE); - $parameter= new Parameter('name', new IsLiteral('string'), null, false, false, ['public'], null, null, self::LINE); + $promoted= new Property(['public'], 'name', new IsLiteral('string'), null, null, null, self::LINE); + $parameter= new Parameter('name', new IsLiteral('string'), null, false, false, $promoted, null, null, self::LINE); $class->declare(new Method(['public'], '__construct', new Signature([$parameter], null, false, self::LINE), $this->empty, null, null, self::LINE)); $this->assertParsed([$class], 'class A { public function __construct(public string $name) { } }'); } + #[Test] + public function promoted_parameter_with_hook() { + $class= new ClassDeclaration([], new IsValue('\\A'), null, [], [], null, null, self::LINE); + $promoted= new Property(['public'], 'name', new IsLiteral('string'), null, null, null, self::LINE); + $promoted->hooks['get']= new Hook([], 'get', new Literal('"Hello"', self::LINE), false, null, self::LINE); + $parameter= new Parameter('name', new IsLiteral('string'), null, false, false, $promoted, null, null, self::LINE); + $class->declare(new Method(['public'], '__construct', new Signature([$parameter], null, false, self::LINE), $this->empty, null, null, self::LINE)); + + $this->assertParsed([$class], 'class A { public function __construct(public string $name { get => "Hello"; }) { } }'); + } + #[Test] public function instance_property_access() { $this->assertParsed( @@ -423,7 +435,8 @@ public function asymmetric_property() { #[Test] public function asymmetric_property_as_constructor_argument() { - $params= [new Parameter('a', new IsLiteral('int'), null, false, false, ['private(set)'], null, null, self::LINE)]; + $promoted= new Property(['private(set)'], 'a', new IsLiteral('int'), null, null, null, self::LINE); + $params= [new Parameter('a', new IsLiteral('int'), null, false, false, $promoted, null, null, self::LINE)]; $class= new ClassDeclaration([], new IsValue('\\A'), null, [], [], null, null, self::LINE); $class->declare(new Method(['public'], '__construct', new Signature($params, null, false, self::LINE), $this->empty, null, null, self::LINE));