Fading Coder

An Old Coder’s Final Dance

Home > Tech > Content

Streaming Excel-Compatible Tables in PHP Using HTTP Headers

Tech 1

When you don’t need a heavy library like PHPExcel/PhpSpreadsheet, you can generate a tab‑separated file (TSV) and instruct the browser to download it as an Excel‑readable worksheet using standard HTTP headers.

Reusable exporter

<?php
/**
 * Stream an Excel-readable TSV file to the browser.
 *
 * @param array  $rows     Array of rows (associative or numeric arrays)
 * @param array  $header   Optional header: either list of keys or [key => label]
 * @param string $filename Base filename without extension
 */
function sendExcelTsv(array $rows = [], array $header = [], string $filename = 'report'): void
{
    // Ensure a safe filename and append extension Excel recognizes
    $name = preg_replace('/[^\w\d\-_.]+/', '_', $filename) . '.xls';

    // Clear any active output buffers to avoid corrupting the file or breaking headers
    while (ob_get_level() > 0) {
        ob_end_clean();
    }

    // Headers to force download and hint Excel
    header('Content-Type: application/vnd.ms-excel; charset=UTF-8');
    header('Content-Disposition: attachment; filename="' . $name . '"');
    header('Cache-Control: no-store, no-cache, must-revalidate');
    header('Pragma: no-cache');
    header('Expires: 0');

    // UTF-8 BOM helps some Excel versions interpret encoding correctly
    echo "\xEF\xBB\xBF";

    // Resolve column order (keys) and labels (header row)
    $keys = [];
    $labels = [];

    if ($header) {
        $isAssoc = array_keys($header) !== range(0, count($header) - 1);
        if ($isAssoc) {
            $keys = array_keys($header);
            $labels = array_values($header);
        } else {
            $keys = $header;      // keys match passed column names
            $labels = $header;    // labels are identical to keys
        }
    } elseif ($rows) {
        $first = reset($rows);
        $keys = array_keys((array) $first);
        $labels = $keys;
    }

    if ($labels) {
        echo implode("\t", array_map('tsvEscape', $labels)) . "\r\n";
    }

    foreach ($rows as $row) {
        $line = [];
        if ($keys) {
            foreach ($keys as $k) {
                $line[] = tsvEscape($row[$k] ?? '');
            }
        } else {
            foreach ((array) $row as $v) {
                $line[] = tsvEscape($v);
            }
        }
        echo implode("\t", $line) . "\r\n";
    }
}

/**
 * Escape a value for TSV: remove tabs/newlines and mitigate formula injection.
 */
function tsvEscape($value): string
{
    if ($value === null) return '';
    $s = (string) $value;

    // If content starts with characters that Excel treats as a formula, prefix with a single quote
    if (preg_match('/^[=\-+@]/', $s)) {
        $s = "'" . $s;
    }

    // Replace control characters that would break TSV
    $s = str_replace(["\t", "\r", "\n"], ' ', $s);
    return $s;
}

Example usage

<?php
// Fetch rows (e.g., from a database abstraction layer)
$rows = M('User')->field('id,account,password,nickname')->select();

// Define column order and labels
$header = [
    'id'       => 'ID',
    'account'  => 'Account',
    'password' => 'Password',
    'nickname' => 'Nickname',
];

sendExcelTsv($rows, $header, 'users');

Notes

  • Do not echo or print anything before calling the function; any prior output can break headers or corrupt the file.
  • The function uses UTF‑8 with a BOM. If you must target legacy Windows Excel expecting GBK/GB2312, switch the header charset and transcode values via iconv/mb_convert_encoding before outputting.
  • TSV with a .xls extension prompts Excel to open the data directly, even though it’s a plain text format.
  • If you need comma-separated files instead, replace the tab delimiter ("\t") with commas and adjust the contant type accordingly.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.