Skip to content
MNB PHPExcelDeveloper Guide
AI

Migration guides

MNB PHPExcel vs PhpSpreadsheet Performance Benchmark

Compare MNB PHPExcel and PhpSpreadsheet with a reproducible PHP benchmark for elapsed time, peak memory, rows processed and package fit.

Level
Advanced
Reading time
16 min
Package
mnb/mnb-phpexcel
Updated
MnbExcel::read()ReadSession::rows()ReadSession::withHeaderRow()ReadSession::withOptions()

Start with equivalent work#

No spreadsheet library is universally fastest for every workbook. Formulas, styles, shared strings, worksheet count, cell density, PHP extensions and the selected API all affect the result. Benchmark the exact files and operations your application performs.

01Same fixture

Use the same local XLSX file and worksheet.

02Same workload

Match value, header, empty-row and metadata behavior.

03Fresh process

Run each library separately and publish repeated results.

Control the benchmark#

Before timing either library, make the measured work equivalent and record the environment that can change the result.

Benchmark controlMNB PHPExcelPhpSpreadsheetWhy it matters
Package under testmnb/mnb-phpexcel or mnb/mnb-phpexcel-xlsxphpoffice/phpspreadsheetRecord exact resolved versions from Composer.
InputThe same local XLSX fileThe same local XLSX fileNetwork and disk-cache differences distort results.
SheetThe same worksheet nameThe same worksheet nameDifferent sheets can have radically different dimensions.
Data modeValues only; metadata disabled when not requiredsetReadDataOnly(true)Do not compare styled object loading with a values-only stream.
RowsCount the same header and empty-row policyCount the same header and empty-row policyRow totals must match before timing is meaningful.
ProcessOne fresh PHP processOne fresh PHP processPeak memory and opcache state are process-sensitive.
MetricsSeconds, peak MB, rows/sec, result countSeconds, peak MB, rows/sec, result countA single elapsed-time number is incomplete.

Run both libraries under the same conditions#

MNB PHPExcel

Value-only row iteration

benchmark-mnb.php
benchmark-mnb.php
<?php

// benchmark-mnb.php

declare(strict_types=1);

require __DIR__ . '/vendor/autoload.php';

use Mnb\PHPExcel\MnbExcel;

$path = $argv[1] ?? throw new RuntimeException('Pass an XLSX path.');
$sheetName = $argv[2] ?? null;
$startedAt = hrtime(true);
$rows = 0;

$workbook = MnbExcel::read($path);
$sheet = $workbook->sheetOrActive($sheetName)
    ->withHeaderRow(1)
    ->withOptions([
        'skip_empty_rows' => true,
        'include_cell_metadata' => false,
    ]);

foreach ($sheet->rows() as $row) {
    ++$rows;
}

$seconds = (hrtime(true) - $startedAt) / 1_000_000_000;

echo json_encode([
    'library' => 'mnb-phpexcel',
    'rows' => $rows,
    'seconds' => round($seconds, 4),
    'rows_per_second' => $seconds > 0 ? round($rows / $seconds, 2) : null,
    'peak_memory_mb' => round(memory_get_peak_usage(true) / 1048576, 2),
], JSON_PRETTY_PRINT), PHP_EOL;
PhpSpreadsheet

Equivalent data-only load

benchmark-phpspreadsheet.php
benchmark-phpspreadsheet.php
<?php

// benchmark-phpspreadsheet.php

declare(strict_types=1);

require __DIR__ . '/vendor/autoload.php';

use PhpOffice\PhpSpreadsheet\Reader\Xlsx;

$path = $argv[1] ?? throw new RuntimeException('Pass an XLSX path.');
$sheetName = $argv[2] ?? null;
$startedAt = hrtime(true);
$rows = 0;

$reader = new Xlsx();
$reader->setReadDataOnly(true);
if ($sheetName !== null && trim($sheetName) !== '') {
    $reader->setLoadSheetsOnly([$sheetName]);
}

$spreadsheet = $reader->load($path);
$sheet = $sheetName !== null && trim($sheetName) !== ''
    ? $spreadsheet->getSheetByName($sheetName)
    : $spreadsheet->getActiveSheet();

if ($sheet === null) {
    throw new RuntimeException('Worksheet not found.');
}

foreach ($sheet->getRowIterator(2, $sheet->getHighestDataRow()) as $row) {
    if ($row->isEmpty()) {
        continue;
    }

    ++$rows;
}

$seconds = (hrtime(true) - $startedAt) / 1_000_000_000;

echo json_encode([
    'library' => 'phpspreadsheet',
    'rows' => $rows,
    'seconds' => round($seconds, 4),
    'rows_per_second' => $seconds > 0 ? round($rows / $seconds, 2) : null,
    'peak_memory_mb' => round(memory_get_peak_usage(true) / 1048576, 2),
], JSON_PRETTY_PRINT), PHP_EOL;

$spreadsheet->disconnectWorksheets();
unset($spreadsheet);

PhpSpreadsheet also provides read filters and cell caching. Include those configurations when they match your production implementation, and disclose them beside the result.

Repeat, validate, and publish#

Run in alternating order
Terminal
php -d opcache.enable_cli=0 benchmark-mnb.php orders.xlsx Orders
php -d opcache.enable_cli=0 benchmark-phpspreadsheet.php orders.xlsx Orders

# Repeat each command at least five times in alternating order.
Publish enough context to reproduce it
  • Publish the XLSX fixture size, sheet name, row count, column count and data types.
  • Record PHP, operating-system, CPU, memory and enabled extension versions.
  • Show composer show output for both packages.
  • Verify that both scripts return the same logical row count.
  • Report all runs or the median—not only the best run.
  • Keep benchmark source code and fixtures available from the repository when licensing permits.

Choose from the complete requirement set#

Performance is one decision factor. Use the benchmark alongside the workbook features, operational constraints, and APIs your production system actually needs.

Performance

Measure your real fixtures, workload, peak memory, throughput, and correctness.

Workbook behavior

Test formulas, styles, charts, legacy formats, and encrypted files that matter to your application.

Operational fit

Review framework integration, maintenance expectations, and the exact APIs your team must support.