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.
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.
Use the same local XLSX file and worksheet.
Match value, header, empty-row and metadata behavior.
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 control | MNB PHPExcel | PhpSpreadsheet | Why it matters |
|---|---|---|---|
| Package under test | mnb/mnb-phpexcel or mnb/mnb-phpexcel-xlsx | phpoffice/phpspreadsheet | Record exact resolved versions from Composer. |
| Input | The same local XLSX file | The same local XLSX file | Network and disk-cache differences distort results. |
| Sheet | The same worksheet name | The same worksheet name | Different sheets can have radically different dimensions. |
| Data mode | Values only; metadata disabled when not required | setReadDataOnly(true) | Do not compare styled object loading with a values-only stream. |
| Rows | Count the same header and empty-row policy | Count the same header and empty-row policy | Row totals must match before timing is meaningful. |
| Process | One fresh PHP process | One fresh PHP process | Peak memory and opcache state are process-sensitive. |
| Metrics | Seconds, peak MB, rows/sec, result count | Seconds, peak MB, rows/sec, result count | A single elapsed-time number is incomplete. |
Run both libraries under the same conditions#
Value-only row iteration
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;Equivalent data-only load
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#
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 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 showoutput 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.
Measure your real fixtures, workload, peak memory, throughput, and correctness.
Test formulas, styles, charts, legacy formats, and encrypted files that matter to your application.
Review framework integration, maintenance expectations, and the exact APIs your team must support.