{"record":{"id":"ed0541de0536efd6","repo":"PHPOffice/PhpSpreadsheet","slug":"registered-writers-must-implement-phpoffice-phpspr","errorCode":null,"errorMessage":"Registered writers must implement PhpOffice\\PhpSpreadsheet\\Writer\\IWriter","messagePattern":"Registered writers must implement PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\IWriter","errorType":"exception","errorClass":"PhpOffice\\PhpSpreadsheet\\Writer\\Exception","httpStatus":null,"severity":"error","filePath":"src/PhpSpreadsheet/IOFactory.php","lineNumber":269,"sourceCode":"            'gnumeric' => 'Gnumeric',\n            'htm', 'html' => 'Html',\n            // Do nothing\n            // We must not try to use CSV reader since it loads\n            // all files including Excel files etc.\n            'csv' => null,\n            default => null,\n        };\n    }\n\n    /**\n     * Register a writer with its type and class name.\n     *\n     * @param class-string<IWriter> $writerClass\n     */\n    public static function registerWriter(string $writerType, string $writerClass): void\n    {\n        if (!is_a($writerClass, IWriter::class, true)) {\n            throw new Writer\\Exception('Registered writers must implement ' . IWriter::class);\n        }\n\n        self::$writers[$writerType] = $writerClass;\n    }\n\n    /**\n     * Register a reader with its type and class name.\n     *\n     * @param class-string<IReader> $readerClass\n     */\n    public static function registerReader(string $readerType, string $readerClass): void\n    {\n        if (!is_a($readerClass, IReader::class, true)) {\n            throw new Reader\\Exception('Registered readers must implement ' . IReader::class);\n        }\n\n        self::$readers[$readerType] = $readerClass;\n    }","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/PHPOffice/PhpSpreadsheet/blob/65b080eef4d9fd11a5796135ab145883e5c3d6a6/src/PhpSpreadsheet/IOFactory.php#L251-L287","documentation":"IOFactory::registerWriter() validates the class-string with is_a($writerClass, IWriter::class, true) before storing it in the registry. Registering a class that does not implement PhpOffice\\PhpSpreadsheet\\Writer\\IWriter - including nonexistent class names (is_a returns false) and namespace typos - throws immediately instead of failing later during save.","triggerScenarios":"IOFactory::registerWriter('MyPdf', MyPdfWriter::class) where MyPdfWriter does not implement IWriter; passing an object instance instead of the class-string; a class-string with a misspelled namespace.","commonSituations":"Custom PDF/export wrappers written for the older PHPExcel API; registering user-supplied class names from configuration; copy-pasted registrations after a framework upgrade.","solutions":["Implement the full IWriter interface (save(), getSpreadsheet(), etc.) on the custom class, or extend an existing writer such as Writer\\Pdf\\Mpdf","Fix the namespace/class-string and pass the ::class constant, never an instance","When registering dynamic names, check is_a($class, IWriter::class, true) yourself first to give a better error"],"exampleFix":"// before\nclass MyPdfWriter { /* no IWriter */ }\nIOFactory::registerWriter('MyPdf', MyPdfWriter::class);\n\n// after\nclass MyPdfWriter extends \\PhpOffice\\PhpSpreadsheet\\Writer\\Pdf\\Mpdf { }\nIOFactory::registerWriter('MyPdf', MyPdfWriter::class);","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"/** Guard before IOFactory::registerWriter(). */\nfunction isRegisterableWriter(string $class): bool\n{\n    return class_exists($class)\n        && is_a($class, \\PhpOffice\\PhpSpreadsheet\\Writer\\IWriter::class, true);\n}\n\nif (!isRegisterableWriter($writerClass)) {\n    throw new InvalidArgumentException(\"$writerClass must implement IWriter\");\n}\nIOFactory::registerWriter('MyPdf', $writerClass);","tryCatchPattern":"try {\n    IOFactory::registerWriter('MyPdf', $configuredWriterClass);\n} catch (\\PhpOffice\\PhpSpreadsheet\\Writer\\Exception $e) {\n    // registration is safe to skip: continue with built-in writers only\n    error_log(\"Skipping writer registration: {$e->getMessage()}\");\n}","preventionTips":["Always pass ClassName::class, never a string variable of unknown provenance or an instance","Extend an existing writer (e.g. Writer\\Pdf\\Mpdf) instead of reimplementing IWriter from scratch","Validate config-driven class names with is_a() at bootstrap so failures surface at startup, not at save"],"tags":["phpspreadsheet","iofactory","writer","plugin-registration","interface"],"backgroundTag":"invalid-plugin-registration","analyzedSha":"65b080eef4d9fd11a5796135ab145883e5c3d6a6","analyzedAt":"2026-08-17T05:40:41.646Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}