We do an awful lot of messing around with $_POST in various system modules. One of the biggest reasons is buttons: in validation and submit handlers, we have no way of knowing which button was clicked without peeking directly at the post global.

The following patch moves $_POST['op'] to $_POST['edit']['op'], where it becomes a part of the $form_values collection and can be used properly without evil messing about. There are a handful of places where we're using it in ways that can't be easily converted without full Form API refactorings (modules that put their submission logic in the page view function, rather than a _submit() handler), but this patch will make such a conversion simpler.

It's a subtle but important flaw in our system that the API has no direct way of communiting which form button was clicked.

Comments

webchick’s picture

Status: Needs review » Needs work

+57! I love cleaner/more straight-forward code!! And I love the idea of not mucking with $_POST!

However, node forms are broken atm. When I try to preview/submit, I just get redirected back to the add form.

eaton’s picture

This is terribly broken right now, but in progress. ;)

pwolanin’s picture

+1 to this

eaton’s picture

StatusFileSize
new14.83 KB

Slightly updated, still needs work, but things aren't obviously broken. ;)

This actually eliminates the $_POST['edit'] construct entirely in favor of $_POST.

eaton’s picture

Title: FormAPI: Don't mess with $_POST to check $op » FormAPI: $_POST['edit'] is unecessary
Status: Needs work » Needs review
StatusFileSize
new17.87 KB

And here's a better version, with form.inc actually included in the patch.

This version of the patch changes all references to $_POST['edit'] and ['#post']['edit'] to $_POST and ['#post'], respectively. Why? A lot of investigating and a conversaion with UnConeD revealed that the only reason $_POST['edit'] exists is the legacy requirement that incoming form edit values be castable straight into a valid node object, to be passed into node_save().

The use of $_POST['edit'] causes major problems integrating submit buttons into the legitimate form values collection, among other things. By rolling everything back into $_POST, we gain the ability to work with the full range of form data (including the clicked button) in our submit and validate handlers.

Reviews appreciated.

eaton’s picture

Title: FormAPI: $_POST['edit'] is unecessary » FormAPI: $_POST['edit'] is harmful.

Changing the title to reflect the direness of $_POST['edit'].

asimmonds’s picture

Status: Needs review » Needs work

Any form with multiple checkboxes is broken, all checkboxes are cleared after form submission.
eg admin/user/access and admin/settings/modules are a couple of affected forms

mfredrickson’s picture

I like the idea. Have you thought about linking buttons to specific functions? I would do this by reappropriating the rather useless #submit option.

eg.

$form['submit']  = array(
'#type' => 'submit',
'#value' => t('Submit'),
'#submit' => 'node_form_submit',
);

#form['preview'] = array(
'#type' => 'submit',
'#value' => t('Preview'),
'#submit' => 'node_form_preview',
);

This attempt is not incompatible with your approach, but might make things cleaner in the long run. Obviously, if '#submit' is unset, this would call $formname_submit

-M

eaton’s picture

Status: Needs work » Needs review
StatusFileSize
new19.41 KB

New patch that fixes the issues with checkboxes (and, in fact, any form elements deeply nested). #name was being built incorrectly. Two lines of array-wiggling in form_builder correct this problem.

mfredrickson, you bring up an excellent point. previous, all buttons were hard-coded with the name 'op'. It didn't matter much, because those values weren't really available to most form processing code. Now, we not only get op, we can name separate sets of buttons differently to accomplish different things and switch based on which one was pressed. It's valuable, but I think we're a bit late in the development cycle to try to add complex function-firing based on it at this point. It's definitely a direction for future improvement, though.

I'll reiterate quickly why this patch is valuable and important.

  1. It allows us to take our hands off of $_POST, and deal exclusively with $form_values, for our logic switching.
  2. It makes a broader range of behaviours possible for multipart forms (where $form_values can be preserved, but $_POST is transient)
  3. It eliminates unecessary legacy cruft inherited from years old Drupal code
  4. It makes $_POST more self-explanitory, with less room for strange 'hidden' properties that exist outside the edit array
eaton’s picture

StatusFileSize
new19.5 KB

Re-rolled against the current HEAD.

eaton’s picture

StatusFileSize
new21.81 KB

And, ironically, $_POST['op'] wasn't making it into $form_values['op']. Fixed.

neclimdul’s picture

+1
This is great! I've always hated grabing 'op' from $_POST. And the ['edit'] thing just seemed strange and clunky. Very good!
I've tested this with a test version of the ecommerce cart module and it is working great.

dries’s picture

Looks good to me.

Eaton: can you make your summary a bit more accessible (more practical, less marketing). You say we no longer have to mess aroudn with $_POST, and yet, there are a lot of $_POSTs in your patch.

mfredrickson: I think your suggestion makes for a great improvement.

