Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork9.7k
[TwigBundle] Fix bug where namespaced paths don't take parent bundles in account#19586
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
[TwigBundle] Fix bug where namespaced paths don't take parent bundles in account#19586
Uh oh!
There was an error while loading.Please reload this page.
Conversation
7308a17 to86c9111Comparejakzal commentedAug 10, 2016
We'll need a test case for this. |
b3d4a15 to4ddc9edComparewesleylancel commentedAug 10, 2016
@jakzal I modified the TwigExtensionTest to include a test case for this situation. |
4ddc9ed to57eceb7Comparewesleylancel commentedAug 10, 2016
AppVeyor ran on an outdated commit, might have to be restarted. |
| if (is_dir($dir)) { | ||
| $this->addTwigPath($twigFilesystemLoaderDefinition,$dir,$bundle); | ||
| $classMock =new$class(); |
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.
This is not really a mock :) what about$object or just$bundle? However im not sure we should couple this here, this way. But i guess for now we dont have much choice...
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'll rename the variable. Don't think there are any options to retrieve the parent in this situation.
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.
Maybe you could callregisterBundles once =/ Basicallynew $class() is somewhat fragile.
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 agree, and this won't work with a third level (ChildChildTwigBundle). How would you recommend callingregisterBundles here? Is there any way to reach the kernel?
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.
kernel service is out of scope, so i guess not. Maybe you can kick in atTwigBundle::createContainerExtension, without breaking BC.
This is somewhat related to#18563 in terms of improving the ecosystem.
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.
What about using reflection andnewInstanceWithoutConstructor() here?
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.
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.
That's true, but the only ways to solve that issue completely would be to either pass bundles to extension classes or to write some logic that determines the paths during cache warmup.
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.
Couldn't a parameter returning the bundle map be envisaged?
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.
The issue is that you still would not have access to actual instances of the bundles (so this would not solve#19586 (comment)).
xabbuh commentedDec 28, 2016
@wesleylancel Do you think you can finish this one? |
wesleylancel commentedDec 28, 2016
@xabbuh: I wish, as this is something that would be extremely useful for us. But, as pointed out by@ro0NL, bundles can extend infinitely A > B > C > D. My PR currently only checks for one level of extending. Ideally, I would be able to access the bundle map that is part of the kernel, but that's not available at this stage yet. If you could point me towards a possible solution I'd be happy to adjust my code accordingly. |
ro0NL commentedDec 28, 2016 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Seeing the related ticket this goes back to 2013. And quite some issues are involved. I've not experienced it myself, but the template problem seems real. Either way allowing kernel introspection would beextremely useful for everybody. Preferably with an immutable kernel. Practically we can movethis a few lines up. Relying on a synthetic service before compilation. We also can expose bundles thru parameters, we only need stringish values here. But mayve@ogizanagi has more idea's? As i also liked his approach with a contextual builder. Maybe it can be simplified. |
ogizanagi commentedDec 28, 2016
Unfortunately#19646 was my only attempt to provide a naive feature for sharing some contextual information while the container is building, avoiding the need to rely on pseudo tmp synthetic services. Along with the As you suggested, maybe exposing a simple representation of the bundle map and bundles informations in a kernel parameter could be the way. But I don't remember all the ins and outs and if it was easily feasible at this point. :/ |
ro0NL commentedDec 28, 2016 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Agree 👍 I feel the same about my own attempts now. Eventually having a builder with context can be done in userland easily. But it doesnt solve it for SF. If there arent any other insights, i'd say go for |
chalasr commentedDec 29, 2016
Not sure it's doable. Putting
|
ro0NL commentedDec 29, 2016 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
It's a stringish map, that's actually cached :) we're not passing bundle instances around here. Something like that i guess. I mean.. this would lead to a nested |
ro0NL commentedDec 29, 2016 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Ie. looking at the issues, im not really sure we need true bundle instances here. Only metadata from bundle introspection. To build things properly. I truly dont hope we're looking for ways to do this since 2013 😆 as it would definitely require the |
This makes the template compatible with projects disabling the Templatingcomponent in recent Symfony versions.
xabbuh commentedJan 6, 2017
👍 I checked this with the issues described inFriendsOfSymfony/FOSUserBundle#2376 andFriendsOfSymfony/FOSUserBundle#2378 and I can confirm that this patch fixes them. Status: Reviewed |
maxime-pasquier commentedJan 6, 2017
@xabbuh great news! Could you tell us on which branches this patch will be applied ? |
chalasr commentedJan 6, 2017
@Kmelia Once merged, this will be available in the next patch releases of each maintained branch (i.e. from 2.7 to 3.2) and the current development version (3.3). |
| } | ||
| if (null !==$bundle['parent']) { | ||
| $this->prependParentPaths($twigFilesystemLoaderDefinition,$container,$name,$bundle['parent']); |
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.
Does this handle a chain of parents ?
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.
@stof: Yes, this handles that. Should be covered with the changed test.
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.
prependParentPaths was confusing at first. Maybe keep actual path registration explicit inload()
while ($parent) {// add parent}// add self
Basically we dont need any path prepending or recursion, only adding paths in the right calling order.
Maybe utilize withaddBundlePaths or so.
| } | ||
| if (null !==$bundle['parent']) { | ||
| $this->prependParentPaths($twigFilesystemLoaderDefinition,$container,$name,$bundle['parent']); |
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.
Prepending paths looks wrong to me here, as it would overwrite user-configured paths for the same namespace too.
What you should do is collecting the child bundles for each bundle, and then adding paths for child bundles before adding the path of the bundle itself
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.
@stof: Did not now about that. That'll require some change to adding the normal paths too, I'll see how to best fix this. Is there an easy way to cover this in the test?
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.
Add a path in the user-configured paths for the same namespace, and ensure that it ends up as the first path in the loader config for this namespace
maxime-pasquier commentedJan 6, 2017
@chalasr thanks! |
| array(__DIR__.'/Fixtures/Bundle/ChildTwigBundle/Resources/views','Twig'), | ||
| array(__DIR__.'/Fixtures/Bundle/ChildChildTwigBundle/Resources/views','ChildTwig'), | ||
| array(__DIR__.'/Fixtures/Bundle/ChildChildTwigBundle/Resources/views','Twig'), | ||
| ),$prependPaths); |
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.
The way these tests are written does not ensure that paths are added in the right order for each namespace. there is nothing guaranteeing that the combinations of appended paths and prepended paths is actually giving us the right priority.
What we should do to have reliable tests is rebuilding the internal structure ofTwig_Loader_Filesystem::$path (i.e. the result of the configuration) and ensuring that it looks good for each namespace.
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.
@stof: I think whatever gets prepended last will have the most priority. If the array above is in this order, the correct path will end up on top. However, this doesn't keep those user-configured paths in mind. I'm not sure where these get added to the loader, but if they get prepended after the bundle paths get prepended, it should work all right? Not sure if that's testable in the existing test though.
If the Twig internals of that loader do need to be changed, I don't think this can be merged before that is resolved?
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.
@stof: What if we move these lineshttps://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php#L84-L90 to herehttps://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php#L109 and change the method call toprependPath instead ofaddPath? This should make sure that user-defined paths from config take precedence over anything else.
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.
the issue is that the way the test is written does not allow to quickly validate what the final order will be when looking at it. This is why I think the test should not be written this way. We don't want to assert 2 separate lists, as we care about a single list (the one being used at runtime).
thus, the test is even confusing when looking at it, because the list ofprependPath here is in reverse order compared to the order of paths in the loader (as prepending adds at the beginning while you append in$prependPaths), making it even harder to validate/understand the assertion.
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.
@stof I'll see if I can collect all the paths before actually adding them to the loader so they can all be tested together in order.
wesleylancel commentedJan 6, 2017
@stof I've added a separate commit so it's easier to see what I've changed (can be squashed later). A hierarchy of bundles is now built first, after that the paths are added to the loader. I'm not sure if I could simplify the code, especially the building of the hierarchy. I could improve the test by asserting the array per namespace if that helps for readability? |
xabbuh commentedJan 7, 2017 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
I think an easier implementation could be to have two loops. The first would collect path information for bundles we override without actually registering them. The second would then first register the additional paths from child bundles followed by paths the bundle provides itself. |
wesleylancel commentedJan 10, 2017
@xabbuh: Isn't that what I'm doing now? First the hierarchy is built and then the paths are registered. |
| ); | ||
| } | ||
| if (is_dir($dir =$container->getParameter('kernel.root_dir').'/Resources/'.$name.'/views')) { |
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.
Note to maintainers: when merging to newer branches, we need to take care of the FileExistenceResource objects here (git conflicts should remind us about this btw)
stof commentedJan 11, 2017
👍 |
1 similar comment
dunglas commentedJan 11, 2017
👍 |
fabpot commentedJan 11, 2017
Thank you@wesleylancel. |
…ent bundles in account (wesleylancel)This PR was squashed before being merged into the 2.7 branch (closes#19586).Discussion----------[TwigBundle] Fix bug where namespaced paths don't take parent bundles in account| Q | A || --- | --- || Branch? | 2.7 || Bug fix? | yes || New feature? | no || BC breaks? | no || Deprecations? | no || Tests pass? | yes || Fixed tickets |#6919 || License | MIT || Doc PR | |Currently namespaced paths for templates such as `{% extends '@App/Layout/layout.html.twig' %}` do not work with bundles that have overruled templates using the `getParent()` method in another bundle. See attached ticket. This change prepends the path of the bundle implementing `getParent()` to the paths of the namespace of bundle returned as a parent.Commits-------0c77ce2 [TwigBundle] Fix bug where namespaced paths don't take parent bundles in account
homersimpsons commentedJan 12, 2017
@fabpot Do you have any hint about the new release (containing this fix) date? |
chalasr commentedJan 12, 2017
@homersimpsons today :) see#21258 |
Uh oh!
There was an error while loading.Please reload this page.
Currently namespaced paths for templates such as
{% extends '@App/Layout/layout.html.twig' %}do not work with bundles that have overruled templates using thegetParent()method in another bundle. See attached ticket. This change prepends the path of the bundle implementinggetParent()to the paths of the namespace of bundle returned as a parent.