SpreadSheet-Coding.com

PHPSpreadsheet · Google Sheets API · Excel

Spreadsheets, driven by code.

Hands-on PHP tutorials for working with Excel and Google Sheets — read and write .xlsx, convert files to JSON, stream downloads in the browser, and insert images, formulas, and styling. Every guide ships with code you can copy, run, and adapt.

upload.php View article
<?php

require 'vendor/autoload.php';

use \PhpOffice\PhpSpreadsheet\IOFactory;
use \PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;

const MAX_BYTES = 2 * 1024 * 1024; // 2 MB

// Note what is NOT here: Csv. A CSV has no signature of its own, so almost any
// text file identifies as one - accepting Csv means accepting anything.
const ALLOWED = ['Xlsx', 'Xls', 'Ods'];

// 1. There must be a file, and the upload must have succeeded.
if (!isset($_FILES['spreadsheet'])) {
    exit("No file was submitted.\n");
}

$file = $_FILES['spreadsheet'];

if ($file['error'] !== UPLOAD_ERR_OK) {
    $reasons = [
        UPLOAD_ERR_INI_SIZE   => 'larger than upload_max_filesize in php.ini',
        UPLOAD_ERR_FORM_SIZE  => 'larger than the form allows',
        UPLOAD_ERR_PARTIAL    => 'only partially uploaded',
        UPLOAD_ERR_NO_FILE    => 'not chosen at all',
        UPLOAD_ERR_NO_TMP_DIR => 'no temporary folder on the server',
        UPLOAD_ERR_CANT_WRITE => 'could not be written to disk',
        UPLOAD_ERR_EXTENSION  => 'blocked by a PHP extension',
    ];
    $why = $reasons[$file['error']] ?? 'rejected for an unknown reason';
    exit("Upload failed: the file was {$why}.\n");
}

// 2. Check the size yourself. php.ini limits are the outer wall, not your rule.
if ($file['size'] > MAX_BYTES) {
    exit("Upload failed: the file is larger than 2 MB.\n");
}

// 3. Confirm PHP really put this file there. This is what stops a crafted
//    request from pointing the script at a file already on the server.
if (!is_uploaded_file($file['tmp_name'])) {
    exit("Upload failed: that was not an uploaded file.\n");
}

// 4. Move it out of the temp directory before touching it. PHP deletes
//    tmp_name the moment the script ends.
$target = __DIR__ . '/uploads/' . bin2hex(random_bytes(8)) . '.upload';

if (!move_uploaded_file($file['tmp_name'], $target)) {
    exit("Upload failed: could not move the file into place.\n");
}

// 5. Only now ask what the file actually is. The name the browser sent and the
//    MIME type it claimed are both attacker-controlled; the bytes are not.
try {
    $type = IOFactory::identify($target);
} catch (ReaderException $e) {
    unlink($target);
    exit("Rejected: that file is not a spreadsheet PhpSpreadsheet can read.\n");
}

if (!in_array($type, ALLOWED, true)) {
    unlink($target);
    exit("Rejected: {$type} files are not accepted here.\n");
}

// 6. It is a spreadsheet, and one we accept. Read it.
$spreadsheet = IOFactory::createReader($type)->load($target);
$worksheet = $spreadsheet->getActiveSheet();

$safeName = htmlspecialchars($file['name'], ENT_QUOTES, 'UTF-8');

echo "Accepted {$safeName}\n";
echo "Detected format : {$type}\n";
echo "Sheet           : {$worksheet->getTitle()}\n";
echo "Rows            : {$worksheet->getHighestRow()}\n";
echo "Columns         : {$worksheet->getHighestColumn()}\n";

unlink($target);

The full script from Handle An Uploaded Excel File In PHP Using PHPSpreadSheet — copy, run, adapt.

IOFactory::load() PhpSpreadsheet
Open any spreadsheet file
getActiveSheet() PhpSpreadsheet
Select the worksheet to fill
fromArray() PhpSpreadsheet
Write many rows at once
getCalculatedValue() PhpSpreadsheet
Read a formula result
save('php://output') PhpSpreadsheet
Stream the file as a download
spreadsheets_values->get() Google Sheets
Read a range of cells
spreadsheets_values->update() Google Sheets
Write a range of cells
spreadsheets->create() Google Sheets
Create a new spreadsheet
json_encode() PHP
Serialize rows to JSON
header() PHP
Send the download headers