Hello @jgdraper328
That “headers already sent” warning is a very common issue when working with redirects in PHP and WordPress. Let’s break down what’s happening and how to fix it.
1. What the Error Means
The error message: PHP Warning: Cannot modify header information – headers already sent by (output started at …public/wp-includes/fonts/class-wp-font-face.php:121) in pluggable.php on line 1450
- “Cannot modify header information”: PHP needs to send HTTP headers (like the
Location: header for a redirect) before any actual content (HTML, whitespace, etc.) is sent to the browser.
- “headers already sent by (output started at …)”: This tells you exactly where the output began before your redirect attempt. In your case, it’s class-wp-font-face.php on line 121.
- “…in pluggable.php on line 1450”: This is the line inside WordPress’s
wp_redirect function where it tries (and fails) to send the Location: header because output already started.
2. What’s Causing the Output?
The file class-wp-font-face.php is part of WordPress core, specifically the Font Face API introduced in WP 6.4. Line 121 in that file is:
// ...existing code...
printf( $this->get_style_element(), $css );
// ...existing code...
This line prints the <style> block containing @font-face rules into the HTML <head> of your page. This is normal WordPress behavior – it’s setting up fonts for your theme. You didn’t do anything wrong here, and you definitely shouldn’t modify this core file.
3. Why is it a Problem for Your Code?
Your search_processing() function attempts to call wp_redirect() after WordPress has already started building the HTML page and printed those font styles. Because output has already started, PHP cannot send the necessary Location: header for the redirect to work.
4. How to Resolve This
The key is to ensure your form processing and potential redirect happen before WordPress starts outputting any HTML. The standard WordPress way to handle this is by using an appropriate action hook.
- Hook Earlier: Instead of running your
search_processing() logic directly in a template file or a shortcode handler (which often run after the headers have started), hook it into an action that fires earlier in the WordPress request lifecycle.
template_redirect: This hook is specifically designed for actions like intercepting page requests, handling form submissions, and performing redirects before the theme’s template file is loaded. This is usually the best hook for your scenario.
init: This hook runs even earlier, but template_redirect is generally preferred for this type of task as conditional tags (like is_page()) are available.
- Separate Processing and Display: Your
search_processing function currently tries to do two things: process the POST request (and potentially redirect) and display the results if a GET parameter is present. You should separate these:
- Processing Function (hooked to
template_redirect): This function should only check for the POST request ($_SERVER['REQUEST_METHOD'] === 'POST'). If it’s a POST request, it performs the lookup, and if an ID is found, it calls wp_redirect() and exit;. It does nothing if it’s not a POST request or if no ID is found.
- Display Logic: Let WordPress handle the display. After the redirect, the browser will load the new URL (e.g.,
your-page/?some_id=123). You can then display the results on that page. How you display depends on your setup:
- If it’s a specific WordPress page, you can modify that page’s template to check for
$_GET['some_id'] and include your results.php view.
- If you’re using a shortcode, the shortcode function can check for
$_GET['some_id'] and display the results.
Example Implementation:
<?php
// Assuming this code is in your plugin's main file or a class
// Hook the processing function to template_redirect
add_action( 'template_redirect', 'my_plugin_handle_search_submission' );
function my_plugin_handle_search_submission() {
// Only process if it's a POST request for your specific form
// (You might add a hidden field to your form to check here too)
if ( 'POST' === $_SERVER['REQUEST_METHOD'] && ! empty( $_POST['email'] ) ) {
// Use local variables instead of class properties if this is a standalone function
$email = sanitize_email( $_POST['email'] );
// Assuming Example_Class is available or loaded
$model = new Example_Class( $email );
$some_id = $model->get_some_id();
if ( ! empty( $some_id ) ) {
// Build the URL to redirect to.
// Replace 'your-results-page-slug' with the actual slug of your results page
$results_page_url = home_url( '/your-results-page-slug/' );
$redirect_url = add_query_arg( array( 'some_id' => $some_id ), $results_page_url );
// Perform the redirect and stop execution
wp_redirect( $redirect_url );
exit;
} else {
// Handle the "ID not found" case. You might want to redirect back
// to the search form with an error message query parameter.
$search_page_url = home_url( '/your-search-page-slug/' ); // Replace with your search page slug
$redirect_url = add_query_arg( array(
'search_error' => 'not_found',
'searched_email' => rawurlencode( $email ) // Pass the email back if needed
), $search_page_url );
wp_redirect( $redirect_url );
exit;
}
}
// If it's not a POST request for this form, do nothing and let WordPress continue.
}
// --- In your results page template OR your shortcode function ---
// Check if the ID is present in the URL
if ( ! empty( $_GET['some_id'] ) ) {
$retrieved_id = absint( $_GET['some_id'] ); // Sanitize the ID
// Now you have $retrieved_id, you can fetch the data and display it
// Example:
// $data_to_display = get_data_by_some_id( $retrieved_id );
include __DIR__ . '/../views/results.php'; // Make sure $retrieved_id is available in this view
} elseif ( ! empty( $_GET['search_error'] ) && 'not_found' === $_GET['search_error'] ) {
// Optionally display the error message on the search page itself
$error_email = ! empty( $_GET['searched_email'] ) ? sanitize_email( rawurldecode( $_GET['searched_email'] ) ) : '';
echo '<p class="error-message">No ID found for ' . esc_html( $error_email ) . '.</p>';
}
// Your regular search form HTML would go here or be part of the template/shortcode output
?>
By hooking into template_redirect, you ensure your redirect logic runs before any HTML output, resolving the “headers already sent” error.