In PR #4716, @RobinvanderVliet points out that Cell::setValueExplicit and Worksheet::setCellValueExplicit are strangely inconsistent, where the latter requires you to specify a DataType but the former does not. The fix in that PR is to make the latter offer a default DataType rather than requiring the parameter. While that does eliminate inconsistency, I think it does so in the wrong direction - the solution should be to eliminate the optionality in the former - an implicit value for setValueExplicit just doesn't make sense. We can't do that without a breaking change, which this is not. However, we can update the doc-block and change log to indicate our intention to make that change in the next breaking release.
PR #4687 corrected how Xls Writer generated its Dimensions records. We ignore the Dimensions record on read since it does not affect our processing in the slightest. However, the PR raises the possibility that someone might wish to see the data in the Dimensions record. (The PR did an adequate test for retrieving Dimensions data, but it is not generalizable.) To accommodate such a case, we add a new ListWorksheetDimensions function to Xls Reader, similar to ListWorksheetInfo. As luck would have it, the spreadsheet with which I tested the new function produced incorrect results for ListWorksheetInfo, which was ignoring XLS_TYPE_MULRK records. So I added the necessary code to fix ListWorksheetInfo as well.
The initial fix only converted column indices to 0-based, but overlooked
that row indices also need the same treatment per BIFF8 specification.
Changes:
- Convert firstRowIndex from 1-based to 0-based (subtract 1)
- Convert lastRowIndex from 1-based to 0-based (subtract 1)
- Update row capping logic for 65536 limit
- Fix test assertions to expect rwMic=0 and rwMac=5 (not 1 and 6)
- Enhanced documentation to clarify all DIMENSIONS indices are 0-based
This now matches the behavior observed in Excel-generated XLS files,
where a file with 3 rows × 5 columns shows: rwMic=0, rwMac=3, colMic=0, colMac=5
All tests pass (53 tests, 244 assertions).
Add proper type assertions to handle potential false returns:
- Assert file_get_contents() returns string, not false
- Assert strpos() returns int, not false
- Assert unpack() returns array, not false
This resolves all 7 PHPStan errors reported in CI while maintaining
test functionality (12 assertions, all passing).
This commit refines the BIFF8 DIMENSIONS record fix by:
1. **Optimized column index capping**: Replaced the if-statement with min()
function to ensure lastColumnIndex never exceeds 255, improving code
coverage and eliminating unreachable branches in unit tests.
2. **Added comprehensive unit tests**: Created DimensionsRecordTest.php which
directly parses the binary DIMENSIONS record (0x0200) from XLS files to
verify correct 0-based column indices.
The tests validate:
- colMic (first column) = 0 for column A (was incorrectly 1 before fix)
- colMac (last column + 1) uses proper 0-based indexing
- Column indices are correctly capped at 255 (BIFF8 limit)
These tests fail without the fix and pass with it, ensuring the DIMENSIONS
record is correctly written for compatibility with legacy XLS parsers that
expect 0-based column indices per the BIFF8 specification.
The XLS writer incorrectly used 1-based column indices in the BIFF8
DIMENSIONS record, violating the Microsoft Excel Binary File Format
specification which requires 0-based indices.
This bug caused an extra empty column to appear when converting XLS
files to other formats (e.g., CSV).
Changes:
- Modified column index initialization to subtract 1 from the result
of Coordinate::columnIndexFromString() to convert from 1-based to
0-based indexing
- Updated COLINFO loop to use the corrected 0-based lastColumnIndex
Per BIFF8 specification:
- colMic (first column) must be 0-based
- colMac (column after last column) must be 0-based
Example: For columns A-G (7 columns):
- Before: colMic=1, colMac=8 (incorrect)
- After: colMic=0, colMac=7 (correct)
Fixes#4682
Fix#4673. Our password hasher can handle different algorithms, but the workbook password (for maintaining the structure of the workbook, not for encrypting the entire workbook) currently supports only the single algorithm that was in place many years ago. Expand it, and Xlsx Reader and Writer, to be able to use, say, SHA-512, which is what Excel itself uses.
The revisions password needs to be expanded in the same way as the workbook password. It is used for file sharing, but MS has deprecated it because it feels that modern technologies introduce better ways to accomplish what it was needed for. You'll need to look pretty hard to even find it in Excel - it's not, for example, on any of the ribbons. Nevertheless, the solution here is pretty much identical to the solution for the workbook password, so I am fixing it at the same time.