-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[RFC][Ast][Routing] Use Ast to generate the UrlGenerator #19555
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
Conversation
1abbd58
to
b039808
Compare
* @return Expr | ||
*/ | ||
public static function value($value) | ||
{ |
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.
Why not directly convert the value to ast with the parser, by using var_export (as all the logic is already handled) ?
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.
You mean passing the raw value to the Node
s directly ? This is managed by the Builder
s but not by the Expr
s.
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.
I forget to mention to use var_export, that's what i'm using here : https://github.com/jolicode/jane-openapi/blob/65fe879139b8eaad18d132e850cebadd85f88dca/src/Generator/Parameter/NonBodyParameterGenerator.php#L67
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.
I'm not sure it would support well every edge cases such as non-associative array (var_export
always prints the keys if i'm not wrong).
BTW this part of the code is extracted from nikic's library.
b039808
to
d1f1588
Compare
* | ||
* @author Joel Wurtz <joel.wurtz@gmail.com> | ||
*/ | ||
class AstGeneratorChain implements AstGeneratorInterface |
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.
Why this class is open for inheritance (not marked as final
)?
Same question about UrlGeneratorGenerator
, PhpMatcherGenerator
, UniqueVariableScope
.
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.
For this class and UniqueVariableScope
i agree it would make sense even if there aren't that much final classes in symfony.
But for the routing generators i would like to allow the user to easily hook into them and it could pass by inheritance.
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.
But for the routing generators i would like to allow the user to easily hook into them and it could pass by inheritance.
How? Those classes have only private
members and 2 public methods. And those 2 public methods can be easily changed with composition if developer really needs it.
This is weird Symfony internal rule: to keep members private
by default, but in the same time do not require final
: there is no point to open them for inheritance then.
If you find valid reason to extend the classes, you can provide sane extension point(s) instead of inheritance: strategies, events, hooks, etc.
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.
For now they are private but it may change. But you're right the class should be final as long as there are no extensions possible, i'll change ASAP.
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.
For now they are private but it may change.
Sure, but instead of changing private
properties to protected
, you probably can introduce better extension point.
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.
Thx. I wasnt really opinionated on it, yet. https://ocramius.github.io/blog/when-to-declare-classes-final/ is also a nice read. Anyway, it looks good practice to me.. not sure if it's common enough (yet) for symfony though.
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.
@Ener-Getick then maybe it would be better to lock them (final
and @internal
) first no?
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.
Once you fix it, our comments will disappear :)
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.
@theofidry i'm using my phone tonight :P
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.
Good thing, you shouldn't work a saturday evening anyway ;)
@theofidry the component itself is more or less ready but it isn't especially useful if we don't use it in symfony. |
Then #17516 could be merged somehow no? It would need to be locked first ( But I'm saying that to simplify this PR, if you feel it's not needed it's alright. |
Some parts of #17516 aren't ready to be merged and would make sf tests fail so i don't think it would be accepted, even tagged as |
->addStmt($this->generateMatchMethod($object, $context)); | ||
} | ||
|
||
private function generateMatchMethod($object, array $context) |
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.
Both arguments are unused.
While the AstGeneratorInterface make sense for generating normalizer and types (as we have different kinds of objects and metadata), it does not make sense here like @unkind said, as we always have the same input. Given this, the ASTGenerator Component does not add a value in this context (just use php-parser). I have begin the work on the AstGeneratorComponent for the purpose of generating better and faster normalizer like in #17516 Then the idea comes to also apply AST generation to components already generating code (like the router or the DIC). Seeing this pr from @Ener-Getick and comments made me realise that we don't need to do an ASTGenerator Component, but instead separate the generation of each component into itself. However this does not mean we should stop working on ASTGenerator Component, instead we must renaming it to ASTComponent which will provides utility class that works on a AST (a.k.a. NodeList):
Also we should provide a unified way of writing AST into the differents components (not speaking about interface, but more about the choices and being consistent of the way of doing across components) |
@joelwurtz I agree the ast component would indeed be more valuable like that. |
No need for a printer there is already php cs fixer :) |
@joelwurtz it's not part of symfony and won't be applied to the code cached anyway so i think it makes sense to have our own printer (based on the one included in class AstDumper
{
public function __construct(PrettyPrinterAbstract $basePrinter = null);
public function dump(NodeList $nodes);
} |
d1f1588
to
90705e8
Compare
90705e8
to
791a955
Compare
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function generate(array $options = array()) |
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.
Ironically, I'd swap options and RouteCollection
.
We have RouteCollection
and we want to generate AST for this route collection. It looks natural to pass it as an argument of the generate($routeCollection)
method, doesn't it?
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.
I reused the current signature. I have no strong opinion about passing the route collection but it looks weird to me to not know the name of the class that will be generated (most of the time the caller doesn't instantiate the class it calls).
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.
By analogy, why do you pass NodeList
in the dump
method instead of constructor in example above (AstDumper
)? Does it look weird that you don't know printer parameters that will be used?
You can pass options as 2nd and 3rd arguments if you think you need to pass them explicitly:
public function generate(
RouteCollection $routeCollection,
string $class = 'ProjectUrlGenerator',
string $baseClass = UrlGenerator::class
)
If you take a look on your interface below, it just says that it generates AST for routes, but it wonders me which routes description is talking about?
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.
For me this dumper can be used to print different files (you can use the same config to print them without combining them) this is why i think the ast should be passed in the dump method.
About the generate
method that's true, i only have to find how it is used exactly in the framework to ensure we can prefer explicitness without creating issues.
I don't see much value in using an AST-based code generator here. |
@nicolas-grekas i was thinking allowing the user to hook into the process and eventually change the So maybe after all that's better to begin with something where ast brings a clear improvement and then we will see if it should be extanded to other components or not. |
This PR applies the bases proposed in #17516 to the Routing component.
Generating a
Normalizer
is complicated (i think that's worth it but that's not ideal to introduce a new component) so I decided to try to generate the cached matcher/generator of the routing component with AST.I created an util class to easily convert simple php code to an ast node (convenient when you have a part of your code which never changes dynamically).
I didn't finish this PR because I'm waiting for the core team approval.
The main advantages of using ast I see:
ping @joelwurtz