Commit Graph

6541 Commits

Author SHA1 Message Date
Julien Chavée bf9a0210e2 Avoid getStyle on every setValueExplicit quote-prefix check 2026-08-10 11:21:39 +02:00
oleibman 49affe510f Merge pull request #4954 from gaoflow/fix-betainv-underflow-false-convergence
Fix BETAINV false convergence when the Beta CDF underflows
2026-08-09 07:03:04 +00:00
oleibman 3518d40436 Merge branch 'master' into fix-betainv-underflow-false-convergence 2026-08-08 23:59:16 -07:00
oleibman e91b33a638 Merge pull request #4953 from gaoflow/fix-gamma-large-shape
Fix gamma-family overflow for large shape parameters
2026-08-09 03:57:53 +00:00
oleibman 5278916aab Merge pull request #4935 from kemo/perf/xlsx-writer-cell-fetch
Reduce redundant cell lookups in Xlsx writer hot path
2026-08-09 03:49:27 +00:00
oleibman d46c2ff387 Merge pull request #4955 from PHPOffice/dependabot/composer/squizlabs/php_codesniffer-3.13.6
Bump squizlabs/php_codesniffer from 3.13.5 to 3.13.6
2026-08-08 14:52:10 +00:00
dependabot[bot] 688c44f4db Bump squizlabs/php_codesniffer from 3.13.5 to 3.13.6
Bumps [squizlabs/php_codesniffer](https://github.com/PHPCSStandards/PHP_CodeSniffer) from 3.13.5 to 3.13.6.
- [Release notes](https://github.com/PHPCSStandards/PHP_CodeSniffer/releases)
- [Changelog](https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/4.x/CHANGELOG-3.x.md)
- [Commits](https://github.com/PHPCSStandards/PHP_CodeSniffer/compare/3.13.5...3.13.6)

---
updated-dependencies:
- dependency-name: squizlabs/php_codesniffer
  dependency-version: 3.13.6
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-08 08:27:06 +00:00
Vincent Gao 52da37f4a7 Fix gamma-family overflow for large shape parameters
GAMMA.INV silently returned the first bisection midpoint for alpha in
~[143, 171.62] (e.g. GAMMA.INV(0.5, 143, 1) gave 358.0 instead of
142.667) and #NUM! above that, because the Newton-step pdf evaluates
Gamma(a), b**a and value**(a-1) in linear domain, all of which overflow
even though the density itself is a small representable number. The same
pattern breaks the GAMMA.DIST, CHISQ.DIST and F.DIST densities and
GAMMALN, which computed log(Gamma(x)) through Gamma(x).

Evaluate these in log domain via the existing logGamma, and scale the
incomplete-gamma series/continued-fraction iteration cap as O(sqrt(a)),
which both expansions need to converge near x ~ a once the shape is
large (GAMMA.INV drifted from the true quantile above alpha ~5000 and
returned alpha+1 by alpha=10000; same for CHISQ.INV at high df).
2026-08-07 01:14:45 +02:00
Vincent Gao 3046ce9214 Add changelog entry for #4954 2026-08-05 12:29:20 +02:00
Vincent Gao f687d261e8 Fix BETAINV false convergence when the Beta CDF underflows
calculateInverse() treated a CDF value of exactly 0.0 as "the guess is the
root" and collapsed the bracket with $b = $a, ending the search. inverse()
already rejects probability <= 0, so a CDF of 0.0 inside the bisection can
only be a float64 underflow at a guess far below the root - never an exact
hit. It is just an ordinary "guess too low" and belongs in the existing
else branch.

The underflow is reached whenever a probe lands many standard deviations
from the mean, so the search collapses after two or three iterations and
returns whichever midpoint it was holding. BETAINV(0.5, 5000, 5000) gave
0.25, though Beta(a, a) is symmetric and its median is exactly 0.5;
BETAINV(0.5, 20000, 3) gave 0.5 against a true 0.99986. Onset is around
alpha = 1080 for beta = 1, where the closed form 0.5 ** (1 / alpha) is
available to check against.

That test also happened to stop the search for shapes where incompleteBeta
declines to evaluate at all and returns 0 for every x, so inverse() now
rejects alpha + beta above that documented limit up front: the CDF is
identically zero there, so no quantile exists to search for.

Tests cover a shape x probability grid against scipy reference values plus
three checks that need no external oracle: the symmetric median, the
Beta(alpha, 1) closed form, and the BETADIST round trip / mirror identity.
2026-08-05 12:28:05 +02:00
oleibman 3006859ecb Merge pull request #4942 from saifulferoz/feature/pivot-table-read
Feature/pivot table read
2026-08-05 03:22:00 +00:00
oleibman 47aca381d8 Merge pull request #4945 from gaoflow/fix-incomplete-gamma-convergence
Fix incomplete gamma convergence for GAMMA.DIST / CHISQ.DIST
2026-08-05 03:12:07 +00:00
Vincent Gao a0e67af496 Update the now-reachable extreme-tail case for GAMMA.INV
Merging #4946 (bracket expansion) with #4945 (accurate incomplete
gamma) means p=0.9999999 no longer hits the alpha*beta*5 safety
ceiling -- the corrected regularizedGammaP/Q resolve the true root.
Fold it into the extreme-tail data provider against the closed-form
-ln(1-p) check instead of asserting the stale clamped bound.
2026-08-04 08:28:27 +02:00
Vincent Gao 270456a20e test: cover GammaBase::incompleteGamma() directly
It has no remaining callers (kept only for BC) so nothing else exercises
it. Values checked against mpmath's gammainc(a, 0, x); the deep-tail case
(a=4, x=80) reproduces the convergence bug this PR fixes.
2026-08-04 08:25:52 +02:00
gaoflow b3023fd66c Update CHANGELOG for #4945 2026-08-04 08:25:51 +02:00
gaoflow e88cd615cc Fix incomplete gamma convergence for GAMMA.DIST / CHISQ.DIST
The incomplete gamma primitive used a fixed 32-term power series with no
convergence test, so GAMMA.DIST, GAMMADIST, CHISQ.DIST(.RT), GAMMAINV and
CHISQ.INV were grossly wrong once the series argument reached ~32
(e.g. CHISQ.DIST.RT(80, 4) returned 0.806 instead of 1.74e-16).

Replace it with the standard convergence-tested regularized incomplete
gamma: series P(a,x) for x < a+1, continued fraction Q(a,x) for x >= a+1.
CHISQ.DIST.RT now uses Q directly so the right tail stays free of
1 - P cancellation. Consolidates the duplicate copy that already existed
privately in ChiSquared onto the shared primitive.
2026-08-04 08:25:36 +02:00
oleibman 8b4b934011 Merge pull request #4946 from gaoflow/fix-gammainv-tail-bracket-clamp
Expand GAMMAINV bracket so tail quantiles are not clamped
2026-08-04 02:43:29 +00:00
oleibman 08eb18d7ae Unset PivotTableCollection at Destruct, plus some Performance Tweaks 2026-08-03 19:05:00 -07:00
saifulislamferoz 2e4cc54a0d Fix corrupt date-grouped pivot table and import ordering
Excel reported sample 3's ByQuarter sheet as corrupt: a date field
group (rangePr groupBy="quarters") was emitted without the date bounds
Excel requires. When no explicit start/end date is supplied, fall back
to the sentinel range (1900-01-01 .. 9999-12-31) so both the sharedItems
(minDate/maxDate) and the rangePr (startDate/endDate) are always present,
matching the <1/1/1900 / >12/31/9999 out-of-range group items.

Also fixes ordered_imports in Worksheet.php (php-cs-fixer CI failure):
the PivotTable\PivotTable use statement is moved before the Sparkline
imports.
2026-08-03 16:16:35 +06:00
saifulislamferoz 5efba3ee64 Increase pivot table test coverage
Cover the previously-unexercised code paths flagged by Coveralls:
- writer: month and year date grouping, and a fractional numeric interval
  (the non-integer num() path);
- reader: reading an explicit dataField subtotal attribute back from a saved
  file;
- worksheet: the getPivotTables() alias, addPivotTable(), case-insensitive
  getPivotTableByName(), the no-match lookup, and removePivotTableCollection().
2026-08-03 11:52:58 +06:00
saifulislamferoz 2046913138 Add unit tests for pivot table model accessors
Cover the PivotFieldGroup, PivotField, PivotCacheDefinition and PivotTable
accessors that were not exercised by the reader/builder tests (numeric and
date grouping getters, field-group storage on the cache definition, pivot
field setters, and PivotTable::__toString), restoring code coverage.
2026-08-03 11:52:58 +06:00
saifulislamferoz e5b0477559 Fix PHPStan errors in pivot table tests
Use getSheetByNameOrThrow() instead of the nullable getSheetByName() for
chained calls, assert non-null before dereferencing getCacheDefinition() and
getPivotTableByName(), and replace an inline array_map closure with a typed
helper so PHPStan (level 8) is satisfied.
2026-08-03 11:52:58 +06:00
saifulislamferoz 7d7f5061ae Apply php-cs-fixer style fixes to pivot table files
Shorten @see references to unqualified class names and drop a stray blank
line after the sample header docblock, matching the project's php-cs-fixer
and phpcs rules.
2026-08-03 11:52:58 +06:00
saifulislamferoz 6afe310f8a Add PivotTable samples
Adds runnable samples under samples/PivotTable demonstrating the pivot table
feature, discovered automatically by the sample browser:

- 01_Create_PivotTable: build a basic pivot (row/column/value fields) from a
  source range and save it as Xlsx.
- 02_PivotTable_Page_Filter: add a page (report filter) field.
- 03_PivotTable_Grouping: group a numeric field into ranges and a date field
  by quarter.
- 04_Read_PivotTable: write a pivot, load it back, and inspect the pivot table
  object model (source, row/column/page fields, value aggregation).

Directory is named PivotTable to match the PascalCase convention used by the
other sample categories (Table, Chart, ...).
2026-08-03 11:52:58 +06:00
saifulislamferoz c899e5cb75 Add pivot page (filter) fields and numeric/date grouping
Extends PivotTableBuilder with the two remaining common pivot capabilities.

Page fields: addPageField() places a field on the page (report filter) axis,
and the writer emits a <pageFields> section (in its correct schema position,
after colFields and before dataFields) with the field marked axis="axisPage".

Grouping: a new Worksheet\PivotTable\PivotFieldGroup value object captures how a
field is grouped, and the builder gains groupFieldByNumericRange() (fixed-width
buckets between a start and end number) and groupFieldByDate() (group a date
field by a calendar unit such as quarters or months). The grouping is written
into the pivot cache definition as a <fieldGroup> with <rangePr> and computed
<groupItems>; with refresh-on-load the spreadsheet application materialises the
buckets when it opens the file.

The generated cache definition, workbook wiring and content types remain a
structurally consistent OPC package, and pivots with page fields or grouping
read back into the object model. Tests cover page-field placement/output,
numeric range grouping, date quarter grouping, and validation of unknown
fields.
2026-08-03 11:52:55 +06:00
saifulislamferoz 5aa558b6d6 Add pivot table creation via PivotTableBuilder (Xlsx)
Adds the ability to create a new pivot table from a range of source data,
building on the existing read model and round-trip preservation.

New Worksheet\PivotTable\PivotTableBuilder provides a fluent API: point it at a
source worksheet and range (its first row supplies the field names), place
fields on the row/column axes and add value fields with an aggregation
function, then build() produces a PivotTable and registers it on a target
sheet. Source field names are resolved from the header row (RichText/inline
string headers included), and distinct values are collected for the cache
definition's sharedItems.

The model gains what generation needs: PivotTable::isGenerated(), per-field
data-field captions, subtotal-function constants on PivotField, and sharedItems
on PivotCacheDefinition.

Writer: a new Writer\Xlsx\PivotTable generates the pivotTableDefinition,
pivotCacheDefinition (with refreshOnLoad set) and an empty pivotCacheRecords
part, plus their rels. On save, generated pivot tables are assigned indices
that continue past any preserved pivot parts (so a workbook can mix loaded and
new pivots without collision) and are wired into the worksheet relationships,
workbook <pivotCaches> registry, workbook relationships and content types via
the same unparsed-data path used for preservation. Values are computed by the
spreadsheet application on open via refresh-on-load, so we don't reimplement
the aggregation engine.

Output is a structurally consistent OPC package and reads back into the object
model. Tests cover building, validation errors, the generated parts and
wiring, aggregation attributes, and mixing preserved with generated pivots.
2026-08-03 11:52:45 +06:00
saifulislamferoz 351c5e7f73 Preserve Xlsx pivot tables through a load/save round-trip
Previously, loading an Xlsx file that contained a pivot table and saving it
again silently dropped every pivot part, corrupting the user's pivot tables.
This makes the writer round-trip them intact.

Reader: alongside parsing pivot tables into the object model, the raw pivot
parts are now captured into the unparsed loaded data - the pivot table part
and its rels (per sheet), the pivot cache definition, its rels and the cache
records - each keyed by its original archive path so the relationships between
them stay valid. The workbook <pivotCaches> registry (cacheId -> cache
definition) and the pivot content-type overrides are captured too.

Writer: the preserved parts are re-added to the archive; worksheet
relationships gain a pivotTable relationship, workbook relationships gain a
pivotCacheDefinition relationship, and workbook.xml re-emits <pivotCaches>
with an r:id that matches the regenerated workbook relationship. Content types
are restored via the existing override_content_types pass-through.

The output is a structurally consistent OPC package (all rels and content
types resolve) and is stable across repeated round-trips. Tests cover part
preservation, workbook/rels/content-type wiring, and that the pivot object
model still reads back after a save.
2026-08-03 11:52:30 +06:00
saifulislamferoz 0f318f0aa6 Add read-only object model for Xlsx pivot tables
Introduces a first-class, read-only representation of pivot tables that
already exist in a loaded Xlsx file (issue #4534). Previously pivot tables
were completely inaccessible from the object model.

New model classes under Worksheet\PivotTable:
- PivotTable: name, location, source cache definition and field layout,
  with axis helpers (getRowFields/getColumnFields/getPageFields/getDataFields).
- PivotCacheDefinition: cache id, source worksheet/range and cache field names.
- PivotField: field index, name, axis placement, and data-field aggregation.

Reader\Xlsx\PivotTableReader parses a pivotTableDefinition part and its
associated pivotCacheDefinition part into that model. The main Xlsx reader
discovers pivot parts via worksheet relationships and wires them up (skipped
under setReadDataOnly). Worksheet gains getPivotTableCollection(),
getPivotTables(), addPivotTable(), getPivotTableByName(), getPivotTableNames()
and removePivotTableCollection().

This is inspection-only: it does not create, modify, recalculate or render
pivot tables, and does not change what the writer emits. A minimal pivot-table
fixture and functional tests cover reading name, location, cache source,
fields and axis placement, plus the read-data-only path.
2026-08-03 11:51:43 +06:00
oleibman 63e4e229a3 Merge pull request #4943 from saifulferoz/feature/sparklines
Add support for Excel sparklines
2026-08-03 05:40:02 +00:00
Feroz 21b641b8bc Merge branch 'master' into feature/sparklines 2026-08-02 23:52:33 +06:00
Feroz 8d2cd9f68d Merge branch 'master' into feature/sparklines 2026-08-02 23:37:03 +06:00
oleibman 85be5ac255 Merge pull request #4833 from kemo/perf/xls-reader-optimizations
Optimize XLS (BIFF8) reader performance with reduced per-cell overhead
2026-08-02 07:00:41 +00:00
oleibman 9ef98dfe8f Merge pull request #4951 from PHPOffice/dependabot/composer/friendsofphp/php-cs-fixer-3.95.17
Bump friendsofphp/php-cs-fixer from 3.95.11 to 3.95.17
2026-08-01 14:46:02 +00:00
dependabot[bot] a27503b72d Bump friendsofphp/php-cs-fixer from 3.95.11 to 3.95.17
Bumps [friendsofphp/php-cs-fixer](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer) from 3.95.11 to 3.95.17.
- [Release notes](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/releases)
- [Changelog](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/compare/v3.95.11...v3.95.17)

---
updated-dependencies:
- dependency-name: friendsofphp/php-cs-fixer
  dependency-version: 3.95.17
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-01 11:02:39 +00:00
Vincent Gao c497c12eb3 Evaluate GAMMA.INV via spreadsheet formula in tests; document bounded tail fallback 2026-07-29 04:59:19 +02:00
gaoflow 0b9dff4813 Update CHANGELOG for #4946 2026-07-28 12:18:08 +02:00
gaoflow 2a82540c1f Expand GAMMAINV bracket so tail quantiles are not clamped
GammaBase::calculateInverse fixed its upper bound at alpha*beta*5, so any
GAMMA.INV/GAMMAINV quantile larger than that was clamped to it: e.g.
GAMMAINV(0.9999, 1, 1) returned 5 rather than ~9.2103, breaking the
round-trip GAMMADIST(GAMMAINV(p)) == p.

Grow the upper bound geometrically until it brackets the root. If the CDF
stops increasing first (the series approximation is past its usable range)
keep the original bound instead of expanding into it, which also stops a
probability the series cannot reach from running the bound away.
2026-07-28 12:17:22 +02:00
saifulislamferoz bbcfc69328 Address review: use fixed namespace URIs in sparkline reader
Per review feedback on PR #4943:

- Remove the $this->ns property from Reader/Xlsx/Sparklines and the
  getNamespaces() call that populated it. The reader now navigates the XML
  with the fixed namespace URIs (Namespaces::DATA_VALIDATIONS1 for x14 and
  Namespaces::DATA_VALIDATIONS2 for xm) via SimpleXMLElement::children(),
  so it no longer depends on the prefixes a third-party writer happens to
  use.
- Move the SPARKLINE_URI constant to the Namespaces class (alongside
  STYLE_CHECKBOX_URI) and reference it from both the reader and writer.
2026-07-28 11:57:45 +06:00
saifulislamferoz 041c85060f Improve sparkline test coverage and harden reader
Add tests for previously-uncovered SparklineGroup setters (axis types,
manual limits, additional display options and colours) and a hand-crafted
Xlsx fixture exercising the reader's edge cases (non-sparkline ext, group
without sparklines, sparkline with empty sqref, missing colour elements).

Harden Reader/Xlsx/Sparklines against malformed input: guard the
sparklineGroup and sparkline iterations against a null children set so an
empty <x14:sparklineGroups> or <x14:sparklines> element no longer emits a
PHP warning.
2026-07-28 06:40:12 +06:00
saifulislamferoz 41c051f54d Add support for Excel sparklines
Implements native sparkline support (line, column, and win/loss) in the
Xlsx reader and writer, resolving issue #4941.

- New model classes under Worksheet/Sparkline: Sparkline, SparklineGroup,
  and the SparklineType enum.
- Worksheet gains a sparkline group collection with addSparkline(),
  addSparklineGroup(), getSparklineGroupCollection(), and
  removeSparklineGroupCollection(), plus deep-clone support.
- Reader/Xlsx/Sparklines parses x14:sparklineGroups from the sheet extLst.
- Writer/Xlsx/Worksheet emits x14:sparklineGroups; writeExtLst is
  refactored so conditional-formatting data bars and sparklines share a
  single extLst.
- Adds unit tests, an Xlsx round-trip test, a runnable sample, and docs.
2026-07-28 06:04:37 +06:00
oleibman 540dfb463a Merge pull request #4944 from oleibman/phpunitxml
Update phpunit xml
2026-07-27 19:39:05 +00:00
oleibman 895633344c Update phpunit xml 2026-07-27 12:34:12 -07:00
oleibman e9d4062bc7 Merge pull request #4940 from PHPOffice/dependabot/composer/dompdf/dompdf-3.1.6
Bump dompdf/dompdf from 3.1.5 to 3.1.6
2026-07-26 17:25:18 +00:00
dependabot[bot] bcf1998d9a Bump dompdf/dompdf from 3.1.5 to 3.1.6
Bumps [dompdf/dompdf](https://github.com/dompdf/dompdf) from 3.1.5 to 3.1.6.
- [Release notes](https://github.com/dompdf/dompdf/releases)
- [Commits](https://github.com/dompdf/dompdf/compare/v3.1.5...v3.1.6)

---
updated-dependencies:
- dependency-name: dompdf/dompdf
  dependency-version: 3.1.6
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-26 15:59:24 +00:00
oleibman 05e99ebf61 Merge pull request #4938 from oleibman/changelog20260712
Update ChangeLog
5.9.0
2026-07-12 19:17:39 +00:00
oleibman 60f79311d2 Update ChangeLog 2026-07-12 12:12:58 -07:00
oleibman 2601644333 Merge pull request #4907 from oleibman/nophp81
Drop Support for Php 8.1
2026-07-12 14:44:00 +00:00
oleibman 0a107a2f37 Merge pull request #4937 from oleibman/webdebug
Account for Unable to Use Web Server in Github
2026-07-12 12:29:47 +00:00
oleibman dc455c45f9 Possibly Acceptable 2026-07-12 03:20:25 -07:00
oleibman 40bdbb3c90 WIP Testing 2026-07-12 01:53:11 -07:00