Reading workbooks
Read Excel Files in PHP Step by Step
Read Excel files in PHP step by step: choose a package, inspect the workbook, select a sheet, configure rows, consume results, and handle failures.
MnbExcel::read()Xlsx::read()ReadSession::inspect()ReadSession::sheetNames()ReadSession::activeSheet()ReadSession::hasSheet()ReadSession::sheetIfExists()ReadSession::withHeaderRow()ReadSession::isEmpty()ReadSession::countRows()ReadSession::rows()ReadSession::toArray()ReadSession::first()ReadSession::selectColumns()ReadSession::cell()ReadSession::toStructuredSheetArray()Choose a package and open the workbook#
- 01Choose & openPackage, entry class, file validation
- 02Inspect & selectMetadata, sheets, active fallback
- 03ConfigureHeaders, empty rows, limits
- 04ConsumeRows, arrays, cells, summaries
- 05ValidateResult checks and exceptions
The full package and focused XLSX package lead to the same read-session API after the workbook is opened. Pick one installation model and keep that boundary explicit.
// Install: composer require mnb/mnb-phpexcel:^2.0
use Mnb\PHPExcel\MnbExcel;
use Mnb\PHPExcel\Support\MnbExcelException;
$path = __DIR__ . '/excels/excel1.xlsx';
if (!is_file($path) || filesize($path) === 0) {
throw new RuntimeException('The Excel file is missing or empty.');
}
$workbook = MnbExcel::read($path);// Install: composer require mnb/mnb-phpexcel-xlsx:^2.0
use Mnb\PHPExcel\Format\Xlsx;
use Mnb\PHPExcel\Support\MnbExcelException;
$path = __DIR__ . '/excels/excel1.xlsx';
if (!is_file($path) || filesize($path) === 0) {
throw new RuntimeException('The Excel file is missing or empty.');
}
$workbook = Xlsx::read($path);Package rule: use MnbExcel with the complete package or Xlsx with the individual XLSX package. Do not mix the entry classes in one example or service boundary.
Inspect before selecting business data#
Use metadata and sheet discovery before assuming a worksheet name. This keeps uploads, optional tabs, and active-sheet fallbacks predictable.
Check status, format, encryption, warnings, errors, and worksheet metadata.
inspect()Show available worksheet names before choosing one.
sheetNames()Require a known sheet or intentionally use the workbook active sheet.
hasSheet() activeSheet()Use a nullable lookup only when fallback behavior is part of the application contract.
sheetIfExists()$metadata = $workbook->inspect();
$sheetNames = $workbook->sheetNames();
$active = $workbook->activeSheetInfo();
if ($sheetNames === []) {
throw new RuntimeException('The workbook has no readable worksheets.');
}
$requiredSheet = 'ALL_PARAMETERS';
$sheet = $workbook->sheetIfExists($requiredSheet)
?? $workbook->activeSheet();
print_r([
'format' => $metadata['format'] ?? 'xlsx',
'encrypted' => $metadata['encrypted'] ?? false,
'sheets' => $sheetNames,
'active_sheet' => $active['name'] ?? null,
]);Indexing rule: worksheet numbers are 1-based. PHP arrays returned later are 0-based, so keep worksheet position and returned-row position conceptually separate.
Configure one read session#
Describe the read before executing it.
Header selection, empty-row policy, limits, ranges, and projections belong on the session. Keep configuration together so the terminal operation sees one coherent policy.
- Headers
withHeaderRow(1)- Empty rows
skip_empty_rows- Safety limit
max_rows- Projection
selectColumns()
$session = $sheet
->withHeaderRow(1)
->withOptions([
'skip_empty_rows' => true,
'max_rows' => 5000,
]);
if ($session->isEmpty()) {
return;
}
$rowCount = $session->countRows();
$firstRow = $session->first();Return-type rule: configuration methods keep returning a read session. Methods such as rows(), toArray(), first(), and countRows() are terminal operations and return another type.
Choose the smallest result your task needs#
rows()Yield rows incrementally instead of collecting the complete result.first()Stop after the first normalized data row.toArray()Return a native PHP array for indexing, count(), and array_column().selectColumns()Project columns before the terminal read instead of discarding data afterward.cell("B2")Read an exact Excel cell without depending on the returned row array.toStructuredSheetArray()Return rows plus source, empty, skipped, header, and final returned counts.foreach ($session->rows() as $row) {
echo $row['Email'] ?? '';
}$rows = $session->toArray();
$firstEmail = $rows[0]['Email'] ?? null;
$fifthCustomerId = $rows[4]['Customer ID'] ?? null;
$emails = array_column($rows, 'Email');$value = $sheet->cell('B2');
echo 'B2: ' . (string) $value . PHP_EOL;$structured = $sheet->toStructuredSheetArray([
'header_row' => 1,
'skip_empty_rows' => true,
'include_cell_metadata' => false,
'row_format' => 'flat',
]);
$counts = $structured['summary']['row_counts'];
print_r([
'total' => $counts['total'],
'non_empty' => $counts['non_empty'],
'empty' => $counts['empty'],
'skipped_empty' => $counts['skipped_empty'],
'header' => $counts['header'],
'returned' => $counts['returned'],
]);Performance rule: the examples are separate for learning. In production, avoid calling several terminal methods just to inspect the same data; each terminal operation can require another worksheet pass.
Finish with validation and boundary-level error handling#
- 01Validate the file
Confirm the path exists, is readable, and is not zero bytes.
- 02Inspect once
Review workbook metadata and available sheets before processing business rows.
- 03Select explicitly
Require the expected sheet or document an intentional active-sheet fallback.
- 04Configure once
Apply headers, empty-row behavior, ranges, limits, and projections together.
- 05Execute one useful result
Stream, collect, count, read one row, or request structured output according to the job.
- 06Handle failures at the boundary
Return safe application messages while retaining detailed diagnostics for logs.
try {
$sheet = $workbook->sheet('ALL_PARAMETERS');
foreach ($sheet->withHeaderRow(1)->rows() as $row) {
// Validate and process each row.
}
} catch (MnbExcelException $error) {
// Log detailed diagnostics server-side.
error_log(json_encode($error->toErrorArray(debug: true)));
// Return a safe application-level message.
echo 'The workbook could not be processed.';
} catch (RuntimeException $error) {
echo $error->getMessage();
}Production rule: keep file validation, workbook selection, session configuration, terminal execution, and exception handling as visible boundaries. That structure makes spreadsheet imports much easier to review and debug.