How to add fields to the comment form and validate them in PHP in WordPress

Contents

Overview

This article explains, in exhaustive detail, how to add custom fields to the WordPress comment form and validate, sanitize and persist those fields in PHP. It covers: where to inject fields, how to enforce server-side validation safely (nonces and sanitization), how to save fields to comment meta when a comment is stored, how to show the data in the front-end and admin, and optional client-side validation and styling. All code examples are ready to paste into a themes functions.php or a small plugin file.

Key concepts and hooks used

You will use these WordPress hooks and functions:

  • comment_form_default_fields — filter the default author/email/url fields array.
  • comment_form_after_fields, comment_form_logged_in_after — action hooks to output extra form markup.
  • pre_comment_on_post — action to validate input before the comment is inserted.
  • comment_post — action to act after a comment is stored (save comment meta).
  • add_comment_meta / update_comment_meta / get_comment_meta — store and retrieve custom fields.
  • manage_edit-comments_columns and manage_comments_custom_column — extend the Comments admin list columns.
  • comment_text or a custom walker for comments — display fields with comments on the front-end.

Security and sanitization rules

Always validate on the server. Client-side JavaScript can improve UX, but it is not a security control. Use nonces for anti-CSRF when appropriate, then:

  • Use sanitize_text_field() for single-line text data.
  • Use sanitize_email() for email addresses, esc_attr() and esc_html() when outputting.
  • Use wp_die() with a clear, localized error message and back_link to return the user to the form on validation failure.

Example: Add a phone number field and a consent checkbox

We will add two fields: a required phone number and an optional consent checkbox. Implementation steps:

  1. Insert fields into the comment form markup (for both logged-in and not-logged-in states).
  2. Insert a nonce for CSRF protection.
  3. Validate required values on pre_comment_on_post.
  4. Save the validated data on comment_post into comment meta.
  5. Show the phone number in front-end comment output and create an admin column.

Full PHP implementation (place in functions.php or a simple plugin)

This example shows adding fields, nonce output, validating, storing meta, and adding an admin column and front-end display.


        
        
    

// Insert phone into the fields array after url (example). new_fields = array() foreach (fields as key => value) { new_fields[key] = value if (url === key) { new_fields[phone] = phone_field } } return new_fields } add_filter(comment_form_default_fields, mytheme_add_comment_fields, 20) / Output the consent checkbox and the nonce for CSRF protection for all visitors (including logged-in). / function mytheme_comment_form_after_fields() { // Consent checkbox (optional). echo // Nonce field for verifying the form submission. wp_nonce_field(my_comment_nonce_action, my_comment_nonce_field, true, true) } add_action(comment_form_after_fields, mytheme_comment_form_after_fields) add_action(comment_form_logged_in_after, mytheme_comment_form_after_fields) // for logged-in users / 2) Server-side validation: pre_comment_on_post Validate the phone as required and check nonce. If invalid, use wp_die() with back_link => true so WordPress returns the user to the form. / function mytheme_validate_comment_fields(comment_post_id) { // Only validate on standard comment submit (comment_post will call this earlier). if ( wp_doing_ajax() ) { // Optionally handle Ajax separately } // Verify nonce - check it exists and is valid. if ( empty(_POST[my_comment_nonce_field]) ! wp_verify_nonce( sanitize_text_field( wp_unslash( _POST[my_comment_nonce_field] ) ), my_comment_nonce_action ) ) { wp_die( esc_html__(Security check failed: invalid form submission., textdomain), esc_html__(Comment Submission Error, textdomain), array( back_link => true ) ) } // Phone required: check presence. if ( empty( _POST[phone] ) ) { wp_die( esc_html__(Please enter your phone number., textdomain), esc_html__(Missing Phone, textdomain), array( back_link => true ) ) } // Sanitize and validate phone number format. phone_raw = wp_unslash( _POST[phone] ) phone = sanitize_text_field( phone_raw ) // Simple regex example: digits, spaces, parentheses, plus and hyphen are allowed. Adjust to your locale. if ( ! preg_match( /^[0-9 -() .]{6,20}/, phone ) ) { wp_die( esc_html__(Please enter a valid phone number., textdomain), esc_html__(Invalid Phone, textdomain), array( back_link => true ) ) } } add_action(pre_comment_on_post, mytheme_validate_comment_fields) / 3) Save the validated fields to comment meta after the comment is inserted. The comment_post hook passes the new comment ID and the comment approved status. / function mytheme_save_comment_meta(comment_id, comment_approved) { // Save phone (always sanitize before saving). if ( isset( _POST[phone] ) ) { phone = sanitize_text_field( wp_unslash( _POST[phone] ) ) add_comment_meta( comment_id, phone, phone, true ) } // Save consent checkbox as 1 or 0. consent = isset( _POST[comment_consent] ) 1 === _POST[comment_consent] ? 1 : 0 add_comment_meta( comment_id, comment_consent, consent, true ) } add_action(comment_post, mytheme_save_comment_meta, 10, 2) / 4) Display phone with a comment on the front-end (append to comment text) Use comment_text filter to append sanitized, escaped phone. / function mytheme_append_phone_to_comment_text(comment_text, comment = null) { if ( null === comment ) { return comment_text } phone = get_comment_meta( comment->comment_ID, phone, true ) if ( ! empty( phone ) ) { phone_escaped = esc_html( phone ) // Example output adjust markup to match your theme. comment_text .=

