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.
Currently it can only alter the properties of elements that have a #type. Let's make sure we can also alter default properties.
Comment | File | Size | Author |
---|---|---|---|
#67 | drupal.render-cache-get-attached.patch | 753 bytes | sun |
#66 | drupal.render-empty-fix.66.patch | 763 bytes | effulgentsia |
#60 | drupal.render-empty.60.patch | 8.59 KB | effulgentsia |
#58 | drupal.render-empty.58.patch | 8.48 KB | effulgentsia |
#53 | drupal.render-empty.53.patch | 8.38 KB | sun |
Comments
Comment #1
Damien Tournoud CreditAttribution: Damien Tournoud commentedHere is what I have in mind.
Comment #2
suns/element/elements/
We need benchmarks for this. I see that it's 1 function call less, but also 1 function call more. ;)
This review is powered by Dreditor.
Comment #3
Damien Tournoud CreditAttribution: Damien Tournoud commentedFixed.
Comment #4
Damien Tournoud CreditAttribution: Damien Tournoud commentedComment #6
mattyoung CreditAttribution: mattyoung commented.
Comment #7
Damien Tournoud CreditAttribution: Damien Tournoud commentedThis one should have a little bit less notices.
Comment #9
Damien Tournoud CreditAttribution: Damien Tournoud commentedAnd probably even less notices.
Comment #10
sunThis looks RTBC to me and seems to be totally required for generic alterations/customizations.
I'd like moshe to sign this off. I'll ping him.
Comment #11
moshe weitzman CreditAttribution: moshe weitzman commentedSeems reasonable to me too. Ideally, eaton or frando will chime in as well.
Comment #12
eaton CreditAttribution: eaton commentedYeah, this is definitely a nice little cleanup, and it moves some unnecessarily twisty special-casing from drupal_render().
Comment #13
webchickI see this is tagged "Performance." Where are the benchmarks?
Comment #14
effulgentsia CreditAttribution: effulgentsia commentedHere's the same patch as #9, but with the part where #defaults_loaded is set to TRUE commented out. This to better benchmark compare apples to apples with HEAD, which never sets #defaults_loaded to TRUE. I think doing so is a good idea, and should be done as a follow-up issue, but commenting that out for now helps us evaluate performance implications of what this patch is really about separately from evaluating performance implications of that other cleanup.
I'm also attaching a micro-benchmarking script (download to drupal root, rename extension to .php, and visit its url in the browser) that measures the time it takes to invoke drupal_render() when #type is set and when it's not. Here's the results on my computer (running MAMP, so not a production environment) (number is micro-seconds per invocation):
So, we have a tiny boost in performance when rendering elements with a type and a small loss in performance when rendering elements without a type (as to be expected given what this patch allows for elements without a type). However, what page within Drupal do we have where we're rendering hundreds or thousands of elements without a type, which is the only place where this loss would turn into anything noticeable?
Also, I think this patch is a great idea, and if these numbers are too discouraging, I think it would be good to generate them on a production environment, where the difference might be much smaller.
Comment #15
Crell CreditAttribution: Crell commentedThis looks good to me with one exception. There's no reason to inline element_basic_defaults(). With the static variable we're not calling the function repeatedly but only once per request, which is fine, but with this patch we are no longer able to access the defaults from anywhere except within drupal_render(). That's a feature regression, and nominally API change.
Let's not do that part unless there are benchmarks on that change specifically, but the rest looks good to me.
Comment #16
effulgentsia CreditAttribution: effulgentsia commented@Crell: as you point out, there's no performance difference between leaving or losing the element_basic_defaults() function. However, with this patch, even though that function has been removed, you can still get the defaults with
element_info('defaults')
. The reason element_basic_defaults() has been removed is so that module_invoke_all('element_info') and drupal_alter() could be called from a single place. Would it make sense to have an element_basic_defaults() function that also calls these? If not, would there be a use-case for calling element_basic_defaults() that returns something that hasn't gone through those steps? Set back to CNW if you feel that function is still needed.Comment #17
sunCan we clarify that comment a bit, please? I don't understand what it's asking for.
Why are these changes required for this patch?
This review is powered by Dreditor.
Comment #18
effulgentsia CreditAttribution: effulgentsia commentedWith sun's feedback. I removed the todo. See comment #14. Setting #defaults_loaded there was something introduced in earlier versions of this patch, and doing that makes sense to me, but should be a separate issue. No need for this patch to even mention it in the code -- let that be a separate issue.
Regarding those other changes, I suspect they are needed if setting #defaults_loaded to TRUE, but since we're not doing that in this patch, let's get rid of them, and keep this patch simple.
I've been looking into why the extra overhead added by invoking element_info() and it's because of drupal_static(). I've been working on an idea to speed that up and will open an issue for that separately, because it affects performance in a few places, not just here. I don't think we should hold this patch up because of that, however, unless someone comes up with a use-case where an extra 5 microseconds per renderable element that has no type would make any noticeable difference.
Comment #20
effulgentsia CreditAttribution: effulgentsia commentedAh ok, those extra hunks that were being asked about in #17 was a result of HEAD still adding basic defaults, even if #defaults_loaded has been set by form_builder(). The above patches tried to change that, which seems like a good clean-up to me, but not for this issue. Here's a patch that really minimizes what's done by this patch. I'd like to see follow-up issues resolve the #defaults_loaded situation but I'm trying to remove the bottlenecks from this issue landing. This patch also includes the optimization to drupal_static() I referred to in #18. What do you all think of it? There's other places that can use it. Code that doesn't need this level of micro-optimization can still use the simpler way of invoking drupal_static(), but this patch allows functions that get called hundreds of times per page request to still have resettable statics without needing to suffer an extra function call overhead on each iteration. And as a result, this patch has no performance impact (micro-benchmarking script from #14):
Comment #21
sunUgh. No, the global is an absolute no-go. You can read the original drupal_static() issue to learn more.
I'll have a crack at this now.
Comment #22
sunThis already contains the performance considerations from #602522: Links in renderable arrays and forms (e.g. "Operations") are not alterable.
Comment #24
sunFixing the notices.
Comment #26
sunAnd another notice bites the dust.
Comment #27
effulgentsia CreditAttribution: effulgentsia commentedSide issue in response to #21: #620452: Optimize drupal_static(): we shouldn't sacrifice simpletest coverage for performance and vice versa
Comment #28
sunso...? Any objections to #26?
Comment #29
effulgentsia CreditAttribution: effulgentsia commentedIt works for me. As I mentioned in #620452: Optimize drupal_static(): we shouldn't sacrifice simpletest coverage for performance and vice versa, I'm not completely thrilled with non-resettable statics, because they limit the ability of simpletests to change which modules are enabled or what the active theme is between tests within the same page request. However, most simpletests work by invoking a new page request anyway, and we're already implementing non-resettable statics in places where performance matters (theme(), url()), and this is a place where performance matters, so it makes sense. I'll keep working on making the case for #619666: Make performance-critical usage of drupal_static() grokkable, and if that ever lands, then we can replace all non-resettable statics with fast resettable ones. But until then, I think this patch is great.
Comment #30
sunGreat.
In the meantime, I'm _entirely_ questioning those basic defaults for stuff that has no #type. However, that I want to tackle in a separate issue - which, of course, depends on this patch being committed.
This patch resolves the bug stated in the issue title: Default element properties cannot be altered.
This patch may lead to a minor performance improvement, but I suspect that the numbers are so small that my system is unsuitable to provide proper benchmarks.
After all, the intention of this patch is not at all about performance. We only ensured that the performance doesn't stink after applying it, and as mentioned before, the implementation could even have very small performance improvement. But that's totally off-topic. I'm thereby removing the Performance tag.
Comment #31
effulgentsia CreditAttribution: effulgentsia commentedI would mark this RTBC, but given Moshe's annoyance with the proliferation of static caches, I think it makes sense for him to have a chance to weigh in on this. In this case, it's the replacement of a static variable with a bigger static variable, so maybe he'll be ok with it.
Comment #32
sunI changed my mind.
We are baby-sitting broken code here. The entire basic defaults are stupid.
If your #type or #theme or #theme_wrappers expect some #title or #attributes, then you should goddarnit define it. The entire logic for "basic defaults for all" is flawed.
Comment #34
Damien Tournoud CreditAttribution: Damien Tournoud commented@sun: on the contrary, we NEED the default. It allows you to add a property to all the elements at the same time. For example, add a custom #pre_render callback to all the elements.
Comment #35
Damien Tournoud CreditAttribution: Damien Tournoud commentedPlus, merge with my previous patch if you want to get rid of the exceptions.
Comment #36
sun@Damien:
No.
We are baby-sitting broken code. And, your "use-case" is a total edge-case scenario. I can't even think of a real-world use-case where I'd want to do that. Why would I want to to apply the identical #pre_render or #post_render or #title or #attributes to type "token", "markup", and "form"? That makes zero sense.
Contrary to that, every single #property slows down sorting in element_children(), but also drupal_render(), because we have no clue whether an element is empty or empty. Yes, empty or empty, because basic defaults means: Nothing is empty. We need to recurse to find out.
I'll look into the notices now.
Comment #37
sunAdditionally, 80% of those basic defaults are intended and usable for form elements only. They make _zero_ sense for any other renderable element. They are plain cruft, trash, overhead, senseless, or whatever you want to call it for any other element.
The remaining 20% is #attached, but that doesn't need to be defined by default and is totally special for every element in a structured array.
I could see the use-case for altering the properties for all #type 'block', 'node', 'user', or whatever. But for that, we need a separate patch that makes those use a #type and therefore hook_element_info().
Comment #38
sunThe exceptions were caused by the theme_fieldset() override in Seven theme. (which is a totally useless override, btw)
Comment #40
Damien Tournoud CreditAttribution: Damien Tournoud commentedOk, I agree. Those are pretty compelling arguments.
Let's remove that.
Comment #41
sunThis one should pass.
Actually - and this is a completely separate issue - some of the required changes unveil some very mad assumptions, e.g. taking over only specific parts of the original element in #process functions.
Comment #42
moshe weitzman CreditAttribution: moshe weitzman commentedFine with me
Comment #43
sunok, if no one reviews in-depth, then I'll do myself. :P
This is plain senseless. ;)
I'm on crack. Are you, too?
Comment #44
effulgentsia CreditAttribution: effulgentsia commentedWhy in this patch? If it's for performance, are we sure that the savings of a stack call for empty elements is worth an extra if statement for non-empty elements?
As you found yourself, absolutely senseless.
This review is powered by Dreditor.
Comment #45
effulgentsia CreditAttribution: effulgentsia commentedJust, checking, this approach basically means we don't get the benefit of #10. No way for modules to apply some property to all elements without a type. I'm ok with this, but wanted to point it out. Elements without a type are often used as generic grouping elements. Can anyone think of a reason why a module would need to set some property on all of these? It's functionality we don't have in HEAD anyway, but something people wanted at the top of this issue.
Comment #46
sunSince elements can be empty now, we need the !empty() check in drupal_render() anyway. Performing the check additionally in drupal_render_children() already should save a lot of function calls to drupal_render(). I have no clue whether this makes any difference in performance, but to me, it looks like cleaner code, because I usually don't invoke something if I don't have any data to pass.
re: #45: See my rant above. It simply makes no sense.
Comment #47
effulgentsia CreditAttribution: effulgentsia commentedOk, I'm satisfied. And according to #40 and #42, so are Damien and Moshe.
Comment #48
webchickUgh. This change makes the calling code absolutely hideous.
This is also an enormous API change that wasn't started until 2 weeks after the 2nd code freeze. I much prefer Damien's initial approach at this stage in the release cycle as it's far less invasive.
The rationale in #36 seems to be "babysitting broken code / the code makes no sense" (which is stuff we can clean up in D8) and also that it should be faster since we're we don't have to recurse for elements_children(). If this patch results in a big performance impact, perhaps we can consider it, but otherwise, I'd far prefer we go back to the simpler, non-API-breaking approach.
So. Benchmarks?
Comment #49
Damien Tournoud CreditAttribution: Damien Tournoud commented*Sigh*
Comment #50
sunActually, I think this will have a slight performance impact, especially the larger the (entire) rendered page structure gets.
Additionally, this patch should probably rather considered as required starting point for further render system performance improvements. The render system is one of the bottlenecks in terms of performance currently and as already mentioned elsewhere, we need to streamline the system to figure out the location where we can attack with a sledge-hammer.
Re-rolled against HEAD.
Also attaching benchmarks that show a performance improvement.
Comment #52
webchicksun bugged me about this on IRC again and I took another look.
The patch looks way more horrendous than it actually is because there are code style fixes sneaked in here along with the actual changes. GRRR. Please stop doing that. :\
So the real changes are just:
1. system_element_info() required a #attributes => array() index
2. $element['#required'] turns into !empty($element['#required']) (um. wtf?)
3. #title and #description are not pre-defined.
I do not like introducing 1 and 2 after code freeze. I don't think 3 will have that big of an impact on existing code, since I don't know anything that counts on #title being pre-populated (though I may be wrong...).
sun and I came up with a compromise to set form-only attributes in the form building code, rather than the global render stuff. This should both minimize the API impact, and also ensure that you don't get non-sensical crap like #required for things like blocks. Then in D8, we can go full-blown lack of hand-holding here.
Comment #53
sunYes, and this actually makes even more sense.
Let's see what the bot thinks.
Comment #54
sunSo, yeah, let's go with this compromise.
chx already started with a nice attempt to streamline drupal_render() even more in #627038: Make drupal_render iterative
Comment #55
webchickWell. Let's get a review on it first, eh?
Comment #56
effulgentsia CreditAttribution: effulgentsia commentedLooks awesome to me. #46 was RTBC and this is a cleaner version, so I think it also qualifies as RTBC.
Comment #57
webchickThis seems to no longer apply. Could we get a quick re-roll?
Comment #58
effulgentsia CreditAttribution: effulgentsia commentedStraight re-roll. As long as bot likes it, it's RTBC again.
Comment #60
effulgentsia CreditAttribution: effulgentsia commentedThis one should pass tests. The only difference relative to #58 is this hunk
#58:
#60:
So, the net change is to apply the '#required' and '#attributes' properties to all elements being processed through form_builder(), even if they don't have a type, which makes sense in case a widget wants to apply a '#theme' => 'fieldset' on an element without setting a type on it. Considering that what we're removing from HEAD is the applying of these defaults ALWAYS, there's no regression introduced by now applying them only on elements routed through form_builder() instead of only on elements routed through form_builder() that have a type.
Given the above, I'm considering this still RTBC, unless webchick sets it to "needs review" in order to get additional confirmation that this change makes sense.
Comment #61
webchickHm. Looking at the test failures, it appears to be because seven_fieldset() is a copy/paste of the old theme_fieldset(). I would just update it with this patch rather than masking the error.
Comment #62
effulgentsia CreditAttribution: effulgentsia commentedNo, the change to seven_fieldset() is the same as it was in #53. The reason for it failing now and not before is because of the change between 1.9 and 1.10 of file.field.inc, where a multi-valued file widget creates an element with no #type or #attributes, but with a #theme_wrapper of 'fieldset'. Theme functions for form elements should be able to rely on '#attributes' existing and being an array. Otherwise, too much client code needs to change, and that's what you didn't like in earlier versions of this patch.
Comment #63
webchickFair enough then. :)
Committed to HEAD!
Comment #64
webchickHm. I am pretty sure that this patch broke HEAD; http://qa.drupal.org/pifr/test/16620 shows a bunch of errors in Forum module related to #attached not being found. As a result, testing bot is now shut off. :(
What's not clear to me is why the testing bot didn't find this until /after/ this was committed. I don't think I committed anything else last night that would have affected this.
Anyway, could someone check to see if they can replicate the test failures before/after reverting this patch?
Comment #65
effulgentsia CreditAttribution: effulgentsia commentedLooking into it now...
Comment #66
effulgentsia CreditAttribution: effulgentsia commentedNot sure why bot didn't catch it before HEAD got updated, but here's a fix. The problem is that drupal_render_cache_get() calls drupal_process_attached() without ensuring that #attached exists. It could be fixed there, but I decided instead to make drupal_process_attached() more robust. Since this is fixing a broken HEAD, I'm fast-tracking it to RTBC. People following this thread can suggest alternate solutions after HEAD is working, if they like.
Comment #67
sunI'd prefer this.
Comment #68
effulgentsia CreditAttribution: effulgentsia commentedThat works too.
Comment #69
webchickCommitted #67 to HEAD. Thanks. Let's see if testing bot likes this one better.
Comment #71
effulgentsia CreditAttribution: effulgentsia commentedThose of you involved in this cleanup from way back may be interested in this: #740834: Form elements cannot be rendered without form_builder().