PHPOffice/PhpSpreadsheet · error · PhpSpreadsheetException
Reader classes must implement their own listWorksheetInfo()
Error message
Reader classes must implement their own listWorksheetInfo() method
What it means
BaseReader::listWorksheetInfo() is a stub in the template-method style: it returns sheet metadata (name, last column, total rows, ...) only when a concrete reader overrides it. Calling it on a subclass that never implemented it hits the base throw, mirroring loadSpreadsheetFromFile().
Source
Thrown at src/PhpSpreadsheet/Reader/BaseReader.php:321
// Open file
$fileHandle = fopen($filename, 'rb');
}
if ($fileHandle === false) {
throw new ReaderException('Could not open file ' . $filename . ' for reading.');
}
$this->fileHandle = $fileHandle;
}
/**
* Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
*
* @return array<int, array{worksheetName: string, lastColumnLetter: string, lastColumnIndex: int, totalRows: int, totalColumns: int, sheetState: string}>
*/
public function listWorksheetInfo(string $filename): array
{
throw new PhpSpreadsheetException('Reader classes must implement their own listWorksheetInfo() method');
}
/**
* Returns names of the worksheets from a file,
* possibly without parsing the whole file to a Spreadsheet object.
* Readers will often have a more efficient method with which
* they can override this method.
*
* @return string[]
*/
public function listWorksheetNames(string $filename): array
{
$returnArray = [];
$info = $this->listWorksheetInfo($filename);
foreach ($info as $infoArray) {
$returnArray[] = $infoArray['worksheetName'];
}
View on GitHub (pinned to 65b080eef4)
Solutions
- Implement public listWorksheetInfo(string $filename): array in the subclass, returning entries shaped per the docblock (worksheetName, lastColumnLetter, lastColumnIndex, totalRows, totalColumns, sheetState)
- Or extend a concrete reader whose implementation you can reuse
- Guard UI code to skip the info call when the reader does not really implement it (reflection check on the declaring class)
Example fix
// before
class MyReader extends BaseReader { }
(new MyReader())->listWorksheetInfo('data.foo'); // throws
// after
class MyReader extends BaseReader
{
public function listWorksheetInfo(string $filename): array
{
return [
['worksheetName' => 'Sheet1', 'lastColumnLetter' => 'A', 'lastColumnIndex' => 0,
'totalRows' => count(file($filename)), 'totalColumns' => 1, 'sheetState' => 'visible'],
];
}
} Defensive patterns
Strategy: type-guard
Validate before calling
$m = new ReflectionMethod($reader, 'listWorksheetInfo');
if ($m->getDeclaringClass()->getName() === BaseReader::class) {
return []; // reader does not really implement sheet info — skip instead of crashing
} Type guard
function implementsWorksheetInfo(string $readerClass): bool
{
return (new ReflectionMethod($readerClass, 'listWorksheetInfo'))
->getDeclaringClass()->getName() !== BaseReader::class;
} Prevention
- When building custom readers, implement both template methods (load and listWorksheetInfo) from the start
- Have UI code degrade gracefully (hide the sheet-metadata panel) when a reader lacks a real info implementation
When it happens
Trigger: A custom BaseReader subclass without an override being passed to UI/reporting code that calls listWorksheetInfo($filename); tooling that enumerates sheet metadata over any IReader implementation.
Common situations: Custom readers written only for load(); admin tooling or preview panes that additionally query worksheet info and discover the gap at runtime.
Related errors
AI-assisted analysis of PHPOffice/PhpSpreadsheet@65b080eef4 (2026-08-17).
Data as JSON: /api/errors/c9babf170fb75fc3.
Report an issue: GitHub.