Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
@Berdir mentioned this issue to me today on IRC.
Relevant code from twig.engine:
if (is_object($arg)) {
if (method_exists($arg, '__toString')) {
return (string) $arg;
}
throw new Exception(t('Object of type "@class" cannot be printed.', array('@class' => get_class($arg))));
}
If possible we should mention the variable name as well as the template where this is being called from - that would be a big improvement.
Comment | File | Size | Author |
---|---|---|---|
#70 | 2031311-70.patch | 9.39 KB | vacho |
#64 | 2031311-interdiff-61-64.txt | 3.89 KB | cburschka |
#64 | 2031311-64.patch | 9.39 KB | cburschka |
#61 | 2031311-interdiff-60-61.txt | 2.65 KB | cburschka |
#61 | 2031311-61.patch | 9.27 KB | cburschka |
Comments
Comment #1
star-szrI thought #1922304: Remove TwigReference objects in favor of a high speed implementation by using NodeVisitors more cleverly might help us here since it adds $context and $template arguments to twig_render_var(), but at least in the current patch those seem to always be empty.
Comment #2
star-szrWould love to have this one fixed before beta, tagging for http://drupaltwig.org/issues/focus.
Comment #3
joelpittetThis can be done a couple of ways. Either way you'll likely need a second argument passed through twig_render_var.
One way is to cast the expression node to a string and pass pass that as the second function to render_var in the node visitor:
The resulting argument will look like this:
{{ attributes }}
or
{{ 'Pages'|t }}
or {{ tab.attributes }}
Or maybe a prettier solution is to extend Twig_Node_Expression_Function's compile to get the arguments name.
That way it will literally print out the argument as it was compiled to php in the Twig_Template
Sorry if this looks messy but wanted to portray the scope of this feature request. When nodes are compiled they may be wrapped in other functions before passed to twig_render_var().
Comment #4
star-szrWhat does Twig itself do if you try to print an object without a __toString() method? Is it any more helpful? I guess we should check that first and maybe this will relate back to #1815250: Type twig variables in PHP and remove superfluous render_var() and getAttribute() calls from the generated code or maybe we need to look upstream at the exceptions in general.
Comment #5
joelpittetPHP just deals with it the way PHP deals with it (in my case xdebug picked it up)
Comment #6
joelpittetFor your own testing fun:
Then visit http://d8.dev/twig
Comment #7
star-szrI ran into this exception last night and the message I got actually was pretty helpful overall, but maybe it was because it was an include or something? This was in the webprofiler module (33aa7a6 on the event_dispatcher2 branch) after clicking the route name in the bottom toolbar (e.g. view.frontpage.page_1).
Twig_Error_Runtime: An exception has been thrown during the rendering of a template ("Object of type "Drupal\Core\Extension\Extension" cannot be printed.") in "@webprofiler/Profiler/table.html.twig" at line 16. in Twig_Template->displayWithErrorHandling() (line 291 of core/vendor/twig/twig/lib/Twig/Template.php).
Comment #8
star-szrBut I guess even still we could try to display the variable name.
Comment #9
dawehnerEven more important let's not hide what "printed" means. Just use something like
"@varname of type @class don't implement __toString"
Comment #10
mariancalinro CreditAttribution: mariancalinro commentedComment #11
mariancalinro CreditAttribution: mariancalinro commentedI went with extending Twig_Node_Expression_Function and sending extra arguments to the render_var function.
I just couldn't figure out how to get the argument, so I went with the node's name, if it exists. As a fallback I used the string "Object", as the only place we are using this is in the exception thrown when an object's class doesn't implement __toString(), so a generic Object of class @class doesn't implement __toString is still readable.
Comment #12
BerdirA similar one, not sure if it is related or not, but at least as problematic:
Someone just reported that in #drupal-contribute, it happens if you for example have node.nid in your template instead of node.id (the id() mehod) or node.nid.value (=> $node->nid->value)
Comment #13
BerdirAh, this happens if you pass set a FieldItemList object as a #default_value on a form element.
Comment #14
steveoliver CreditAttribution: steveoliver commentedNot exactly sure what the next steps are, but based on Berdir's last comments it does seem this issue needs work.
Comment #15
m1r1k CreditAttribution: m1r1k commented#12 and #13 because of
that allows using any object, when this object has to extend AttributeValueBase or at least implement __toString() method.
Should we include it into the current patch?
Comment #16
m1r1k CreditAttribution: m1r1k commentedHere is draft version of this validation.
Comment #17
joelpittet@m1r1k that may conflict a tiny bit with #2285451: Create addClass() and removeClass() methods on Attribute object for merging css class names. which should get in any time now... Not entirely but just a heads up.
Comment #20
m1r1k CreditAttribution: m1r1k commentedRerolled.
Also I'm not sure about used Exception type but I didn't find better existed Exception class.
Comment #22
m1r1k CreditAttribution: m1r1k commentedWill add test that it throws exception properly
Comment #23
m1r1k CreditAttribution: m1r1k commentedComment #25
m1r1k CreditAttribution: m1r1k commentedFixed tests and remove t() from exception at all because \Exception from inside of Attribute class will break render process.
Comment #26
m1r1k CreditAttribution: m1r1k commentedComment #28
m1r1k CreditAttribution: m1r1k commentedAnother one fix-reroll and extra DocComment as well.
Comment #30
joelpittetNice job, only one fail and I think it's the new test, this seems to be coming together!
Another note in the Attribute class:
The return value expects AttributeValueBase and if if the $value object has a __toString() method then I think it would be best if we instantiate as an AttributeString object out of it's string value or it will likely fail on line 187 Attribute::__toString()'s call to a render() method of AttributeValueBase.
So maybe that can just be the else clause and move the
!method_exists($value, '__toString')
condition to the condition above? Or something along those lines.Comment #31
m1r1k CreditAttribution: m1r1k commentedYeh, previous tests failed because Drupal\Core\StringTranslation\TranslationWrapper is passed as object to createAttributeValue() and not typecasted to string (even it has right method) but it has "public function render()" as AttributeValueBase has. So we had a bug in the logic but have tests only with TranslationWrapper passed as object there that doesn't throw an exception :D
Comment #32
dawehnerUse String::format(). Do we have a better exception here? InvalidArgumentException?
It would be more helpful to explain why ... or what this is doing.
Some documentation would be cool.
Can we document here that this does not interfere with the existing variables in the $context? All that code is not necessarily trivial. Someone should also do some profiling because adding all those variables could for example increase the needed memory.
minimal: newlinew needed
Maybe something like Provides test data for testCreateAttributeValue
It doesn't really make sense to add a provider for a single item.
We discussed before that it could make sense to add this into a generic functionality, though it never went to something. damiankloip even had a patch for it.
Would be great to document $arg_name
Let's now skip the t() method. This is absolute BS. Isn't that also a InvalidArgumentException?
Comment #33
m1r1k CreditAttribution: m1r1k commented1), 10) Switched to String::format. According to SPL documentation \UnexpectedValueException makes more sense at least for me.
4) Well the only diff between these two classes is "$this->setAttribute('arguments', array_merge($function->getArguments(), $this->extra_arguments));" line and array_merge function. I don't think we need additional comments and profiling here.
8) Totally agreed and ready to help with that task, where is it? :)
Comment #34
joelpittet@m1r1k thanks for all those changes!
Can you post another patch after catching up with a pull from 8.0.x? Looks like some changes got in while you were making the patch.
Comment #35
m1r1k CreditAttribution: m1r1k commentedRerolled agains updated HEAD
Comment #36
joelpittet@m1r1k sorry there is still that link file that snuck in to your patch somehow.
core/lib/Drupal/Core/Link.php
Have a quick check though I think that is the only bit that snuck in extra from #31 to #35
Comment #37
m1r1k CreditAttribution: m1r1k commentedHeh, my bad :)
Comment #38
joelpittet@m1r1k thanks I reviewed the patch and it looks great!
Wondering if it's possible to test the exception throws produce the expected output as well?
Comment #39
m1r1k CreditAttribution: m1r1k commentedYes, I'll added annotation for this. Also will try to find test related to twig_render_var() to test new passed exception. Also think that to provide 100% test coverage we need to cover new Twig node visitor. What do you think @joelpittet
Comment #40
joelpittetI'm of the opinion we only need a test for output from twig_render_var(). Though if you have something in mind for testing the NodeVisitor don't let me stop you:)
Comment #41
m1r1k CreditAttribution: m1r1k commentedAdded expected exception text.
Was trying to add exception test for twig_render_var() but didn't find proper place. May be we just miss such tests :)
If so @joelpittet please create proper followup please.
Comment #42
star-szrI'm wondering if Twig 1.16.0 will change things at all for us:
http://blog.twig.sensiolabs.org/post/91042890778/twig-1-16-0-released
Comment #43
jhedstromPatch in #41 no longer applies (and also appears to contain unrelated
\Drupal\Core\Link\Link
code).Comment #44
ravi.khetri CreditAttribution: ravi.khetri commentedRe-rolled.
Comment #46
ankitgarg CreditAttribution: ankitgarg commented#44 Fail Fix.
Comment #48
kerby70 CreditAttribution: kerby70 commentedReroll attached, changes in twig.engine caused a patch apply failure that may need to be looked at.
Comment #49
kerby70 CreditAttribution: kerby70 commentedComment #59
gagarine CreditAttribution: gagarine as a volunteer commented6 years ago and Twig is still a nightmare to work with in Drupal for themer. Raise that to D9 as most dev don't look on stable version.
Comment #60
cburschkaThe last patch seems to be corrupt and doesn't apply to the repository's 2015-03-19 state... it also includes some odd changes to the Drupal\Core\Link class that do not seem to be related.
Here is a rewrite.
- Adjusted for changes in Twig and phpunit, and code style.
- Reduced the need for some code duplication by overriding ::compileCallable instead of ::compile; this allows the method to call its parent.
- Instead of
$node->getNode('expr')->getAttribute('name')
, use(string) $node->getNode('expr')
. This should (as I understand) be a string representation of the expression, and therefore works even if the expression is not a simple variable.Comment #61
cburschkaSome small fixups.
Comment #64
cburschkaMisunderstood something about the function compiler. The arguments attribute apparently specifies arguments to be prepended to the arguments, so they need to come first in the signature of render_var.
Also, the string representation of the Twig node is not useful; revert to using the name attribute.
Comment #65
cburschkaThere's still several problems with this approach:
1. Adding a new argument to the *start* of a method's signature is a BC break, since it cannot be made optional. The only workaround for adding an optional argument to the start is to remove type hints, make the last argument optional, and shift the arguments if it isn't provided.
2.
$node->getAttribute('name')
doesn't always yield helpful things either. For example, in{{ function(arg) }}
, it will providefunction
as the "name".Comment #66
gagarine CreditAttribution: gagarine as a volunteer commentedAlso changing the message to something that actually explain what the problem is such as "Object XXX cannot be printed because it has no __toString method.',
Comment #70
vacho CreditAttribution: vacho at Skilld commentedPatch rebased for 9.3.x