Заголовки уже отправлены в пользовательском плагине (функция экспорта)

Вопрос или проблема

Я разработал плагин и у меня есть функция экспорта, которая экспортирует данные из 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 подходит для вашей функции экспорта. Вот краткий план решения проблемы:

  1. Регистрация действия экспорта.
  2. Обработка действия экспорта и генерация 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. Убедитесь, что функция экспорта вызывается до начала отправки контента браузеру, и это решит возникающие проблемы с заголовками. Если у вас возникнут дополнительные вопросы или потребуется помощь, не стесняйтесь обращаться за поддержкой.

Оцените материал
Добавить комментарий

Капча загружается...