{"record":{"id":"a101e119ee9caf9a","repo":"PHPOffice/PhpSpreadsheet","slug":"reader-classes-must-implement-their-own-loadspread","errorCode":null,"errorMessage":"Reader classes must implement their own loadSpreadsheetFromFile() method","messagePattern":"Reader classes must implement their own loadSpreadsheetFromFile\\(\\) method","errorType":"exception","errorClass":"PhpOffice\\PhpSpreadsheet\\Exception","httpStatus":null,"severity":"error","filePath":"src/PhpSpreadsheet/Reader/BaseReader.php","lineNumber":274,"sourceCode":"            $this->setReadEmptyCells(false);\n        }\n        if (((bool) ($flags & self::IGNORE_ROWS_WITH_NO_CELLS)) === true) {\n            $this->setIgnoreRowsWithNoCells(true);\n        }\n        if (((bool) ($flags & self::ALLOW_EXTERNAL_IMAGES)) === true) {\n            $this->setAllowExternalImages(true);\n        }\n        if (((bool) ($flags & self::DONT_ALLOW_EXTERNAL_IMAGES)) === true) {\n            $this->setAllowExternalImages(false);\n        }\n        if (((bool) ($flags & self::CREATE_BLANK_SHEET_IF_NONE_READ)) === true) {\n            $this->setCreateBlankSheetIfNoneRead(true);\n        }\n    }\n\n    protected function loadSpreadsheetFromFile(string $filename): Spreadsheet\n    {\n        throw new PhpSpreadsheetException('Reader classes must implement their own loadSpreadsheetFromFile() method');\n    }\n\n    /**\n     * Loads Spreadsheet from file.\n     *\n     * @param int $flags the optional second parameter flags may be used to identify specific elements\n     *                       that should be loaded, but which won't be loaded by default, using these values:\n     *                            IReader::LOAD_WITH_CHARTS - Include any charts that are defined in the loaded file\n     */\n    public function load(string $filename, int $flags = 0): Spreadsheet\n    {\n        $this->processFlags($flags);\n\n        try {\n            return $this->loadSpreadsheetFromFile($filename);\n        } catch (ReaderException $e) {\n            throw $e;\n        }","sourceCodeStart":256,"sourceCodeEnd":292,"githubUrl":"https://github.com/PHPOffice/PhpSpreadsheet/blob/65b080eef4d9fd11a5796135ab145883e5c3d6a6/src/PhpSpreadsheet/Reader/BaseReader.php#L256-L292","documentation":"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.","triggerScenarios":"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.","commonSituations":"Scaffolding custom readers for in-house formats (fixed-width text, proprietary XML); maintaining a fork of a stock reader where the method signature drifted.","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"],"exampleFix":"// before\nclass MyReader extends BaseReader { }\n(new MyReader())->load('data.foo'); // throws: stub not overridden\n\n// after\nclass MyReader extends BaseReader\n{\n    protected function loadSpreadsheetFromFile(string $filename): Spreadsheet\n    {\n        $spreadsheet = new Spreadsheet();\n        $sheet = $spreadsheet->getActiveSheet();\n        foreach (file($filename, FILE_IGNORE_NEW_LINES) as $r => $line) {\n            $sheet->getCellByColumnAndRow(1, $r + 1)->setValue($line);\n        }\n        return $spreadsheet;\n    }\n}","handlingStrategy":"type-guard","validationCode":"$m = new ReflectionMethod($readerClass, 'loadSpreadsheetFromFile');\nif ($m->getDeclaringClass()->getName() === BaseReader::class) {\n    throw new InvalidArgumentException(\"$readerClass does not implement loadSpreadsheetFromFile()\");\n}","typeGuard":"function canActuallyLoad(string $readerClass): bool\n{\n    return (new ReflectionMethod($readerClass, 'loadSpreadsheetFromFile'))\n        ->getDeclaringClass()->getName() !== BaseReader::class;\n}","tryCatchPattern":null,"preventionTips":["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"],"tags":["custom-reader","template-method","base-reader"],"backgroundTag":"unimplemented-abstract-method","analyzedSha":"65b080eef4d9fd11a5796135ab145883e5c3d6a6","analyzedAt":"2026-08-17T05:40:41.646Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}