. esc_html__(Phone:, textdomain) . . phone_escaped .

} return comment_text } add_filter(comment_text, mytheme_append_phone_to_comment_text, 10, 2) / 5) Add a Phone column to the admin Comments list. / function mytheme_comments_columns(columns) { new_columns = array() foreach (columns as key => value) { new_columns[key] = value if ( author === key ) { new_columns[phone] = esc_html__(Phone, textdomain) } } return new_columns } add_filter(manage_edit-comments_columns, mytheme_comments_columns) / Render the phone column content / function mytheme_comments_custom_column(column, comment_ID) { if ( phone === column ) { phone = get_comment_meta(comment_ID, phone, true) if ( ! empty(phone) ) { echo esc_html(phone) } else { echo mdash } } } add_action(manage_comments_custom_column, mytheme_comments_custom_column, 10, 2) ?>

Client-side validation (optional)

Use JavaScript to validate inputs before submission to improve user experience. Remember client-side checks do not replace server-side validation.

// Example JavaScript: simple phone required validation.
// Enqueue this script properly in a theme/plugin via wp_enqueue_script.
document.addEventListener(DOMContentLoaded, function() {
    var commentForm = document.getElementById(commentform)
    if (!commentForm) return

    commentForm.addEventListener(submit, function(e) {
        var phoneField = document.getElementById(phone)
        if (phoneField) {
            var phone = phoneField.value.trim()
            if (!phone  !/^[0-9 -() .]{6,20}/.test(phone)) {
                e.preventDefault()
                alert(Please enter a valid phone number.)
                phoneField.focus()
                return false
            }
        }
    }, false)
})

Styling the new fields (optional)

Add CSS to your theme to make the new inputs match your design.

/ Example CSS /
.comment-form-phone input {
    width: 100%
    max-width: 320px
}
.comment-phone {
    margin-top: .5em
    font-size: .95em
    color: #333
}

Hook and filter summary table

Hook / Filter Purpose
comment_form_default_fields Alter or inject inputs into the default author/email/url fields for non-logged-in users.
comment_form_after_fields Echo additional markup after the default fields (non-logged-in).
comment_form_logged_in_after Echo additional markup for logged-in users after the comment fields.
pre_comment_on_post Server-side validation before comment is inserted. Use wp_die() on failure.
comment_post Save comment meta after the comment is created.
comment_text Filter comment output to display saved meta in front-end.
manage_edit-comments_columns / manage_comments_custom_column Add and render custom admin columns for comments list.

Additional best practices considerations

  • Use nonces for the comment form if you are adding fields that must be protected against forged posts. For high-traffic sites or APIs consider rate-limiting or honeypot controls.
  • Sanitize and escape consistently. sanitize_ when saving, esc_ when showing.
  • Localization — wrap user-facing strings in translation functions (e.g., esc_html__(), esc_attr__(), __()).
  • Accessibility — include label elements and aria attributes where appropriate.
  • Spam handling — metadata you add can affect spam detection validate and sanitize data before passing to third-party spam detectors.
  • Compatibility — test with comment plugins (comment pagination, nesting, custom forms). If a theme or plugin already customizes comment_form(), ensure your hooks run after theirs (adjust priority).
  • Multisite and cache — meta storage is per comment so it’s safe, but cached comment lists may not reflect newly saved meta until cache invalidation.

Troubleshooting common issues

  • Fields not showing: Ensure your theme calls comment_form() and not a custom template that hardcodes fields. If the theme uses a custom form layout, add your fields directly into that template or use the appropriate hooks the theme exposes.
  • Validation not firing: pre_comment_on_post will only fire when WordPress handles the POST if a plugin is intercepting and handling comment submission via AJAX, ensure validation code is executed in the same request or handle AJAX separately.
  • Nonce fails: Make sure you output the nonce in the form and check it on submission. For caching systems, ensure the comment form page isnt cached with stale nonces prefer server-side verification and short-lived nonces if the page is dynamic.
  • Data not saved: Hook priority: comment_post is the correct hook make sure you bind with appropriate priority and that you are checking isset(_POST[phone]) when saving.

Example variations

  • Add the field only for non-logged-in users by adding logic to the field filter or action: if ( is_user_logged_in() ) return.
  • Use update_comment_meta() instead of add_comment_meta to allow updates if the same meta key exists.
  • Store structured data (like JSON) in meta if you need multiple values but prefer separate meta keys where possible for query simplicity.

References



Acepto donaciones de BAT's mediante el navegador Brave 🙂



Leave a Reply

Your email address will not be published. Required fields are marked *