Thanks and keep up the good work! :)

eaton’s picture

Good point. Here's the rationale for the chat, minus hype. :)

  1. With the addition of multi-step form handling, and the possible addition of drupal_execute() for programmatically submitting forms, we're starting to rely more and more on the contents of a $form_values array. However, some form submission handlers (and even some recursive building functions) rely on the button operation. To switch to a confirm form, to execute different logic, etc.
  2. Because we currently keep the contents of our form in $_POST['edit'] while the buttons reside in $_POST, $op is never available in $form_values. That means that forms with different button actions can never be part of Drupal's multistep form processes. In addition, it means that many locations peek directly at $_POST when they shouldn't really have to. That's brittle.
  3. The reason that we keep some values in $_POST['edit'], but buttons in $_POST, is purely historical. According to UnConeD, it was necessary because the old pre-4.7 node form turned the $_POST['edit'] values into a node object directly, then validated the node object. Adding the button values would've polluted it. We no longer use this method, however -- everything goes through Form API validation and submit functions.
  4. This first step to eliminating unecessary uses of $_POST is moving ALL form values into $_POST proper, and eliminating the unecessary ['edit']. This patch does that.
  5. The second step is eliminating references to $_POST['op'] wherever possible. For example, the patch changes node.module's node_form function:
    -  $op = isset($_POST['op']) ? $_POST['op'] : '';
    -  if ($op == t('Preview')) {
    +  $op = isset($form_values['op']) ? $form_values['op'] : '';
    +  if ($op == $form_values['preview']) {
    
  6. In some cases, $_POST is examined when forms are redirecting to each other, or when our API would not be handing off the $form_values at all. These cases still remain in the code, as they need more individual attention and (potentially) more complex logic conversion. This patch, though, lays the groundwork for them and makes almost all 'normal' cases of $_POST peeking unecessary.

  7. As others have noted, it points out an unrelated but potentially cool direction for future expansion: the ability to have different buttons trigger different formAPI hooks. Right now, we have one flag: #executes_submit_callback. We use p[ switching to determine the button that was pressed. Instead, individual buttons could have a single #callback property, which FormAPI would use to determine what function to call when processing. I haven't tried to add that to this patch; the freeze is close enough I'd rather get the essential change in before tinkering with 'cool stuff.' If you think it's worth pursuing, Dries, I can give it a shot -- it wouldn't be too difficult to try making *_submit an overridable default #callback for buttons.

I like ordered lists.

eaton’s picture

StatusFileSize
new20.84 KB

Re-rolled against HEAD

dries’s picture

Status: Needs review » Reviewed & tested by the community

Thanks for the extended information. It really helps to keep on top of the work you guys are doing.

I'm OK with this patch now. I'm marking this RTBC but I leave it to be co-maintainers to review it one last time.

(I think the individual callbacks for button is really worth pursuing in a seperate patch, but only if it actually cleans up the code (not when it introduces a second, alternative mechanism to submit forms). If it can be the 'one and only' mechanism, it strikes me as a significant improvement in term of readability and ease of use.)

eaton’s picture

Dries, I'm going to let this patch sit as it is at RTBC, and pursue an add-on this evening that implements the button mechanism discussed above.

chx’s picture

StatusFileSize
new17.53 KB

Drumm asked for id to be prefixed edit- for CSS/JS namespacing (and theme backwards compatibility). As form API does not use this attribute, there are no new concerns.

drumm’s picture

Committed to HEAD with code cleanup.

drumm’s picture

Status: Reviewed & tested by the community » Fixed
Steven’s picture

Status: Fixed » Needs review
StatusFileSize
new3.75 KB

This patch broke file uploads. The edit prefix was removed, but the file_check_upload() code was not updated.

However, even when we used edit[upload] as the field name="..." attribute, the values didn't end up in the $_POST['edit'] array anyway. They ended up in $_FILES['edit'][...]['upload'].

So, for #type => file fields, I restored the array notation with the more descriptive 'files' name instead of 'edit'. That way, we don't have to unnecessarily rewrite file_check_upload() to accomodate the different array structure.

chx’s picture

StatusFileSize
new3.2 KB

I am not content with this change. #name changes should not occur in the theming layer.

chx’s picture

StatusFileSize
new3.2 KB

Works a lot better if I actually put the elements in the files array and not in the file one...

eaton’s picture

Priority: Normal » Critical
Status: Needs review » Reviewed & tested by the community

Just tested on a fresh HEAD; uploads are in fact working again and all is well in JavaScript land. Changing the handling of the files array to prevent attempts at nesting is also a good idea; such things can only end in tears.

drumm’s picture

Status: Reviewed & tested by the community » Fixed

Committed to HEAD.

Anonymous’s picture

Status: Fixed » Closed (fixed)