PHPOffice/PhpSpreadsheet · error · PhpOffice\PhpSpreadsheet\Exception
Reader classes must implement their own loadSpreadsheetFromF
Error message
Reader classes must implement their own loadSpreadsheetFromFile() method
What it means
BaseReader::loadSpreadsheetFromFile() is a deliberate stub: load() delegates to it, and each concrete format reader overrides it with real parsing. The base implementation turns 'subclass forgot to implement the template method' into an explicit runtime exception instead of an abstract-method declaration error.
Source
Thrown at src/PhpSpreadsheet/Reader/BaseReader.php:274
$this->setReadEmptyCells(false);
}
if (((bool) ($flags & self::IGNORE_ROWS_WITH_NO_CELLS)) === true) {
$this->setIgnoreRowsWithNoCells(true);
}
if (((bool) ($flags & self::ALLOW_EXTERNAL_IMAGES)) === true) {
$this->setAllowExternalImages(true);
}
if (((bool) ($flags & self::DONT_ALLOW_EXTERNAL_IMAGES)) === true) {
$this->setAllowExternalImages(false);
}
if (((bool) ($flags & self::CREATE_BLANK_SHEET_IF_NONE_READ)) === true) {
$this->setCreateBlankSheetIfNoneRead(true);
}
}
protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
{
throw new PhpSpreadsheetException('Reader classes must implement their own loadSpreadsheetFromFile() method');
}
/**
* Loads Spreadsheet from file.
*
* @param int $flags the optional second parameter flags may be used to identify specific elements
* that should be loaded, but which won't be loaded by default, using these values:
* IReader::LOAD_WITH_CHARTS - Include any charts that are defined in the loaded file
*/
public function load(string $filename, int $flags = 0): Spreadsheet
{
$this->processFlags($flags);
try {
return $this->loadSpreadsheetFromFile($filename);
} catch (ReaderException $e) {
throw $e;
}View on GitHub (pinned to 65b080eef4)
Solutions
- Implement protected loadSpreadsheetFromFile(string $filename): Spreadsheet in the subclass that parses the file and returns a populated Spreadsheet
- Alternatively extend the closest concrete reader and reuse/extend its parser
- For standard formats, just use IOFactory::createReader() instead of hand-rolled subclasses
Example fix
// before
class MyReader extends BaseReader { }
(new MyReader())->load('data.foo'); // throws: stub not overridden
// after
class MyReader extends BaseReader
{
protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
foreach (file($filename, FILE_IGNORE_NEW_LINES) as $r => $line) {
$sheet->getCellByColumnAndRow(1, $r + 1)->setValue($line);
}
return $spreadsheet;
}
} Defensive patterns
Strategy: type-guard
Validate before calling
$m = new ReflectionMethod($readerClass, 'loadSpreadsheetFromFile');
if ($m->getDeclaringClass()->getName() === BaseReader::class) {
throw new InvalidArgumentException("$readerClass does not implement loadSpreadsheetFromFile()");
} Type guard
function canActuallyLoad(string $readerClass): bool
{
return (new ReflectionMethod($readerClass, 'loadSpreadsheetFromFile'))
->getDeclaringClass()->getName() !== BaseReader::class;
} Prevention
- Treat BaseReader as an interface-with-stubs: implement loadSpreadsheetFromFile() in every subclass before wiring it into pipelines
- Cover custom readers with a load smoke test in CI so a dropped override fails the build, not production
When it happens
Trigger: Instantiating a custom class MyReader extends BaseReader that does not override protected loadSpreadsheetFromFile(string $filename): Spreadsheet, then calling (new MyReader())->load('file.ext'); a refactor that renamed or accidentally deleted the override in a stock-format fork.
Common situations: Scaffolding custom readers for in-house formats (fixed-width text, proprietary XML); maintaining a fork of a stock reader where the method signature drifted.
Related errors
AI-assisted analysis of PHPOffice/PhpSpreadsheet@65b080eef4 (2026-08-17).
Data as JSON: /api/errors/a101e119ee9caf9a.
Report an issue: GitHub.