Validating Custom Field Data on save_post

Contents

Introduction

When building robust WordPress plugins or themes, custom fields (post meta) allow you to extend the editing interface with bespoke data. However, accepting arbitrary user input without rigorous validation and sanitization exposes your site to security vulnerabilities, data corruption, or unexpected behavior. This article dives deeply into the save_post hook and demonstrates how to validate custom field data properly before saving it to the database.

Understanding the save_post Hook

The save_post action is triggered whenever a post or custom post type is created or updated. It provides a programmatic entry point to inspect, validate, sanitize, and save meta values in one place.

Reference: save_post on WordPress Developer Resources

Why Validate Custom Field Data

  • Security: Prevent injection attacks (SQL, XSS).
  • Data Integrity: Guarantee data types, formats, and ranges.
  • User Experience: Provide clear error messages and fail gracefully.
  • Maintainability: Ensure stored data follows expected patterns.

Sanitization vs. Validation

While sanitization cleans data (removing harmful characters), validation ensures the input meets specific criteria (format, length, type). They often work together.

Function Purpose
sanitize_text_field() Removes tags, encodes special characters.
is_email() Checks for valid email format.
filter_var(url, FILTER_VALIDATE_URL) Validates URL structure.
absint() Converts to non-negative integer.

Implementing Validation in save_post

The following section outlines a typical workflow for validating and saving custom field data.

1. Register the Meta Box

Attach a meta box to the post edit screen to present custom fields.

add_action(add_meta_boxes, function() {
  add_meta_box(
    my_meta_box,
    My Custom Data,
    render_my_meta_box,
    post,
    normal,
    high
  )
})

2. Add a Nonce Field

Protect against CSRF by generating and verifying a nonce.

function render_my_meta_box(post) {
  wp_nonce_field(save_my_meta, my_meta_nonce)
  value = get_post_meta(post->ID, _my_field, true)
  echo ltlabelgtEnter Value:lt/labelgt
  echo ltinput type=text name=my_field value= . esc_attr(value) .  /gt
}

3. Hook into save_post

Validate the input, sanitize, and save only if all checks pass:

add_action(save_post, save_my_custom_field)
function save_my_custom_field(post_id) {
  // 1. Check autosave
  if (defined(DOING_AUTOSAVE)  DOING_AUTOSAVE) {
    return
  }
  // 2. Verify nonce
  if (!isset(_POST[my_meta_nonce])  !wp_verify_nonce(_POST[my_meta_nonce], save_my_meta)) {
    return
  }
  // 3. Check permissions
  if (!current_user_can(edit_post, post_id)) {
    return
  }
  // 4. Retrieve  validate
  if (isset(_POST[my_field])) {
    raw = _POST[my_field]
    // Example validation: non-empty, max length 100
    error = 
    if (strlen(raw) === 0) {
      error = This field cannot be empty.
    } elseif (strlen(raw) > 100) {
      error = Input exceeds 100 characters.
    }
    if (empty(error)) {
      clean = sanitize_text_field(raw)
      update_post_meta(post_id, _my_field, clean)
    } else {
      // Handle error (store in transient to show admin notice)
      set_transient(my_field_error_ . post_id, error, 45)
    }
  }
}

Displaying Validation Errors

Use admin notices to inform users of validation failures:

add_action(admin_notices, function() {
  global post
  if (!empty(post)  error = get_transient(my_field_error_ . post->ID)) {
    echo ltdiv class=notice notice-errorgtltpgt . esc_html(error) . lt/pgtlt/divgt
    delete_transient(my_field_error_ . post->ID)
  }
})

Common Validation Patterns

  • Email: if (!is_email(raw)) { / error / }
  • URL: if (filter_var(raw, FILTER_VALIDATE_URL) === false) { / error / }
  • Integer: int = absint(raw) if (int === 0 raw !== 0) { / error / }
  • Date: Compare DateTime::createFromFormat() output and format.

Best Practices and Pitfalls

  • Always verify nonces before processing.
  • Use current_user_can() to check capabilities.
  • Avoid saving unvalidated or unsanitized data. Reject rather than force adjust.
  • Keep validation logic small and testable (unit tests).
  • Store error messages temporarily (transients or session) to display in the admin.

Security Considerations

  • Cross-Site Request Forgery (CSRF) protection via nonces.
  • Privilege escalation prevention with capability checks.
  • Strict data type enforcement to prevent injection attacks.
  • No direct use of _POST values without filtering.

Further Reading

By applying these validation and sanitization strategies within the save_post hook, you ensure that custom field data is secure, consistent, and reliable, contributing to a safer and more maintainable WordPress codebase.



Acepto donaciones de BAT's mediante el navegador Brave 🙂



Leave a Reply

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