PHPOffice/PhpSpreadsheet · error · PhpSpreadsheetException

Reader classes must implement their own listWorksheetInfo()

Error message

Reader classes must implement their own listWorksheetInfo() method

What it means

BaseReader::listWorksheetInfo() is a stub in the template-method style: it returns sheet metadata (name, last column, total rows, ...) only when a concrete reader overrides it. Calling it on a subclass that never implemented it hits the base throw, mirroring loadSpreadsheetFromFile().

Source

Thrown at src/PhpSpreadsheet/Reader/BaseReader.php:321

            // Open file
            $fileHandle = fopen($filename, 'rb');
        }
        if ($fileHandle === false) {
            throw new ReaderException('Could not open file ' . $filename . ' for reading.');
        }

        $this->fileHandle = $fileHandle;
    }

    /**
     * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
     *
     * @return array<int, array{worksheetName: string, lastColumnLetter: string, lastColumnIndex: int, totalRows: int, totalColumns: int, sheetState: string}>
     */
    public function listWorksheetInfo(string $filename): array
    {
        throw new PhpSpreadsheetException('Reader classes must implement their own listWorksheetInfo() method');
    }

    /**
     * Returns names of the worksheets from a file,
     * possibly without parsing the whole file to a Spreadsheet object.
     * Readers will often have a more efficient method with which
     * they can override this method.
     *
     * @return string[]
     */
    public function listWorksheetNames(string $filename): array
    {
        $returnArray = [];
        $info = $this->listWorksheetInfo($filename);
        foreach ($info as $infoArray) {
            $returnArray[] = $infoArray['worksheetName'];
        }

View on GitHub (pinned to 65b080eef4)

Solutions

  1. Implement public listWorksheetInfo(string $filename): array in the subclass, returning entries shaped per the docblock (worksheetName, lastColumnLetter, lastColumnIndex, totalRows, totalColumns, sheetState)
  2. Or extend a concrete reader whose implementation you can reuse
  3. Guard UI code to skip the info call when the reader does not really implement it (reflection check on the declaring class)

Example fix

// before
class MyReader extends BaseReader { }
(new MyReader())->listWorksheetInfo('data.foo'); // throws

// after
class MyReader extends BaseReader
{
    public function listWorksheetInfo(string $filename): array
    {
        return [
            ['worksheetName' => 'Sheet1', 'lastColumnLetter' => 'A', 'lastColumnIndex' => 0,
             'totalRows' => count(file($filename)), 'totalColumns' => 1, 'sheetState' => 'visible'],
        ];
    }
}
Defensive patterns

Strategy: type-guard

Validate before calling

$m = new ReflectionMethod($reader, 'listWorksheetInfo');
if ($m->getDeclaringClass()->getName() === BaseReader::class) {
    return []; // reader does not really implement sheet info — skip instead of crashing
}

Type guard

function implementsWorksheetInfo(string $readerClass): bool
{
    return (new ReflectionMethod($readerClass, 'listWorksheetInfo'))
        ->getDeclaringClass()->getName() !== BaseReader::class;
}

Prevention

When it happens

Trigger: A custom BaseReader subclass without an override being passed to UI/reporting code that calls listWorksheetInfo($filename); tooling that enumerates sheet metadata over any IReader implementation.

Common situations: Custom readers written only for load(); admin tooling or preview panes that additionally query worksheet info and discover the gap at runtime.

Related errors


AI-assisted analysis of PHPOffice/PhpSpreadsheet@65b080eef4 (2026-08-17). Data as JSON: /api/errors/c9babf170fb75fc3. Report an issue: GitHub.