Repository navigation
zend_ast: Fix FCC in trait const expressions resolving to the first using class #25083
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| --TEST-- | ||
| FCC for self:: static method in trait default argument is resolved per using class | ||
| --FILE-- | ||
| <?php | ||
|
|
||
| trait T { | ||
| public static function make($f = self::who(...)) { return $f(); } | ||
| } | ||
| class A { use T; public static function who() { return 'A'; } } | ||
| class B { use T; public static function who() { return 'B'; } } | ||
|
|
||
| echo A::make(), PHP_EOL; | ||
| echo B::make(), PHP_EOL; | ||
|
|
||
| ?> | ||
| --EXPECT-- | ||
| A | ||
| B |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| --TEST-- | ||
| FCC for parent:: static method in trait default argument is resolved per using class | ||
| --FILE-- | ||
| <?php | ||
|
|
||
| class P1 { public static function who() { return 'P1'; } } | ||
| class P2 { public static function who() { return 'P2'; } } | ||
| trait T { | ||
| public static function make($f = parent::who(...)) { return $f(); } | ||
| } | ||
| class A extends P1 { use T; } | ||
| class B extends P2 { use T; } | ||
|
|
||
| echo A::make(), PHP_EOL; | ||
| echo B::make(), PHP_EOL; | ||
|
|
||
| ?> | ||
| --EXPECT-- | ||
| P1 | ||
| P2 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| --TEST-- | ||
| FCC for self:: static method in trait constant and property default is resolved per using class | ||
| --FILE-- | ||
| <?php | ||
|
|
||
| trait T { | ||
| const C = self::who(...); | ||
| public $p = self::who(...); | ||
| } | ||
| class A { use T; public static function who() { return 'A'; } } | ||
| class B { use T; public static function who() { return 'B'; } } | ||
|
|
||
| echo (A::C)(), PHP_EOL; | ||
| echo (B::C)(), PHP_EOL; | ||
| echo ((new A)->p)(), PHP_EOL; | ||
| echo ((new B)->p)(), PHP_EOL; | ||
|
|
||
| ?> | ||
| --EXPECT-- | ||
| A | ||
| B | ||
| A | ||
| B |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| --TEST-- | ||
| FCC for self:: private static method in trait default argument does not leak to other using classes | ||
| --FILE-- | ||
| <?php | ||
|
|
||
| trait T { | ||
| public static function make($f = self::who(...)) { return $f(); } | ||
| } | ||
| class A { use T; private static function who() { return 'A'; } } | ||
| class B { use T; } | ||
|
|
||
| echo A::make(), PHP_EOL; | ||
| try { | ||
| B::make(); | ||
| } catch (Error $e) { | ||
| echo $e::class, ": ", $e->getMessage(), PHP_EOL; | ||
| } | ||
|
|
||
| ?> | ||
| --EXPECT-- | ||
| A | ||
| Error: Call to undefined method B::who() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| --TEST-- | ||
| FCC for named class private static method in trait default argument checks visibility per using class | ||
| --FILE-- | ||
| <?php | ||
|
|
||
| trait T { | ||
| public static function make($f = A::who(...)) { return $f(); } | ||
| } | ||
| class A { use T; private static function who() { return 'A'; } } | ||
| class B { use T; } | ||
|
|
||
| echo A::make(), PHP_EOL; | ||
| try { | ||
| B::make(); | ||
| } catch (Error $e) { | ||
| echo $e::class, ": ", $e->getMessage(), PHP_EOL; | ||
| } | ||
|
|
||
| ?> | ||
| --EXPECT-- | ||
| A | ||
| Error: Call to private method A::who() from scope B | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| --TEST-- | ||
| FCC for named class public static method in trait default argument is shared across using classes | ||
| --FILE-- | ||
| <?php | ||
|
|
||
| trait T { | ||
| public static function make($f = A::who(...)) { return $f(); } | ||
| } | ||
| class A { use T; public static function who() { return 'A'; } } | ||
| class B { use T; } | ||
|
|
||
| echo A::make(), PHP_EOL; | ||
| echo B::make(), PHP_EOL; | ||
| echo A::make(), PHP_EOL; | ||
|
|
||
| ?> | ||
| --EXPECT-- | ||
| A | ||
| A | ||
| A |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1148,7 +1148,11 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner( | |
| } | ||
| } | ||
|
|
||
| ZEND_MAP_PTR_SET(fcc_ast->fptr, fptr); | ||
| /* The AST may be shared between scopes (e.g. via traits), so only cache scope-independent results. */ | ||
| if ((ast->child[0]->attr >> ZEND_CONST_EXPR_NEW_FETCH_TYPE_SHIFT) == ZEND_FETCH_CLASS_DEFAULT | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This also disables caching for |
||
| && (fptr->common.fn_flags & ZEND_ACC_PUBLIC)) { | ||
| ZEND_MAP_PTR_SET(fcc_ast->fptr, fptr); | ||
| } | ||
| } | ||
|
|
||
| break; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add a case for the branch that still caches across scopes? A trait with a named public-method default (
A::who(...)) used by two classes, both returningA. 005 covers named+private; the named+public cached path isn't exercised.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense. test case added.