PHPOffice/PhpSpreadsheet · error · PhpOffice\PhpSpreadsheet\Writer\Exception
Registered writers must implement PhpOffice\PhpSpreadsheet\W
Error message
Registered writers must implement PhpOffice\PhpSpreadsheet\Writer\IWriter
What it means
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.
Source
Thrown at src/PhpSpreadsheet/IOFactory.php:269
'gnumeric' => 'Gnumeric',
'htm', 'html' => 'Html',
// Do nothing
// We must not try to use CSV reader since it loads
// all files including Excel files etc.
'csv' => null,
default => null,
};
}
/**
* Register a writer with its type and class name.
*
* @param class-string<IWriter> $writerClass
*/
public static function registerWriter(string $writerType, string $writerClass): void
{
if (!is_a($writerClass, IWriter::class, true)) {
throw new Writer\Exception('Registered writers must implement ' . IWriter::class);
}
self::$writers[$writerType] = $writerClass;
}
/**
* Register a reader with its type and class name.
*
* @param class-string<IReader> $readerClass
*/
public static function registerReader(string $readerType, string $readerClass): void
{
if (!is_a($readerClass, IReader::class, true)) {
throw new Reader\Exception('Registered readers must implement ' . IReader::class);
}
self::$readers[$readerType] = $readerClass;
}View on GitHub (pinned to 65b080eef4)
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
Example fix
// before
class MyPdfWriter { /* no IWriter */ }
IOFactory::registerWriter('MyPdf', MyPdfWriter::class);
// after
class MyPdfWriter extends \PhpOffice\PhpSpreadsheet\Writer\Pdf\Mpdf { }
IOFactory::registerWriter('MyPdf', MyPdfWriter::class); Defensive patterns
Strategy: type-guard
Type guard
/** Guard before IOFactory::registerWriter(). */
function isRegisterableWriter(string $class): bool
{
return class_exists($class)
&& is_a($class, \PhpOffice\PhpSpreadsheet\Writer\IWriter::class, true);
}
if (!isRegisterableWriter($writerClass)) {
throw new InvalidArgumentException("$writerClass must implement IWriter");
}
IOFactory::registerWriter('MyPdf', $writerClass); Try / catch
try {
IOFactory::registerWriter('MyPdf', $configuredWriterClass);
} catch (\PhpOffice\PhpSpreadsheet\Writer\Exception $e) {
// registration is safe to skip: continue with built-in writers only
error_log("Skipping writer registration: {$e->getMessage()}");
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: Custom PDF/export wrappers written for the older PHPExcel API; registering user-supplied class names from configuration; copy-pasted registrations after a framework upgrade.
Related errors
- Registered readers must implement PhpOffice\PhpSpreadsheet\R
- No writer found for type $writerType
- Directory does not exist: $cacheDirectory
- Could not open file "{$filename}" for writing.
- Could not close file after writing.
AI-assisted analysis of PHPOffice/PhpSpreadsheet@65b080eef4 (2026-08-17).
Data as JSON: /api/errors/ed0541de0536efd6.
Report an issue: GitHub.