Skip to content

feat(polyfill): IOFactory registerWriter/registerReader + createReade…#2

Draft
saifulferoz wants to merge 4 commits into
xiidea:mainfrom
saifulferoz:feat/iofactory-register-writers-readers
Draft

feat(polyfill): IOFactory registerWriter/registerReader + createReade…#2
saifulferoz wants to merge 4 commits into
xiidea:mainfrom
saifulferoz:feat/iofactory-register-writers-readers

Conversation

@saifulferoz

Copy link
Copy Markdown
Contributor

…rForFile

Adds the custom writer/reader registration surface of PhpSpreadsheet's IOFactory so applications that plug their own writers (e.g. an Html-based Pdf writer registered as 'Pdf', or an overridden 'Html' writer) run unchanged under the compat alias:

  • IOFactory::registerWriter/registerReader with is_a() validation against the IWriter / new Reader\IReader contracts; registered entries are consulted before the built-ins so formats can be overridden or added.
  • IOFactory::createReaderForFile: extension-based identify() first, then canRead() probing (registered readers before built-ins, Csv last since its canRead accepts any readable file). load() now routes through it, matching PhpSpreadsheet semantics, and forwards $flags.
  • New Reader\IReader interface (canRead/load + LOAD_WITH_CHARTS/ READ_DATA_ONLY/IGNORE_EMPTY_CELLS), implemented by Reader\Xlsx and Reader\Csv; createReader() return type widened to IReader.
  • New Writer\Exception and Reader\Exception (extend Compat\Exception), matching PhpSpreadsheet's error contracts for invalid registrations and unidentifiable files; both auto-alias via the prefix-swap bootstrap.
  • Tests: registration/override/restore, invalid-class throws, probing via a registered reader for an unknown extension, extension fast path, and alias resolution for the new classes. COMPAT.md/MISSING.md updated.

…rForFile

Adds the custom writer/reader registration surface of PhpSpreadsheet's
IOFactory so applications that plug their own writers (e.g. an Html-based
Pdf writer registered as 'Pdf', or an overridden 'Html' writer) run
unchanged under the compat alias:

- IOFactory::registerWriter/registerReader with is_a() validation against
  the IWriter / new Reader\IReader contracts; registered entries are
  consulted before the built-ins so formats can be overridden or added.
- IOFactory::createReaderForFile: extension-based identify() first, then
  canRead() probing (registered readers before built-ins, Csv last since
  its canRead accepts any readable file). load() now routes through it,
  matching PhpSpreadsheet semantics, and forwards $flags.
- New Reader\IReader interface (canRead/load + LOAD_WITH_CHARTS/
  READ_DATA_ONLY/IGNORE_EMPTY_CELLS), implemented by Reader\Xlsx and
  Reader\Csv; createReader() return type widened to IReader.
- New Writer\Exception and Reader\Exception (extend Compat\Exception),
  matching PhpSpreadsheet's error contracts for invalid registrations and
  unidentifiable files; both auto-alias via the prefix-swap bootstrap.
- Tests: registration/override/restore, invalid-class throws, probing via
  a registered reader for an unknown extension, extension fast path, and
  alias resolution for the new classes. COMPAT.md/MISSING.md updated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@saifulferoz
saifulferoz marked this pull request as draft July 6, 2026 03:18
saifulferoz and others added 3 commits July 6, 2026 17:53
… eager alias surface

Three PhpSpreadsheet-parity gaps hit by a real ERP report suite:

- Spreadsheet::setValueBinder()/getValueBinder(): the PhpSpreadsheet >= 2.x
  workbook-level binder API. The workbook binder wins over the legacy static
  Cell::setValueBinder(); setCellValue()/fromArray() route through it.

- new Worksheet() may now be constructed detached (both ctor args optional)
  and attached later via the new Spreadsheet::addSheet($sheet, ?$index) —
  the FilterLoopReportRunner/multi-sheet idiom. Title get/set work while
  detached; native-touching calls throw a clear message via workbookHandle().
  rebindParent() rejects cross-workbook moves.

- aliasCompatSurface(): eagerly alias every Compat class at bootstrap. The
  engine skips autoloading for instanceof and typed-parameter checks, so a
  lazily-aliased class that was only ever *returned* (never explicitly
  loaded) failed type hints like function (Worksheet $ws): the alias did not
  exist yet. Lazy aliasing stays for unimplemented classes (strict throw /
  fallback defer).

Validated against the ERP report generator suite under EASY_EXCEL_ALIAS=strict:
19/19 tests pass (previously 6 binder errors + 1 ctor error + latent
type-check failures).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…k writes

fromArray()/setCellValue() sent numeric strings to the extension as bare
scalars, letting the Go binder coerce them to float64. That corrupted
integer strings past 2^53 (e.g. 17-digit member IDs -> 9.9e16) and dropped
leading zeros ("0123" -> 123) — the exact cases PhpSpreadsheet's Xlsx
writer keeps as text.

bindValue() now marks such strings MARK_STRING so they round-trip as text,
matching PhpSpreadsheet, while numeric strings that survive a float64 round
trip stay on the fast bare-scalar path. This removes the need for a custom
StringValueBinder in consuming apps — which forced the slow per-cell path
(~5.7k rows/s) instead of bulk writeRows (~79k rows/s).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
saifulferoz added a commit to saifulferoz/easy-excel-polyfill that referenced this pull request Jul 12, 2026
…preservation in bulk writes

Mirrors saifulferoz/easy-excel da2d6f5 + 5e37c54 (php/ subtree, upstream PR xiidea/easy-excel#2).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant