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

  1. Implement protected loadSpreadsheetFromFile(string $filename): Spreadsheet in the subclass that parses the file and returns a populated Spreadsheet
  2. Alternatively extend the closest concrete reader and reuse/extend its parser
  3. 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

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.