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 🙂 |