Вопрос или проблема
Я разработал плагин и у меня есть функция экспорта, которая экспортирует данные из WordPress в CSV.. проблема в том, что оператор “Header” вызывает ошибку “Заголовки уже отправлены”.
Я проверил свой код, и ничего HTML или что-то подобное не отправляется заранее.
Ошибка возникает в script-loader.php (вывод начат в /home/test1/public_html/wp-includes/script-loader.php:1403)
Я ознакомился с основным кодом php, и секция показывает следующее –
function _print_styles() {
global $compress_css;
$wp_styles = wp_styles();
$zip = $compress_css ? 1 : 0;
if ( $zip && defined('ENFORCE_GZIP') && ENFORCE_GZIP )
$zip = 'gzip';
if ( $concat = trim( $wp_styles->concat, ', ' ) ) {
$dir = $wp_styles->text_direction;
$ver = $wp_styles->default_version;
$concat = str_split( $concat, 128 );
$concat="load%5B%5D=" . implode( '&load%5B%5D=', $concat );
$href = $wp_styles->base_url . "/wp-admin/load-styles.php?c={$zip}&dir={$dir}&" . $concat . '&ver=" . $ver;
echo "<link rel="stylesheet' href="" . esc_attr($href) . "" type="text/css" media="all" />\n";
if ( !empty($wp_styles->print_code) ) {
echo "<style type="text/css">\n";
echo $wp_styles->print_code;
echo "\n</style>\n";
}
}
if ( !empty($wp_styles->print_html) )
echo $wp_styles->print_html;
}
Конкретная строка 1403 –
if ( !empty($wp_styles->print_html) )
echo $wp_styles->print_html;
Вот моя функция, которая конкретно выполняет экспорт.
function process_bulk_action()
{
global $wpdb;
$table_name = $wpdb->prefix . 'cj_raffle_tickets'; // не забывайте о префиксе таблиц
if ('delete' === $this->current_action()) {
$ids = isset($_REQUEST['ticketid']) ? $_REQUEST['ticketid'] : array();
if (is_array($ids)) $ids = implode(',', $ids);
if (!empty($ids)) {
$wpdb->query("DELETE FROM $table_name WHERE ticketid IN($ids)");
}
}
if ('export' === $this->current_action()) {
//csv_export();
//ob_start();
$output=""; //Назначение переменной для хранения всех данных будущего CSV файла
$result = $wpdb->get_results("SHOW COLUMNS FROM " . $table_name . ""); //Отображает все ИМЕНА СТОЛБЦОВ в столбце 'Field' в возвращенных записях
if (count($result) > 0) {
foreach($result as $row) {
$output = $output . $row->Field . ",";
}
$output = substr($output, 0, -1); //Удаление последнего разделителя, потому что именно так работают CSV
}
$output .= "\n";
$ids = isset($_REQUEST['ticketid']) ? $_REQUEST['ticketid'] : array();
if (is_array($ids)) $ids = implode(',', $ids);
if (!empty($ids)) {
$values = $wpdb->get_results("SELECT * FROM $table_name where ticketid IN ($ids)"); //Это здесь
}
foreach ($values as $rowr) {
$fields = array_values((array) $rowr); //Избавляемся от ключей и используем числовой массив для получения значений
$output .= implode(",", $fields); //Генерация строки с разделителем полей
$output .= "\n"; //Да...
}
// Загрузка файла
$upload_dir = wp_upload_dir();
$filedir = $upload_dir['path'];
$filename="ticketssold_" . time() . '.csv';
if ( ! is_writable( $filedir ) ) {
wp_die( "<p>Каталог загрузок недоступен для записи, невозможно сохранить файл </p>", 'Каталог недоступен для записи' );
}
$handle = fopen( $filedir . "https://wordpress.stackexchange.com/" . $filename, 'w' );
fwrite( $handle, $output );
fclose( $handle );
header( 'Content-Description: File Transfer' );
header( 'Content-Type: application/octet-stream' );
header( 'Content-Disposition: attachment; filename=".$filename );
header( "Content-Transfer-Encoding: binary' );
header( 'Expires: 0' );
header( 'Cache-Control: must-revalidate' );
header( 'Pragma: public' );
header( 'Content-Length: ' . filesize( $filedir . "https://wordpress.stackexchange.com/" . $filename ) );
flush();
readfile( $filedir . "https://wordpress.stackexchange.com/" . $filename );
exit;
}
Может кто-то помочь мне с этим.. Я искал на форуме за форумом, пробуя всё от ob_clean до ob_start, ob_flush и т.д.
спасибо
Крейга.
Вы, вероятно, выполняете вашу функцию process_bulk_action
в шаблоне, где уже отправлены некоторые HTML и заголовки. Вам нужно использовать хук handle_bulk_actions-edit-post
, чтобы выполнить ваш код до отправки любого содержимого. Это может выглядеть примерно так:
function wpse_287406_register_export_option($bulk_actions) {
$bulk_actions['export_csv'] = __( 'Экспорт в CSV');
return $bulk_actions;
}
add_filter( 'bulk_actions-edit-post', 'wpse_287406_register_export_option' );
function wpse_287406_export_csv( $redirect_to, $doaction, $post_ids ) {
if ( $doaction !== 'export_csv' ) {
return $redirect_to;
}
// Код экспорта CSV
$file = realpath( dirname( __FILE__ ) . '/file.csv' );
header( 'Content-Description: File Transfer' );
header( 'Content-Type: application/octet-stream' );
header( 'Content-Disposition: attachment; filename=file.csv' );
header( 'Content-Transfer-Encoding: binary' );
header( 'Expires: 0' );
header( 'Cache-Control: must-revalidate' );
header( 'Pragma: public' );
header( 'Content-Length: ' . filesize($file) );
readfile($file);
exit;
}
add_action( 'handle_bulk_actions-edit-post', 'wpse_287406_export_csv', 10, 3 );
Ответ или решение
Ошибка "Headers already sent" в PHP — это распространенная проблема, возникающая, когда в скрипте происходит отправка заголовков HTTP после того, как был выведен какой-либо контент. Давайте подробно рассмотрим, как избежать этой проблемы в вашем плагине для WordPress, осуществляющем экспорт данных в CSV-файл.
1. Причина проблемы
Ошибка "Headers already sent" обычно возникает, когда перед вызовами функции header()
появляется вывод данных. Если перед вашими HTTP-заголовками уже было что-то выведено (например, HTML, пробелы или строки текста), PHP не сможет установить новые заголовки.
В вашем случае ошибка вызвана тем, что функция process_bulk_action
вызывается в момент, когда WordPress уже начал отправлять вывод, что связано с использованием хуков и фильтров.
2. Решение
Чтобы избежать этой проблемы, вам необходимо использовать хук, который срабатывает до того, как начинается вывод HTML-контента. Например, хук handle_bulk_actions-edit-post
подходит для вашей функции экспорта. Вот краткий план решения проблемы:
- Регистрация действия экспорта.
- Обработка действия экспорта и генерация CSV-файла.
3. Пример исправленного кода
Ниже представлен обновлённый пример вашего кода, который использует указанный хук:
function wpse_287406_register_export_option($bulk_actions) {
$bulk_actions['export_csv'] = __( 'Экспорт в CSV', 'textdomain' );
return $bulk_actions;
}
add_filter( 'bulk_actions-edit-post', 'wpse_287406_register_export_option' );
function wpse_287406_export_csv( $redirect_to, $doaction, $post_ids ) {
if ( $doaction !== 'export_csv' ) {
return $redirect_to;
}
global $wpdb;
$table_name = $wpdb->prefix . 'cj_raffle_tickets';
// Здесь код для генерации CSV
$output = ""; // Для хранения данных CSV
$result = $wpdb->get_results("SHOW COLUMNS FROM " . $table_name);
if (count($result) > 0) {
foreach($result as $row) {
$output .= $row->Field . ",";
}
$output = rtrim($output, ',') . "\n"; // Убираем последний разделитель
}
$ids = isset($_REQUEST['ticketid']) ? $_REQUEST['ticketid'] : array();
if (is_array($ids)) $ids = implode(',', $ids);
if (!empty($ids)) {
$values = $wpdb->get_results("SELECT * FROM $table_name WHERE ticketid IN ($ids)");
foreach ($values as $rowr) {
$fields = array_values((array) $rowr);
$output .= implode(",", $fields) . "\n"; // Добавляем строки данных
}
}
// Устанавливаем заголовки для скачивания файла
header('Content-Description: File Transfer');
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="export.csv"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . strlen($output));
// Отправляем CSV данные в выходной поток
echo $output;
exit;
}
add_action( 'handle_bulk_actions-edit-post', 'wpse_287406_export_csv', 10, 3 );
4. Объяснение кода
- hook
bulk_actions-edit-post
: этот хук используется для регистрации нового действия (в данном случае экспорта в CSV). handle_bulk_actions-edit-post
: здесь происходит обработка выбранных действий, и если было выбраноexport_csv
, формируется и отправляется CSV-файл.- Функция
header()
: она вызывается до вывода любого содержимого. Это предотвращает ошибку "Headers already sent".
5. Заключение
Следуя указанным шагам, вы сможете избежать ошибки "Headers already sent" в вашем плагине WordPress. Убедитесь, что функция экспорта вызывается до начала отправки контента браузеру, и это решит возникающие проблемы с заголовками. Если у вас возникнут дополнительные вопросы или потребуется помощь, не стесняйтесь обращаться за поддержкой.