feat: Version 3.5.2 - Configuration Stripe et gestion des immeubles
- Configuration complète Stripe pour les 3 environnements (DEV/REC/PROD) * DEV: Clés TEST Pierre (mode test) * REC: Clés TEST Client (mode test) * PROD: Clés LIVE Client (mode live) - Ajout de la gestion des bases de données immeubles/bâtiments * Configuration buildings_database pour DEV/REC/PROD * Service BuildingService pour enrichissement des adresses - Optimisations pages et améliorations ergonomie - Mises à jour des dépendances Composer - Nettoyage des fichiers obsolètes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
406
api/vendor/phpoffice/phpspreadsheet/CHANGELOG.md
vendored
406
api/vendor/phpoffice/phpspreadsheet/CHANGELOG.md
vendored
@@ -3,36 +3,267 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com)
|
||||
and this project adheres to [Semantic Versioning](https://semver.org).
|
||||
and this project adheres to [Semantic Versioning](https://semver.org). Thia is always true of the master branch. Some earlier branches remain supported and security fixes are applied to them; if the security fix represents a breaking change, it may have to be applied as a minor or patch version.
|
||||
|
||||
# 2025-02-07 - 2.3.8
|
||||
## TBD - 5.2.0
|
||||
|
||||
### Fixed
|
||||
### Added
|
||||
|
||||
- Xls writer Parser Mishandling True/False Argument. Backport of [PR #4333](https://github.com/PHPOffice/PhpSpreadsheet/pull/4333)
|
||||
- Xls writer Parser Parse By Character Not Byte. Backport of [PR #4344](https://github.com/PHPOffice/PhpSpreadsheet/pull/4344)
|
||||
- Nothing yet.
|
||||
|
||||
# 2025-01-26 - 2.3.7
|
||||
### Removed
|
||||
|
||||
### Fixed
|
||||
- Nothing yet.
|
||||
|
||||
- Backported security patch for control characters in protocol.
|
||||
- Use Composer\Pcre in Xls/Parser. Partial backport of [PR #4203](https://github.com/PHPOffice/PhpSpreadsheet/pull/4203)
|
||||
### Changed
|
||||
|
||||
# 2025-01-11 - 2.3.6
|
||||
- Nothing yet.
|
||||
|
||||
### Moved
|
||||
|
||||
- Nothing yet.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Worksheet::getHashCode is no longer needed.
|
||||
- Nothing yet.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Backported security patch for Html navigation.
|
||||
- Change hash code for worksheet. Backport of [PR #4207](https://github.com/PHPOffice/PhpSpreadsheet/pull/4207)
|
||||
- Retitling cloned worksheets. Backport of [PR #4302](https://github.com/PHPOffice/PhpSpreadsheet/pull/4302)
|
||||
- Nothing yet.
|
||||
|
||||
## 2025-09-03 - 5.1.0
|
||||
|
||||
# 2024-12-26 - 2.3.5
|
||||
### Added
|
||||
|
||||
- Add Conditional Formatting with IconSet (Xlsx only). [Issue #4560](https://github.com/PHPOffice/PhpSpreadsheet/issues/4560) [PR #4574](https://github.com/PHPOffice/PhpSpreadsheet/pull/4574)
|
||||
- Copy cell adjusting formula. [Issue #1203](https://github.com/PHPOffice/PhpSpreadsheet/issues/1203) [PR #4577](https://github.com/PHPOffice/PhpSpreadsheet/pull/4577)
|
||||
- splitRange and ProtectedRange. [Issue #1457](https://github.com/PHPOffice/PhpSpreadsheet/issues/1457) [PR #4580](https://github.com/PHPOffice/PhpSpreadsheet/pull/4580)
|
||||
- Option to create Blank Sheet if LoadSheetsOnly doesn't find any. [PR #4618](https://github.com/PHPOffice/PhpSpreadsheet/pull/4618)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Google-only formulas exported from Google Sheets. [Issue #1637](https://github.com/PHPOffice/PhpSpreadsheet/issues/1637) [PR #4579](https://github.com/PHPOffice/PhpSpreadsheet/pull/4579)
|
||||
- Maximum column width. [PR #4581](https://github.com/PHPOffice/PhpSpreadsheet/pull/4581)
|
||||
- PrintArea after row/column delete. [Issue #2912](https://github.com/PHPOffice/PhpSpreadsheet/issues/2912) [PR #4598](https://github.com/PHPOffice/PhpSpreadsheet/pull/4598)
|
||||
- Remove deprecated imagedestroy call. [PR #4625](https://github.com/PHPOffice/PhpSpreadsheet/pull/4625)
|
||||
- Excel 2007 problem with newlines. [Issue #4619](https://github.com/PHPOffice/PhpSpreadsheet/issues/4619) [PR #4620](https://github.com/PHPOffice/PhpSpreadsheet/pull/4620)
|
||||
- Compatibility changes for Php 8.5. [PR #4601](https://github.com/PHPOffice/PhpSpreadsheet/pull/4601) [PR #4611](https://github.com/PHPOffice/PhpSpreadsheet/pull/4611)
|
||||
|
||||
## 2025-08-10 - 5.0.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- Images will be loaded from an external source (e.g. http://example.com/img.png) only if the reader is explicitly set to allow it via `$reader->setAllowExternalImages(true)`. We do not believe that loading of external images is a widely used feature.
|
||||
- Deletion of items deprecated in Release 4. See "removed" below.
|
||||
- Move some properties from Base Reader to Html Reader. [PR #4551](https://github.com/PHPOffice/PhpSpreadsheet/pull/4551)
|
||||
- DefaultValueBinder will treat integers with more than 15 digits as strings. [Issue #4522](https://github.com/PHPOffice/PhpSpreadsheet/issues/4522) [PR #4527](https://github.com/PHPOffice/PhpSpreadsheet/pull/4527)
|
||||
|
||||
### Removed
|
||||
|
||||
- Theme public constants COLOR_SCHEME_2013_PLUS_NAME (use COLOR_SCHEME_2013_2022_NAME) and COLOR_SCHEME_2013_PLUS (use COLOR_SCHEME_2013_2022).
|
||||
|
||||
### Fixed
|
||||
|
||||
- Additional floating-point precision changes. [Issue #1324](https://github.com/PHPOffice/PhpSpreadsheet/issues/1324) [PR #4575](https://github.com/PHPOffice/PhpSpreadsheet/pull/4575)
|
||||
- Header/Footer images expand location. [Issue #484](https://github.com/PHPOffice/PhpSpreadsheet/issues/484) [Issue #1318](https://github.com/PHPOffice/PhpSpreadsheet/issues/1318) [PR #4572](https://github.com/PHPOffice/PhpSpreadsheet/pull/4572)
|
||||
- Create uninitialized cell if used in calculation. [Issue #4558](https://github.com/PHPOffice/PhpSpreadsheet/issues/4558) [Issue #4530](https://github.com/PHPOffice/PhpSpreadsheet/issues/4530) [PR #4565](https://github.com/PHPOffice/PhpSpreadsheet/pull/4565)
|
||||
- Shared/Date::isDateTime handle cells which calculate as arrays. [Issue #4557](https://github.com/PHPOffice/PhpSpreadsheet/issues/4557) [PR #4562](https://github.com/PHPOffice/PhpSpreadsheet/pull/4562)
|
||||
- Xlsx Writer eliminate xml:space from non-text nodes. [Issue #4542](https://github.com/PHPOffice/PhpSpreadsheet/issues/4542) [PR #4556](https://github.com/PHPOffice/PhpSpreadsheet/pull/4556)
|
||||
|
||||
## 2025-07-23 - 4.5.0
|
||||
|
||||
### Added
|
||||
|
||||
- Add to all readers the option to allow or forbid fetching external images. This is unconditionally allowed now. The default will be set to "allow", so no code changes are necessary. However, we are giving consideration to changing the default. [PR #4543](https://github.com/PHPOffice/PhpSpreadsheet/pull/4543)
|
||||
- Address Excel Inappropriate Number Format Substitution. [PR #4532](https://github.com/PHPOffice/PhpSpreadsheet/pull/4532)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Html Writer Conditional Formatting Inline Css. [Issue #4539](https://github.com/PHPOffice/PhpSpreadsheet/issues/4539) [PR #4541](https://github.com/PHPOffice/PhpSpreadsheet/pull/4541)
|
||||
- Do not use htmlspecialchars when formatting XML. [Issue #4537](https://github.com/PHPOffice/PhpSpreadsheet/issues/4537) [PR #4540](https://github.com/PHPOffice/PhpSpreadsheet/pull/4540)
|
||||
- Writer Html/Pdf support RTL alignment of tables. [Issue #1104](https://github.com/PHPOffice/PhpSpreadsheet/issues/1104) [PR #4535](https://github.com/PHPOffice/PhpSpreadsheet/pull/4535)
|
||||
- Xlsx Reader use dynamic arrays if spreadsheet did so. [PR #4533](https://github.com/PHPOffice/PhpSpreadsheet/pull/4533)
|
||||
- Ods Reader Nested table-row. [Issue #4528](https://github.com/PHPOffice/PhpSpreadsheet/issues/4528) [Issue #2507](https://github.com/PHPOffice/PhpSpreadsheet/issues/2507) [PR #4531](https://github.com/PHPOffice/PhpSpreadsheet/pull/4531)
|
||||
- Recognize application/x-empty mimetype. [Issue #4521](https://github.com/PHPOffice/PhpSpreadsheet/issues/4521) [PR #4524](https://github.com/PHPOffice/PhpSpreadsheet/pull/4524)
|
||||
- Micro-optimization in getSheetByName. [PR #4499](https://github.com/PHPOffice/PhpSpreadsheet/pull/4499)
|
||||
- Bug in resizeMatricesExtend. [Issue #4451](https://github.com/PHPOffice/PhpSpreadsheet/issues/4451) [PR #4474](https://github.com/PHPOffice/PhpSpreadsheet/pull/4474)
|
||||
- Allow Replace of Dummy Function with Custom Function. [PR #4544](https://github.com/PHPOffice/PhpSpreadsheet/pull/4544)
|
||||
- Preserve 0x0a in Strings if Desired. [Issue #347](https://github.com/PHPOffice/PhpSpreadsheet/issues/347) [PR #4536](https://github.com/PHPOffice/PhpSpreadsheet/pull/4536)
|
||||
|
||||
## 2025-06-22 - 4.4.0
|
||||
|
||||
### Added
|
||||
|
||||
- VSTACK and HSTACK. [Issue #4485](https://github.com/PHPOffice/PhpSpreadsheet/issues/4485) [PR #4492](https://github.com/PHPOffice/PhpSpreadsheet/pull/4492)
|
||||
- TOCOL and TOROW. [PR #4493](https://github.com/PHPOffice/PhpSpreadsheet/pull/4493)
|
||||
- Support Current Office Theme. [PR #4500](https://github.com/PHPOffice/PhpSpreadsheet/pull/4500)
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Theme constants COLOR_SCHEME_2013_PLUS_NAME (use COLOR_SCHEME_2013_2022_NAME) and COLOR_SCHEME_2013_PLUS (use COLOR_SCHEME_2013_2022).
|
||||
|
||||
### Fixed
|
||||
|
||||
- Various Writers RichText TextElement Should Inherit Cell Style. [Issue #1154](https://github.com/PHPOffice/PhpSpreadsheet/issues/1154) [PR #4487](https://github.com/PHPOffice/PhpSpreadsheet/pull/4487)
|
||||
- Minor Changes to FILTER function. [PR #4491](https://github.com/PHPOffice/PhpSpreadsheet/pull/4491)
|
||||
- Allow Xlsx Reader/Writer to support Font Charset. [Issue #2760](https://github.com/PHPOffice/PhpSpreadsheet/issues/2760) [PR #4501](https://github.com/PHPOffice/PhpSpreadsheet/pull/4501)
|
||||
- AutoColor for LibreOffice Dark Mode [Discussion 4502](https://github.com/PHPOffice/PhpSpreadsheet/discussions/4502) [PR #4503](https://github.com/PHPOffice/PhpSpreadsheet/pull/4503)
|
||||
- Xlsx Style Writer Minor Refactoring. [PR #4508](https://github.com/PHPOffice/PhpSpreadsheet/pull/4508)
|
||||
- Allow Xlsx Reader to Specify ParseHuge. [Issue #4260](https://github.com/PHPOffice/PhpSpreadsheet/issues/4260) [PR #4515](https://github.com/PHPOffice/PhpSpreadsheet/pull/4515)
|
||||
|
||||
## 2025-05-26 - 4.3.1
|
||||
|
||||
### Fixed
|
||||
|
||||
- Regression in Date::stringToExcel. [Issue #4488](https://github.com/PHPOffice/PhpSpreadsheet/issues/4488) [PR #4489](https://github.com/PHPOffice/PhpSpreadsheet/pull/4489)
|
||||
|
||||
## 2025-05-25 - 4.3.0
|
||||
|
||||
### Added
|
||||
|
||||
- Xml Reader recognize indents. [Issue #4448](https://github.com/PHPOffice/PhpSpreadsheet/issues/4448) [PR #4449](https://github.com/PHPOffice/PhpSpreadsheet/pull/4449)
|
||||
|
||||
### Changed
|
||||
|
||||
- Phpstan Level 10.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Micro-optimization for excelToDateTimeObject. [Issue #4438](https://github.com/PHPOffice/PhpSpreadsheet/issues/4438) [PR #4442](https://github.com/PHPOffice/PhpSpreadsheet/pull/4442)
|
||||
- Removing Columns/Rows Containing Merged Cells. [Issue #282](https://github.com/PHPOffice/PhpSpreadsheet/issues/282) [PR #4465](https://github.com/PHPOffice/PhpSpreadsheet/pull/4465)
|
||||
- Print Area and Row Break. [Issue #1275](https://github.com/PHPOffice/PhpSpreadsheet/issues/1275) [PR #4450](https://github.com/PHPOffice/PhpSpreadsheet/pull/4450)
|
||||
- Copy Styles after insertNewColumnBefore. [Issue #1425](https://github.com/PHPOffice/PhpSpreadsheet/issues/1425) [PR #4468](https://github.com/PHPOffice/PhpSpreadsheet/pull/4468)
|
||||
- Xls Writer Treat Hyperlink Starting with # as Internal. [Issue #56](https://github.com/PHPOffice/PhpSpreadsheet/issues/56) [PR #4453](https://github.com/PHPOffice/PhpSpreadsheet/pull/4453)
|
||||
- More Precision for Float to String Casts. [Issue #3899](https://github.com/PHPOffice/PhpSpreadsheet/issues/3899) [PR #4479](https://github.com/PHPOffice/PhpSpreadsheet/pull/4479)
|
||||
- Hyperlink Styles. [Issue #1632](https://github.com/PHPOffice/PhpSpreadsheet/issues/1632) [PR #4478](https://github.com/PHPOffice/PhpSpreadsheet/pull/4478)
|
||||
- ODS Handling of Ceiling and Floor. [Issue #477](https://github.com/PHPOffice/PhpSpreadsheet/issues/407) [PR #4466](https://github.com/PHPOffice/PhpSpreadsheet/pull/4466)
|
||||
- Xlsx Reader Do Not Process Printer Settings for Dataonly. [Issue #4477](https://github.com/PHPOffice/PhpSpreadsheet/issues/4477) [PR #4480](https://github.com/PHPOffice/PhpSpreadsheet/pull/4480)
|
||||
|
||||
## 2025-04-16 - 4.2.0
|
||||
|
||||
### Added
|
||||
|
||||
- Add ability to add custom functions to Calculation. [PR #4390](https://github.com/PHPOffice/PhpSpreadsheet/pull/4390)
|
||||
- Add FormulaRange to IgnoredErrors. [PR #4393](https://github.com/PHPOffice/PhpSpreadsheet/pull/4393)
|
||||
- TextGrid improvements. [PR #4418](https://github.com/PHPOffice/PhpSpreadsheet/pull/4418)
|
||||
- Permit read to class which extends Spreadsheet. [Discussion #4402](https://github.com/PHPOffice/PhpSpreadsheet/discussions/4402) [PR #4404](https://github.com/PHPOffice/PhpSpreadsheet/pull/4404)
|
||||
- Conditional and table formatting support for html writer [PR #4412](https://github.com/PHPOffice/PhpSpreadsheet/pull/4412)
|
||||
|
||||
### Changed
|
||||
|
||||
- Phpstan Version 2. [PR #4384](https://github.com/PHPOffice/PhpSpreadsheet/pull/4384)
|
||||
- Start migration to Phpstan level 9. [PR #4396](https://github.com/PHPOffice/PhpSpreadsheet/pull/4396)
|
||||
- Calculation locale logic moved to separate class. [PR #4398](https://github.com/PHPOffice/PhpSpreadsheet/pull/4398)
|
||||
- TREND_POLYNOMIAL_* and TREND_BEST_FIT do not work, and are changed to throw Exceptions if attempted. (TREND_BEST_FIT_NO_POLY works.) An attempt to use an unknown trend type will now also throw an exception. [Issue #4400](https://github.com/PHPOffice/PhpSpreadsheet/issues/4400) [PR #4339](https://github.com/PHPOffice/PhpSpreadsheet/pull/4339)
|
||||
- Month parameter of DATE function will now return VALUE if an ordinal string (e.g. '3rd') is used, but will accept bool or null. [PR #4420](https://github.com/PHPOffice/PhpSpreadsheet/pull/4420)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Ignore fractional part of Drawing Shadow Alpha. [Issue #4415](https://github.com/PHPOffice/PhpSpreadsheet/issues/4415) [PR #4417](https://github.com/PHPOffice/PhpSpreadsheet/pull/4417)
|
||||
- BIN2DEC, OCT2DEC, and HEX2DEC return numbers rather than strings. [Issue #4383](https://github.com/PHPOffice/PhpSpreadsheet/issues/4383) [PR #4389](https://github.com/PHPOffice/PhpSpreadsheet/pull/4389)
|
||||
- Fix TREND_BEST_FIT_NO_POLY. [Issue #4400](https://github.com/PHPOffice/PhpSpreadsheet/issues/4400) [PR #4339](https://github.com/PHPOffice/PhpSpreadsheet/pull/4339)
|
||||
- Ods Reader No DataType for Null Value. [Issue #4435](https://github.com/PHPOffice/PhpSpreadsheet/issues/4435) [PR #4436](https://github.com/PHPOffice/PhpSpreadsheet/pull/4436)
|
||||
- Column widths not preserved when using read filter. [Issue #4416](https://github.com/PHPOffice/PhpSpreadsheet/issues/4416) [PR #4423](https://github.com/PHPOffice/PhpSpreadsheet/pull/4423)
|
||||
- Fix typo in Style exportArray quotePrefix. [Issue #4422](https://github.com/PHPOffice/PhpSpreadsheet/issues/4422) [PR #4424](https://github.com/PHPOffice/PhpSpreadsheet/pull/4424)
|
||||
- Tweak Spreadsheet clone. [PR #4419](https://github.com/PHPOffice/PhpSpreadsheet/pull/4419)
|
||||
- Better handling of Chart DisplayBlanksAs. [Issue #4411](https://github.com/PHPOffice/PhpSpreadsheet/issues/4411) [PR #4414](https://github.com/PHPOffice/PhpSpreadsheet/pull/4414)
|
||||
|
||||
## 2025-03-02 - 4.1.0
|
||||
|
||||
### Added
|
||||
|
||||
- Support Justify Last Line. [Issue #4374](https://github.com/PHPOffice/PhpSpreadsheet/issues/4374) [PR #4373](https://github.com/PHPOffice/PhpSpreadsheet/pull/4373)
|
||||
- Allow Spreadsheet clone. [PR #4370](https://github.com/PHPOffice/PhpSpreadsheet/pull/4370)
|
||||
|
||||
### Changed
|
||||
|
||||
- ListWorksheetInfo will now return sheetState (visible, hidden, veryHidden). [Issue #4345](https://github.com/PHPOffice/PhpSpreadsheet/issues/4345) [PR #4366](https://github.com/PHPOffice/PhpSpreadsheet/pull/4366)
|
||||
- Start migration to Phpstan 2. [PR #4359](https://github.com/PHPOffice/PhpSpreadsheet/pull/4359)
|
||||
- IOFactory identify can return, and createReader and CreateWriter can accept, a class name rather than a file type. [Issue #4357](https://github.com/PHPOffice/PhpSpreadsheet/issues/4357) [PR #4361](https://github.com/PHPOffice/PhpSpreadsheet/pull/4361)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Refactor Helper/Html. [PR #4359](https://github.com/PHPOffice/PhpSpreadsheet/pull/4359)
|
||||
- Handle #REF! as Argument to AVERAGEIF/COUNTIF/SUMIF. [Issue #4381](https://github.com/PHPOffice/PhpSpreadsheet/issues/4381) [PR #4382](https://github.com/PHPOffice/PhpSpreadsheet/pull/4382)
|
||||
- Ignore ignoredErrors when not applicable. [Issue #4375](https://github.com/PHPOffice/PhpSpreadsheet/issues/4375) [PR #4377](https://github.com/PHPOffice/PhpSpreadsheet/pull/4377)
|
||||
- Better handling of defined names on sheets whose titles include apostrophes. [Issue #4356](https://github.com/PHPOffice/PhpSpreadsheet/issues/4356) [Issue #4362](https://github.com/PHPOffice/PhpSpreadsheet/issues/4362) [Issue #4376](https://github.com/PHPOffice/PhpSpreadsheet/issues/4376) [PR #4360](https://github.com/PHPOffice/PhpSpreadsheet/pull/4360)
|
||||
- Partial solution for removing rows or columns that include edge ranges. [Issue #1449](https://github.com/PHPOffice/PhpSpreadsheet/issues/1449) [PR #3528](https://github.com/PHPOffice/PhpSpreadsheet/pull/3528)
|
||||
- Prefer mb_str_split to str_split. [PR #3341](https://github.com/PHPOffice/PhpSpreadsheet/pull/3341)
|
||||
|
||||
## 2025-02-08 - 4.0.0
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
- Data Validations will be stored by worksheet, not cell. Index can be one or more cells or cell ranges. [Issue #797](https://github.com/PHPOffice/PhpSpreadsheet/issues/797) [Issue #4091](https://github.com/PHPOffice/PhpSpreadsheet/issues/4091) [Issue #4206](https://github.com/PHPOffice/PhpSpreadsheet/issues/4206) [PR #4240](https://github.com/PHPOffice/PhpSpreadsheet/pull/4240)
|
||||
- Conditional Formatting adds Priority property and handles overlapping ranges better. [Issue #4312](https://github.com/PHPOffice/PhpSpreadsheet/issues/4312) [Issue #4318](https://github.com/PHPOffice/PhpSpreadsheet/issues/4318) [PR #4314](https://github.com/PHPOffice/PhpSpreadsheet/pull/4314)
|
||||
- Csv Reader will no longer auto-detect Mac line endings by default. Prior behavior can be explicitly enabled via `setTestAutoDetect(true)`, and it will not be possible at all with Php9+. [Issue #4092](https://github.com/PHPOffice/PhpSpreadsheet/issues/4092) [PR #4340](https://github.com/PHPOffice/PhpSpreadsheet/pull/4340)
|
||||
- Html Writer will now use "better boolean" logic. Booleans will now be output by default as TRUE/FALSE rather than 1/null-string. Prior behavior can be explicitly enabled via `setBetterBoolean(false)`. [PR #4340](https://github.com/PHPOffice/PhpSpreadsheet/pull/4340)
|
||||
- Xlsx Writer will now use false as the default for `forceFullCalc`. This affects writes with `preCalculateFormulas` set to false. Prior behavior can be explicitly enabled via `setForceFullCalc(null)`.[PR #4340](https://github.com/PHPOffice/PhpSpreadsheet/pull/4340)
|
||||
- Deletion of items deprecated in Release 3. See "removed" below.
|
||||
|
||||
### Added
|
||||
|
||||
- Pdf Charts and Drawings. [Discussion #4129](https://github.com/PHPOffice/PhpSpreadsheet/discussions/4129) [Discussion #4168](https://github.com/PHPOffice/PhpSpreadsheet/discussions/4168) [PR #4327](https://github.com/PHPOffice/PhpSpreadsheet/pull/4327)
|
||||
- Allow spreadsheet serialization. [Discussion #4324](https://github.com/PHPOffice/PhpSpreadsheet/discussions/4324) [Issue #1741](https://github.com/PHPOffice/PhpSpreadsheet/issues/1741) [Issue #1757](https://github.com/PHPOffice/PhpSpreadsheet/issues/1757) [PR #4326](https://github.com/PHPOffice/PhpSpreadsheet/pull/4326)
|
||||
|
||||
### Removed
|
||||
|
||||
- Worksheet::getStyles - no replacement. [PR #4330](https://github.com/PHPOffice/PhpSpreadsheet/pull/4330)
|
||||
- The following items were deprecated in release 3 and are now removed.
|
||||
- Drawing::setIsUrl - no replacement.
|
||||
- Settings::setLibXmlLoaderOptions() and Settings::getLibXmlLoaderOptions() - no replacement.
|
||||
- Worksheet::getHashCode - no replacement.
|
||||
- IReader::SKIP_EMPTY_CELLS - use its alias IGNORE_EMPTY_CELLS instead.
|
||||
- Worksheet::getProtectedCells - use getProtectedCellRanges instead.
|
||||
- Writer/Html::isMpdf property - use instanceof Mpdf instead.
|
||||
|
||||
### Changed
|
||||
|
||||
- Nothing yet.
|
||||
|
||||
### Moved
|
||||
|
||||
- Nothing yet.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Nothing yet.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Xls writer Parser Mishandling True/False Argument. [Issue #4331](https://github.com/PHPOffice/PhpSpreadsheet/issues/4331) [PR #4333](https://github.com/PHPOffice/PhpSpreadsheet/pull/4333)
|
||||
- Xls writer Parser Parse By Character Not Byte. [PR #4344](https://github.com/PHPOffice/PhpSpreadsheet/pull/4344)
|
||||
- Minor changes to dynamic array calculations exposed by using explicit array return types in some tests. [PR #4328](https://github.com/PHPOffice/PhpSpreadsheet/pull/4328)
|
||||
|
||||
## 2025-01-26 - 3.9.0
|
||||
|
||||
### Added
|
||||
|
||||
- Methods to get style for row or column. [PR #4317](https://github.com/PHPOffice/PhpSpreadsheet/pull/4317)
|
||||
- Method for duplicating worksheet in spreadsheet. [PR #4315](https://github.com/PHPOffice/PhpSpreadsheet/pull/4315)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Security patch for control characters in protocol.
|
||||
- Ods Reader Sheet Names with Period. [Issue #4311](https://github.com/PHPOffice/PhpSpreadsheet/issues/4311) [PR #4313](https://github.com/PHPOffice/PhpSpreadsheet/pull/4313)
|
||||
- Mpdf and Tcpdf Hidden Columns and Merged Cells. [Issue #4319](https://github.com/PHPOffice/PhpSpreadsheet/issues/4319) [PR #4320](https://github.com/PHPOffice/PhpSpreadsheet/pull/4320)
|
||||
- Html Writer Allow mailto. [Issue #4316](https://github.com/PHPOffice/PhpSpreadsheet/issues/4316) [PR #4322](https://github.com/PHPOffice/PhpSpreadsheet/pull/4322)
|
||||
- Use composer/pcre rather than preg_* in Writer. [PR #4323](https://github.com/PHPOffice/PhpSpreadsheet/pull/4323)
|
||||
|
||||
## 2025-01-11 - 3.8.0
|
||||
|
||||
### Added
|
||||
|
||||
- CHOOSECOLS, CHOOSEROWS, DROP, TAKE, and EXPAND. [PR #4286](https://github.com/PHPOffice/PhpSpreadsheet/pull/4286)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Security patch for Html navigation.
|
||||
- Xlsx Reader Shared Formula with Boolean Result. Partial solution for [Issue #4280](https://github.com/PHPOffice/PhpSpreadsheet/issues/4280) [PR #4281](https://github.com/PHPOffice/PhpSpreadsheet/pull/4281)
|
||||
- Retitling cloned Worksheets. [Issue #641](https://github.com/PHPOffice/PhpSpreadsheet/issues/641) [PR #4302](https://github.com/PHPOffice/PhpSpreadsheet/pull/4302)
|
||||
- Extremely limited support for GROUPBY function. Partial response to [Issue #4282](https://github.com/PHPOffice/PhpSpreadsheet/issues/4282) [PR #4283](https://github.com/PHPOffice/PhpSpreadsheet/pull/4283)
|
||||
|
||||
## 2024-12-26 - 3.7.0
|
||||
|
||||
### Deprecated
|
||||
|
||||
@@ -40,58 +271,145 @@ and this project adheres to [Semantic Versioning](https://semver.org).
|
||||
|
||||
### Fixed
|
||||
|
||||
- More context options may be needed for http(s) image. Backport of [PR #4276](https://github.com/PHPOffice/PhpSpreadsheet/pull/4276)
|
||||
- Backported security patches for Samples.
|
||||
- Backported security patches for Html Writer.
|
||||
- Security patches for Samples.
|
||||
- Security patches for Html Writer.
|
||||
- Avoid unexpected charset in currency symbol. [PR #4279](https://github.com/PHPOffice/PhpSpreadsheet/pull/4279)
|
||||
- Add forceFullCalc option to Xlsx Writer. [Issue #4269](https://github.com/PHPOffice/PhpSpreadsheet/issues/4269) [PR #4271](https://github.com/PHPOffice/PhpSpreadsheet/pull/4271)
|
||||
- More context options may be needed for http(s) image. [Php issue 17121](https://github.com/php/php-src/issues/17121) [PR #4276](https://github.com/PHPOffice/PhpSpreadsheet/pull/4276)
|
||||
- Coverage-related tweaks to Xls Reader. [PR #4277](https://github.com/PHPOffice/PhpSpreadsheet/pull/4277)
|
||||
- Several fixed to ODS Writer. [Issue #4261](https://github.com/PHPOffice/PhpSpreadsheet/issues/4261) [PR #4263](https://github.com/PHPOffice/PhpSpreadsheet/pull/4263) [PR #4264](https://github.com/PHPOffice/PhpSpreadsheet/pull/4264) [PR #4266](https://github.com/PHPOffice/PhpSpreadsheet/pull/4266)
|
||||
|
||||
## 2024-12-08 - 2.3.4
|
||||
## 2024-12-08 - 3.6.0
|
||||
|
||||
### Fixed
|
||||
### Added
|
||||
|
||||
- Fix Minor Break Handling Drawings. Backport of [PR #4244](https://github.com/PHPOffice/PhpSpreadsheet/pull/4244)
|
||||
- Swapped Row and Column Indexes in Reference Helper. Backport of [PR #4247](https://github.com/PHPOffice/PhpSpreadsheet/pull/4247)
|
||||
- Upgrade locked version of Dompdf (Php8.4 compatibility).
|
||||
- Remove unnecessary files from Composer package.
|
||||
|
||||
## 2024-11-22 - 2.3.3
|
||||
- Nothing yet.
|
||||
|
||||
### Changed
|
||||
|
||||
- Settings::libXmlLoaderOptions is ignored. Backport of [PR #4233](https://github.com/PHPOffice/PhpSpreadsheet/pull/4233)
|
||||
- Nothing yet.
|
||||
|
||||
### Moved
|
||||
|
||||
- Nothing yet.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Nothing yet.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Html Reader/Writer Better Handling of Booleans. [PR #4257](https://github.com/PHPOffice/PhpSpreadsheet/pull/4257)
|
||||
- Fill Patterns/Colors When Xml Attributes are Missing. [Issue #4248](https://github.com/PHPOffice/PhpSpreadsheet/issues/4248) [PR #4250](https://github.com/PHPOffice/PhpSpreadsheet/pull/4250)
|
||||
- Remove Unneccesary files from Composer Package. [PR #4262](https://github.com/PHPOffice/PhpSpreadsheet/pull/4262)
|
||||
- Swapped row and column indexes in ReferenceHelper. [Issue #4246](https://github.com/PHPOffice/PhpSpreadsheet/issues/4246) [PR #4247](https://github.com/PHPOffice/PhpSpreadsheet/pull/4247)
|
||||
- Fix minor break handling drawings. [Issue #4241](https://github.com/PHPOffice/PhpSpreadsheet/issues/4241) [PR #4244](https://github.com/PHPOffice/PhpSpreadsheet/pull/4244)
|
||||
- Ignore cell formatting when the format is a single @. [Issue #4242](https://github.com/PHPOffice/PhpSpreadsheet/issues/4242) [PR #4243](https://github.com/PHPOffice/PhpSpreadsheet/pull/4243)
|
||||
- Upgrade Dompdf to Php-8.4 compatible version [PR #4267](https://github.com/PHPOffice/PhpSpreadsheet/pull/4267)
|
||||
|
||||
## 2024-11-22 - 3.5.0
|
||||
|
||||
### Added
|
||||
|
||||
- Nothing yet.
|
||||
|
||||
### Changed
|
||||
|
||||
- Settings::libXmlLoaderOptions is ignored. [PR #4233](https://github.com/PHPOffice/PhpSpreadsheet/pull/4233)
|
||||
|
||||
### Moved
|
||||
|
||||
- Nothing yet.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Settings::setLibXmlLoaderOptions() and Settings::getLibXmlLoaderOptions() are no longer needed - no replacement.
|
||||
|
||||
## 2024-11-10 - 2.3.2
|
||||
- Worksheet::getHashCode is no longer needed.
|
||||
|
||||
### Fixed
|
||||
|
||||
- 2.3.1 omitted.
|
||||
- Backported security patches.
|
||||
- Write ignoredErrors Tag Before Drawings. Backport of [PR #4212](https://github.com/PHPOffice/PhpSpreadsheet/pull/4212) intended for 3.4.0.
|
||||
- Changes to ROUNDDOWN/ROUNDUP/TRUNC. Backport of [PR #4214](https://github.com/PHPOffice/PhpSpreadsheet/pull/4214) intended for 3.4.0.
|
||||
- Add support for `<s>` tag when converting HTML to RichText. [Issue #4223](https://github.com/PHPOffice/PhpSpreadsheet/issues/4223) [PR #4224](https://github.com/PHPOffice/PhpSpreadsheet/pull/4224)
|
||||
- Change hash code for worksheet. [Issue #4192](https://github.com/PHPOffice/PhpSpreadsheet/issues/4192) [PR #4207](https://github.com/PHPOffice/PhpSpreadsheet/pull/4207)
|
||||
|
||||
## 2024-11-10 - 3.4.0
|
||||
|
||||
### Security Fix
|
||||
|
||||
- Several security patches.
|
||||
|
||||
### Added
|
||||
|
||||
- Method to Test Whether Csv Will Be Affected by Php9. Backport of [PR #4189](https://github.com/PHPOffice/PhpSpreadsheet/pull/4189) intended for 3.4.0.
|
||||
|
||||
## 2024-09-29 - 2.3.0
|
||||
|
||||
### Fixed
|
||||
|
||||
- Backported security patches.
|
||||
- Improve Xlsx Reader speed (backport of PR #4153 intended for 3.0.0). [Issue #3917](https://github.com/PHPOffice/PhpSpreadsheet/issues/3917)
|
||||
- Change to Csv Reader (see below under Deprecated). Backport of PR #4162 intended for 3.0.0. [Issue #4161](https://github.com/PHPOffice/PhpSpreadsheet/issues/4161)
|
||||
- Tweak to AMORDEGRC. Backport of PR #4164 intended for 3.0.0.
|
||||
- Add Dynamic valueBinder Property to Spreadsheet and Readers. [Issue #1395](https://github.com/PHPOffice/PhpSpreadsheet/issues/1395) [PR #4185](https://github.com/PHPOffice/PhpSpreadsheet/pull/4185)
|
||||
- Allow Omitting Chart Border. [Issue #562](https://github.com/PHPOffice/PhpSpreadsheet/issues/562) [PR #4188](https://github.com/PHPOffice/PhpSpreadsheet/pull/4188)
|
||||
- Method to Test Whether Csv Will Be Affected by Php9. [PR #4189](https://github.com/PHPOffice/PhpSpreadsheet/pull/4189)
|
||||
|
||||
### Changed
|
||||
|
||||
- Refactor Xls Reader. [PR #4118](https://github.com/PHPOffice/PhpSpreadsheet/pull/4118)
|
||||
|
||||
### Deprecated
|
||||
|
||||
- IReader::SKIP_EMPTY_CELLS - use its alias IGNORE_EMPTY_CELLS instead.
|
||||
- Worksheet::getProtectedCells was deprecated in release 2, but was not properly documented, and not removed in release 3. Use getProtectedCellRanges instead.
|
||||
- Writer/Html::isMpdf property was deprecated in release 2, but was not properly documented, and not removed in release 3. Use instanceof Mpdf instead.
|
||||
|
||||
### Moved
|
||||
|
||||
- Nothing yet.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Xls Writer Condtional Rules Applied to Whole Rows or Columns. [Issue #3185](https://github.com/PHPOffice/PhpSpreadsheet/issues/3185) [PR #4152](https://github.com/PHPOffice/PhpSpreadsheet/pull/4152)
|
||||
- Xlsx Writer Duplicate ContentTypes Entry for Background Image. [Issue #4179](https://github.com/PHPOffice/PhpSpreadsheet/issues/4179) [PR #4180](https://github.com/PHPOffice/PhpSpreadsheet/pull/4180)
|
||||
- Check strictNullComparison outside of loops. [PR #3347](https://github.com/PHPOffice/PhpSpreadsheet/pull/3347)
|
||||
- SUMIFS Does Not Require xlfn. [Issue #4182](https://github.com/PHPOffice/PhpSpreadsheet/issues/4182) [PR #4186](https://github.com/PHPOffice/PhpSpreadsheet/pull/4186)
|
||||
- Image Transparency/Opacity with Html Reader Changes. [Discussion #4117](https://github.com/PHPOffice/PhpSpreadsheet/discussions/4117) [PR #4142](https://github.com/PHPOffice/PhpSpreadsheet/pull/4142)
|
||||
- Option to Write Hyperlink Rather Than Label to Csv. [Issue #1412](https://github.com/PHPOffice/PhpSpreadsheet/issues/1412) [PR #4151](https://github.com/PHPOffice/PhpSpreadsheet/pull/4151)
|
||||
- Invalid Html Due to Cached Filesize. [Issue #1107](https://github.com/PHPOffice/PhpSpreadsheet/issues/1107) [PR #4184](https://github.com/PHPOffice/PhpSpreadsheet/pull/4184)
|
||||
- Excel 2003 Allows Html Entities. [Issue #2157](https://github.com/PHPOffice/PhpSpreadsheet/issues/2157) [PR #4187](https://github.com/PHPOffice/PhpSpreadsheet/pull/4187)
|
||||
- Changes to ROUNDDOWN/ROUNDUP/TRUNC. [Issue #4213](https://github.com/PHPOffice/PhpSpreadsheet/issues/4213) [PR #4214](https://github.com/PHPOffice/PhpSpreadsheet/pull/4214)
|
||||
- Writer Xlsx ignoredErrors Before Drawings. [Issue #4200](https://github.com/PHPOffice/PhpSpreadsheet/issues/4200) [Issue #4145](https://github.com/PHPOffice/PhpSpreadsheet/issues/4145) [PR #4212](https://github.com/PHPOffice/PhpSpreadsheet/pull/4212)
|
||||
- Allow ANCHORARRAY as Data Validation list. [Issue #4197](https://github.com/PHPOffice/PhpSpreadsheet/issues/4197) [PR #4203](https://github.com/PHPOffice/PhpSpreadsheet/pull/4203)
|
||||
|
||||
## 2024-09-29 - 3.3.0 (no 3.0.\*, 3.1.\*, 3.2.\*)
|
||||
|
||||
### Dynamic Arrays
|
||||
|
||||
- Support for Excel dynamic arrays is added. It is an opt-in feature, so our hope is that there will be no BC breaks, but it is a very large change. Full support is added for Xlsx. It is emulated as Ctrl-Shift-Enter arrays for Ods read and write and Excel2003 and Gnumeric read. Html/Pdf and Csv writers will populate cells on output if they are the result of array formulas. No support is added for Xls or Slk.
|
||||
|
||||
### Added
|
||||
|
||||
- Excel Dynamic Arrays. [Issue #3901](https://github.com/PHPOffice/PhpSpreadsheet/issues/3901) [Issue #3659](https://github.com/PHPOffice/PhpSpreadsheet/issues/3659) [Issue #1834](https://github.com/PHPOffice/PhpSpreadsheet/issues/1834) [PR #3962](https://github.com/PHPOffice/PhpSpreadsheet/pull/3962)
|
||||
- String Value Binder Allow Setting "Ignore Number Stored as Text". [PR #4141](https://github.com/PHPOffice/PhpSpreadsheet/pull/4141)
|
||||
|
||||
### Changed
|
||||
|
||||
- Xlsx Reader default datatype when none is specified in Xml is changed from string to numeric, which is how Excel treats it. There is expected to be little impact because DefaultValueBinder and AdvancedValueBinder correct mis-identification as string, and StringValueBinder usually expects string. [PR #4139](https://github.com/PHPOffice/PhpSpreadsheet/pull/4139)
|
||||
- Currency and Accounting Wizards are changed to act like Excel, and a new CurrencyBase Wizard is added for for non-Excel formats. [Issue #4125](https://github.com/PHPOffice/PhpSpreadsheet/issues/4125) [Issue #4124](https://github.com/PHPOffice/PhpSpreadsheet/issues/4124) [PR #4127](https://github.com/PHPOffice/PhpSpreadsheet/pull/4127)
|
||||
- Images will not be added to spreadsheet if they cannot be validated as images.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Php8.4 will deprecate the escape parameter of fgetcsv. Csv Reader is affected by this; code is changed to be unaffected, but this will mean a breaking change is coming with Php9. Any code which uses the default escape value of backslash will fail in Php9. It is recommended to explicitly set the escape value to null string before then.
|
||||
- Nothing yet.
|
||||
|
||||
### Removed
|
||||
|
||||
- The following items were deprecated in release 2 and are now removed.
|
||||
- Writer\Xls\Style\ColorMap (no longer needed).
|
||||
- Reader\Xml::trySimpleXMLLoadString (should not have been public, no public replacement).
|
||||
- Calculation\Calculation::_translateFormulaToLocale (use method name translateFormulaToLocale without leading underscore).
|
||||
- Calculation\Calculation::_translateFormulaToEnglish (use method name translateFormulaToEnglish without leading underscore).
|
||||
|
||||
### Moved
|
||||
|
||||
- Nothing yet.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Several security patches.
|
||||
- Xls Reader Some Ranges Not Handled Properly. [Issue #1570](https://github.com/PHPOffice/PhpSpreadsheet/issues/1570) [PR #4140](https://github.com/PHPOffice/PhpSpreadsheet/pull/4140)
|
||||
- Better Handling of legacyDrawing Xml. [Issue #4105](https://github.com/PHPOffice/PhpSpreadsheet/issues/4105) [PR #4122](https://github.com/PHPOffice/PhpSpreadsheet/pull/4122)
|
||||
- Improve Xlsx Reader Speed. [Issue #3917](https://github.com/PHPOffice/PhpSpreadsheet/issues/3917) [PR #4153](https://github.com/PHPOffice/PhpSpreadsheet/pull/4153)
|
||||
|
||||
## 2024-08-07 - 2.2.2
|
||||
|
||||
|
||||
@@ -4,10 +4,11 @@ If you would like to contribute, here are some notes and guidelines:
|
||||
|
||||
- All new development should be on feature/fix branches, which are then merged to the `master` branch once stable and approved; so the `master` branch is always the most up-to-date, working code
|
||||
- If you are going to submit a pull request, please fork from `master`, and submit your pull request back as a fix/feature branch referencing the GitHub issue number
|
||||
- Install (development) dependencies by running `composer install` inside your PhpSpreadsheet clone.
|
||||
- The code must work with all PHP versions that we support.
|
||||
- You can call `composer versions` to test version compatibility.
|
||||
- Code style should be maintained.
|
||||
- `composer style` will identify any issues with Coding Style`.
|
||||
- `composer style` will identify any issues with Coding Style.
|
||||
- `composer fix` will fix most issues with Coding Style.
|
||||
- All code changes must be validated by `composer check`.
|
||||
- Please include Unit Tests to verify that a bug exists, and that this PR fixes it.
|
||||
@@ -39,7 +40,10 @@ This makes it easier to see exactly what is being tested when reviewing the PR.
|
||||
2. Tag subject must be the version number, eg: `1.2.3`
|
||||
3. Tag body must be a copy-paste of the changelog entries.
|
||||
3. Push the tag with `git push --tags`, GitHub Actions will create a GitHub release automatically, and the release details will automatically be sent to packagist.
|
||||
4. Github seems to remove markdown headings in the Release Notes, so you should edit to restore these.
|
||||
|
||||
> **Note:** Tagged releases are made from the `master` branch. Only in an emergency should a tagged release be made from the `release` branch. (i.e. cherry-picked hot-fixes.)
|
||||
4. By default, Github removes markdown headings in the Release Notes. You can either edit to restore these, or, probably preferably, change the default comment character on your system - `git config core.commentChar ";"`.
|
||||
|
||||
> **Note:** Tagged releases are made from the `master` branch. Only in an emergency should a tagged release be made from the `release` branch. (i.e. cherry-picked hot-fixes.) However, there are 4 branches which have been updated to apply security patches, and those may be tagged if future security updates are needed.
|
||||
- release1291 (no further updates aside from security patches, including code changes needed for Php 8.5 compatibility)
|
||||
- release210 (no further updates aside from security patches, including code changes needed for Php 8.5 compatibility)
|
||||
- release222
|
||||
- release390
|
||||
|
||||
14
api/vendor/phpoffice/phpspreadsheet/README.md
vendored
14
api/vendor/phpoffice/phpspreadsheet/README.md
vendored
@@ -1,8 +1,7 @@
|
||||
# PhpSpreadsheet
|
||||
|
||||
[](https://github.com/PHPOffice/PhpSpreadsheet/actions)
|
||||
[](https://scrutinizer-ci.com/g/PHPOffice/PhpSpreadsheet/?branch=master)
|
||||
[](https://scrutinizer-ci.com/g/PHPOffice/PhpSpreadsheet/?branch=master)
|
||||
[](https://coveralls.io/github/PHPOffice/PhpSpreadsheet?branch=master)
|
||||
[](https://packagist.org/packages/phpoffice/phpspreadsheet)
|
||||
[](https://packagist.org/packages/phpoffice/phpspreadsheet)
|
||||
[](https://packagist.org/packages/phpoffice/phpspreadsheet)
|
||||
@@ -11,6 +10,17 @@
|
||||
PhpSpreadsheet is a library written in pure PHP and offers a set of classes that
|
||||
allow you to read and write various spreadsheet file formats such as Excel and LibreOffice Calc.
|
||||
|
||||
This is the master branch, and is maintained for security and bug fixes.
|
||||
|
||||
## PHP Version Support
|
||||
|
||||
LTS: For maintained branches, support for PHP versions will only be maintained for a period of six months beyond the
|
||||
[end of life](https://www.php.net/supported-versions) of that PHP version.
|
||||
|
||||
Currently the required PHP minimum version is PHP __8.1__, and we [will support that version](https://www.php.net/supported-versions.php) until 30th June 2026.
|
||||
|
||||
See the `composer.json` for other requirements.
|
||||
|
||||
## Installation
|
||||
|
||||
See the [install instructions](https://phpspreadsheet.readthedocs.io/en/latest/#installation).
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"platform": {
|
||||
"php" : "8.1.99"
|
||||
},
|
||||
"process-timeout": 600,
|
||||
"sort-packages": true,
|
||||
"allow-plugins": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": true
|
||||
@@ -45,12 +46,12 @@
|
||||
],
|
||||
"scripts": {
|
||||
"check": [
|
||||
"./bin/check-phpdoc-types",
|
||||
"php bin/check-phpdoc-types.php",
|
||||
"phpcs samples/ src/ tests/ --report=checkstyle",
|
||||
"phpcs samples/ src/ tests/ --standard=PHPCompatibility --runtime-set testVersion 8.0- -n",
|
||||
"phpcs samples/ src/ tests/ --standard=PHPCompatibility --runtime-set testVersion 8.0- --exclude=PHPCompatibility.Variables.ForbiddenThisUseContexts -n",
|
||||
"php-cs-fixer fix --ansi --dry-run --diff",
|
||||
"phpunit --color=always",
|
||||
"phpstan analyse --ansi --memory-limit=2048M"
|
||||
"phpstan analyse --ansi --memory-limit=2048M",
|
||||
"phpunit --color=always"
|
||||
],
|
||||
"style": [
|
||||
"phpcs samples/ src/ tests/ --report=checkstyle",
|
||||
@@ -61,7 +62,7 @@
|
||||
"php-cs-fixer fix"
|
||||
],
|
||||
"versions": [
|
||||
"phpcs samples/ src/ tests/ --standard=PHPCompatibility --runtime-set testVersion 8.0- -n"
|
||||
"phpcs samples/ src/ tests/ --standard=PHPCompatibility --runtime-set testVersion 8.0- --exclude=PHPCompatibility.Variables.ForbiddenThisUseContexts -n"
|
||||
]
|
||||
},
|
||||
"require": {
|
||||
@@ -79,7 +80,7 @@
|
||||
"ext-xmlwriter": "*",
|
||||
"ext-zip": "*",
|
||||
"ext-zlib": "*",
|
||||
"composer/pcre": "^1 || ^2 || ^3",
|
||||
"composer/pcre": "^1||^2||^3",
|
||||
"maennchen/zipstream-php": "^2.1 || ^3.0",
|
||||
"markbaker/complex": "^3.0",
|
||||
"markbaker/matrix": "^3.0",
|
||||
@@ -94,9 +95,10 @@
|
||||
"mitoteam/jpgraph": "^10.3",
|
||||
"mpdf/mpdf": "^8.1.1",
|
||||
"phpcompatibility/php-compatibility": "^9.3",
|
||||
"phpstan/phpstan": "^1.1",
|
||||
"phpstan/phpstan-phpunit": "^1.0",
|
||||
"phpunit/phpunit": "^9.6 || ^10.5",
|
||||
"phpstan/phpstan": "^1.1 || ^2.0",
|
||||
"phpstan/phpstan-phpunit": "^1.0 || ^2.0",
|
||||
"phpstan/phpstan-deprecation-rules": "^1.0 || ^2.0",
|
||||
"phpunit/phpunit": "^10.5",
|
||||
"squizlabs/php_codesniffer": "^3.7",
|
||||
"tecnickcom/tcpdf": "^6.5"
|
||||
},
|
||||
|
||||
@@ -12,21 +12,25 @@ trait ArrayEnabled
|
||||
private static ArrayArgumentHelper $arrayArgumentHelper;
|
||||
|
||||
/**
|
||||
* @param array|false $arguments Can be changed to array for Php8.1+
|
||||
* @param mixed[] $arguments
|
||||
*/
|
||||
private static function initialiseHelper($arguments): void
|
||||
private static function initialiseHelper(array $arguments): void
|
||||
{
|
||||
if (self::$initializationNeeded === true) {
|
||||
self::$arrayArgumentHelper = new ArrayArgumentHelper();
|
||||
self::$initializationNeeded = false;
|
||||
}
|
||||
self::$arrayArgumentHelper->initialise(($arguments === false) ? [] : $arguments);
|
||||
self::$arrayArgumentHelper->initialise($arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles array argument processing when the function accepts a single argument that can be an array argument.
|
||||
* Example use for:
|
||||
* DAYOFMONTH() or FACT().
|
||||
*
|
||||
* @param mixed[] $values
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
protected static function evaluateSingleArgumentArray(callable $method, array $values): array
|
||||
{
|
||||
@@ -43,6 +47,8 @@ trait ArrayEnabled
|
||||
* and any of them can be an array argument.
|
||||
* Example use for:
|
||||
* ROUND() or DATE().
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
protected static function evaluateArrayArguments(callable $method, mixed ...$arguments): array
|
||||
{
|
||||
@@ -58,6 +64,8 @@ trait ArrayEnabled
|
||||
* Example use for:
|
||||
* NETWORKDAYS() or CONCATENATE(), where the last argument is a matrix (or a series of values) that need
|
||||
* to be treated as a such rather than as an array arguments.
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
protected static function evaluateArrayArgumentsSubset(callable $method, int $limit, mixed ...$arguments): array
|
||||
{
|
||||
@@ -80,6 +88,8 @@ trait ArrayEnabled
|
||||
* Example use for:
|
||||
* Z.TEST() or INDEX(), where the first argument 1 is a matrix that needs to be treated as a dataset
|
||||
* rather than as an array argument.
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
protected static function evaluateArrayArgumentsSubsetFrom(callable $method, int $start, mixed ...$arguments): array
|
||||
{
|
||||
@@ -105,6 +115,8 @@ trait ArrayEnabled
|
||||
* Example use for:
|
||||
* HLOOKUP() and VLOOKUP(), where argument 1 is a matrix that needs to be treated as a database
|
||||
* rather than as an array argument.
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
protected static function evaluateArrayArgumentsIgnore(callable $method, int $ignore, mixed ...$arguments): array
|
||||
{
|
||||
|
||||
@@ -14,13 +14,15 @@ class BinaryComparison
|
||||
/**
|
||||
* Compare two strings in the same way as strcmp() except that lowercase come before uppercase letters.
|
||||
*
|
||||
* @param null|string $str1 First string value for the comparison
|
||||
* @param null|string $str2 Second string value for the comparison
|
||||
* @param mixed $str1 First string value for the comparison, expect ?string
|
||||
* @param mixed $str2 Second string value for the comparison, expect ?string
|
||||
*/
|
||||
private static function strcmpLowercaseFirst(?string $str1, ?string $str2): int
|
||||
private static function strcmpLowercaseFirst(mixed $str1, mixed $str2): int
|
||||
{
|
||||
$inversedStr1 = StringHelper::strCaseReverse($str1 ?? '');
|
||||
$inversedStr2 = StringHelper::strCaseReverse($str2 ?? '');
|
||||
$str1 = StringHelper::convertToString($str1);
|
||||
$str2 = StringHelper::convertToString($str2);
|
||||
$inversedStr1 = StringHelper::strCaseReverse($str1);
|
||||
$inversedStr2 = StringHelper::strCaseReverse($str2);
|
||||
|
||||
return strcmp($inversedStr1, $inversedStr2);
|
||||
}
|
||||
@@ -28,12 +30,15 @@ class BinaryComparison
|
||||
/**
|
||||
* PHP8.1 deprecates passing null to strcmp.
|
||||
*
|
||||
* @param null|string $str1 First string value for the comparison
|
||||
* @param null|string $str2 Second string value for the comparison
|
||||
* @param mixed $str1 First string value for the comparison, expect ?string
|
||||
* @param mixed $str2 Second string value for the comparison, expect ?string
|
||||
*/
|
||||
private static function strcmpAllowNull(?string $str1, ?string $str2): int
|
||||
private static function strcmpAllowNull(mixed $str1, mixed $str2): int
|
||||
{
|
||||
return strcmp($str1 ?? '', $str2 ?? '');
|
||||
$str1 = StringHelper::convertToString($str1);
|
||||
$str2 = StringHelper::convertToString($str2);
|
||||
|
||||
return strcmp($str1, $str2);
|
||||
}
|
||||
|
||||
public static function compare(mixed $operand1, mixed $operand2, string $operator): bool
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,4 +18,5 @@ abstract class Category
|
||||
const CATEGORY_TEXT_AND_DATA = 'Text and Data';
|
||||
const CATEGORY_WEB = 'Web';
|
||||
const CATEGORY_UNCATEGORISED = 'Uncategorised';
|
||||
const CATEGORY_MICROSOFT_INTERNAL = 'MS Internal';
|
||||
}
|
||||
|
||||
@@ -19,12 +19,12 @@ class DAverage extends DatabaseAbstract
|
||||
* A database is a list of related data in which rows of related
|
||||
* information are records, and columns of data are fields. The
|
||||
* first row of the list contains labels for each column.
|
||||
* @param null|array|int|string $field Indicates which column is used in the function. Enter the
|
||||
* @param null|array<mixed>|int|string $field Indicates which column is used in the function. Enter the
|
||||
* column label enclosed between double quotation marks, such as
|
||||
* "Age" or "Yield," or a number (without quotation marks) that
|
||||
* represents the position of the column within the list: 1 for
|
||||
* the first column, 2 for the second column, and so on.
|
||||
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
|
||||
* @param mixed[][] $criteria The range of cells that contains the conditions you specify.
|
||||
* You can use any range for the criteria argument, as long as it
|
||||
* includes at least one column label and at least one cell below
|
||||
* the column label in which you specify a condition for the
|
||||
|
||||
@@ -20,12 +20,12 @@ class DCount extends DatabaseAbstract
|
||||
* A database is a list of related data in which rows of related
|
||||
* information are records, and columns of data are fields. The
|
||||
* first row of the list contains labels for each column.
|
||||
* @param null|array|int|string $field Indicates which column is used in the function. Enter the
|
||||
* @param null|array<mixed>|int|string $field Indicates which column is used in the function. Enter the
|
||||
* column label enclosed between double quotation marks, such as
|
||||
* "Age" or "Yield," or a number (without quotation marks) that
|
||||
* represents the position of the column within the list: 1 for
|
||||
* the first column, 2 for the second column, and so on.
|
||||
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
|
||||
* @param mixed[][] $criteria The range of cells that contains the conditions you specify.
|
||||
* You can use any range for the criteria argument, as long as it
|
||||
* includes at least one column label and at least one cell below
|
||||
* the column label in which you specify a condition for the
|
||||
|
||||
@@ -19,12 +19,12 @@ class DCountA extends DatabaseAbstract
|
||||
* A database is a list of related data in which rows of related
|
||||
* information are records, and columns of data are fields. The
|
||||
* first row of the list contains labels for each column.
|
||||
* @param null|array|int|string $field Indicates which column is used in the function. Enter the
|
||||
* @param null|array<mixed>|int|string $field Indicates which column is used in the function. Enter the
|
||||
* column label enclosed between double quotation marks, such as
|
||||
* "Age" or "Yield," or a number (without quotation marks) that
|
||||
* represents the position of the column within the list: 1 for
|
||||
* the first column, 2 for the second column, and so on.
|
||||
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
|
||||
* @param mixed[][] $criteria The range of cells that contains the conditions you specify.
|
||||
* You can use any range for the criteria argument, as long as it
|
||||
* includes at least one column label and at least one cell below
|
||||
* the column label in which you specify a condition for the
|
||||
|
||||
@@ -19,12 +19,12 @@ class DGet extends DatabaseAbstract
|
||||
* A database is a list of related data in which rows of related
|
||||
* information are records, and columns of data are fields. The
|
||||
* first row of the list contains labels for each column.
|
||||
* @param null|array|int|string $field Indicates which column is used in the function. Enter the
|
||||
* @param null|array<mixed>|int|string $field Indicates which column is used in the function. Enter the
|
||||
* column label enclosed between double quotation marks, such as
|
||||
* "Age" or "Yield," or a number (without quotation marks) that
|
||||
* represents the position of the column within the list: 1 for
|
||||
* the first column, 2 for the second column, and so on.
|
||||
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
|
||||
* @param mixed[][] $criteria The range of cells that contains the conditions you specify.
|
||||
* You can use any range for the criteria argument, as long as it
|
||||
* includes at least one column label and at least one cell below
|
||||
* the column label in which you specify a condition for the
|
||||
@@ -42,6 +42,7 @@ class DGet extends DatabaseAbstract
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
/** @var array<null|float|int|string> */
|
||||
$row = array_pop($columnData);
|
||||
|
||||
return array_pop($row);
|
||||
|
||||
@@ -20,12 +20,12 @@ class DMax extends DatabaseAbstract
|
||||
* A database is a list of related data in which rows of related
|
||||
* information are records, and columns of data are fields. The
|
||||
* first row of the list contains labels for each column.
|
||||
* @param null|array|int|string $field Indicates which column is used in the function. Enter the
|
||||
* @param null|array<mixed>|int|string $field Indicates which column is used in the function. Enter the
|
||||
* column label enclosed between double quotation marks, such as
|
||||
* "Age" or "Yield," or a number (without quotation marks) that
|
||||
* represents the position of the column within the list: 1 for
|
||||
* the first column, 2 for the second column, and so on.
|
||||
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
|
||||
* @param mixed[][] $criteria The range of cells that contains the conditions you specify.
|
||||
* You can use any range for the criteria argument, as long as it
|
||||
* includes at least one column label and at least one cell below
|
||||
* the column label in which you specify a condition for the
|
||||
|
||||
@@ -20,12 +20,12 @@ class DMin extends DatabaseAbstract
|
||||
* A database is a list of related data in which rows of related
|
||||
* information are records, and columns of data are fields. The
|
||||
* first row of the list contains labels for each column.
|
||||
* @param null|array|int|string $field Indicates which column is used in the function. Enter the
|
||||
* @param null|array<mixed>|int|string $field Indicates which column is used in the function. Enter the
|
||||
* column label enclosed between double quotation marks, such as
|
||||
* "Age" or "Yield," or a number (without quotation marks) that
|
||||
* represents the position of the column within the list: 1 for
|
||||
* the first column, 2 for the second column, and so on.
|
||||
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
|
||||
* @param mixed[][] $criteria The range of cells that contains the conditions you specify.
|
||||
* You can use any range for the criteria argument, as long as it
|
||||
* includes at least one column label and at least one cell below
|
||||
* the column label in which you specify a condition for the
|
||||
|
||||
@@ -19,12 +19,12 @@ class DProduct extends DatabaseAbstract
|
||||
* A database is a list of related data in which rows of related
|
||||
* information are records, and columns of data are fields. The
|
||||
* first row of the list contains labels for each column.
|
||||
* @param null|array|int|string $field Indicates which column is used in the function. Enter the
|
||||
* @param null|array<mixed>|int|string $field Indicates which column is used in the function. Enter the
|
||||
* column label enclosed between double quotation marks, such as
|
||||
* "Age" or "Yield," or a number (without quotation marks) that
|
||||
* represents the position of the column within the list: 1 for
|
||||
* the first column, 2 for the second column, and so on.
|
||||
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
|
||||
* @param mixed[][] $criteria The range of cells that contains the conditions you specify.
|
||||
* You can use any range for the criteria argument, as long as it
|
||||
* includes at least one column label and at least one cell below
|
||||
* the column label in which you specify a condition for the
|
||||
|
||||
@@ -20,12 +20,12 @@ class DStDev extends DatabaseAbstract
|
||||
* A database is a list of related data in which rows of related
|
||||
* information are records, and columns of data are fields. The
|
||||
* first row of the list contains labels for each column.
|
||||
* @param null|array|int|string $field Indicates which column is used in the function. Enter the
|
||||
* @param null|array<mixed>|int|string $field Indicates which column is used in the function. Enter the
|
||||
* column label enclosed between double quotation marks, such as
|
||||
* "Age" or "Yield," or a number (without quotation marks) that
|
||||
* represents the position of the column within the list: 1 for
|
||||
* the first column, 2 for the second column, and so on.
|
||||
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
|
||||
* @param mixed[][] $criteria The range of cells that contains the conditions you specify.
|
||||
* You can use any range for the criteria argument, as long as it
|
||||
* includes at least one column label and at least one cell below
|
||||
* the column label in which you specify a condition for the
|
||||
|
||||
@@ -20,12 +20,12 @@ class DStDevP extends DatabaseAbstract
|
||||
* A database is a list of related data in which rows of related
|
||||
* information are records, and columns of data are fields. The
|
||||
* first row of the list contains labels for each column.
|
||||
* @param null|array|int|string $field Indicates which column is used in the function. Enter the
|
||||
* @param null|array<mixed>|int|string $field Indicates which column is used in the function. Enter the
|
||||
* column label enclosed between double quotation marks, such as
|
||||
* "Age" or "Yield," or a number (without quotation marks) that
|
||||
* represents the position of the column within the list: 1 for
|
||||
* the first column, 2 for the second column, and so on.
|
||||
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
|
||||
* @param mixed[][] $criteria The range of cells that contains the conditions you specify.
|
||||
* You can use any range for the criteria argument, as long as it
|
||||
* includes at least one column label and at least one cell below
|
||||
* the column label in which you specify a condition for the
|
||||
|
||||
@@ -19,12 +19,12 @@ class DSum extends DatabaseAbstract
|
||||
* A database is a list of related data in which rows of related
|
||||
* information are records, and columns of data are fields. The
|
||||
* first row of the list contains labels for each column.
|
||||
* @param null|array|int|string $field Indicates which column is used in the function. Enter the
|
||||
* @param null|array<mixed>|int|string $field Indicates which column is used in the function. Enter the
|
||||
* column label enclosed between double quotation marks, such as
|
||||
* "Age" or "Yield," or a number (without quotation marks) that
|
||||
* represents the position of the column within the list: 1 for
|
||||
* the first column, 2 for the second column, and so on.
|
||||
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
|
||||
* @param mixed[][] $criteria The range of cells that contains the conditions you specify.
|
||||
* You can use any range for the criteria argument, as long as it
|
||||
* includes at least one column label and at least one cell below
|
||||
* the column label in which you specify a condition for the
|
||||
|
||||
@@ -20,12 +20,12 @@ class DVar extends DatabaseAbstract
|
||||
* A database is a list of related data in which rows of related
|
||||
* information are records, and columns of data are fields. The
|
||||
* first row of the list contains labels for each column.
|
||||
* @param null|array|int|string $field Indicates which column is used in the function. Enter the
|
||||
* @param null|array<mixed>|int|string $field Indicates which column is used in the function. Enter the
|
||||
* column label enclosed between double quotation marks, such as
|
||||
* "Age" or "Yield," or a number (without quotation marks) that
|
||||
* represents the position of the column within the list: 1 for
|
||||
* the first column, 2 for the second column, and so on.
|
||||
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
|
||||
* @param mixed[][] $criteria The range of cells that contains the conditions you specify.
|
||||
* You can use any range for the criteria argument, as long as it
|
||||
* includes at least one column label and at least one cell below
|
||||
* the column label in which you specify a condition for the
|
||||
|
||||
@@ -20,12 +20,12 @@ class DVarP extends DatabaseAbstract
|
||||
* A database is a list of related data in which rows of related
|
||||
* information are records, and columns of data are fields. The
|
||||
* first row of the list contains labels for each column.
|
||||
* @param null|array|int|string $field Indicates which column is used in the function. Enter the
|
||||
* @param null|array<mixed>|int|string $field Indicates which column is used in the function. Enter the
|
||||
* column label enclosed between double quotation marks, such as
|
||||
* "Age" or "Yield," or a number (without quotation marks) that
|
||||
* represents the position of the column within the list: 1 for
|
||||
* the first column, 2 for the second column, and so on.
|
||||
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
|
||||
* @param mixed[][] $criteria The range of cells that contains the conditions you specify.
|
||||
* You can use any range for the criteria argument, as long as it
|
||||
* includes at least one column label and at least one cell below
|
||||
* the column label in which you specify a condition for the
|
||||
|
||||
@@ -5,9 +5,26 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Database;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Internal\WildcardMatch;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
|
||||
abstract class DatabaseAbstract
|
||||
{
|
||||
/**
|
||||
* @param mixed[] $database The range of cells that makes up the list or database.
|
||||
* A database is a list of related data in which rows of related
|
||||
* information are records, and columns of data are fields. The
|
||||
* first row of the list contains labels for each column.
|
||||
* @param null|array<mixed>|int|string $field Indicates which column is used in the function. Enter the
|
||||
* column label enclosed between double quotation marks, such as
|
||||
* "Age" or "Yield," or a number (without quotation marks) that
|
||||
* represents the position of the column within the list: 1 for
|
||||
* the first column, 2 for the second column, and so on.
|
||||
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
|
||||
* You can use any range for the criteria argument, as long as it
|
||||
* includes at least one column label and at least one cell below
|
||||
* the column label in which you specify a condition for the
|
||||
* column.
|
||||
*/
|
||||
abstract public static function evaluate(array $database, array|null|int|string $field, array $criteria): null|float|int|string;
|
||||
|
||||
/**
|
||||
@@ -27,12 +44,16 @@ abstract class DatabaseAbstract
|
||||
*/
|
||||
protected static function fieldExtract(array $database, mixed $field): ?int
|
||||
{
|
||||
$field = strtoupper(Functions::flattenSingleValue($field) ?? '');
|
||||
/** @var ?string */
|
||||
$single = Functions::flattenSingleValue($field);
|
||||
$field = strtoupper($single ?? '');
|
||||
if ($field === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$fieldNames = array_map('strtoupper', array_shift($database));
|
||||
/** @var callable */
|
||||
$callable = 'strtoupper';
|
||||
$fieldNames = array_map($callable, array_shift($database)); //* @phpstan-ignore-line
|
||||
if (is_numeric($field)) {
|
||||
$field = (int) $field - 1;
|
||||
if ($field < 0 || $field >= count($fieldNames)) {
|
||||
@@ -56,7 +77,7 @@ abstract class DatabaseAbstract
|
||||
* A database is a list of related data in which rows of related
|
||||
* information are records, and columns of data are fields. The
|
||||
* first row of the list contains labels for each column.
|
||||
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
|
||||
* @param mixed[][] $criteria The range of cells that contains the conditions you specify.
|
||||
* You can use any range for the criteria argument, as long as it
|
||||
* includes at least one column label and at least one cell below
|
||||
* the column label in which you specify a condition for the
|
||||
@@ -66,16 +87,25 @@ abstract class DatabaseAbstract
|
||||
*/
|
||||
protected static function filter(array $database, array $criteria): array
|
||||
{
|
||||
/** @var mixed[] */
|
||||
$fieldNames = array_shift($database);
|
||||
$criteriaNames = array_shift($criteria);
|
||||
|
||||
// Convert the criteria into a set of AND/OR conditions with [:placeholders]
|
||||
/** @var string[] $criteriaNames */
|
||||
$query = self::buildQuery($criteriaNames, $criteria);
|
||||
|
||||
// Loop through each row of the database
|
||||
/** @var mixed[][] $criteriaNames */
|
||||
return self::executeQuery($database, $query, $criteriaNames, $fieldNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $database The range of cells that makes up the list or database
|
||||
* @param mixed[][] $criteria
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
protected static function getFilteredColumn(array $database, ?int $field, array $criteria): array
|
||||
{
|
||||
// reduce the database to a set of rows that match all the criteria
|
||||
@@ -84,6 +114,7 @@ abstract class DatabaseAbstract
|
||||
|
||||
// extract an array of values for the requested column
|
||||
$columnData = [];
|
||||
/** @var mixed[] $row */
|
||||
foreach ($database as $rowKey => $row) {
|
||||
$keys = array_keys($row);
|
||||
$key = $keys[$field] ?? null;
|
||||
@@ -94,6 +125,10 @@ abstract class DatabaseAbstract
|
||||
return $columnData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $criteriaNames
|
||||
* @param mixed[][] $criteria
|
||||
*/
|
||||
private static function buildQuery(array $criteriaNames, array $criteria): string
|
||||
{
|
||||
$baseQuery = [];
|
||||
@@ -108,7 +143,7 @@ abstract class DatabaseAbstract
|
||||
}
|
||||
|
||||
$rowQuery = array_map(
|
||||
fn ($rowValue): string => (count($rowValue) > 1) ? 'AND(' . implode(',', $rowValue) . ')' : ($rowValue[0] ?? ''),
|
||||
fn ($rowValue): string => (count($rowValue) > 1) ? 'AND(' . implode(',', $rowValue) . ')' : ($rowValue[0] ?? ''), // @phpstan-ignore-line
|
||||
$baseQuery
|
||||
);
|
||||
|
||||
@@ -135,12 +170,21 @@ abstract class DatabaseAbstract
|
||||
return $condition;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $database
|
||||
* @param mixed[][] $criteria
|
||||
* @param array<mixed> $fields
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function executeQuery(array $database, string $query, array $criteria, array $fields): array
|
||||
{
|
||||
foreach ($database as $dataRow => $dataValues) {
|
||||
// Substitute actual values from the database row for our [:placeholders]
|
||||
$conditions = $query;
|
||||
foreach ($criteria as $criterion) {
|
||||
/** @var string $criterion */
|
||||
/** @var mixed[] $dataValues */
|
||||
$conditions = self::processCondition($criterion, $fields, $dataValues, $conditions);
|
||||
}
|
||||
|
||||
@@ -156,6 +200,10 @@ abstract class DatabaseAbstract
|
||||
return $database;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<mixed> $fields
|
||||
* @param array<mixed> $dataValues
|
||||
*/
|
||||
private static function processCondition(string $criterion, array $fields, array $dataValues, string $conditions): string
|
||||
{
|
||||
$key = array_search($criterion, $fields, true);
|
||||
@@ -169,7 +217,10 @@ abstract class DatabaseAbstract
|
||||
if (is_string($dataValue) && str_contains($dataValue, '"')) {
|
||||
$dataValue = str_replace('"', '""', $dataValue);
|
||||
}
|
||||
$dataValue = (is_string($dataValue)) ? Calculation::wrapResult(strtoupper($dataValue)) : $dataValue;
|
||||
if (is_string($dataValue)) {
|
||||
$dataValue = Calculation::wrapResult(strtoupper($dataValue));
|
||||
}
|
||||
$dataValue = StringHelper::convertToString($dataValue);
|
||||
}
|
||||
|
||||
return str_replace('[:' . $criterion . ']', $dataValue, $conditions);
|
||||
|
||||
@@ -28,7 +28,7 @@ class Date
|
||||
* A Month name or abbreviation (English only at this point) such as 'January' or 'Jan' will still be accepted,
|
||||
* as will a day value with a suffix (e.g. '21st' rather than simply 21); again only English language.
|
||||
*
|
||||
* @param array|float|int|string $year The value of the year argument can include one to four digits.
|
||||
* @param array<mixed>|float|int|string $year The value of the year argument can include one to four digits.
|
||||
* Excel interprets the year argument according to the configured
|
||||
* date system: 1900 or 1904.
|
||||
* If year is between 0 (zero) and 1899 (inclusive), Excel adds that
|
||||
@@ -39,7 +39,7 @@ class Date
|
||||
* 2008.
|
||||
* If year is less than 0 or is 10000 or greater, Excel returns the
|
||||
* #NUM! error value.
|
||||
* @param array|float|int|string $month A positive or negative integer representing the month of the year
|
||||
* @param array<mixed>|float|int|string $month A positive or negative integer representing the month of the year
|
||||
* from 1 to 12 (January to December).
|
||||
* If month is greater than 12, month adds that number of months to
|
||||
* the first month in the year specified. For example, DATE(2008,14,2)
|
||||
@@ -48,7 +48,7 @@ class Date
|
||||
* number of months, plus 1, from the first month in the year
|
||||
* specified. For example, DATE(2008,-3,2) returns the serial number
|
||||
* representing September 2, 2007.
|
||||
* @param array|float|int|string $day A positive or negative integer representing the day of the month
|
||||
* @param array<mixed>|float|int|string $day A positive or negative integer representing the day of the month
|
||||
* from 1 to 31.
|
||||
* If day is greater than the number of days in the month specified,
|
||||
* day adds that number of days to the first day in the month. For
|
||||
@@ -59,12 +59,12 @@ class Date
|
||||
* example, DATE(2008,1,-15) returns the serial number representing
|
||||
* December 16, 2007.
|
||||
*
|
||||
* @return array|DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||
* @return array<mixed>|DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||
* depending on the value of the ReturnDateType flag
|
||||
* If an array of numbers is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function fromYMD(array|float|int|string $year, array|float|int|string $month, array|float|int|string $day): float|int|DateTime|string|array
|
||||
public static function fromYMD(array|float|int|string $year, null|array|bool|float|int|string $month, array|float|int|string $day): float|int|DateTime|string|array
|
||||
{
|
||||
if (is_array($year) || is_array($month) || is_array($day)) {
|
||||
return self::evaluateArrayArguments([self::class, __FUNCTION__], $year, $month, $day);
|
||||
@@ -92,7 +92,11 @@ class Date
|
||||
*/
|
||||
private static function getYear(mixed $year, int $baseYear): int
|
||||
{
|
||||
$year = ($year !== null) ? StringHelper::testStringAsNumeric((string) $year) : 0;
|
||||
if ($year === null) {
|
||||
$year = 0;
|
||||
} elseif (is_scalar($year)) {
|
||||
$year = StringHelper::testStringAsNumeric((string) $year);
|
||||
}
|
||||
if (!is_numeric($year)) {
|
||||
throw new Exception(ExcelError::VALUE());
|
||||
}
|
||||
@@ -117,11 +121,15 @@ class Date
|
||||
*/
|
||||
private static function getMonth(mixed $month): int
|
||||
{
|
||||
if (($month !== null) && (!is_numeric($month))) {
|
||||
$month = SharedDateHelper::monthStringToNumber($month);
|
||||
if (is_string($month)) {
|
||||
if (!is_numeric($month)) {
|
||||
$month = SharedDateHelper::monthStringToNumber($month);
|
||||
}
|
||||
} elseif ($month === null) {
|
||||
$month = 0;
|
||||
} elseif (is_bool($month)) {
|
||||
$month = (int) $month;
|
||||
}
|
||||
|
||||
$month = ($month !== null) ? StringHelper::testStringAsNumeric((string) $month) : 0;
|
||||
if (!is_numeric($month)) {
|
||||
throw new Exception(ExcelError::VALUE());
|
||||
}
|
||||
@@ -134,11 +142,15 @@ class Date
|
||||
*/
|
||||
private static function getDay(mixed $day): int
|
||||
{
|
||||
if (($day !== null) && (!is_numeric($day))) {
|
||||
if (is_string($day) && !is_numeric($day)) {
|
||||
$day = SharedDateHelper::dayStringToNumber($day);
|
||||
}
|
||||
|
||||
$day = ($day !== null) ? StringHelper::testStringAsNumeric((string) $day) : 0;
|
||||
if ($day === null) {
|
||||
$day = 0;
|
||||
} elseif (is_scalar($day)) {
|
||||
$day = StringHelper::testStringAsNumeric((string) $day);
|
||||
}
|
||||
if (!is_numeric($day)) {
|
||||
throw new Exception(ExcelError::VALUE());
|
||||
}
|
||||
@@ -151,11 +163,11 @@ class Date
|
||||
if ($month < 1) {
|
||||
// Handle year/month adjustment if month < 1
|
||||
--$month;
|
||||
$year += ceil($month / 12) - 1;
|
||||
$year += (int) (ceil($month / 12) - 1);
|
||||
$month = 13 - abs($month % 12);
|
||||
} elseif ($month > 12) {
|
||||
// Handle year/month adjustment if month > 12
|
||||
$year += floor($month / 12);
|
||||
$year += intdiv($month, 12);
|
||||
$month = ($month % 12);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ class DateParts
|
||||
* PHP DateTime object, or a standard date string
|
||||
* Or can be an array of date values
|
||||
*
|
||||
* @return array|int|string Day of the month
|
||||
* @return array<mixed>|int|string Day of the month
|
||||
* If an array of numbers is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -65,7 +65,7 @@ class DateParts
|
||||
* PHP DateTime object, or a standard date string
|
||||
* Or can be an array of date values
|
||||
*
|
||||
* @return array|int|string Month of the year
|
||||
* @return array<mixed>|int|string Month of the year
|
||||
* If an array of numbers is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -104,7 +104,7 @@ class DateParts
|
||||
* PHP DateTime object, or a standard date string
|
||||
* Or can be an array of date values
|
||||
*
|
||||
* @return array|int|string Year
|
||||
* @return array<mixed>|int|string Year
|
||||
* If an array of numbers is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
|
||||
@@ -25,7 +25,7 @@ class DateValue
|
||||
* Excel Function:
|
||||
* DATEVALUE(dateValue)
|
||||
*
|
||||
* @param null|array|bool|float|int|string $dateValue Text that represents a date in a Microsoft Excel date format.
|
||||
* @param null|array<mixed>|bool|float|int|string $dateValue Text that represents a date in a Microsoft Excel date format.
|
||||
* For example, "1/30/2008" or "30-Jan-2008" are text strings within
|
||||
* quotation marks that represent dates. Using the default date
|
||||
* system in Excel for Windows, date_text must represent a date from
|
||||
@@ -35,7 +35,7 @@ class DateValue
|
||||
* #VALUE! error value if date_text is out of this range.
|
||||
* Or can be an array of date values
|
||||
*
|
||||
* @return array|DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||
* @return array<mixed>|DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||
* depending on the value of the ReturnDateType flag
|
||||
* If an array of numbers is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
@@ -47,7 +47,7 @@ class DateValue
|
||||
}
|
||||
|
||||
// try to parse as date iff there is at least one digit
|
||||
if (is_string($dateValue) && preg_match('/\\d/', $dateValue) !== 1) {
|
||||
if (is_string($dateValue) && preg_match('/\d/', $dateValue) !== 1) {
|
||||
return ExcelError::VALUE();
|
||||
}
|
||||
|
||||
@@ -86,6 +86,7 @@ class DateValue
|
||||
return self::finalResults($PHPDateArray, $dti, $baseYear);
|
||||
}
|
||||
|
||||
/** @param mixed[] $t1 */
|
||||
private static function t1ToString(array $t1, DateTimeImmutable $dti, bool $yearFound): string
|
||||
{
|
||||
if (count($t1) == 2) {
|
||||
@@ -108,6 +109,8 @@ class DateValue
|
||||
|
||||
/**
|
||||
* Parse date.
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function setUpArray(string $dateValue, DateTimeImmutable $dti): array
|
||||
{
|
||||
@@ -132,6 +135,8 @@ class DateValue
|
||||
/**
|
||||
* Final results.
|
||||
*
|
||||
* @param mixed[] $PHPDateArray
|
||||
*
|
||||
* @return DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||
* depending on the value of the ReturnDateType flag
|
||||
*/
|
||||
@@ -139,6 +144,7 @@ class DateValue
|
||||
{
|
||||
$retValue = ExcelError::Value();
|
||||
if (Helpers::dateParseSucceeded($PHPDateArray)) {
|
||||
/** @var array{year: int, month: int, day: int, hour: int, minute: int, second: int} $PHPDateArray */
|
||||
// Execute function
|
||||
Helpers::replaceIfEmpty($PHPDateArray['year'], $dti->format('Y'));
|
||||
if ($PHPDateArray['year'] < $baseYear) {
|
||||
@@ -146,12 +152,13 @@ class DateValue
|
||||
}
|
||||
Helpers::replaceIfEmpty($PHPDateArray['month'], $dti->format('m'));
|
||||
Helpers::replaceIfEmpty($PHPDateArray['day'], $dti->format('d'));
|
||||
/** @var array{year: int, month: int, day: int, hour: int, minute: int, second: int} $PHPDateArray */
|
||||
$PHPDateArray['hour'] = 0;
|
||||
$PHPDateArray['minute'] = 0;
|
||||
$PHPDateArray['second'] = 0;
|
||||
$month = (int) $PHPDateArray['month'];
|
||||
$day = (int) $PHPDateArray['day'];
|
||||
$year = (int) $PHPDateArray['year'];
|
||||
$month = self::getInt($PHPDateArray, 'month');
|
||||
$day = self::getInt($PHPDateArray, 'day');
|
||||
$year = self::getInt($PHPDateArray, 'year');
|
||||
if (!checkdate($month, $day, $year)) {
|
||||
return ($year === 1900 && $month === 2 && $day === 29) ? Helpers::returnIn3FormatsFloat(60.0) : ExcelError::VALUE();
|
||||
}
|
||||
@@ -160,4 +167,10 @@ class DateValue
|
||||
|
||||
return $retValue;
|
||||
}
|
||||
|
||||
/** @param mixed[] $array */
|
||||
private static function getInt(array $array, string $index): int
|
||||
{
|
||||
return (array_key_exists($index, $array) && is_numeric($array[$index])) ? (int) $array[$index] : 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,14 +20,14 @@ class Days
|
||||
* Excel Function:
|
||||
* DAYS(endDate, startDate)
|
||||
*
|
||||
* @param array|DateTimeInterface|float|int|string $endDate Excel date serial value (float),
|
||||
* @param array<mixed>|DateTimeInterface|float|int|string $endDate Excel date serial value (float),
|
||||
* PHP date timestamp (integer), PHP DateTime object, or a standard date string
|
||||
* Or can be an array of date values
|
||||
* @param array|DateTimeInterface|float|int|string $startDate Excel date serial value (float),
|
||||
* @param array<mixed>|DateTimeInterface|float|int|string $startDate Excel date serial value (float),
|
||||
* PHP date timestamp (integer), PHP DateTime object, or a standard date string
|
||||
* Or can be an array of date values
|
||||
*
|
||||
* @return array|int|string Number of days between start date and end date or an error
|
||||
* @return array<mixed>|int|string Number of days between start date and end date or an error
|
||||
* If an array of values is passed for the $startDate or $endDays,arguments, then the returned result
|
||||
* will also be an array with matching dimensions
|
||||
*/
|
||||
@@ -50,7 +50,7 @@ class Days
|
||||
|
||||
$days = ExcelError::VALUE();
|
||||
$diff = $PHPStartDateObject->diff($PHPEndDateObject);
|
||||
if ($diff !== false && !is_bool($diff->days)) {
|
||||
if (!is_bool($diff->days)) {
|
||||
$days = $diff->days;
|
||||
if ($diff->invert) {
|
||||
$days = -$days;
|
||||
|
||||
@@ -40,7 +40,7 @@ class Days360
|
||||
* same month.
|
||||
* Or can be an array of methods
|
||||
*
|
||||
* @return array|int|string Number of days between start date and end date
|
||||
* @return array<mixed>|int|string Number of days between start date and end date
|
||||
* If an array of values is passed for the $startDate or $endDays,arguments, then the returned result
|
||||
* will also be an array with matching dimensions
|
||||
*/
|
||||
|
||||
@@ -22,9 +22,9 @@ class Difference
|
||||
* @param mixed $endDate Excel date serial value, PHP date/time stamp, PHP DateTime object
|
||||
* or a standard date string
|
||||
* Or can be an array of date values
|
||||
* @param array|string $unit Or can be an array of unit values
|
||||
* @param array<mixed>|string $unit Or can be an array of unit values
|
||||
*
|
||||
* @return array|int|string Interval between the dates
|
||||
* @return array<mixed>|int|string Interval between the dates
|
||||
* If an array of values is passed for the $startDate or $endDays,arguments, then the returned result
|
||||
* will also be an array with matching dimensions
|
||||
*/
|
||||
|
||||
@@ -44,7 +44,9 @@ class Helpers
|
||||
if (!is_numeric($dateValue)) {
|
||||
$saveReturnDateType = Functions::getReturnDateType();
|
||||
Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
|
||||
$dateValue = DateValue::fromString($dateValue);
|
||||
if (is_string($dateValue)) {
|
||||
$dateValue = DateValue::fromString($dateValue);
|
||||
}
|
||||
Functions::setReturnDateType($saveReturnDateType);
|
||||
if (!is_numeric($dateValue)) {
|
||||
throw new Exception(ExcelError::VALUE());
|
||||
@@ -75,8 +77,10 @@ class Helpers
|
||||
|
||||
/**
|
||||
* Adjust date by given months.
|
||||
*
|
||||
* @param float|int $dateValue date to be adjusted
|
||||
*/
|
||||
public static function adjustDateByMonths(mixed $dateValue = 0, float $adjustmentMonths = 0): DateTime
|
||||
public static function adjustDateByMonths($dateValue = 0, float $adjustmentMonths = 0): DateTime
|
||||
{
|
||||
// Execute function
|
||||
$PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
|
||||
@@ -119,7 +123,7 @@ class Helpers
|
||||
if (!is_numeric($testVal1) || $testVal1 < 31) {
|
||||
if (!is_numeric($testVal2) || $testVal2 < 12) {
|
||||
if (is_numeric($testVal3) && $testVal3 < 12) {
|
||||
$testVal3 += 2000;
|
||||
$testVal3 = (string) ($testVal3 + 2000);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,6 +131,8 @@ class Helpers
|
||||
|
||||
/**
|
||||
* Return result in one of three formats.
|
||||
*
|
||||
* @param array{year: int, month: int, day: int, hour: int, minute: int, second: int} $dateArray
|
||||
*/
|
||||
public static function returnIn3FormatsArray(array $dateArray, bool $noFrac = false): DateTime|float|int
|
||||
{
|
||||
@@ -264,11 +270,16 @@ class Helpers
|
||||
}
|
||||
}
|
||||
|
||||
/** @return array{year: int, month: int, day: int, hour: int, minute: int, second: int} */
|
||||
public static function dateParse(string $string): array
|
||||
{
|
||||
return self::forceArray(date_parse($string));
|
||||
/** @var array{year: int, month: int, day: int, hour: int, minute: int, second: int} */
|
||||
$temp = self::forceArray(date_parse($string));
|
||||
|
||||
return $temp;
|
||||
}
|
||||
|
||||
/** @param mixed[] $dateArray */
|
||||
public static function dateParseSucceeded(array $dateArray): bool
|
||||
{
|
||||
return $dateArray['error_count'] === 0;
|
||||
@@ -278,10 +289,19 @@ class Helpers
|
||||
* Despite documentation, date_parse probably never returns false.
|
||||
* Just in case, this routine helps guarantee it.
|
||||
*
|
||||
* @param array|false $dateArray
|
||||
* @param array<mixed>|false $dateArray
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function forceArray(array|bool $dateArray): array
|
||||
{
|
||||
return is_array($dateArray) ? $dateArray : ['error_count' => 1];
|
||||
}
|
||||
|
||||
public static function floatOrInt(mixed $value): float|int
|
||||
{
|
||||
$result = Functions::scalar($value);
|
||||
|
||||
return is_numeric($result) ? ($result + 0) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,12 +24,12 @@ class Month
|
||||
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
|
||||
* PHP DateTime object, or a standard date string
|
||||
* Or can be an array of date values
|
||||
* @param array|int $adjustmentMonths The number of months before or after start_date.
|
||||
* @param array<mixed>|int $adjustmentMonths The number of months before or after start_date.
|
||||
* A positive value for months yields a future date;
|
||||
* a negative value yields a past date.
|
||||
* Or can be an array of adjustment values
|
||||
*
|
||||
* @return array|DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||
* @return array<mixed>|DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||
* depending on the value of the ReturnDateType flag
|
||||
* If an array of values is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
@@ -68,12 +68,12 @@ class Month
|
||||
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
|
||||
* PHP DateTime object, or a standard date string
|
||||
* Or can be an array of date values
|
||||
* @param array|int $adjustmentMonths The number of months before or after start_date.
|
||||
* @param array<mixed>|int $adjustmentMonths The number of months before or after start_date.
|
||||
* A positive value for months yields a future date;
|
||||
* a negative value yields a past date.
|
||||
* Or can be an array of adjustment values
|
||||
*
|
||||
* @return array|DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||
* @return array<mixed>|DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||
* depending on the value of the ReturnDateType flag
|
||||
* If an array of values is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
|
||||
@@ -29,7 +29,7 @@ class NetworkDays
|
||||
* Or can be an array of date values
|
||||
* @param mixed $dateArgs An array of dates (such as holidays) to exclude from the calculation
|
||||
*
|
||||
* @return array|int|string Interval between the dates
|
||||
* @return array<mixed>|int|string Interval between the dates
|
||||
* If an array of values is passed for the $startDate or $endDate arguments, then the returned result
|
||||
* will also be an array with matching dimensions
|
||||
*/
|
||||
|
||||
@@ -24,21 +24,21 @@ class Time
|
||||
* Excel Function:
|
||||
* TIME(hour,minute,second)
|
||||
*
|
||||
* @param null|array|bool|float|int|string $hour A number from 0 (zero) to 32767 representing the hour.
|
||||
* @param null|array<mixed>|bool|float|int|string $hour A number from 0 (zero) to 32767 representing the hour.
|
||||
* Any value greater than 23 will be divided by 24 and the remainder
|
||||
* will be treated as the hour value. For example, TIME(27,0,0) =
|
||||
* TIME(3,0,0) = .125 or 3:00 AM.
|
||||
* @param null|array|bool|float|int|string $minute A number from 0 to 32767 representing the minute.
|
||||
* @param null|array<mixed>|bool|float|int|string $minute A number from 0 to 32767 representing the minute.
|
||||
* Any value greater than 59 will be converted to hours and minutes.
|
||||
* For example, TIME(0,750,0) = TIME(12,30,0) = .520833 or 12:30 PM.
|
||||
* @param null|array|bool|float|int|string $second A number from 0 to 32767 representing the second.
|
||||
* @param null|array<mixed>|bool|float|int|string $second A number from 0 to 32767 representing the second.
|
||||
* Any value greater than 59 will be converted to hours, minutes,
|
||||
* and seconds. For example, TIME(0,0,2000) = TIME(0,33,22) = .023148
|
||||
* or 12:33:20 AM
|
||||
* If an array of numbers is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*
|
||||
* @return array|DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||
* @return array<mixed>|DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||
* depending on the value of the ReturnDateType flag
|
||||
* If an array of numbers is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
@@ -87,13 +87,13 @@ class Time
|
||||
private static function adjustSecond(int &$second, int &$minute): void
|
||||
{
|
||||
if ($second < 0) {
|
||||
$minute += floor($second / 60);
|
||||
$minute += (int) floor($second / 60);
|
||||
$second = 60 - abs($second % 60);
|
||||
if ($second == 60) {
|
||||
$second = 0;
|
||||
}
|
||||
} elseif ($second >= 60) {
|
||||
$minute += floor($second / 60);
|
||||
$minute += intdiv($second, 60);
|
||||
$second = $second % 60;
|
||||
}
|
||||
}
|
||||
@@ -101,13 +101,13 @@ class Time
|
||||
private static function adjustMinute(int &$minute, int &$hour): void
|
||||
{
|
||||
if ($minute < 0) {
|
||||
$hour += floor($minute / 60);
|
||||
$hour += (int) floor($minute / 60);
|
||||
$minute = 60 - abs($minute % 60);
|
||||
if ($minute == 60) {
|
||||
$minute = 0;
|
||||
}
|
||||
} elseif ($minute >= 60) {
|
||||
$hour += floor($minute / 60);
|
||||
$hour += intdiv($minute, 60);
|
||||
$minute = $minute % 60;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ class TimeParts
|
||||
* PHP DateTime object, or a standard time string
|
||||
* Or can be an array of date/time values
|
||||
*
|
||||
* @return array|int|string Hour
|
||||
* @return array<mixed>|int|string Hour
|
||||
* If an array of numbers is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -35,7 +35,7 @@ class TimeParts
|
||||
|
||||
try {
|
||||
Helpers::nullFalseTrueToNumber($timeValue);
|
||||
if (!is_numeric($timeValue)) {
|
||||
if (is_string($timeValue) && !is_numeric($timeValue)) {
|
||||
$timeValue = Helpers::getTimeValue($timeValue);
|
||||
}
|
||||
Helpers::validateNotNegative($timeValue);
|
||||
@@ -64,7 +64,7 @@ class TimeParts
|
||||
* PHP DateTime object, or a standard time string
|
||||
* Or can be an array of date/time values
|
||||
*
|
||||
* @return array|int|string Minute
|
||||
* @return array<mixed>|int|string Minute
|
||||
* If an array of numbers is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -76,7 +76,7 @@ class TimeParts
|
||||
|
||||
try {
|
||||
Helpers::nullFalseTrueToNumber($timeValue);
|
||||
if (!is_numeric($timeValue)) {
|
||||
if (is_string($timeValue) && !is_numeric($timeValue)) {
|
||||
$timeValue = Helpers::getTimeValue($timeValue);
|
||||
}
|
||||
Helpers::validateNotNegative($timeValue);
|
||||
@@ -105,7 +105,7 @@ class TimeParts
|
||||
* PHP DateTime object, or a standard time string
|
||||
* Or can be an array of date/time values
|
||||
*
|
||||
* @return array|int|string Second
|
||||
* @return array<mixed>|int|string Second
|
||||
* If an array of numbers is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -117,7 +117,7 @@ class TimeParts
|
||||
|
||||
try {
|
||||
Helpers::nullFalseTrueToNumber($timeValue);
|
||||
if (!is_numeric($timeValue)) {
|
||||
if (is_string($timeValue) && !is_numeric($timeValue)) {
|
||||
$timeValue = Helpers::getTimeValue($timeValue);
|
||||
}
|
||||
Helpers::validateNotNegative($timeValue);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
|
||||
|
||||
use Composer\Pcre\Preg;
|
||||
use Datetime;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
@@ -12,6 +13,19 @@ class TimeValue
|
||||
{
|
||||
use ArrayEnabled;
|
||||
|
||||
private const EXTRACT_TIME = '/\b'
|
||||
. '(\d+)' // match[1] - hour
|
||||
. '(:' // start of match[2] (rest of string) - colon
|
||||
. '(\d+' // start of match[3] - minute
|
||||
. '(:\d+' // start of match[4] - colon and seconds
|
||||
. '([.]\d+)?' // match[5] - optional decimal point followed by fractional seconds
|
||||
. ')?' // end of match[4], which is optional
|
||||
. ')' // end of match 3
|
||||
// Excel does not require 'm' to trail 'a' or 'p'; Php does
|
||||
. '(\s*(a|p))?' // match[6] optional whitespace followed by optional match[7] a or p
|
||||
. ')' // end of match[2]
|
||||
. '/i';
|
||||
|
||||
/**
|
||||
* TIMEVALUE.
|
||||
*
|
||||
@@ -25,13 +39,13 @@ class TimeValue
|
||||
* Excel Function:
|
||||
* TIMEVALUE(timeValue)
|
||||
*
|
||||
* @param null|array|bool|float|int|string $timeValue A text string that represents a time in any one of the Microsoft
|
||||
* @param null|array<mixed>|bool|float|int|string $timeValue A text string that represents a time in any one of the Microsoft
|
||||
* Excel time formats; for example, "6:45 PM" and "18:45" text strings
|
||||
* within quotation marks that represent time.
|
||||
* Date information in time_text is ignored.
|
||||
* Or can be an array of date/time values
|
||||
*
|
||||
* @return array|Datetime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||
* @return array<mixed>|Datetime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||
* depending on the value of the ReturnDateType flag
|
||||
* If an array of numbers is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
@@ -43,17 +57,20 @@ class TimeValue
|
||||
}
|
||||
|
||||
// try to parse as time iff there is at least one digit
|
||||
if (is_string($timeValue) && preg_match('/\\d/', $timeValue) !== 1) {
|
||||
if (is_string($timeValue) && !Preg::isMatch('/\d/', $timeValue)) {
|
||||
return ExcelError::VALUE();
|
||||
}
|
||||
|
||||
$timeValue = trim((string) $timeValue, '"');
|
||||
$timeValue = str_replace(['/', '.'], '-', $timeValue);
|
||||
|
||||
$arraySplit = preg_split('/[\/:\-\s]/', $timeValue) ?: [];
|
||||
if ((count($arraySplit) == 2 || count($arraySplit) == 3) && $arraySplit[0] > 24) {
|
||||
$arraySplit[0] = ((int) $arraySplit[0] % 24);
|
||||
$timeValue = implode(':', $arraySplit);
|
||||
if (Preg::isMatch(self::EXTRACT_TIME, $timeValue, $matches)) {
|
||||
if (empty($matches[6])) { // am/pm
|
||||
$hour = (int) $matches[0];
|
||||
$timeValue = ($hour % 24) . $matches[2];
|
||||
} elseif ($matches[6] === $matches[7]) { // Excel wants space before am/pm
|
||||
return ExcelError::VALUE();
|
||||
} else {
|
||||
$timeValue = $matches[0] . 'm';
|
||||
}
|
||||
}
|
||||
|
||||
$PHPDateArray = Helpers::dateParse($timeValue);
|
||||
|
||||
@@ -28,7 +28,7 @@ class Week
|
||||
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
|
||||
* PHP DateTime object, or a standard date string
|
||||
* Or can be an array of date values
|
||||
* @param array|int $method Week begins on Sunday or Monday
|
||||
* @param array<mixed>|int $method Week begins on Sunday or Monday
|
||||
* 1 or omitted Week begins on Sunday.
|
||||
* 2 Week begins on Monday.
|
||||
* 11 Week begins on Monday.
|
||||
@@ -41,7 +41,7 @@ class Week
|
||||
* 21 ISO (Jan. 4 is week 1, begins on Monday).
|
||||
* Or can be an array of methods
|
||||
*
|
||||
* @return array|int|string Week Number
|
||||
* @return array<mixed>|int|string Week Number
|
||||
* If an array of values is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -101,7 +101,7 @@ class Week
|
||||
* PHP DateTime object, or a standard date string
|
||||
* Or can be an array of date values
|
||||
*
|
||||
* @return array|int|string Week Number
|
||||
* @return array<mixed>|int|string Week Number
|
||||
* If an array of numbers is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -137,7 +137,7 @@ class Week
|
||||
* Excel Function:
|
||||
* WEEKDAY(dateValue[,style])
|
||||
*
|
||||
* @param null|array|bool|float|int|string $dateValue Excel date serial value (float), PHP date timestamp (integer),
|
||||
* @param null|array<mixed>|bool|float|int|string $dateValue Excel date serial value (float), PHP date timestamp (integer),
|
||||
* PHP DateTime object, or a standard date string
|
||||
* Or can be an array of date values
|
||||
* @param mixed $style A number that determines the type of return value
|
||||
@@ -146,7 +146,7 @@ class Week
|
||||
* 3 Numbers 0 (Monday) through 6 (Sunday).
|
||||
* Or can be an array of styles
|
||||
*
|
||||
* @return array|int|string Day of the week value
|
||||
* @return array<mixed>|int|string Day of the week value
|
||||
* If an array of values is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
|
||||
@@ -22,16 +22,16 @@ class WorkDay
|
||||
* Excel Function:
|
||||
* WORKDAY(startDate,endDays[,holidays[,holiday[,...]]])
|
||||
*
|
||||
* @param array|mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
|
||||
* @param array<mixed>|mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
|
||||
* PHP DateTime object, or a standard date string
|
||||
* Or can be an array of date values
|
||||
* @param array|int $endDays The number of nonweekend and nonholiday days before or after
|
||||
* @param array<mixed>|int $endDays The number of nonweekend and nonholiday days before or after
|
||||
* startDate. A positive value for days yields a future date; a
|
||||
* negative value yields a past date.
|
||||
* Or can be an array of int values
|
||||
* @param null|mixed $dateArgs An array of dates (such as holidays) to exclude from the calculation
|
||||
*
|
||||
* @return array|DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||
* @return array<mixed>|DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||
* depending on the value of the ReturnDateType flag
|
||||
* If an array of values is passed for the $startDate or $endDays,arguments, then the returned result
|
||||
* will also be an array with matching dimensions
|
||||
@@ -72,6 +72,8 @@ class WorkDay
|
||||
|
||||
/**
|
||||
* Use incrementing logic to determine Workday.
|
||||
*
|
||||
* @param array<mixed> $holidayArray
|
||||
*/
|
||||
private static function incrementing(float $startDate, int $endDays, array $holidayArray): float|int|DateTime
|
||||
{
|
||||
@@ -103,10 +105,12 @@ class WorkDay
|
||||
return Helpers::returnIn3FormatsFloat($endDate);
|
||||
}
|
||||
|
||||
/** @param array<mixed> $holidayArray */
|
||||
private static function incrementingArray(float $startDate, float $endDate, array $holidayArray): float
|
||||
{
|
||||
$holidayCountedArray = $holidayDates = [];
|
||||
foreach ($holidayArray as $holidayDate) {
|
||||
/** @var float $holidayDate */
|
||||
if (self::getWeekDay($holidayDate, 3) < 5) {
|
||||
$holidayDates[] = $holidayDate;
|
||||
}
|
||||
@@ -131,6 +135,8 @@ class WorkDay
|
||||
|
||||
/**
|
||||
* Use decrementing logic to determine Workday.
|
||||
*
|
||||
* @param array<mixed> $holidayArray
|
||||
*/
|
||||
private static function decrementing(float $startDate, int $endDays, array $holidayArray): float|int|DateTime
|
||||
{
|
||||
@@ -162,10 +168,12 @@ class WorkDay
|
||||
return Helpers::returnIn3FormatsFloat($endDate);
|
||||
}
|
||||
|
||||
/** @param array<mixed> $holidayArray */
|
||||
private static function decrementingArray(float $startDate, float $endDate, array $holidayArray): float
|
||||
{
|
||||
$holidayCountedArray = $holidayDates = [];
|
||||
foreach ($holidayArray as $holidayDate) {
|
||||
/** @var float $holidayDate */
|
||||
if (self::getWeekDay($holidayDate, 3) < 5) {
|
||||
$holidayDates[] = $holidayDate;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ class YearFrac
|
||||
* @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
|
||||
* PHP DateTime object, or a standard date string
|
||||
* Or can be an array of methods
|
||||
* @param array|int $method Method used for the calculation
|
||||
* @param array<mixed>|int $method Method used for the calculation
|
||||
* 0 or omitted US (NASD) 30/360
|
||||
* 1 Actual/actual
|
||||
* 2 Actual/360
|
||||
@@ -39,7 +39,7 @@ class YearFrac
|
||||
* 4 European 30/360
|
||||
* Or can be an array of methods
|
||||
*
|
||||
* @return array|float|int|string fraction of the year, or a string containing an error
|
||||
* @return array<mixed>|float|int|string fraction of the year, or a string containing an error
|
||||
* If an array of values is passed for the $startDate or $endDays,arguments, then the returned result
|
||||
* will also be an array with matching dimensions
|
||||
*/
|
||||
@@ -62,11 +62,11 @@ class YearFrac
|
||||
}
|
||||
|
||||
return match ($method) {
|
||||
0 => Functions::scalar(Days360::between($startDate, $endDate)) / 360,
|
||||
0 => Helpers::floatOrInt(Days360::between($startDate, $endDate)) / 360,
|
||||
1 => self::method1($startDate, $endDate),
|
||||
2 => Functions::scalar(Difference::interval($startDate, $endDate)) / 360,
|
||||
3 => Functions::scalar(Difference::interval($startDate, $endDate)) / 365,
|
||||
4 => Functions::scalar(Days360::between($startDate, $endDate, true)) / 360,
|
||||
2 => Helpers::floatOrInt(Difference::interval($startDate, $endDate)) / 360,
|
||||
3 => Helpers::floatOrInt(Difference::interval($startDate, $endDate)) / 365,
|
||||
4 => Helpers::floatOrInt(Days360::between($startDate, $endDate, true)) / 360,
|
||||
default => ExcelError::NAN(),
|
||||
};
|
||||
}
|
||||
@@ -91,7 +91,7 @@ class YearFrac
|
||||
|
||||
private static function method1(float $startDate, float $endDate): float
|
||||
{
|
||||
$days = Functions::scalar(Difference::interval($startDate, $endDate));
|
||||
$days = Helpers::floatOrInt(Difference::interval($startDate, $endDate));
|
||||
$startYear = (int) DateParts::year($startDate);
|
||||
$endYear = (int) DateParts::year($endDate);
|
||||
$years = $endYear - $startYear + 1;
|
||||
|
||||
@@ -8,14 +8,18 @@ class ArrayArgumentHelper
|
||||
{
|
||||
protected int $indexStart = 0;
|
||||
|
||||
/** @var mixed[] */
|
||||
protected array $arguments;
|
||||
|
||||
protected int $argumentCount;
|
||||
|
||||
/** @var int[] */
|
||||
protected array $rows;
|
||||
|
||||
/** @var int[] */
|
||||
protected array $columns;
|
||||
|
||||
/** @param mixed[] $arguments */
|
||||
public function initialise(array $arguments): void
|
||||
{
|
||||
$keys = array_keys($arguments);
|
||||
@@ -34,6 +38,7 @@ class ArrayArgumentHelper
|
||||
}
|
||||
}
|
||||
|
||||
/** @return mixed[] */
|
||||
public function arguments(): array
|
||||
{
|
||||
return $this->arguments;
|
||||
@@ -65,6 +70,7 @@ class ArrayArgumentHelper
|
||||
return count($rowVectors) === 1 ? array_pop($rowVectors) : null;
|
||||
}
|
||||
|
||||
/** @return int[] */
|
||||
private function getRowVectors(): array
|
||||
{
|
||||
$rowVectors = [];
|
||||
@@ -84,6 +90,7 @@ class ArrayArgumentHelper
|
||||
return count($columnVectors) === 1 ? array_pop($columnVectors) : null;
|
||||
}
|
||||
|
||||
/** @return int[] */
|
||||
private function getColumnVectors(): array
|
||||
{
|
||||
$columnVectors = [];
|
||||
@@ -96,6 +103,7 @@ class ArrayArgumentHelper
|
||||
return $columnVectors;
|
||||
}
|
||||
|
||||
/** @return int[] */
|
||||
public function getMatrixPair(): array
|
||||
{
|
||||
for ($i = $this->indexStart; $i < ($this->indexStart + $this->argumentCount - 1); ++$i) {
|
||||
@@ -134,6 +142,11 @@ class ArrayArgumentHelper
|
||||
return $this->columns[$argument];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $arguments
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
private function rows(array $arguments): array
|
||||
{
|
||||
return array_map(
|
||||
@@ -142,14 +155,17 @@ class ArrayArgumentHelper
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $arguments
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
private function columns(array $arguments): array
|
||||
{
|
||||
return array_map(
|
||||
function (mixed $argument): int {
|
||||
return is_array($argument) && is_array($argument[array_keys($argument)[0]])
|
||||
fn (mixed $argument): int => is_array($argument) && is_array($argument[array_keys($argument)[0]])
|
||||
? count($argument[array_keys($argument)[0]])
|
||||
: 1;
|
||||
},
|
||||
: 1,
|
||||
$arguments
|
||||
);
|
||||
}
|
||||
@@ -166,6 +182,13 @@ class ArrayArgumentHelper
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $arguments
|
||||
* @param int[] $rows
|
||||
* @param int[] $columns
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private function flattenSingleCellArrays(array $arguments, array $rows, array $columns): array
|
||||
{
|
||||
foreach ($arguments as $index => $argument) {
|
||||
@@ -180,6 +203,11 @@ class ArrayArgumentHelper
|
||||
return $arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $array
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private function filterArray(array $array): array
|
||||
{
|
||||
return array_filter(
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Engine;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
|
||||
class ArrayArgumentProcessor
|
||||
{
|
||||
private static ArrayArgumentHelper $arrayArgumentHelper;
|
||||
|
||||
/** @return mixed[] */
|
||||
public static function processArguments(
|
||||
ArrayArgumentHelper $arrayArgumentHelper,
|
||||
callable $method,
|
||||
@@ -54,23 +56,30 @@ class ArrayArgumentProcessor
|
||||
return ['#VALUE!'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $matrixIndexes
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function evaluateVectorMatrixPair(callable $method, array $matrixIndexes, mixed ...$arguments): array
|
||||
{
|
||||
$matrix2 = array_pop($matrixIndexes);
|
||||
/** @var array $matrixValues2 */
|
||||
$matrix2 = array_pop($matrixIndexes) ?? throw new Exception('empty array 2');
|
||||
/** @var mixed[][] $matrixValues2 */
|
||||
$matrixValues2 = $arguments[$matrix2];
|
||||
$matrix1 = array_pop($matrixIndexes);
|
||||
/** @var array $matrixValues1 */
|
||||
$matrix1 = array_pop($matrixIndexes) ?? throw new Exception('empty array 1');
|
||||
/** @var mixed[][] $matrixValues1 */
|
||||
$matrixValues1 = $arguments[$matrix1];
|
||||
|
||||
$rows = min(array_map([self::$arrayArgumentHelper, 'rowCount'], [$matrix1, $matrix2]));
|
||||
$columns = min(array_map([self::$arrayArgumentHelper, 'columnCount'], [$matrix1, $matrix2]));
|
||||
/** @var non-empty-array<int> */
|
||||
$matrix12 = [$matrix1, $matrix2];
|
||||
$rows = min(array_map(self::$arrayArgumentHelper->rowCount(...), $matrix12));
|
||||
$columns = min(array_map(self::$arrayArgumentHelper->columnCount(...), $matrix12));
|
||||
|
||||
if ($rows === 1) {
|
||||
$rows = max(array_map([self::$arrayArgumentHelper, 'rowCount'], [$matrix1, $matrix2]));
|
||||
$rows = max(array_map(self::$arrayArgumentHelper->rowCount(...), $matrix12));
|
||||
}
|
||||
if ($columns === 1) {
|
||||
$columns = max(array_map([self::$arrayArgumentHelper, 'columnCount'], [$matrix1, $matrix2]));
|
||||
$columns = max(array_map(self::$arrayArgumentHelper->columnCount(...), $matrix12));
|
||||
}
|
||||
|
||||
$result = [];
|
||||
@@ -92,13 +101,18 @@ class ArrayArgumentProcessor
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $matrixIndexes
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function evaluateMatrixPair(callable $method, array $matrixIndexes, mixed ...$arguments): array
|
||||
{
|
||||
$matrix2 = array_pop($matrixIndexes);
|
||||
/** @var array $matrixValues2 */
|
||||
/** @var mixed[][] $matrixValues2 */
|
||||
$matrixValues2 = $arguments[$matrix2];
|
||||
$matrix1 = array_pop($matrixIndexes);
|
||||
/** @var array $matrixValues1 */
|
||||
/** @var mixed[][] $matrixValues1 */
|
||||
$matrixValues1 = $arguments[$matrix1];
|
||||
|
||||
$result = [];
|
||||
@@ -119,6 +133,7 @@ class ArrayArgumentProcessor
|
||||
return $result;
|
||||
}
|
||||
|
||||
/** @return mixed[] */
|
||||
private static function evaluateVectorPair(callable $method, int $rowIndex, int $columnIndex, mixed ...$arguments): array
|
||||
{
|
||||
$rowVector = Functions::flattenArray($arguments[$rowIndex]);
|
||||
@@ -141,11 +156,13 @@ class ArrayArgumentProcessor
|
||||
|
||||
/**
|
||||
* Note, offset is from 1 (for the first argument) rather than from 0.
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function evaluateNthArgumentAsArray(callable $method, int $nthArgument, mixed ...$arguments): array
|
||||
{
|
||||
$values = array_slice($arguments, $nthArgument - 1, 1);
|
||||
/** @var array $values */
|
||||
/** @var mixed[] $values */
|
||||
$values = array_pop($values);
|
||||
|
||||
$result = [];
|
||||
|
||||
@@ -16,40 +16,30 @@ class FormattedNumber
|
||||
// preg_quoted string for major currency symbols, with a %s for locale currency
|
||||
private const CURRENCY_CONVERSION_LIST = '\$€£¥%s';
|
||||
|
||||
private const STRING_CONVERSION_LIST = [
|
||||
[self::class, 'convertToNumberIfNumeric'],
|
||||
[self::class, 'convertToNumberIfFraction'],
|
||||
[self::class, 'convertToNumberIfPercent'],
|
||||
[self::class, 'convertToNumberIfCurrency'],
|
||||
];
|
||||
|
||||
/**
|
||||
* Identify whether a string contains a formatted numeric value,
|
||||
* and convert it to a numeric if it is.
|
||||
*
|
||||
* @param string $operand string value to test
|
||||
* @param float|string $operand string value to test
|
||||
*/
|
||||
public static function convertToNumberIfFormatted(string &$operand): bool
|
||||
public static function convertToNumberIfFormatted(float|string &$operand): bool
|
||||
{
|
||||
foreach (self::STRING_CONVERSION_LIST as $conversionMethod) {
|
||||
if ($conversionMethod($operand) === true) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return self::convertToNumberIfNumeric($operand)
|
||||
|| self::convertToNumberIfFraction($operand)
|
||||
|| self::convertToNumberIfPercent($operand)
|
||||
|| self::convertToNumberIfCurrency($operand);
|
||||
}
|
||||
|
||||
/**
|
||||
* Identify whether a string contains a numeric value,
|
||||
* and convert it to a numeric if it is.
|
||||
*
|
||||
* @param string $operand string value to test
|
||||
* @param float|string $operand string value to test
|
||||
*/
|
||||
public static function convertToNumberIfNumeric(string &$operand): bool
|
||||
public static function convertToNumberIfNumeric(float|string &$operand): bool
|
||||
{
|
||||
$thousandsSeparator = preg_quote(StringHelper::getThousandsSeparator(), '/');
|
||||
$value = preg_replace(['/(\d)' . $thousandsSeparator . '(\d)/u', '/([+-])\s+(\d)/u'], ['$1$2', '$1$2'], trim($operand));
|
||||
$value = preg_replace(['/(\d)' . $thousandsSeparator . '(\d)/u', '/([+-])\s+(\d)/u'], ['$1$2', '$1$2'], trim("$operand"));
|
||||
$decimalSeparator = preg_quote(StringHelper::getDecimalSeparator(), '/');
|
||||
$value = preg_replace(['/(\d)' . $decimalSeparator . '(\d)/u', '/([+-])\s+(\d)/u'], ['$1.$2', '$1$2'], $value ?? '');
|
||||
|
||||
@@ -68,13 +58,15 @@ class FormattedNumber
|
||||
*
|
||||
* @param string $operand string value to test
|
||||
*/
|
||||
public static function convertToNumberIfFraction(string &$operand): bool
|
||||
public static function convertToNumberIfFraction(float|string &$operand): bool
|
||||
{
|
||||
if (preg_match(self::STRING_REGEXP_FRACTION, $operand, $match)) {
|
||||
if (is_string($operand) && preg_match(self::STRING_REGEXP_FRACTION, $operand, $match)) {
|
||||
$sign = ($match[1] === '-') ? '-' : '+';
|
||||
$wholePart = ($match[3] === '') ? '' : ($sign . $match[3]);
|
||||
$fractionFormula = '=' . $wholePart . $sign . $match[4];
|
||||
$operand = Calculation::getInstance()->_calculateFormulaValue($fractionFormula);
|
||||
/** @var string */
|
||||
$operandx = Calculation::getInstance()->_calculateFormulaValue($fractionFormula);
|
||||
$operand = $operandx;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -86,12 +78,12 @@ class FormattedNumber
|
||||
* Identify whether a string contains a percentage, and if so,
|
||||
* convert it to a numeric.
|
||||
*
|
||||
* @param string $operand string value to test
|
||||
* @param float|string $operand string value to test
|
||||
*/
|
||||
public static function convertToNumberIfPercent(string &$operand): bool
|
||||
public static function convertToNumberIfPercent(float|string &$operand): bool
|
||||
{
|
||||
$thousandsSeparator = preg_quote(StringHelper::getThousandsSeparator(), '/');
|
||||
$value = preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', trim($operand));
|
||||
$value = preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', trim("$operand"));
|
||||
$decimalSeparator = preg_quote(StringHelper::getDecimalSeparator(), '/');
|
||||
$value = preg_replace(['/(\d)' . $decimalSeparator . '(\d)/u', '/([+-])\s+(\d)/u'], ['$1.$2', '$1$2'], $value ?? '');
|
||||
|
||||
@@ -111,13 +103,13 @@ class FormattedNumber
|
||||
* Identify whether a string contains a currency value, and if so,
|
||||
* convert it to a numeric.
|
||||
*
|
||||
* @param string $operand string value to test
|
||||
* @param float|string $operand string value to test
|
||||
*/
|
||||
public static function convertToNumberIfCurrency(string &$operand): bool
|
||||
public static function convertToNumberIfCurrency(float|string &$operand): bool
|
||||
{
|
||||
$currencyRegexp = self::currencyMatcherRegexp();
|
||||
$thousandsSeparator = preg_quote(StringHelper::getThousandsSeparator(), '/');
|
||||
$value = preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', $operand);
|
||||
$value = preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', "$operand");
|
||||
|
||||
$match = [];
|
||||
if ($value !== null && preg_match($currencyRegexp, $value, $match, PREG_UNMATCHED_AS_NULL)) {
|
||||
|
||||
@@ -78,7 +78,7 @@ class Logger
|
||||
{
|
||||
// Only write the debug log if logging is enabled
|
||||
if ($this->writeDebugLog) {
|
||||
$message = sprintf($message, ...$args);
|
||||
$message = sprintf($message, ...$args); //* @phpstan-ignore-line
|
||||
$cellReference = implode(' -> ', $this->cellStack->showStack());
|
||||
if ($this->echoDebugLog) {
|
||||
echo $cellReference,
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Engine\Operands;
|
||||
|
||||
interface Operand
|
||||
{
|
||||
/** @param string[] $matches */
|
||||
public static function fromParser(string $formula, int $index, array $matches): self;
|
||||
|
||||
public function value(): string;
|
||||
|
||||
@@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Cell;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Table;
|
||||
use Stringable;
|
||||
|
||||
@@ -29,7 +30,7 @@ final class StructuredReference implements Operand, Stringable
|
||||
self::ITEM_SPECIFIER_TOTALS,
|
||||
];
|
||||
|
||||
private const TABLE_REFERENCE = '/([\p{L}_\\\\][\p{L}\p{N}\._]+)?(\[(?:[^\]\[]+|(?R))*+\])/miu';
|
||||
private const TABLE_REFERENCE = '/([\p{L}_\\\][\p{L}\p{N}\._]+)?(\[(?:[^\]\[]+|(?R))*+\])/miu';
|
||||
|
||||
private string $value;
|
||||
|
||||
@@ -47,6 +48,7 @@ final class StructuredReference implements Operand, Stringable
|
||||
|
||||
private ?int $totalsRow;
|
||||
|
||||
/** @var mixed[] */
|
||||
private array $columns;
|
||||
|
||||
public function __construct(string $structuredReference)
|
||||
@@ -54,6 +56,7 @@ final class StructuredReference implements Operand, Stringable
|
||||
$this->value = $structuredReference;
|
||||
}
|
||||
|
||||
/** @param string[] $matches */
|
||||
public static function fromParser(string $formula, int $index, array $matches): self
|
||||
{
|
||||
$val = $matches[0];
|
||||
@@ -171,14 +174,20 @@ final class StructuredReference implements Operand, Stringable
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{array{string, int}, array{string, int}} $tableRange
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private function getColumns(Cell $cell, array $tableRange): array
|
||||
{
|
||||
$worksheet = $cell->getWorksheet();
|
||||
$cellReference = $cell->getCoordinate();
|
||||
|
||||
$columns = [];
|
||||
$lastColumn = ++$tableRange[1][0];
|
||||
for ($column = $tableRange[0][0]; $column !== $lastColumn; ++$column) {
|
||||
$lastColumn = StringHelper::stringIncrement($tableRange[1][0]);
|
||||
for ($column = $tableRange[0][0]; $column !== $lastColumn; StringHelper::stringIncrement($column)) {
|
||||
/** @var string $column */
|
||||
$columns[$column] = $worksheet
|
||||
->getCell($column . ($this->headersRow ?? ($this->firstDataRow - 1)))
|
||||
->getCalculatedValue();
|
||||
@@ -196,7 +205,7 @@ final class StructuredReference implements Operand, Stringable
|
||||
$reference = str_replace('[' . self::ITEM_SPECIFIER_THIS_ROW . '],', '', $reference);
|
||||
|
||||
foreach ($this->columns as $columnId => $columnName) {
|
||||
$columnName = str_replace("\u{a0}", ' ', $columnName);
|
||||
$columnName = str_replace("\u{a0}", ' ', $columnName); //* @phpstan-ignore-line
|
||||
$reference = $this->adjustRowReference($columnName, $reference, $cell, $columnId);
|
||||
}
|
||||
|
||||
@@ -330,7 +339,7 @@ final class StructuredReference implements Operand, Stringable
|
||||
{
|
||||
$columnsSelected = false;
|
||||
foreach ($this->columns as $columnId => $columnName) {
|
||||
$columnName = str_replace("\u{a0}", ' ', $columnName ?? '');
|
||||
$columnName = str_replace("\u{a0}", ' ', $columnName ?? ''); //* @phpstan-ignore-line
|
||||
$cellFrom = "{$columnId}{$startRow}";
|
||||
$cellTo = "{$columnId}{$endRow}";
|
||||
$cellReference = ($cellFrom === $cellTo) ? $cellFrom : "{$cellFrom}:{$cellTo}";
|
||||
|
||||
@@ -31,7 +31,7 @@ class BesselI
|
||||
* If $ord < 0, BESSELI returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string Result, or a string containing an error
|
||||
* @return array<mixed>|float|string Result, or a string containing an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
|
||||
@@ -30,7 +30,7 @@ class BesselJ
|
||||
* If $ord < 0, BESSELJ returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string Result, or a string containing an error
|
||||
* @return array<mixed>|float|string Result, or a string containing an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
|
||||
@@ -28,7 +28,7 @@ class BesselK
|
||||
* If $ord < 0, BESSELKI returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string Result, or a string containing an error
|
||||
* @return array<mixed>|float|string Result, or a string containing an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
|
||||
@@ -27,7 +27,7 @@ class BesselY
|
||||
* If $ord < 0, BESSELY returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string Result, or a string containing an error
|
||||
* @return array<mixed>|float|string Result, or a string containing an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
|
||||
@@ -30,10 +30,10 @@ class BitWise
|
||||
* Excel Function:
|
||||
* BITAND(number1, number2)
|
||||
*
|
||||
* @param null|array|bool|float|int|string $number1 Or can be an array of values
|
||||
* @param null|array|bool|float|int|string $number2 Or can be an array of values
|
||||
* @param null|array<mixed>|bool|float|int|string $number1 Or can be an array of values
|
||||
* @param null|array<mixed>|bool|float|int|string $number2 Or can be an array of values
|
||||
*
|
||||
* @return array|int|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|int|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function BITAND(null|array|bool|float|int|string $number1, null|array|bool|float|int|string $number2): array|string|int|float
|
||||
@@ -62,10 +62,10 @@ class BitWise
|
||||
* Excel Function:
|
||||
* BITOR(number1, number2)
|
||||
*
|
||||
* @param null|array|bool|float|int|string $number1 Or can be an array of values
|
||||
* @param null|array|bool|float|int|string $number2 Or can be an array of values
|
||||
* @param null|array<mixed>|bool|float|int|string $number1 Or can be an array of values
|
||||
* @param null|array<mixed>|bool|float|int|string $number2 Or can be an array of values
|
||||
*
|
||||
* @return array|int|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|int|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function BITOR(null|array|bool|float|int|string $number1, null|array|bool|float|int|string $number2): array|string|int|float
|
||||
@@ -95,10 +95,10 @@ class BitWise
|
||||
* Excel Function:
|
||||
* BITXOR(number1, number2)
|
||||
*
|
||||
* @param null|array|bool|float|int|string $number1 Or can be an array of values
|
||||
* @param null|array|bool|float|int|string $number2 Or can be an array of values
|
||||
* @param null|array<mixed>|bool|float|int|string $number1 Or can be an array of values
|
||||
* @param null|array<mixed>|bool|float|int|string $number2 Or can be an array of values
|
||||
*
|
||||
* @return array|int|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|int|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function BITXOR(null|array|bool|float|int|string $number1, null|array|bool|float|int|string $number2): array|string|int|float
|
||||
@@ -128,10 +128,10 @@ class BitWise
|
||||
* Excel Function:
|
||||
* BITLSHIFT(number, shift_amount)
|
||||
*
|
||||
* @param null|array|bool|float|int|string $number Or can be an array of values
|
||||
* @param null|array|bool|float|int|string $shiftAmount Or can be an array of values
|
||||
* @param null|array<mixed>|bool|float|int|string $number Or can be an array of values
|
||||
* @param null|array<mixed>|bool|float|int|string $shiftAmount Or can be an array of values
|
||||
*
|
||||
* @return array|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function BITLSHIFT(null|array|bool|float|int|string $number, null|array|bool|float|int|string $shiftAmount): array|string|float
|
||||
@@ -163,10 +163,10 @@ class BitWise
|
||||
* Excel Function:
|
||||
* BITRSHIFT(number, shift_amount)
|
||||
*
|
||||
* @param null|array|bool|float|int|string $number Or can be an array of values
|
||||
* @param null|array|bool|float|int|string $shiftAmount Or can be an array of values
|
||||
* @param null|array<mixed>|bool|float|int|string $number Or can be an array of values
|
||||
* @param null|array<mixed>|bool|float|int|string $shiftAmount Or can be an array of values
|
||||
*
|
||||
* @return array|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function BITRSHIFT(null|array|bool|float|int|string $number, null|array|bool|float|int|string $shiftAmount): array|string|float
|
||||
@@ -221,7 +221,7 @@ class BitWise
|
||||
$value = self::nullFalseTrueToNumber($value);
|
||||
|
||||
if (is_numeric($value)) {
|
||||
if (abs($value) > 53) {
|
||||
if (abs($value + 0) > 53) {
|
||||
throw new Exception(ExcelError::NAN());
|
||||
}
|
||||
|
||||
|
||||
@@ -20,12 +20,12 @@ class Compare
|
||||
* functions you calculate the count of equal pairs. This function is also known as the
|
||||
* Kronecker Delta function.
|
||||
*
|
||||
* @param array|bool|float|int|string $a the first number
|
||||
* @param array<mixed>|bool|float|int|string $a the first number
|
||||
* Or can be an array of values
|
||||
* @param array|bool|float|int|string $b The second number. If omitted, b is assumed to be zero.
|
||||
* @param array<mixed>|bool|float|int|string $b The second number. If omitted, b is assumed to be zero.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|int|string (string in the event of an error)
|
||||
* @return array<mixed>|int|string (string in the event of an error)
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -55,12 +55,12 @@ class Compare
|
||||
* Use this function to filter a set of values. For example, by summing several GESTEP
|
||||
* functions you calculate the count of values that exceed a threshold.
|
||||
*
|
||||
* @param array|bool|float|int|string $number the value to test against step
|
||||
* @param array<mixed>|bool|float|int|string $number the value to test against step
|
||||
* Or can be an array of values
|
||||
* @param null|array|bool|float|int|string $step The threshold value. If you omit a value for step, GESTEP uses zero.
|
||||
* @param null|array<mixed>|bool|float|int|string $step The threshold value. If you omit a value for step, GESTEP uses zero.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|int|string (string in the event of an error)
|
||||
* @return array<mixed>|int|string (string in the event of an error)
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
|
||||
@@ -28,7 +28,7 @@ class Complex
|
||||
* If omitted, the suffix is assumed to be "i".
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function COMPLEX(mixed $realNumber = 0.0, mixed $imaginary = 0.0, mixed $suffix = 'i'): array|string
|
||||
@@ -65,11 +65,11 @@ class Complex
|
||||
* Excel Function:
|
||||
* IMAGINARY(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the imaginary
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the imaginary
|
||||
* coefficient
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string (string if an error)
|
||||
* @return array<mixed>|float|string (string if an error)
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -96,10 +96,10 @@ class Complex
|
||||
* Excel Function:
|
||||
* IMREAL(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the real coefficient
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the real coefficient
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string (string if an error)
|
||||
* @return array<mixed>|float|string (string if an error)
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
|
||||
@@ -19,10 +19,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMABS(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the absolute value
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the absolute value
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMABS(array|string $complexNumber): array|float|string
|
||||
@@ -49,10 +49,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMARGUMENT(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the argument theta
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the argument theta
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMARGUMENT(array|string $complexNumber): array|float|string
|
||||
@@ -82,10 +82,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMCONJUGATE(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the conjugate
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the conjugate
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMCONJUGATE(array|string $complexNumber): array|string
|
||||
@@ -111,10 +111,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMCOS(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the cosine
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the cosine
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMCOS(array|string $complexNumber): array|string
|
||||
@@ -140,10 +140,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMCOSH(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the hyperbolic cosine
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the hyperbolic cosine
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMCOSH(array|string $complexNumber): array|string
|
||||
@@ -169,10 +169,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMCOT(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the cotangent
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the cotangent
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMCOT(array|string $complexNumber): array|string
|
||||
@@ -198,10 +198,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMCSC(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the cosecant
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the cosecant
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMCSC(array|string $complexNumber): array|string
|
||||
@@ -227,10 +227,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMCSCH(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the hyperbolic cosecant
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the hyperbolic cosecant
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMCSCH(array|string $complexNumber): array|string
|
||||
@@ -256,10 +256,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMSIN(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the sine
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the sine
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMSIN(array|string $complexNumber): array|string
|
||||
@@ -285,10 +285,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMSINH(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the hyperbolic sine
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the hyperbolic sine
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMSINH(array|string $complexNumber): array|string
|
||||
@@ -314,10 +314,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMSEC(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the secant
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the secant
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMSEC(array|string $complexNumber): array|string
|
||||
@@ -343,10 +343,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMSECH(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the hyperbolic secant
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the hyperbolic secant
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMSECH(array|string $complexNumber): array|string
|
||||
@@ -372,10 +372,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMTAN(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the tangent
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the tangent
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMTAN(array|string $complexNumber): array|string
|
||||
@@ -401,10 +401,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMSQRT(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the square root
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the square root
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMSQRT(array|string $complexNumber): array|string
|
||||
@@ -435,10 +435,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMLN(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the natural logarithm
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the natural logarithm
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMLN(array|string $complexNumber): array|string
|
||||
@@ -468,10 +468,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMLOG10(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the common logarithm
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the common logarithm
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMLOG10(array|string $complexNumber): array|string
|
||||
@@ -501,10 +501,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMLOG2(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the base-2 logarithm
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the base-2 logarithm
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMLOG2(array|string $complexNumber): array|string
|
||||
@@ -534,10 +534,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMEXP(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the exponential
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the exponential
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMEXP(array|string $complexNumber): array|string
|
||||
@@ -563,12 +563,12 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMPOWER(complexNumber,realNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number you want to raise to a power
|
||||
* @param array<mixed>|string $complexNumber the complex number you want to raise to a power
|
||||
* Or can be an array of values
|
||||
* @param array|float|int|string $realNumber the power to which you want to raise the complex number
|
||||
* @param array<mixed>|float|int|string $realNumber the power to which you want to raise the complex number
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMPOWER(array|string $complexNumber, array|float|int|string $realNumber): array|string
|
||||
|
||||
@@ -20,12 +20,12 @@ class ComplexOperations
|
||||
* Excel Function:
|
||||
* IMDIV(complexDividend,complexDivisor)
|
||||
*
|
||||
* @param array|string $complexDividend the complex numerator or dividend
|
||||
* @param array<mixed>|string $complexDividend the complex numerator or dividend
|
||||
* Or can be an array of values
|
||||
* @param array|string $complexDivisor the complex denominator or divisor
|
||||
* @param array<mixed>|string $complexDivisor the complex denominator or divisor
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMDIV(array|string $complexDividend, array|string $complexDivisor): array|string
|
||||
@@ -49,12 +49,12 @@ class ComplexOperations
|
||||
* Excel Function:
|
||||
* IMSUB(complexNumber1,complexNumber2)
|
||||
*
|
||||
* @param array|string $complexNumber1 the complex number from which to subtract complexNumber2
|
||||
* @param array<mixed>|string $complexNumber1 the complex number from which to subtract complexNumber2
|
||||
* Or can be an array of values
|
||||
* @param array|string $complexNumber2 the complex number to subtract from complexNumber1
|
||||
* @param array<mixed>|string $complexNumber2 the complex number to subtract from complexNumber1
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMSUB(array|string $complexNumber1, array|string $complexNumber2): array|string
|
||||
|
||||
@@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
|
||||
abstract class ConvertBase
|
||||
{
|
||||
@@ -26,7 +27,7 @@ abstract class ConvertBase
|
||||
}
|
||||
}
|
||||
|
||||
return strtoupper((string) $value);
|
||||
return strtoupper(StringHelper::convertToString($value));
|
||||
}
|
||||
|
||||
protected static function validatePlaces(mixed $places = null): ?int
|
||||
|
||||
@@ -15,7 +15,7 @@ class ConvertBinary extends ConvertBase
|
||||
* Excel Function:
|
||||
* BIN2DEC(x)
|
||||
*
|
||||
* @param array|bool|float|int|string $value The binary number (as a string) that you want to convert. The number
|
||||
* @param array<mixed>|bool|float|int|string $value The binary number (as a string) that you want to convert. The number
|
||||
* cannot contain more than 10 characters (10 bits). The most significant
|
||||
* bit of number is the sign bit. The remaining 9 bits are magnitude bits.
|
||||
* Negative numbers are represented using two's-complement notation.
|
||||
@@ -23,7 +23,7 @@ class ConvertBinary extends ConvertBase
|
||||
* 10 characters (10 bits), BIN2DEC returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* @return array<mixed>|float|int|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -44,10 +44,10 @@ class ConvertBinary extends ConvertBase
|
||||
// Two's Complement
|
||||
$value = substr($value, -9);
|
||||
|
||||
return '-' . (512 - bindec($value));
|
||||
return -(512 - bindec($value));
|
||||
}
|
||||
|
||||
return (string) bindec($value);
|
||||
return bindec($value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,14 +58,14 @@ class ConvertBinary extends ConvertBase
|
||||
* Excel Function:
|
||||
* BIN2HEX(x[,places])
|
||||
*
|
||||
* @param array|bool|float|int|string $value The binary number (as a string) that you want to convert. The number
|
||||
* @param array<mixed>|bool|float|int|string $value The binary number (as a string) that you want to convert. The number
|
||||
* cannot contain more than 10 characters (10 bits). The most significant
|
||||
* bit of number is the sign bit. The remaining 9 bits are magnitude bits.
|
||||
* Negative numbers are represented using two's-complement notation.
|
||||
* If number is not a valid binary number, or if number contains more than
|
||||
* 10 characters (10 bits), BIN2HEX returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
* @param null|array|float|int|string $places The number of characters to use. If places is omitted, BIN2HEX uses the
|
||||
* @param null|array<mixed>|float|int|string $places The number of characters to use. If places is omitted, BIN2HEX uses the
|
||||
* minimum number of characters necessary. Places is useful for padding the
|
||||
* return value with leading 0s (zeros).
|
||||
* If places is not an integer, it is truncated.
|
||||
@@ -73,7 +73,7 @@ class ConvertBinary extends ConvertBase
|
||||
* If places is negative, BIN2HEX returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* @return array<mixed>|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -111,14 +111,14 @@ class ConvertBinary extends ConvertBase
|
||||
* Excel Function:
|
||||
* BIN2OCT(x[,places])
|
||||
*
|
||||
* @param array|bool|float|int|string $value The binary number (as a string) that you want to convert. The number
|
||||
* @param array<mixed>|bool|float|int|string $value The binary number (as a string) that you want to convert. The number
|
||||
* cannot contain more than 10 characters (10 bits). The most significant
|
||||
* bit of number is the sign bit. The remaining 9 bits are magnitude bits.
|
||||
* Negative numbers are represented using two's-complement notation.
|
||||
* If number is not a valid binary number, or if number contains more than
|
||||
* 10 characters (10 bits), BIN2OCT returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
* @param null|array|float|int|string $places The number of characters to use. If places is omitted, BIN2OCT uses the
|
||||
* @param null|array<mixed>|float|int|string $places The number of characters to use. If places is omitted, BIN2OCT uses the
|
||||
* minimum number of characters necessary. Places is useful for padding the
|
||||
* return value with leading 0s (zeros).
|
||||
* If places is not an integer, it is truncated.
|
||||
@@ -126,7 +126,7 @@ class ConvertBinary extends ConvertBase
|
||||
* If places is negative, BIN2OCT returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* @return array<mixed>|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
|
||||
@@ -22,7 +22,7 @@ class ConvertDecimal extends ConvertBase
|
||||
* Excel Function:
|
||||
* DEC2BIN(x[,places])
|
||||
*
|
||||
* @param array|bool|float|int|string $value The decimal integer you want to convert. If number is negative,
|
||||
* @param array<mixed>|bool|float|int|string $value The decimal integer you want to convert. If number is negative,
|
||||
* valid place values are ignored and DEC2BIN returns a 10-character
|
||||
* (10-bit) binary number in which the most significant bit is the sign
|
||||
* bit. The remaining 9 bits are magnitude bits. Negative numbers are
|
||||
@@ -33,7 +33,7 @@ class ConvertDecimal extends ConvertBase
|
||||
* If DEC2BIN requires more than places characters, it returns the #NUM!
|
||||
* error value.
|
||||
* Or can be an array of values
|
||||
* @param null|array|float|int|string $places The number of characters to use. If places is omitted, DEC2BIN uses
|
||||
* @param null|array<mixed>|float|int|string $places The number of characters to use. If places is omitted, DEC2BIN uses
|
||||
* the minimum number of characters necessary. Places is useful for
|
||||
* padding the return value with leading 0s (zeros).
|
||||
* If places is not an integer, it is truncated.
|
||||
@@ -41,7 +41,7 @@ class ConvertDecimal extends ConvertBase
|
||||
* If places is zero or negative, DEC2BIN returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* @return array<mixed>|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -79,7 +79,7 @@ class ConvertDecimal extends ConvertBase
|
||||
* Excel Function:
|
||||
* DEC2HEX(x[,places])
|
||||
*
|
||||
* @param array|bool|float|int|string $value The decimal integer you want to convert. If number is negative,
|
||||
* @param array<mixed>|bool|float|int|string $value The decimal integer you want to convert. If number is negative,
|
||||
* places is ignored and DEC2HEX returns a 10-character (40-bit)
|
||||
* hexadecimal number in which the most significant bit is the sign
|
||||
* bit. The remaining 39 bits are magnitude bits. Negative numbers
|
||||
@@ -90,7 +90,7 @@ class ConvertDecimal extends ConvertBase
|
||||
* If DEC2HEX requires more than places characters, it returns the
|
||||
* #NUM! error value.
|
||||
* Or can be an array of values
|
||||
* @param null|array|float|int|string $places The number of characters to use. If places is omitted, DEC2HEX uses
|
||||
* @param null|array<mixed>|float|int|string $places The number of characters to use. If places is omitted, DEC2HEX uses
|
||||
* the minimum number of characters necessary. Places is useful for
|
||||
* padding the return value with leading 0s (zeros).
|
||||
* If places is not an integer, it is truncated.
|
||||
@@ -98,7 +98,7 @@ class ConvertDecimal extends ConvertBase
|
||||
* If places is zero or negative, DEC2HEX returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* @return array<mixed>|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -155,7 +155,7 @@ class ConvertDecimal extends ConvertBase
|
||||
* Excel Function:
|
||||
* DEC2OCT(x[,places])
|
||||
*
|
||||
* @param array|bool|float|int|string $value The decimal integer you want to convert. If number is negative,
|
||||
* @param array<mixed>|bool|float|int|string $value The decimal integer you want to convert. If number is negative,
|
||||
* places is ignored and DEC2OCT returns a 10-character (30-bit)
|
||||
* octal number in which the most significant bit is the sign bit.
|
||||
* The remaining 29 bits are magnitude bits. Negative numbers are
|
||||
@@ -166,7 +166,7 @@ class ConvertDecimal extends ConvertBase
|
||||
* If DEC2OCT requires more than places characters, it returns the
|
||||
* #NUM! error value.
|
||||
* Or can be an array of values
|
||||
* @param array|int $places The number of characters to use. If places is omitted, DEC2OCT uses
|
||||
* @param array<mixed>|int $places The number of characters to use. If places is omitted, DEC2OCT uses
|
||||
* the minimum number of characters necessary. Places is useful for
|
||||
* padding the return value with leading 0s (zeros).
|
||||
* If places is not an integer, it is truncated.
|
||||
@@ -174,7 +174,7 @@ class ConvertDecimal extends ConvertBase
|
||||
* If places is zero or negative, DEC2OCT returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* @return array<mixed>|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
|
||||
@@ -15,7 +15,7 @@ class ConvertHex extends ConvertBase
|
||||
* Excel Function:
|
||||
* HEX2BIN(x[,places])
|
||||
*
|
||||
* @param array|bool|float|string $value The hexadecimal number you want to convert.
|
||||
* @param array<mixed>|bool|float|string $value The hexadecimal number you want to convert.
|
||||
* Number cannot contain more than 10 characters.
|
||||
* The most significant bit of number is the sign bit (40th bit from the right).
|
||||
* The remaining 9 bits are magnitude bits.
|
||||
@@ -26,7 +26,7 @@ class ConvertHex extends ConvertBase
|
||||
* If number is not a valid hexadecimal number, HEX2BIN returns the #NUM! error value.
|
||||
* If HEX2BIN requires more than places characters, it returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
* @param array|int $places The number of characters to use. If places is omitted,
|
||||
* @param array<mixed>|int $places The number of characters to use. If places is omitted,
|
||||
* HEX2BIN uses the minimum number of characters necessary. Places
|
||||
* is useful for padding the return value with leading 0s (zeros).
|
||||
* If places is not an integer, it is truncated.
|
||||
@@ -34,7 +34,7 @@ class ConvertHex extends ConvertBase
|
||||
* If places is negative, HEX2BIN returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* @return array<mixed>|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -65,7 +65,7 @@ class ConvertHex extends ConvertBase
|
||||
* Excel Function:
|
||||
* HEX2DEC(x)
|
||||
*
|
||||
* @param array|bool|float|int|string $value The hexadecimal number you want to convert. This number cannot
|
||||
* @param array<mixed>|bool|float|int|string $value The hexadecimal number you want to convert. This number cannot
|
||||
* contain more than 10 characters (40 bits). The most significant
|
||||
* bit of number is the sign bit. The remaining 39 bits are magnitude
|
||||
* bits. Negative numbers are represented using two's-complement
|
||||
@@ -74,7 +74,7 @@ class ConvertHex extends ConvertBase
|
||||
* #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* @return array<mixed>|float|int|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -96,7 +96,7 @@ class ConvertHex extends ConvertBase
|
||||
}
|
||||
|
||||
$binX = '';
|
||||
foreach (str_split($value) as $char) {
|
||||
foreach (mb_str_split($value, 1, 'UTF-8') as $char) {
|
||||
$binX .= str_pad(base_convert($char, 16, 2), 4, '0', STR_PAD_LEFT);
|
||||
}
|
||||
if (strlen($binX) == 40 && $binX[0] == '1') {
|
||||
@@ -104,10 +104,10 @@ class ConvertHex extends ConvertBase
|
||||
$binX[$i] = ($binX[$i] == '1' ? '0' : '1');
|
||||
}
|
||||
|
||||
return (string) ((bindec($binX) + 1) * -1);
|
||||
return (bindec($binX) + 1) * -1;
|
||||
}
|
||||
|
||||
return (string) bindec($binX);
|
||||
return bindec($binX);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,7 +118,7 @@ class ConvertHex extends ConvertBase
|
||||
* Excel Function:
|
||||
* HEX2OCT(x[,places])
|
||||
*
|
||||
* @param array|bool|float|int|string $value The hexadecimal number you want to convert. Number cannot
|
||||
* @param array<mixed>|bool|float|int|string $value The hexadecimal number you want to convert. Number cannot
|
||||
* contain more than 10 characters. The most significant bit of
|
||||
* number is the sign bit. The remaining 39 bits are magnitude
|
||||
* bits. Negative numbers are represented using two's-complement
|
||||
@@ -132,7 +132,7 @@ class ConvertHex extends ConvertBase
|
||||
* If HEX2OCT requires more than places characters, it returns
|
||||
* the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
* @param array|int $places The number of characters to use. If places is omitted, HEX2OCT
|
||||
* @param array<mixed>|int $places The number of characters to use. If places is omitted, HEX2OCT
|
||||
* uses the minimum number of characters necessary. Places is
|
||||
* useful for padding the return value with leading 0s (zeros).
|
||||
* If places is not an integer, it is truncated.
|
||||
@@ -141,7 +141,7 @@ class ConvertHex extends ConvertBase
|
||||
* If places is negative, HEX2OCT returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* @return array<mixed>|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
|
||||
@@ -15,7 +15,7 @@ class ConvertOctal extends ConvertBase
|
||||
* Excel Function:
|
||||
* OCT2BIN(x[,places])
|
||||
*
|
||||
* @param array|bool|float|int|string $value The octal number you want to convert. Number may not
|
||||
* @param array<mixed>|bool|float|int|string $value The octal number you want to convert. Number may not
|
||||
* contain more than 10 characters. The most significant
|
||||
* bit of number is the sign bit. The remaining 29 bits
|
||||
* are magnitude bits. Negative numbers are represented
|
||||
@@ -29,7 +29,7 @@ class ConvertOctal extends ConvertBase
|
||||
* If OCT2BIN requires more than places characters, it
|
||||
* returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
* @param array|int $places The number of characters to use. If places is omitted,
|
||||
* @param array<mixed>|int $places The number of characters to use. If places is omitted,
|
||||
* OCT2BIN uses the minimum number of characters necessary.
|
||||
* Places is useful for padding the return value with
|
||||
* leading 0s (zeros).
|
||||
@@ -40,7 +40,7 @@ class ConvertOctal extends ConvertBase
|
||||
* value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* @return array<mixed>|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -69,7 +69,7 @@ class ConvertOctal extends ConvertBase
|
||||
* Excel Function:
|
||||
* OCT2DEC(x)
|
||||
*
|
||||
* @param array|bool|float|int|string $value The octal number you want to convert. Number may not contain
|
||||
* @param array<mixed>|bool|float|int|string $value The octal number you want to convert. Number may not contain
|
||||
* more than 10 octal characters (30 bits). The most significant
|
||||
* bit of number is the sign bit. The remaining 29 bits are
|
||||
* magnitude bits. Negative numbers are represented using
|
||||
@@ -78,7 +78,7 @@ class ConvertOctal extends ConvertBase
|
||||
* #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* @return array<mixed>|float|int|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -96,7 +96,7 @@ class ConvertOctal extends ConvertBase
|
||||
}
|
||||
|
||||
$binX = '';
|
||||
foreach (str_split($value) as $char) {
|
||||
foreach (mb_str_split($value, 1, 'UTF-8') as $char) {
|
||||
$binX .= str_pad(decbin((int) $char), 3, '0', STR_PAD_LEFT);
|
||||
}
|
||||
if (strlen($binX) == 30 && $binX[0] == '1') {
|
||||
@@ -104,10 +104,10 @@ class ConvertOctal extends ConvertBase
|
||||
$binX[$i] = ($binX[$i] == '1' ? '0' : '1');
|
||||
}
|
||||
|
||||
return (string) ((bindec($binX) + 1) * -1);
|
||||
return (bindec($binX) + 1) * -1;
|
||||
}
|
||||
|
||||
return (string) bindec($binX);
|
||||
return bindec($binX);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,7 +118,7 @@ class ConvertOctal extends ConvertBase
|
||||
* Excel Function:
|
||||
* OCT2HEX(x[,places])
|
||||
*
|
||||
* @param array|bool|float|int|string $value The octal number you want to convert. Number may not contain
|
||||
* @param array<mixed>|bool|float|int|string $value The octal number you want to convert. Number may not contain
|
||||
* more than 10 octal characters (30 bits). The most significant
|
||||
* bit of number is the sign bit. The remaining 29 bits are
|
||||
* magnitude bits. Negative numbers are represented using
|
||||
@@ -130,7 +130,7 @@ class ConvertOctal extends ConvertBase
|
||||
* If OCT2HEX requires more than places characters, it returns
|
||||
* the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
* @param array|int $places The number of characters to use. If places is omitted, OCT2HEX
|
||||
* @param array<mixed>|int $places The number of characters to use. If places is omitted, OCT2HEX
|
||||
* uses the minimum number of characters necessary. Places is useful
|
||||
* for padding the return value with leading 0s (zeros).
|
||||
* If places is not an integer, it is truncated.
|
||||
@@ -138,7 +138,7 @@ class ConvertOctal extends ConvertBase
|
||||
* If places is negative, OCT2HEX returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* @return array<mixed>|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
|
||||
@@ -230,7 +230,7 @@ class ConvertUOM
|
||||
/**
|
||||
* Details of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
|
||||
*
|
||||
** @var array<string, array{multiplier: float|int, name: string}>
|
||||
* @var array<string, array{multiplier: float|int, name: string}>
|
||||
*/
|
||||
private static array $binaryConversionMultipliers = [
|
||||
'Yi' => ['multiplier' => 2 ** 80, 'name' => 'yobi'],
|
||||
@@ -435,6 +435,8 @@ class ConvertUOM
|
||||
/**
|
||||
* getConversionGroups
|
||||
* Returns a list of the different conversion groups for UOM conversions.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function getConversionCategories(): array
|
||||
{
|
||||
@@ -451,6 +453,8 @@ class ConvertUOM
|
||||
* Returns an array of units of measure, for a specified conversion group, or for all groups.
|
||||
*
|
||||
* @param ?string $category The group whose units of measure you want to retrieve
|
||||
*
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function getConversionCategoryUnits(?string $category = null): array
|
||||
{
|
||||
@@ -468,6 +472,8 @@ class ConvertUOM
|
||||
* getConversionGroupUnitDetails.
|
||||
*
|
||||
* @param ?string $category The group whose units of measure you want to retrieve
|
||||
*
|
||||
* @return array<string, list<array<string, string>>>
|
||||
*/
|
||||
public static function getConversionCategoryUnitDetails(?string $category = null): array
|
||||
{
|
||||
@@ -488,7 +494,7 @@ class ConvertUOM
|
||||
* getConversionMultipliers
|
||||
* Returns an array of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
|
||||
*
|
||||
* @return mixed[]
|
||||
* @return array<string, array{multiplier: float, name: string}>
|
||||
*/
|
||||
public static function getConversionMultipliers(): array
|
||||
{
|
||||
@@ -499,7 +505,7 @@ class ConvertUOM
|
||||
* getBinaryConversionMultipliers
|
||||
* Returns an array of the additional Multiplier prefixes that can be used with Information Units of Measure in CONVERTUOM().
|
||||
*
|
||||
* @return mixed[]
|
||||
* @return array<string, array{multiplier: float|int, name: string}>
|
||||
*/
|
||||
public static function getBinaryConversionMultipliers(): array
|
||||
{
|
||||
@@ -516,14 +522,14 @@ class ConvertUOM
|
||||
* Excel Function:
|
||||
* CONVERT(value,fromUOM,toUOM)
|
||||
*
|
||||
* @param array|float|int|string $value the value in fromUOM to convert
|
||||
* @param array<mixed>|float|int|string $value the value in fromUOM to convert
|
||||
* Or can be an array of values
|
||||
* @param array|string $fromUOM the units for value
|
||||
* @param string|string[] $fromUOM the units for value
|
||||
* Or can be an array of values
|
||||
* @param array|string $toUOM the units for the result
|
||||
* @param string|string[] $toUOM the units for the result
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string Result, or a string containing an error
|
||||
* @return float|mixed[]|string Result, or a string containing an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -566,6 +572,7 @@ class ConvertUOM
|
||||
return ($baseValue * self::$unitConversions[$fromCategory][$toUOM]) / $toMultiplier;
|
||||
}
|
||||
|
||||
/** @return array{0: string, 1: string, 2: float} */
|
||||
private static function getUOMDetails(string $uom): array
|
||||
{
|
||||
if (isset(self::$conversionUnits[$uom])) {
|
||||
|
||||
@@ -31,7 +31,7 @@ class Erf
|
||||
* If omitted, ERF integrates between zero and lower_limit
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function ERF(mixed $lower, mixed $upper = null): array|float|string
|
||||
@@ -63,7 +63,7 @@ class Erf
|
||||
* @param mixed $limit Float bound for integrating ERF, other bound is zero
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function ERFPRECISE(mixed $limit)
|
||||
|
||||
@@ -26,7 +26,7 @@ class ErfC
|
||||
* @param mixed $value The float lower bound for integrating ERFC
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function ERFC(mixed $value)
|
||||
|
||||
@@ -54,9 +54,7 @@ class Amortization
|
||||
$salvage = Functions::flattenSingleValue($salvage);
|
||||
$period = Functions::flattenSingleValue($period);
|
||||
$rate = Functions::flattenSingleValue($rate);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$cost = FinancialValidations::validateFloat($cost);
|
||||
@@ -141,9 +139,7 @@ class Amortization
|
||||
$salvage = Functions::flattenSingleValue($salvage);
|
||||
$period = Functions::flattenSingleValue($period);
|
||||
$rate = Functions::flattenSingleValue($rate);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$cost = FinancialValidations::validateFloat($cost);
|
||||
@@ -171,9 +167,13 @@ class Amortization
|
||||
if (
|
||||
$basis == FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL
|
||||
&& $yearFrac < 1
|
||||
&& DateTimeExcel\Helpers::isLeapYear(Functions::scalar($purchasedYear))
|
||||
) {
|
||||
$yearFrac *= 365 / 366;
|
||||
$temp = Functions::scalar($purchasedYear);
|
||||
if (is_int($temp) || is_string($temp)) {
|
||||
if (DateTimeExcel\Helpers::isLeapYear($temp)) {
|
||||
$yearFrac *= 365 / 366;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$f0Rate = $yearFrac * $rate * $cost;
|
||||
|
||||
@@ -38,9 +38,9 @@ class Periodic
|
||||
): string|float {
|
||||
$rate = Functions::flattenSingleValue($rate);
|
||||
$numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
|
||||
$payment = ($payment === null) ? 0.0 : Functions::flattenSingleValue($payment);
|
||||
$presentValue = ($presentValue === null) ? 0.0 : Functions::flattenSingleValue($presentValue);
|
||||
$type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
|
||||
$payment = Functions::flattenSingleValue($payment) ?? 0.0;
|
||||
$presentValue = Functions::flattenSingleValue($presentValue) ?? 0.0;
|
||||
$type = Functions::flattenSingleValue($type) ?? FinancialConstants::PAYMENT_END_OF_PERIOD;
|
||||
|
||||
try {
|
||||
$rate = CashFlowValidations::validateRate($rate);
|
||||
@@ -77,9 +77,9 @@ class Periodic
|
||||
): string|float {
|
||||
$rate = Functions::flattenSingleValue($rate);
|
||||
$numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
|
||||
$payment = ($payment === null) ? 0.0 : Functions::flattenSingleValue($payment);
|
||||
$futureValue = ($futureValue === null) ? 0.0 : Functions::flattenSingleValue($futureValue);
|
||||
$type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
|
||||
$payment = Functions::flattenSingleValue($payment) ?? 0.0;
|
||||
$futureValue = Functions::flattenSingleValue($futureValue) ?? 0.0;
|
||||
$type = Functions::flattenSingleValue($type) ?? FinancialConstants::PAYMENT_END_OF_PERIOD;
|
||||
|
||||
try {
|
||||
$rate = CashFlowValidations::validateRate($rate);
|
||||
@@ -122,8 +122,8 @@ class Periodic
|
||||
$rate = Functions::flattenSingleValue($rate);
|
||||
$payment = Functions::flattenSingleValue($payment);
|
||||
$presentValue = Functions::flattenSingleValue($presentValue);
|
||||
$futureValue = ($futureValue === null) ? 0.0 : Functions::flattenSingleValue($futureValue);
|
||||
$type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
|
||||
$futureValue = Functions::flattenSingleValue($futureValue) ?? 0.0;
|
||||
$type = Functions::flattenSingleValue($type) ?? FinancialConstants::PAYMENT_END_OF_PERIOD;
|
||||
|
||||
try {
|
||||
$rate = CashFlowValidations::validateRate($rate);
|
||||
@@ -150,7 +150,7 @@ class Periodic
|
||||
float $presentValue,
|
||||
int $type
|
||||
): float {
|
||||
if ($rate !== null && $rate != 0) {
|
||||
if ($rate != 0) {
|
||||
return -$presentValue
|
||||
* (1 + $rate) ** $numberOfPeriods - $payment * (1 + $rate * $type) * ((1 + $rate) ** $numberOfPeriods - 1)
|
||||
/ $rate;
|
||||
|
||||
@@ -41,7 +41,7 @@ class Cumulative
|
||||
$presentValue = Functions::flattenSingleValue($presentValue);
|
||||
$start = Functions::flattenSingleValue($start);
|
||||
$end = Functions::flattenSingleValue($end);
|
||||
$type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
|
||||
$type = Functions::flattenSingleValue($type) ?? FinancialConstants::PAYMENT_END_OF_PERIOD;
|
||||
|
||||
try {
|
||||
$rate = CashFlowValidations::validateRate($rate);
|
||||
@@ -104,7 +104,7 @@ class Cumulative
|
||||
$presentValue = Functions::flattenSingleValue($presentValue);
|
||||
$start = Functions::flattenSingleValue($start);
|
||||
$end = Functions::flattenSingleValue($end);
|
||||
$type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
|
||||
$type = Functions::flattenSingleValue($type) ?? FinancialConstants::PAYMENT_END_OF_PERIOD;
|
||||
|
||||
try {
|
||||
$rate = CashFlowValidations::validateRate($rate);
|
||||
|
||||
@@ -43,7 +43,7 @@ class Interest
|
||||
$numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
|
||||
$presentValue = Functions::flattenSingleValue($presentValue);
|
||||
$futureValue = ($futureValue === null) ? 0.0 : Functions::flattenSingleValue($futureValue);
|
||||
$type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
|
||||
$type = Functions::flattenSingleValue($type) ?? FinancialConstants::PAYMENT_END_OF_PERIOD;
|
||||
|
||||
try {
|
||||
$interestRate = CashFlowValidations::validateRate($interestRate);
|
||||
@@ -160,9 +160,9 @@ class Interest
|
||||
$numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
|
||||
$payment = Functions::flattenSingleValue($payment);
|
||||
$presentValue = Functions::flattenSingleValue($presentValue);
|
||||
$futureValue = ($futureValue === null) ? 0.0 : Functions::flattenSingleValue($futureValue);
|
||||
$type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
|
||||
$guess = ($guess === null) ? 0.1 : Functions::flattenSingleValue($guess);
|
||||
$futureValue = Functions::flattenSingleValue($futureValue) ?? 0.0;
|
||||
$type = Functions::flattenSingleValue($type) ?? FinancialConstants::PAYMENT_END_OF_PERIOD;
|
||||
$guess = Functions::flattenSingleValue($guess) ?? 0.1;
|
||||
|
||||
try {
|
||||
$numberOfPeriods = CashFlowValidations::validateFloat($numberOfPeriods);
|
||||
|
||||
@@ -27,14 +27,14 @@ class Payments
|
||||
mixed $interestRate,
|
||||
mixed $numberOfPeriods,
|
||||
mixed $presentValue,
|
||||
mixed $futureValue = 0,
|
||||
mixed $futureValue = 0.0,
|
||||
mixed $type = FinancialConstants::PAYMENT_END_OF_PERIOD
|
||||
): string|float {
|
||||
$interestRate = Functions::flattenSingleValue($interestRate);
|
||||
$numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
|
||||
$presentValue = Functions::flattenSingleValue($presentValue);
|
||||
$futureValue = ($futureValue === null) ? 0.0 : Functions::flattenSingleValue($futureValue);
|
||||
$type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
|
||||
$futureValue = Functions::flattenSingleValue($futureValue) ?? 0.0;
|
||||
$type = Functions::flattenSingleValue($type) ?? FinancialConstants::PAYMENT_END_OF_PERIOD;
|
||||
|
||||
try {
|
||||
$interestRate = CashFlowValidations::validateRate($interestRate);
|
||||
@@ -83,7 +83,7 @@ class Payments
|
||||
$numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
|
||||
$presentValue = Functions::flattenSingleValue($presentValue);
|
||||
$futureValue = ($futureValue === null) ? 0.0 : Functions::flattenSingleValue($futureValue);
|
||||
$type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
|
||||
$type = Functions::flattenSingleValue($type) ?? FinancialConstants::PAYMENT_END_OF_PERIOD;
|
||||
|
||||
try {
|
||||
$interestRate = CashFlowValidations::validateRate($interestRate);
|
||||
|
||||
@@ -77,13 +77,13 @@ class Single
|
||||
*
|
||||
* Calculates the interest rate required for an investment to grow to a specified future value .
|
||||
*
|
||||
* @param array|float $periods The number of periods over which the investment is made
|
||||
* @param array|float $presentValue Present Value
|
||||
* @param array|float $futureValue Future Value
|
||||
* @param mixed $periods The number of periods over which the investment is made, expect array|float
|
||||
* @param mixed $presentValue Present Value, expect array|float
|
||||
* @param mixed $futureValue Future Value, expect array|float
|
||||
*
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function interestRate(array|float $periods = 0.0, array|float $presentValue = 0.0, array|float $futureValue = 0.0): string|float
|
||||
public static function interestRate(mixed $periods = 0.0, mixed $presentValue = 0.0, mixed $futureValue = 0.0): string|float
|
||||
{
|
||||
$periods = Functions::flattenSingleValue($periods);
|
||||
$presentValue = Functions::flattenSingleValue($presentValue);
|
||||
|
||||
@@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
|
||||
class NonPeriodic
|
||||
{
|
||||
@@ -23,16 +24,17 @@ class NonPeriodic
|
||||
* Excel Function:
|
||||
* =XIRR(values,dates,guess)
|
||||
*
|
||||
* @param float[] $values A series of cash flow payments
|
||||
* @param array<int, float|int|numeric-string> $values A series of cash flow payments, expecting float[]
|
||||
* The series of values must contain at least one positive value & one negative value
|
||||
* @param mixed[] $dates A series of payment dates
|
||||
* @param array<int, float|int|numeric-string> $dates A series of payment dates
|
||||
* The first payment date indicates the beginning of the schedule of payments
|
||||
* All other dates must be later than this date, but they may occur in any order
|
||||
* @param mixed $guess An optional guess at the expected answer
|
||||
*/
|
||||
public static function rate(array $values, array $dates, mixed $guess = self::DEFAULT_GUESS): float|string
|
||||
public static function rate(mixed $values, $dates, mixed $guess = self::DEFAULT_GUESS): float|string
|
||||
{
|
||||
$rslt = self::xirrPart1($values, $dates);
|
||||
/** @var array<int, float|int|numeric-string> $dates */
|
||||
if ($rslt !== '') {
|
||||
return $rslt;
|
||||
}
|
||||
@@ -91,6 +93,7 @@ class NonPeriodic
|
||||
$x2 += 0.5;
|
||||
}
|
||||
if ($found) {
|
||||
/** @var array<int, float|int|numeric-string> $dates */
|
||||
return self::xirrBisection($values, $dates, $x1, $x2);
|
||||
}
|
||||
|
||||
@@ -106,18 +109,18 @@ class NonPeriodic
|
||||
* Excel Function:
|
||||
* =XNPV(rate,values,dates)
|
||||
*
|
||||
* @param array|float $rate the discount rate to apply to the cash flows
|
||||
* @param float[] $values A series of cash flows that corresponds to a schedule of payments in dates.
|
||||
* @param mixed $rate the discount rate to apply to the cash flows, expect array|float
|
||||
* @param array<int,float|int|numeric-string> $values A series of cash flows that corresponds to a schedule of payments in dates, expecting float[].
|
||||
* The first payment is optional and corresponds to a cost or payment that occurs
|
||||
* at the beginning of the investment.
|
||||
* If the first value is a cost or payment, it must be a negative value.
|
||||
* All succeeding payments are discounted based on a 365-day year.
|
||||
* The series of values must contain at least one positive value and one negative value.
|
||||
* @param mixed[] $dates A schedule of payment dates that corresponds to the cash flow payments.
|
||||
* @param mixed $dates A schedule of payment dates that corresponds to the cash flow payments, expecting mixed[].
|
||||
* The first payment date indicates the beginning of the schedule of payments.
|
||||
* All other dates must be later than this date, but they may occur in any order.
|
||||
*/
|
||||
public static function presentValue(array|float $rate, array $values, array $dates): float|string
|
||||
public static function presentValue(mixed $rate, mixed $values, mixed $dates): float|string
|
||||
{
|
||||
return self::xnpvOrdered($rate, $values, $dates, true);
|
||||
}
|
||||
@@ -127,9 +130,12 @@ class NonPeriodic
|
||||
return $neg && $pos;
|
||||
}
|
||||
|
||||
/** @param array<int, float|int|numeric-string> $values */
|
||||
private static function xirrPart1(mixed &$values, mixed &$dates): string
|
||||
{
|
||||
$values = Functions::flattenArray($values);
|
||||
/** @var array<int, float|int|numeric-string> */
|
||||
$temp = Functions::flattenArray($values);
|
||||
$values = $temp;
|
||||
$dates = Functions::flattenArray($dates);
|
||||
$valuesIsArray = count($values) > 1;
|
||||
$datesIsArray = count($dates) > 1;
|
||||
@@ -152,6 +158,7 @@ class NonPeriodic
|
||||
return self::xirrPart2($values);
|
||||
}
|
||||
|
||||
/** @param array<int, float|int|numeric-string> $values */
|
||||
private static function xirrPart2(array &$values): string
|
||||
{
|
||||
$valCount = count($values);
|
||||
@@ -159,7 +166,7 @@ class NonPeriodic
|
||||
$foundneg = false;
|
||||
for ($i = 0; $i < $valCount; ++$i) {
|
||||
$fld = $values[$i];
|
||||
if (!is_numeric($fld)) {
|
||||
if (!is_numeric($fld)) { //* @phpstan-ignore-line
|
||||
return ExcelError::VALUE();
|
||||
} elseif ($fld > 0) {
|
||||
$foundpos = true;
|
||||
@@ -174,6 +181,10 @@ class NonPeriodic
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, float|int|numeric-string> $values
|
||||
* @param array<int, float|int|numeric-string> $dates
|
||||
*/
|
||||
private static function xirrPart3(array $values, array $dates, float $x1, float $x2): float|string
|
||||
{
|
||||
$f = self::xnpvOrdered($x1, $values, $dates, false);
|
||||
@@ -203,6 +214,10 @@ class NonPeriodic
|
||||
return $rslt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, float|int|numeric-string> $values
|
||||
* @param array<int, float|int|numeric-string> $dates
|
||||
*/
|
||||
private static function xirrBisection(array $values, array $dates, float $x1, float $x2): string|float
|
||||
{
|
||||
$rslt = ExcelError::NAN();
|
||||
@@ -239,9 +254,13 @@ class NonPeriodic
|
||||
return $rslt;
|
||||
}
|
||||
|
||||
/** @param array<int,float|int|numeric-string> $values> */
|
||||
private static function xnpvOrdered(mixed $rate, mixed $values, mixed $dates, bool $ordered = true, bool $capAtNegative1 = false): float|string
|
||||
{
|
||||
$rate = Functions::flattenSingleValue($rate);
|
||||
if (!is_numeric($rate)) {
|
||||
return ExcelError::VALUE();
|
||||
}
|
||||
$values = Functions::flattenArray($values);
|
||||
$dates = Functions::flattenArray($dates);
|
||||
$valCount = count($values);
|
||||
@@ -273,10 +292,10 @@ class NonPeriodic
|
||||
$dif = Functions::scalar(DateTimeExcel\Difference::interval($date0, $datei, 'd'));
|
||||
}
|
||||
if (!is_numeric($dif)) {
|
||||
return $dif;
|
||||
return StringHelper::convertToString($dif);
|
||||
}
|
||||
if ($rate <= -1.0) {
|
||||
$xnpv += -abs($values[$i]) / (-1 - $rate) ** ($dif / 365);
|
||||
$xnpv += -abs($values[$i] + 0) / (-1 - $rate) ** ($dif / 365);
|
||||
} else {
|
||||
$xnpv += $values[$i] / (1 + $rate) ** ($dif / 365);
|
||||
}
|
||||
@@ -285,6 +304,10 @@ class NonPeriodic
|
||||
return is_finite($xnpv) ? $xnpv : ExcelError::VALUE();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $values
|
||||
* @param mixed[] $dates
|
||||
*/
|
||||
private static function validateXnpv(mixed $rate, array $values, array $dates): void
|
||||
{
|
||||
if (!is_numeric($rate)) {
|
||||
@@ -294,7 +317,7 @@ class NonPeriodic
|
||||
if ($valCount != count($dates)) {
|
||||
throw new Exception(ExcelError::NAN());
|
||||
}
|
||||
if ($valCount > 1 && ((min($values) > 0) || (max($values) < 0))) {
|
||||
if (count($values) > 1 && ((min($values) > 0) || (max($values) < 0))) {
|
||||
throw new Exception(ExcelError::NAN());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,9 @@ class Periodic
|
||||
}
|
||||
$values = Functions::flattenArray($values);
|
||||
$guess = Functions::flattenSingleValue($guess);
|
||||
if (!is_numeric($guess)) {
|
||||
return ExcelError::VALUE();
|
||||
}
|
||||
|
||||
// create an initial range, with a root somewhere between 0 and guess
|
||||
$x1 = 0.0;
|
||||
@@ -103,7 +106,9 @@ class Periodic
|
||||
return ExcelError::DIV0();
|
||||
}
|
||||
$values = Functions::flattenArray($values);
|
||||
/** @var float */
|
||||
$financeRate = Functions::flattenSingleValue($financeRate);
|
||||
/** @var float */
|
||||
$reinvestmentRate = Functions::flattenSingleValue($reinvestmentRate);
|
||||
$n = count($values);
|
||||
|
||||
@@ -112,6 +117,7 @@ class Periodic
|
||||
|
||||
$npvPos = $npvNeg = 0.0;
|
||||
foreach ($values as $i => $v) {
|
||||
/** @var float $v */
|
||||
if ($v >= 0) {
|
||||
$npvPos += $v / $rr ** $i;
|
||||
} else {
|
||||
@@ -134,12 +140,13 @@ class Periodic
|
||||
*
|
||||
* Returns the Net Present Value of a cash flow series given a discount rate.
|
||||
*
|
||||
* @param array $args
|
||||
* @param array<mixed> $args
|
||||
*/
|
||||
public static function presentValue(mixed $rate, ...$args): int|float
|
||||
{
|
||||
$returnValue = 0;
|
||||
|
||||
/** @var float */
|
||||
$rate = Functions::flattenSingleValue($rate);
|
||||
$aArgs = Functions::flattenArray($args);
|
||||
|
||||
|
||||
@@ -49,9 +49,7 @@ class Coupons
|
||||
$settlement = Functions::flattenSingleValue($settlement);
|
||||
$maturity = Functions::flattenSingleValue($maturity);
|
||||
$frequency = Functions::flattenSingleValue($frequency);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$settlement = FinancialValidations::validateSettlementDate($settlement);
|
||||
@@ -110,9 +108,7 @@ class Coupons
|
||||
$settlement = Functions::flattenSingleValue($settlement);
|
||||
$maturity = Functions::flattenSingleValue($maturity);
|
||||
$frequency = Functions::flattenSingleValue($frequency);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$settlement = FinancialValidations::validateSettlementDate($settlement);
|
||||
@@ -179,9 +175,7 @@ class Coupons
|
||||
$settlement = Functions::flattenSingleValue($settlement);
|
||||
$maturity = Functions::flattenSingleValue($maturity);
|
||||
$frequency = Functions::flattenSingleValue($frequency);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$settlement = FinancialValidations::validateSettlementDate($settlement);
|
||||
@@ -244,9 +238,7 @@ class Coupons
|
||||
$settlement = Functions::flattenSingleValue($settlement);
|
||||
$maturity = Functions::flattenSingleValue($maturity);
|
||||
$frequency = Functions::flattenSingleValue($frequency);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$settlement = FinancialValidations::validateSettlementDate($settlement);
|
||||
@@ -296,9 +288,7 @@ class Coupons
|
||||
$settlement = Functions::flattenSingleValue($settlement);
|
||||
$maturity = Functions::flattenSingleValue($maturity);
|
||||
$frequency = Functions::flattenSingleValue($frequency);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$settlement = FinancialValidations::validateSettlementDate($settlement);
|
||||
@@ -355,9 +345,7 @@ class Coupons
|
||||
$settlement = Functions::flattenSingleValue($settlement);
|
||||
$maturity = Functions::flattenSingleValue($maturity);
|
||||
$frequency = Functions::flattenSingleValue($frequency);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$settlement = FinancialValidations::validateSettlementDate($settlement);
|
||||
|
||||
@@ -25,7 +25,7 @@ class Dollar
|
||||
* If you omit precision, it is assumed to be 2
|
||||
* Or can be an array of precision values
|
||||
*
|
||||
* @return array|string If an array of values is passed for either of the arguments, then the returned result
|
||||
* @return array<mixed>|string If an array of values is passed for either of the arguments, then the returned result
|
||||
* will also be an array with matching dimensions
|
||||
*/
|
||||
public static function format(mixed $number, mixed $precision = 2)
|
||||
@@ -47,6 +47,8 @@ class Dollar
|
||||
* Or can be an array of values
|
||||
* @param mixed $fraction Fraction
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array<mixed>|float|string
|
||||
*/
|
||||
public static function decimal(mixed $fractionalDollar = null, mixed $fraction = 0): array|string|float
|
||||
{
|
||||
@@ -93,6 +95,8 @@ class Dollar
|
||||
* Or can be an array of values
|
||||
* @param mixed $fraction Fraction
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array<mixed>|float|string
|
||||
*/
|
||||
public static function fractional(mixed $decimalDollar = null, mixed $fraction = 0): array|string|float
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@ class Helpers
|
||||
*
|
||||
* Returns the number of days in a specified year, as defined by the "basis" value
|
||||
*
|
||||
* @param int|string $year The year against which we're testing
|
||||
* @param mixed $year The year against which we're testing, expect int|string
|
||||
* @param int|string $basis The type of day count:
|
||||
* 0 or omitted US (NASD) 360
|
||||
* 1 Actual (365 or 366 in a leap year)
|
||||
@@ -24,8 +24,11 @@ class Helpers
|
||||
*
|
||||
* @return int|string Result, or a string containing an error
|
||||
*/
|
||||
public static function daysPerYear($year, $basis = 0): string|int
|
||||
public static function daysPerYear(mixed $year, $basis = 0): string|int
|
||||
{
|
||||
if (!is_int($year) && !is_string($year)) {
|
||||
return ExcelError::VALUE();
|
||||
}
|
||||
if (!is_numeric($basis)) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\YearFrac;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstants;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
|
||||
class AccruedInterest
|
||||
{
|
||||
@@ -59,12 +60,8 @@ class AccruedInterest
|
||||
$settlement = Functions::flattenSingleValue($settlement);
|
||||
$rate = Functions::flattenSingleValue($rate);
|
||||
$parValue = ($parValue === null) ? 1000 : Functions::flattenSingleValue($parValue);
|
||||
$frequency = ($frequency === null)
|
||||
? FinancialConstants::FREQUENCY_ANNUAL
|
||||
: Functions::flattenSingleValue($frequency);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$frequency = Functions::flattenSingleValue($frequency) ?? FinancialConstants::FREQUENCY_ANNUAL;
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$issue = SecurityValidations::validateIssueDate($issue);
|
||||
@@ -81,12 +78,12 @@ class AccruedInterest
|
||||
$daysBetweenIssueAndSettlement = Functions::scalar(YearFrac::fraction($issue, $settlement, $basis));
|
||||
if (!is_numeric($daysBetweenIssueAndSettlement)) {
|
||||
// return date error
|
||||
return $daysBetweenIssueAndSettlement;
|
||||
return StringHelper::convertToString($daysBetweenIssueAndSettlement);
|
||||
}
|
||||
$daysBetweenFirstInterestAndSettlement = Functions::scalar(YearFrac::fraction($firstInterest, $settlement, $basis));
|
||||
if (!is_numeric($daysBetweenFirstInterestAndSettlement)) {
|
||||
// return date error
|
||||
return $daysBetweenFirstInterestAndSettlement;
|
||||
return StringHelper::convertToString($daysBetweenFirstInterestAndSettlement);
|
||||
}
|
||||
|
||||
return $parValue * $rate * $daysBetweenIssueAndSettlement;
|
||||
@@ -125,9 +122,7 @@ class AccruedInterest
|
||||
$settlement = Functions::flattenSingleValue($settlement);
|
||||
$rate = Functions::flattenSingleValue($rate);
|
||||
$parValue = ($parValue === null) ? 1000 : Functions::flattenSingleValue($parValue);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$issue = SecurityValidations::validateIssueDate($issue);
|
||||
@@ -143,7 +138,7 @@ class AccruedInterest
|
||||
$daysBetweenIssueAndSettlement = Functions::scalar(YearFrac::fraction($issue, $settlement, $basis));
|
||||
if (!is_numeric($daysBetweenIssueAndSettlement)) {
|
||||
// return date error
|
||||
return $daysBetweenIssueAndSettlement;
|
||||
return StringHelper::convertToString($daysBetweenIssueAndSettlement);
|
||||
}
|
||||
|
||||
return $parValue * $rate * $daysBetweenIssueAndSettlement;
|
||||
|
||||
@@ -9,6 +9,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Financial\Coupons;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Helpers;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
|
||||
class Price
|
||||
{
|
||||
@@ -52,9 +53,7 @@ class Price
|
||||
$yield = Functions::flattenSingleValue($yield);
|
||||
$redemption = Functions::flattenSingleValue($redemption);
|
||||
$frequency = Functions::flattenSingleValue($frequency);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$settlement = SecurityValidations::validateSettlementDate($settlement);
|
||||
@@ -119,9 +118,7 @@ class Price
|
||||
$maturity = Functions::flattenSingleValue($maturity);
|
||||
$discount = Functions::flattenSingleValue($discount);
|
||||
$redemption = Functions::flattenSingleValue($redemption);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$settlement = SecurityValidations::validateSettlementDate($settlement);
|
||||
@@ -137,7 +134,7 @@ class Price
|
||||
$daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
|
||||
if (!is_numeric($daysBetweenSettlementAndMaturity)) {
|
||||
// return date error
|
||||
return $daysBetweenSettlementAndMaturity;
|
||||
return StringHelper::convertToString($daysBetweenSettlementAndMaturity);
|
||||
}
|
||||
|
||||
return $redemption * (1 - $discount * $daysBetweenSettlementAndMaturity);
|
||||
@@ -178,9 +175,7 @@ class Price
|
||||
$issue = Functions::flattenSingleValue($issue);
|
||||
$rate = Functions::flattenSingleValue($rate);
|
||||
$yield = Functions::flattenSingleValue($yield);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$settlement = SecurityValidations::validateSettlementDate($settlement);
|
||||
@@ -201,19 +196,19 @@ class Price
|
||||
$daysBetweenIssueAndSettlement = Functions::scalar(DateTimeExcel\YearFrac::fraction($issue, $settlement, $basis));
|
||||
if (!is_numeric($daysBetweenIssueAndSettlement)) {
|
||||
// return date error
|
||||
return $daysBetweenIssueAndSettlement;
|
||||
return StringHelper::convertToString($daysBetweenIssueAndSettlement);
|
||||
}
|
||||
$daysBetweenIssueAndSettlement *= $daysPerYear;
|
||||
$daysBetweenIssueAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($issue, $maturity, $basis));
|
||||
if (!is_numeric($daysBetweenIssueAndMaturity)) {
|
||||
// return date error
|
||||
return $daysBetweenIssueAndMaturity;
|
||||
return StringHelper::convertToString($daysBetweenIssueAndMaturity);
|
||||
}
|
||||
$daysBetweenIssueAndMaturity *= $daysPerYear;
|
||||
$daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
|
||||
if (!is_numeric($daysBetweenSettlementAndMaturity)) {
|
||||
// return date error
|
||||
return $daysBetweenSettlementAndMaturity;
|
||||
return StringHelper::convertToString($daysBetweenSettlementAndMaturity);
|
||||
}
|
||||
$daysBetweenSettlementAndMaturity *= $daysPerYear;
|
||||
|
||||
@@ -254,9 +249,7 @@ class Price
|
||||
$maturity = Functions::flattenSingleValue($maturity);
|
||||
$investment = Functions::flattenSingleValue($investment);
|
||||
$discount = Functions::flattenSingleValue($discount);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$settlement = SecurityValidations::validateSettlementDate($settlement);
|
||||
@@ -275,7 +268,7 @@ class Price
|
||||
$daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis);
|
||||
if (!is_numeric($daysBetweenSettlementAndMaturity)) {
|
||||
// return date error
|
||||
return Functions::scalar($daysBetweenSettlementAndMaturity);
|
||||
return StringHelper::convertToString(Functions::scalar($daysBetweenSettlementAndMaturity));
|
||||
}
|
||||
|
||||
return $investment / (1 - ($discount * $daysBetweenSettlementAndMaturity));
|
||||
|
||||
@@ -7,6 +7,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstants;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
|
||||
class Rates
|
||||
{
|
||||
@@ -43,9 +44,7 @@ class Rates
|
||||
$maturity = Functions::flattenSingleValue($maturity);
|
||||
$price = Functions::flattenSingleValue($price);
|
||||
$redemption = Functions::flattenSingleValue($redemption);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$settlement = SecurityValidations::validateSettlementDate($settlement);
|
||||
@@ -65,7 +64,7 @@ class Rates
|
||||
$daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
|
||||
if (!is_numeric($daysBetweenSettlementAndMaturity)) {
|
||||
// return date error
|
||||
return $daysBetweenSettlementAndMaturity;
|
||||
return StringHelper::convertToString($daysBetweenSettlementAndMaturity);
|
||||
}
|
||||
|
||||
return (1 - $price / $redemption) / $daysBetweenSettlementAndMaturity;
|
||||
@@ -104,9 +103,7 @@ class Rates
|
||||
$maturity = Functions::flattenSingleValue($maturity);
|
||||
$investment = Functions::flattenSingleValue($investment);
|
||||
$redemption = Functions::flattenSingleValue($redemption);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$settlement = SecurityValidations::validateSettlementDate($settlement);
|
||||
@@ -126,7 +123,7 @@ class Rates
|
||||
$daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
|
||||
if (!is_numeric($daysBetweenSettlementAndMaturity)) {
|
||||
// return date error
|
||||
return $daysBetweenSettlementAndMaturity;
|
||||
return StringHelper::convertToString($daysBetweenSettlementAndMaturity);
|
||||
}
|
||||
|
||||
return (($redemption / $investment) - 1) / ($daysBetweenSettlementAndMaturity);
|
||||
|
||||
@@ -7,6 +7,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstants;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Helpers;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
|
||||
class Yields
|
||||
{
|
||||
@@ -42,9 +43,7 @@ class Yields
|
||||
$maturity = Functions::flattenSingleValue($maturity);
|
||||
$price = Functions::flattenSingleValue($price);
|
||||
$redemption = Functions::flattenSingleValue($redemption);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$settlement = SecurityValidations::validateSettlementDate($settlement);
|
||||
@@ -64,7 +63,7 @@ class Yields
|
||||
$daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
|
||||
if (!is_numeric($daysBetweenSettlementAndMaturity)) {
|
||||
// return date error
|
||||
return $daysBetweenSettlementAndMaturity;
|
||||
return StringHelper::convertToString($daysBetweenSettlementAndMaturity);
|
||||
}
|
||||
$daysBetweenSettlementAndMaturity *= $daysPerYear;
|
||||
|
||||
@@ -106,9 +105,7 @@ class Yields
|
||||
$issue = Functions::flattenSingleValue($issue);
|
||||
$rate = Functions::flattenSingleValue($rate);
|
||||
$price = Functions::flattenSingleValue($price);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$settlement = SecurityValidations::validateSettlementDate($settlement);
|
||||
@@ -129,19 +126,19 @@ class Yields
|
||||
$daysBetweenIssueAndSettlement = Functions::scalar(DateTimeExcel\YearFrac::fraction($issue, $settlement, $basis));
|
||||
if (!is_numeric($daysBetweenIssueAndSettlement)) {
|
||||
// return date error
|
||||
return $daysBetweenIssueAndSettlement;
|
||||
return StringHelper::convertToString($daysBetweenIssueAndSettlement);
|
||||
}
|
||||
$daysBetweenIssueAndSettlement *= $daysPerYear;
|
||||
$daysBetweenIssueAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($issue, $maturity, $basis));
|
||||
if (!is_numeric($daysBetweenIssueAndMaturity)) {
|
||||
// return date error
|
||||
return $daysBetweenIssueAndMaturity;
|
||||
return StringHelper::convertToString($daysBetweenIssueAndMaturity);
|
||||
}
|
||||
$daysBetweenIssueAndMaturity *= $daysPerYear;
|
||||
$daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
|
||||
if (!is_numeric($daysBetweenSettlementAndMaturity)) {
|
||||
// return date error
|
||||
return $daysBetweenSettlementAndMaturity;
|
||||
return StringHelper::convertToString($daysBetweenSettlementAndMaturity);
|
||||
}
|
||||
$daysBetweenSettlementAndMaturity *= $daysPerYear;
|
||||
|
||||
|
||||
@@ -213,7 +213,7 @@ class FormulaParser
|
||||
// scientific notation check
|
||||
if (str_contains(self::OPERATORS_SN, $this->formula[$index])) {
|
||||
if (strlen($value) > 1) {
|
||||
if (preg_match('/^[1-9]{1}(\\.\\d+)?E{1}$/', $this->formula[$index]) != 0) {
|
||||
if (preg_match('/^[1-9]{1}(\.\d+)?E{1}$/', $this->formula[$index]) != 0) {
|
||||
$value .= $this->formula[$index];
|
||||
++$index;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Cell;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\Date;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
|
||||
class Functions
|
||||
{
|
||||
@@ -130,16 +131,22 @@ class Functions
|
||||
|
||||
public static function isMatrixValue(mixed $idx): bool
|
||||
{
|
||||
$idx = StringHelper::convertToString($idx);
|
||||
|
||||
return (substr_count($idx, '.') <= 1) || (preg_match('/\.[A-Z]/', $idx) > 0);
|
||||
}
|
||||
|
||||
public static function isValue(mixed $idx): bool
|
||||
{
|
||||
$idx = StringHelper::convertToString($idx);
|
||||
|
||||
return substr_count($idx, '.') === 0;
|
||||
}
|
||||
|
||||
public static function isCellValue(mixed $idx): bool
|
||||
{
|
||||
$idx = StringHelper::convertToString($idx);
|
||||
|
||||
return substr_count($idx, '.') > 1;
|
||||
}
|
||||
|
||||
@@ -154,7 +161,8 @@ class Functions
|
||||
$condition = self::operandSpecialHandling($condition);
|
||||
if (is_bool($condition)) {
|
||||
return '=' . ($condition ? 'TRUE' : 'FALSE');
|
||||
} elseif (!is_numeric($condition)) {
|
||||
}
|
||||
if (!is_numeric($condition)) {
|
||||
if ($condition !== '""') { // Not an empty string
|
||||
// Escape any quotes in the string value
|
||||
$condition = (string) preg_replace('/"/ui', '""', $condition);
|
||||
@@ -162,27 +170,32 @@ class Functions
|
||||
$condition = Calculation::wrapResult(strtoupper($condition));
|
||||
}
|
||||
|
||||
return str_replace('""""', '""', '=' . $condition);
|
||||
return str_replace('""""', '""', '=' . StringHelper::convertToString($condition));
|
||||
}
|
||||
$operator = $operand = '';
|
||||
if (1 === preg_match('/(=|<[>=]?|>=?)(.*)/', $condition, $matches)) {
|
||||
[, $operator, $operand] = $matches;
|
||||
}
|
||||
preg_match('/(=|<[>=]?|>=?)(.*)/', $condition, $matches);
|
||||
[, $operator, $operand] = $matches;
|
||||
|
||||
$operand = self::operandSpecialHandling($operand);
|
||||
$operand = (string) self::operandSpecialHandling($operand);
|
||||
if (is_numeric(trim($operand, '"'))) {
|
||||
$operand = trim($operand, '"');
|
||||
} elseif (!is_numeric($operand) && $operand !== 'FALSE' && $operand !== 'TRUE') {
|
||||
$operand = str_replace('"', '""', $operand);
|
||||
$operand = Calculation::wrapResult(strtoupper($operand));
|
||||
$operand = StringHelper::convertToString($operand);
|
||||
}
|
||||
|
||||
return str_replace('""""', '""', $operator . $operand);
|
||||
}
|
||||
|
||||
private static function operandSpecialHandling(mixed $operand): mixed
|
||||
private static function operandSpecialHandling(mixed $operand): bool|float|int|string
|
||||
{
|
||||
if (is_numeric($operand) || is_bool($operand)) {
|
||||
return $operand;
|
||||
} elseif (strtoupper($operand) === Calculation::getTRUE() || strtoupper($operand) === Calculation::getFALSE()) {
|
||||
}
|
||||
$operand = StringHelper::convertToString($operand);
|
||||
if (strtoupper($operand) === Calculation::getTRUE() || strtoupper($operand) === Calculation::getFALSE()) {
|
||||
return strtoupper($operand);
|
||||
}
|
||||
|
||||
@@ -204,7 +217,7 @@ class Functions
|
||||
*
|
||||
* @param mixed $array Array to be flattened
|
||||
*
|
||||
* @return array Flattened array
|
||||
* @return array<mixed> Flattened array
|
||||
*/
|
||||
public static function flattenArray(mixed $array): array
|
||||
{
|
||||
@@ -228,6 +241,32 @@ class Functions
|
||||
return $flattened;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a multi-dimensional array to a simple 1-dimensional array.
|
||||
* Same as above but argument is specified in ... format.
|
||||
*
|
||||
* @param mixed $array Array to be flattened
|
||||
*
|
||||
* @return array<mixed> Flattened array
|
||||
*/
|
||||
public static function flattenArray2(mixed ...$array): array
|
||||
{
|
||||
$flattened = [];
|
||||
$stack = array_values($array);
|
||||
|
||||
while (!empty($stack)) {
|
||||
$value = array_shift($stack);
|
||||
|
||||
if (is_array($value)) {
|
||||
array_unshift($stack, ...array_values($value));
|
||||
} else {
|
||||
$flattened[] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $flattened;
|
||||
}
|
||||
|
||||
public static function scalar(mixed $value): mixed
|
||||
{
|
||||
if (!is_array($value)) {
|
||||
@@ -246,7 +285,7 @@ class Functions
|
||||
*
|
||||
* @param array|mixed $array Array to be flattened
|
||||
*
|
||||
* @return array Flattened array
|
||||
* @return array<mixed> Flattened array
|
||||
*/
|
||||
public static function flattenArrayIndexed($array): array
|
||||
{
|
||||
@@ -310,7 +349,7 @@ class Functions
|
||||
|
||||
public static function trimTrailingRange(string $coordinate): string
|
||||
{
|
||||
return (string) preg_replace('/:[\\w\$]+$/', '', $coordinate);
|
||||
return (string) preg_replace('/:[\w\$]+$/', '', $coordinate);
|
||||
}
|
||||
|
||||
public static function trimSheetFromCellReference(string $coordinate): string
|
||||
|
||||
@@ -15,7 +15,7 @@ class ErrorValue
|
||||
* @param mixed $value Value to check
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function isErr(mixed $value = ''): array|bool
|
||||
@@ -33,7 +33,7 @@ class ErrorValue
|
||||
* @param mixed $value Value to check
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function isError(mixed $value = '', bool $tryNotImplemented = false): array|bool
|
||||
@@ -58,7 +58,7 @@ class ErrorValue
|
||||
* @param mixed $value Value to check
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function isNa(mixed $value = ''): array|bool
|
||||
|
||||
@@ -39,6 +39,8 @@ class ExcelError
|
||||
* ERROR_TYPE.
|
||||
*
|
||||
* @param mixed $value Value to check
|
||||
*
|
||||
* @return array<mixed>|int|string
|
||||
*/
|
||||
public static function type(mixed $value = ''): array|int|string
|
||||
{
|
||||
@@ -152,4 +154,14 @@ class ExcelError
|
||||
{
|
||||
return self::ERROR_CODES['calculation'];
|
||||
}
|
||||
|
||||
/**
|
||||
* SPILL.
|
||||
*
|
||||
* @return string #SPILL!
|
||||
*/
|
||||
public static function SPILL(): string
|
||||
{
|
||||
return self::ERROR_CODES['spill'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Cell;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Exception as SpreadsheetException;
|
||||
use PhpOffice\PhpSpreadsheet\NamedRange;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
|
||||
class Value
|
||||
@@ -20,7 +22,7 @@ class Value
|
||||
* @param mixed $value Value to check
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function isBlank(mixed $value = null): array|bool
|
||||
@@ -39,13 +41,14 @@ class Value
|
||||
*/
|
||||
public static function isRef(mixed $value, ?Cell $cell = null): bool
|
||||
{
|
||||
if ($cell === null || $value === $cell->getCoordinate()) {
|
||||
if ($cell === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$value = StringHelper::convertToString($value);
|
||||
$cellValue = Functions::trimTrailingRange($value);
|
||||
if (preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/ui', $cellValue) === 1) {
|
||||
[$worksheet, $cellValue] = Worksheet::extractSheetTitle($cellValue, true);
|
||||
[$worksheet, $cellValue] = Worksheet::extractSheetTitle($cellValue, true, true);
|
||||
if (!empty($worksheet) && $cell->getWorksheet()->getParentOrThrow()->getSheetByName($worksheet) === null) {
|
||||
return false;
|
||||
}
|
||||
@@ -68,7 +71,7 @@ class Value
|
||||
* @param mixed $value Value to check
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|bool|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|bool|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function isEven(mixed $value = null): array|string|bool
|
||||
@@ -79,11 +82,12 @@ class Value
|
||||
|
||||
if ($value === null) {
|
||||
return ExcelError::NAME();
|
||||
} elseif ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
|
||||
}
|
||||
if (!is_numeric($value)) {
|
||||
return ExcelError::VALUE();
|
||||
}
|
||||
|
||||
return ((int) fmod($value, 2)) === 0;
|
||||
return ((int) fmod($value + 0, 2)) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,7 +96,7 @@ class Value
|
||||
* @param mixed $value Value to check
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|bool|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|bool|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function isOdd(mixed $value = null): array|string|bool
|
||||
@@ -103,11 +107,12 @@ class Value
|
||||
|
||||
if ($value === null) {
|
||||
return ExcelError::NAME();
|
||||
} elseif ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
|
||||
}
|
||||
if (!is_numeric($value)) {
|
||||
return ExcelError::VALUE();
|
||||
}
|
||||
|
||||
return ((int) fmod($value, 2)) !== 0;
|
||||
return ((int) fmod($value + 0, 2)) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,7 +121,7 @@ class Value
|
||||
* @param mixed $value Value to check
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function isNumber(mixed $value = null): array|bool
|
||||
@@ -138,7 +143,7 @@ class Value
|
||||
* @param mixed $value Value to check
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function isLogical(mixed $value = null): array|bool
|
||||
@@ -156,7 +161,7 @@ class Value
|
||||
* @param mixed $value Value to check
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function isText(mixed $value = null): array|bool
|
||||
@@ -174,7 +179,7 @@ class Value
|
||||
* @param mixed $value Value to check
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function isNonText(mixed $value = null): array|bool
|
||||
@@ -191,14 +196,17 @@ class Value
|
||||
*
|
||||
* @param mixed $cellReference The cell to check
|
||||
* @param ?Cell $cell The current cell (containing this formula)
|
||||
*
|
||||
* @return array<mixed>|bool|string
|
||||
*/
|
||||
public static function isFormula(mixed $cellReference = '', ?Cell $cell = null): array|bool|string
|
||||
{
|
||||
if ($cell === null) {
|
||||
return ExcelError::REF();
|
||||
}
|
||||
$cellReference = StringHelper::convertToString($cellReference);
|
||||
|
||||
$fullCellReference = Functions::expandDefinedName((string) $cellReference, $cell);
|
||||
$fullCellReference = Functions::expandDefinedName($cellReference, $cell);
|
||||
|
||||
if (str_contains($cellReference, '!')) {
|
||||
$cellReference = Functions::trimSheetFromCellReference($cellReference);
|
||||
@@ -210,16 +218,24 @@ class Value
|
||||
|
||||
$fullCellReference = Functions::trimTrailingRange($fullCellReference);
|
||||
|
||||
preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $fullCellReference, $matches);
|
||||
|
||||
$fullCellReference = $matches[6] . $matches[7];
|
||||
$worksheetName = str_replace("''", "'", trim($matches[2], "'"));
|
||||
$worksheetName = '';
|
||||
if (1 == preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $fullCellReference, $matches)) {
|
||||
$fullCellReference = $matches[6] . $matches[7];
|
||||
$worksheetName = str_replace("''", "'", trim($matches[2], "'"));
|
||||
}
|
||||
|
||||
$worksheet = (!empty($worksheetName))
|
||||
? $cell->getWorksheet()->getParentOrThrow()->getSheetByName($worksheetName)
|
||||
: $cell->getWorksheet();
|
||||
if ($worksheet === null) {
|
||||
return ExcelError::REF();
|
||||
}
|
||||
|
||||
return ($worksheet !== null) ? $worksheet->getCell($fullCellReference)->isFormula() : ExcelError::REF();
|
||||
try {
|
||||
return $worksheet->getCell($fullCellReference)->isFormula();
|
||||
} catch (SpreadsheetException) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -243,21 +259,14 @@ class Value
|
||||
while (is_array($value)) {
|
||||
$value = array_shift($value);
|
||||
}
|
||||
|
||||
switch (gettype($value)) {
|
||||
case 'double':
|
||||
case 'float':
|
||||
case 'integer':
|
||||
return $value;
|
||||
case 'boolean':
|
||||
return (int) $value;
|
||||
case 'string':
|
||||
// Errors
|
||||
if (($value !== '') && ($value[0] == '#')) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
break;
|
||||
if (is_float($value) || is_int($value)) {
|
||||
return $value;
|
||||
}
|
||||
if (is_bool($value)) {
|
||||
return (int) $value;
|
||||
}
|
||||
if (is_string($value) && substr($value, 0, 1) === '#') {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -281,7 +290,7 @@ class Value
|
||||
public static function type($value = null): int
|
||||
{
|
||||
$value = Functions::flattenArrayIndexed($value);
|
||||
if (is_array($value) && (count($value) > 1)) {
|
||||
if (count($value) > 1) {
|
||||
end($value);
|
||||
$a = key($value);
|
||||
// Range of cells is an error
|
||||
|
||||
@@ -4,7 +4,11 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Internal;
|
||||
|
||||
class MakeMatrix
|
||||
{
|
||||
/** @param array $args */
|
||||
/**
|
||||
* @param mixed[] $args
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
public static function make(...$args): array
|
||||
{
|
||||
return $args;
|
||||
|
||||
@@ -6,10 +6,10 @@ class WildcardMatch
|
||||
{
|
||||
private const SEARCH_SET = [
|
||||
'~~', // convert double tilde to unprintable value
|
||||
'~\\*', // convert tilde backslash asterisk to [*] (matches literal asterisk in regexp)
|
||||
'\\*', // convert backslash asterisk to .* (matches string of any length in regexp)
|
||||
'~\\?', // convert tilde backslash question to [?] (matches literal question mark in regexp)
|
||||
'\\?', // convert backslash question to . (matches one character in regexp)
|
||||
'~\*', // convert tilde backslash asterisk to [*] (matches literal asterisk in regexp)
|
||||
'\*', // convert backslash asterisk to .* (matches string of any length in regexp)
|
||||
'~\?', // convert tilde backslash question to [?] (matches literal question mark in regexp)
|
||||
'\?', // convert backslash question to . (matches one character in regexp)
|
||||
"\x1c", // convert original double tilde to single tilde
|
||||
];
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ class Operations
|
||||
* @param mixed $logical A value or expression that can be evaluated to TRUE or FALSE
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|bool|string the boolean inverse of the argument
|
||||
* @return array<mixed>|bool|string the boolean inverse of the argument
|
||||
* If an array of values is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -130,6 +130,10 @@ class Operations
|
||||
return !$logical;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $args
|
||||
* @param callable(int, int): bool $func
|
||||
*/
|
||||
private static function countTrueValues(array $args, callable $func): bool|string
|
||||
{
|
||||
$trueValueCount = 0;
|
||||
|
||||
@@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\AddressHelper;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
|
||||
class Address
|
||||
{
|
||||
@@ -44,7 +45,7 @@ class Address
|
||||
* @param mixed $sheetName Optional Name of worksheet to use
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of values is passed as the $testValue argument, then the returned result will also be
|
||||
* @return mixed[]|string If an array of values is passed as the $testValue argument, then the returned result will also be
|
||||
* an array with the same dimensions
|
||||
*/
|
||||
public static function cell(mixed $row, mixed $column, mixed $relativity = 1, mixed $referenceStyle = true, mixed $sheetName = ''): array|string
|
||||
@@ -63,14 +64,16 @@ class Address
|
||||
);
|
||||
}
|
||||
|
||||
$relativity = $relativity ?? 1;
|
||||
$relativity = ($relativity === null) ? 1 : (int) StringHelper::convertToString($relativity);
|
||||
$referenceStyle = $referenceStyle ?? true;
|
||||
$row = (int) StringHelper::convertToString($row);
|
||||
$column = (int) StringHelper::convertToString($column);
|
||||
|
||||
if (($row < 1) || ($column < 1)) {
|
||||
return ExcelError::VALUE();
|
||||
}
|
||||
|
||||
$sheetName = self::sheetName($sheetName);
|
||||
$sheetName = self::sheetName(StringHelper::convertToString($sheetName));
|
||||
|
||||
if (is_int($referenceStyle)) {
|
||||
$referenceStyle = (bool) $referenceStyle;
|
||||
|
||||
@@ -30,7 +30,7 @@ class ExcelMatch
|
||||
* @param mixed $matchType The number -1, 0, or 1. -1 means above, 0 means exact match, 1 means below.
|
||||
* If match_type is 1 or -1, the list has to be ordered.
|
||||
*
|
||||
* @return array|float|int|string The relative position of the found item
|
||||
* @return array<mixed>|float|int|string The relative position of the found item
|
||||
*/
|
||||
public static function MATCH(mixed $lookupValue, mixed $lookupArray, mixed $matchType = self::MATCHTYPE_LARGEST_VALUE): array|string|int|float
|
||||
{
|
||||
@@ -70,13 +70,14 @@ class ExcelMatch
|
||||
};
|
||||
|
||||
if ($valueKey !== null) {
|
||||
return ++$valueKey;
|
||||
return ++$valueKey; //* @phpstan-ignore-line
|
||||
}
|
||||
|
||||
// Unsuccessful in finding a match, return #N/A error value
|
||||
return ExcelError::NA();
|
||||
}
|
||||
|
||||
/** @param mixed[] $lookupArray */
|
||||
private static function matchFirstValue(array $lookupArray, mixed $lookupValue): int|string|null
|
||||
{
|
||||
if (is_string($lookupValue)) {
|
||||
@@ -113,6 +114,10 @@ class ExcelMatch
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $lookupArray
|
||||
* @param mixed[] $keySet
|
||||
*/
|
||||
private static function matchLargestValue(array $lookupArray, mixed $lookupValue, array $keySet): mixed
|
||||
{
|
||||
if (is_string($lookupValue)) {
|
||||
@@ -147,6 +152,7 @@ class ExcelMatch
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @param mixed[] $lookupArray */
|
||||
private static function matchSmallestValue(array $lookupArray, mixed $lookupValue): int|string|null
|
||||
{
|
||||
$valueKey = null;
|
||||
@@ -215,6 +221,7 @@ class ExcelMatch
|
||||
return self::MATCHTYPE_FIRST_VALUE;
|
||||
}
|
||||
|
||||
/** @param mixed[] $lookupArray */
|
||||
private static function validateLookupArray(array $lookupArray): void
|
||||
{
|
||||
// Lookup_array should not be empty
|
||||
@@ -224,6 +231,11 @@ class ExcelMatch
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $lookupArray
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function prepareLookupArray(array $lookupArray, mixed $matchType): array
|
||||
{
|
||||
// Lookup_array should contain only number, text, or logical values, or empty (null) cells
|
||||
|
||||
@@ -6,8 +6,12 @@ use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
|
||||
class Filter
|
||||
{
|
||||
public static function filter(array $lookupArray, mixed $matchArray, mixed $ifEmpty = null): mixed
|
||||
public static function filter(mixed $lookupArray, mixed $matchArray, mixed $ifEmpty = null): mixed
|
||||
{
|
||||
if (!is_array($lookupArray)) {
|
||||
return ExcelError::VALUE();
|
||||
}
|
||||
/** @var mixed[] $lookupArray */
|
||||
if (!is_array($matchArray)) {
|
||||
return ExcelError::VALUE();
|
||||
}
|
||||
@@ -21,10 +25,17 @@ class Filter
|
||||
if (empty($result)) {
|
||||
return $ifEmpty ?? ExcelError::CALC();
|
||||
}
|
||||
/** @var callable(mixed): mixed */
|
||||
$func = 'array_values';
|
||||
|
||||
return array_values(array_map('array_values', $result));
|
||||
return array_values(array_map($func, $result));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $sortArray
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function enumerateArrayKeys(array $sortArray): array
|
||||
{
|
||||
array_walk(
|
||||
@@ -39,17 +50,29 @@ class Filter
|
||||
return array_values($sortArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $lookupArray
|
||||
* @param mixed[] $matchArray
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function filterByRow(array $lookupArray, array $matchArray): array
|
||||
{
|
||||
$matchArray = array_values(array_column($matchArray, 0));
|
||||
$matchArray = array_values(array_column($matchArray, 0)); // @phpstan-ignore-line
|
||||
|
||||
return array_filter(
|
||||
array_values($lookupArray),
|
||||
fn ($index): bool => (bool) $matchArray[$index],
|
||||
fn ($index): bool => (bool) ($matchArray[$index] ?? null),
|
||||
ARRAY_FILTER_USE_KEY
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $lookupArray
|
||||
* @param mixed[] $matchArray
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function filterByColumn(array $lookupArray, array $matchArray): array
|
||||
{
|
||||
$lookupArray = Matrix::transpose($lookupArray);
|
||||
@@ -57,7 +80,7 @@ class Filter
|
||||
if (count($matchArray) === 1) {
|
||||
$matchArray = array_pop($matchArray);
|
||||
}
|
||||
|
||||
/** @var mixed[] $matchArray */
|
||||
array_walk(
|
||||
$matchArray,
|
||||
function (&$value): void {
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Cell;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
|
||||
class Formula
|
||||
{
|
||||
@@ -20,13 +21,15 @@ class Formula
|
||||
return ExcelError::REF();
|
||||
}
|
||||
|
||||
preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellReference, $matches);
|
||||
|
||||
$cellReference = $matches[6] . $matches[7];
|
||||
$worksheetName = trim($matches[3], "'");
|
||||
$worksheet = (!empty($worksheetName))
|
||||
? $cell->getWorksheet()->getParentOrThrow()->getSheetByName($worksheetName)
|
||||
: $cell->getWorksheet();
|
||||
$worksheet = null;
|
||||
$cellReference = StringHelper::convertToString($cellReference);
|
||||
if (1 === preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellReference, $matches)) {
|
||||
$cellReference = $matches[6] . $matches[7];
|
||||
$worksheetName = trim($matches[3], "'");
|
||||
$worksheet = (!empty($worksheetName))
|
||||
? $cell->getWorksheet()->getParentOrThrow()->getSheetByName($worksheetName)
|
||||
: $cell->getWorksheet();
|
||||
}
|
||||
|
||||
if (
|
||||
$worksheet === null
|
||||
@@ -36,6 +39,6 @@ class Formula
|
||||
return ExcelError::NA();
|
||||
}
|
||||
|
||||
return $worksheet->getCell($cellReference)->getValue();
|
||||
return $worksheet->getCell($cellReference)->getValueString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,14 +18,14 @@ class HLookup extends LookupBase
|
||||
* in the same column based on the index_number.
|
||||
*
|
||||
* @param mixed $lookupValue The value that you want to match in lookup_array
|
||||
* @param mixed $lookupArray The range of cells being searched
|
||||
* @param mixed $indexNumber The row number in table_array from which the matching value must be returned.
|
||||
* @param mixed[][] $lookupArray The range of cells being searched
|
||||
* @param array<mixed>|float|int|string $indexNumber The row number in table_array from which the matching value must be returned.
|
||||
* The first row is 1.
|
||||
* @param mixed $notExactMatch determines if you are looking for an exact match based on lookup_value
|
||||
*
|
||||
* @return mixed The value of the found cell
|
||||
*/
|
||||
public static function lookup(mixed $lookupValue, mixed $lookupArray, mixed $indexNumber, mixed $notExactMatch = true): mixed
|
||||
public static function lookup(mixed $lookupValue, $lookupArray, $indexNumber, mixed $notExactMatch = true): mixed
|
||||
{
|
||||
if (is_array($lookupValue) || is_array($indexNumber)) {
|
||||
return self::evaluateArrayArgumentsIgnore([self::class, __FUNCTION__], 1, $lookupValue, $lookupArray, $indexNumber, $notExactMatch);
|
||||
@@ -49,6 +49,7 @@ class HLookup extends LookupBase
|
||||
|
||||
$firstkey = $f[0] - 1;
|
||||
$returnColumn = $firstkey + $indexNumber;
|
||||
/** @var mixed[][] $lookupArray */
|
||||
$firstColumn = array_shift($f) ?? 1;
|
||||
$rowNumber = self::hLookupSearch($lookupValue, $lookupArray, $firstColumn, $notExactMatch);
|
||||
|
||||
@@ -62,17 +63,20 @@ class HLookup extends LookupBase
|
||||
|
||||
/**
|
||||
* @param mixed $lookupValue The value that you want to match in lookup_array
|
||||
* @param mixed[][] $lookupArray
|
||||
* @param int|string $column
|
||||
*/
|
||||
private static function hLookupSearch(mixed $lookupValue, array $lookupArray, $column, bool $notExactMatch): ?int
|
||||
{
|
||||
$lookupLower = StringHelper::strToLower((string) $lookupValue);
|
||||
$lookupLower = StringHelper::strToLower(StringHelper::convertToString($lookupValue));
|
||||
|
||||
$rowNumber = null;
|
||||
foreach ($lookupArray[$column] as $rowKey => $rowData) {
|
||||
// break if we have passed possible keys
|
||||
/** @var string $rowKey */
|
||||
$bothNumeric = is_numeric($lookupValue) && is_numeric($rowData);
|
||||
$bothNotNumeric = !is_numeric($lookupValue) && !is_numeric($rowData);
|
||||
/** @var scalar $rowData */
|
||||
$cellDataLower = StringHelper::strToLower((string) $rowData);
|
||||
|
||||
if (
|
||||
@@ -96,6 +100,11 @@ class HLookup extends LookupBase
|
||||
return $rowNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $lookupArray
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function convertLiteralArray(array $lookupArray): array
|
||||
{
|
||||
if (array_key_exists(0, $lookupArray)) {
|
||||
|
||||
@@ -35,6 +35,7 @@ class Helpers
|
||||
}
|
||||
}
|
||||
|
||||
/** @return array{string, ?string, string} */
|
||||
public static function extractCellAddresses(string $cellAddress, bool $a1, Worksheet $sheet, string $sheetName = '', ?int $baseRow = null, ?int $baseCol = null): array
|
||||
{
|
||||
$cellAddress1 = $cellAddress;
|
||||
@@ -57,12 +58,12 @@ class Helpers
|
||||
return [$cellAddress1, $cellAddress2, $cellAddress];
|
||||
}
|
||||
|
||||
/** @return array{string, ?Worksheet, string} */
|
||||
public static function extractWorksheet(string $cellAddress, Cell $cell): array
|
||||
{
|
||||
$sheetName = '';
|
||||
if (str_contains($cellAddress, '!')) {
|
||||
[$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
|
||||
$sheetName = trim($sheetName, "'");
|
||||
[$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true, true);
|
||||
}
|
||||
|
||||
$worksheet = ($sheetName !== '')
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Cell;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
|
||||
class Hyperlink
|
||||
{
|
||||
@@ -22,18 +23,23 @@ class Hyperlink
|
||||
*/
|
||||
public static function set(mixed $linkURL = '', mixed $displayName = null, ?Cell $cell = null): string
|
||||
{
|
||||
$linkURL = ($linkURL === null) ? '' : Functions::flattenSingleValue($linkURL);
|
||||
$linkURL = ($linkURL === null) ? '' : StringHelper::convertToString(Functions::flattenSingleValue($linkURL));
|
||||
$displayName = ($displayName === null) ? '' : Functions::flattenSingleValue($displayName);
|
||||
|
||||
if ((!is_object($cell)) || (trim($linkURL) == '')) {
|
||||
return ExcelError::REF();
|
||||
}
|
||||
|
||||
if ((is_object($displayName)) || trim($displayName) == '') {
|
||||
if (is_object($displayName)) {
|
||||
$displayName = $linkURL;
|
||||
}
|
||||
$displayName = StringHelper::convertToString($displayName);
|
||||
if (trim($displayName) === '') {
|
||||
$displayName = $linkURL;
|
||||
}
|
||||
|
||||
$cell->getHyperlink()->setUrl($linkURL);
|
||||
$cell->getHyperlink()
|
||||
->setUrl($linkURL);
|
||||
$cell->getHyperlink()->setTooltip($displayName);
|
||||
|
||||
return $displayName;
|
||||
|
||||
@@ -34,6 +34,8 @@ class Indirect
|
||||
|
||||
/**
|
||||
* Convert cellAddress to string, verify not null string.
|
||||
*
|
||||
* @param null|mixed[]|string $cellAddress
|
||||
*/
|
||||
private static function validateAddress(array|string|null $cellAddress): string
|
||||
{
|
||||
@@ -54,12 +56,12 @@ class Indirect
|
||||
* Excel Function:
|
||||
* =INDIRECT(cellAddress, bool) where the bool argument is optional
|
||||
*
|
||||
* @param array|string $cellAddress $cellAddress The cell address of the current cell (containing this formula)
|
||||
* @param mixed[]|string $cellAddress $cellAddress The cell address of the current cell (containing this formula)
|
||||
* @param mixed $a1fmt Expect bool Helpers::CELLADDRESS_USE_A1 or CELLADDRESS_USE_R1C1,
|
||||
* but can be provided as numeric which is cast to bool
|
||||
* @param Cell $cell The current cell (containing this formula)
|
||||
*
|
||||
* @return array|string An array containing a cell or range of cells, or a string on error
|
||||
* @return mixed[]|string An array containing a cell or range of cells, or a string on error
|
||||
*/
|
||||
public static function INDIRECT($cellAddress, mixed $a1fmt, Cell $cell): string|array
|
||||
{
|
||||
@@ -99,13 +101,13 @@ class Indirect
|
||||
/**
|
||||
* Extract range values.
|
||||
*
|
||||
* @return array Array of values in range if range contains more than one element.
|
||||
* @return mixed[] Array of values in range if range contains more than one element.
|
||||
* Otherwise, a single value is returned.
|
||||
*/
|
||||
private static function extractRequiredCells(?Worksheet $worksheet, string $cellAddress): array
|
||||
{
|
||||
return Calculation::getInstance($worksheet !== null ? $worksheet->getParent() : null)
|
||||
->extractCellRange($cellAddress, $worksheet, false);
|
||||
->extractCellRange($cellAddress, $worksheet, false, createCell: true);
|
||||
}
|
||||
|
||||
private static function handleRowColumnRanges(?Worksheet $worksheet, string $start, string $end): string
|
||||
|
||||
@@ -28,6 +28,7 @@ class Lookup
|
||||
if (!is_array($lookupVector)) {
|
||||
return ExcelError::NA();
|
||||
}
|
||||
/** @var mixed[][] $lookupVector */
|
||||
$hasResultVector = isset($resultVector);
|
||||
$lookupRows = self::rowCount($lookupVector);
|
||||
$lookupColumns = self::columnCount($lookupVector);
|
||||
@@ -35,16 +36,19 @@ class Lookup
|
||||
if (($lookupRows === 1 && $lookupColumns > 1) || (!$hasResultVector && $lookupRows === 2 && $lookupColumns !== 2)) {
|
||||
$lookupVector = Matrix::transpose($lookupVector);
|
||||
$lookupRows = self::rowCount($lookupVector);
|
||||
/** @var mixed[][] $lookupVector */
|
||||
$lookupColumns = self::columnCount($lookupVector);
|
||||
}
|
||||
|
||||
$resultVector = self::verifyResultVector($resultVector ?? $lookupVector);
|
||||
$resultVector = self::verifyResultVector($resultVector ?? $lookupVector); //* @phpstan-ignore-line
|
||||
|
||||
if ($lookupRows === 2 && !$hasResultVector) {
|
||||
$resultVector = array_pop($lookupVector);
|
||||
$lookupVector = array_shift($lookupVector);
|
||||
}
|
||||
|
||||
/** @var mixed[] $lookupVector */
|
||||
/** @var mixed[] $resultVector */
|
||||
if ($lookupColumns !== 2) {
|
||||
$lookupVector = self::verifyLookupValues($lookupVector, $resultVector);
|
||||
}
|
||||
@@ -52,6 +56,12 @@ class Lookup
|
||||
return VLookup::lookup($lookupValue, $lookupVector, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $lookupVector
|
||||
* @param mixed[] $resultVector
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function verifyLookupValues(array $lookupVector, array $resultVector): array
|
||||
{
|
||||
foreach ($lookupVector as &$value) {
|
||||
@@ -77,6 +87,11 @@ class Lookup
|
||||
return $lookupVector;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[][] $resultVector
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function verifyResultVector(array $resultVector): array
|
||||
{
|
||||
$resultRows = self::rowCount($resultVector);
|
||||
@@ -90,11 +105,13 @@ class Lookup
|
||||
return $resultVector;
|
||||
}
|
||||
|
||||
/** @param mixed[] $dataArray */
|
||||
private static function rowCount(array $dataArray): int
|
||||
{
|
||||
return count($dataArray);
|
||||
}
|
||||
|
||||
/** @param mixed[][] $dataArray */
|
||||
private static function columnCount(array $dataArray): int
|
||||
{
|
||||
$rowKeys = array_keys($dataArray);
|
||||
|
||||
@@ -7,15 +7,18 @@ use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
|
||||
abstract class LookupBase
|
||||
{
|
||||
protected static function validateLookupArray(mixed $lookup_array): void
|
||||
protected static function validateLookupArray(mixed $lookupArray): void
|
||||
{
|
||||
if (!is_array($lookup_array)) {
|
||||
if (!is_array($lookupArray)) {
|
||||
throw new Exception(ExcelError::REF());
|
||||
}
|
||||
}
|
||||
|
||||
/** @param float|int|string $index_number */
|
||||
protected static function validateIndexLookup(array $lookup_array, $index_number): int
|
||||
/**
|
||||
* @param mixed[] $lookupArray
|
||||
* @param float|int|string $index_number number >= 1
|
||||
*/
|
||||
protected static function validateIndexLookup(array $lookupArray, $index_number): int
|
||||
{
|
||||
// index_number must be a number greater than or equal to 1.
|
||||
// Excel results are inconsistent when index is non-numeric.
|
||||
@@ -30,8 +33,8 @@ abstract class LookupBase
|
||||
throw new Exception(ExcelError::VALUE());
|
||||
}
|
||||
|
||||
// index_number must be less than or equal to the number of columns in lookup_array
|
||||
if (empty($lookup_array)) {
|
||||
// index_number must be less than or equal to the number of columns in lookupArray
|
||||
if (empty($lookupArray)) {
|
||||
throw new Exception(ExcelError::REF());
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ class LookupRefValidations
|
||||
public static function validateInt(mixed $value): int
|
||||
{
|
||||
if (!is_numeric($value)) {
|
||||
if (ErrorValue::isError($value)) {
|
||||
if (is_string($value) && ErrorValue::isError($value)) {
|
||||
throw new Exception($value);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ class Matrix
|
||||
|
||||
/**
|
||||
* Helper function; NOT an implementation of any Excel Function.
|
||||
*
|
||||
* @param mixed[] $values
|
||||
*/
|
||||
public static function isColumnVector(array $values): bool
|
||||
{
|
||||
@@ -20,6 +22,8 @@ class Matrix
|
||||
|
||||
/**
|
||||
* Helper function; NOT an implementation of any Excel Function.
|
||||
*
|
||||
* @param mixed[] $values
|
||||
*/
|
||||
public static function isRowVector(array $values): bool
|
||||
{
|
||||
@@ -30,7 +34,9 @@ class Matrix
|
||||
/**
|
||||
* TRANSPOSE.
|
||||
*
|
||||
* @param array|mixed $matrixData A matrix of values
|
||||
* @param mixed $matrixData A matrix of values
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
public static function transpose($matrixData): array
|
||||
{
|
||||
@@ -38,8 +44,12 @@ class Matrix
|
||||
if (!is_array($matrixData)) {
|
||||
$matrixData = [[$matrixData]];
|
||||
}
|
||||
if (!is_array(end($matrixData))) {
|
||||
$matrixData = [$matrixData];
|
||||
}
|
||||
|
||||
$column = 0;
|
||||
/** @var mixed[][] $matrixData */
|
||||
foreach ($matrixData as $matrixRow) {
|
||||
$row = 0;
|
||||
foreach ($matrixRow as $matrixCell) {
|
||||
@@ -82,6 +92,15 @@ class Matrix
|
||||
|
||||
$rowNum = $rowNum ?? 0;
|
||||
$columnNum = $columnNum ?? 0;
|
||||
if (is_scalar($matrix)) {
|
||||
if ($rowNum === 0 || $rowNum === 1) {
|
||||
if ($columnNum === 0 || $columnNum === 1) {
|
||||
if ($columnNum === 1 || $rowNum === 1) {
|
||||
return $matrix;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$rowNum = LookupRefValidations::validatePositiveInt($rowNum);
|
||||
@@ -106,7 +125,7 @@ class Matrix
|
||||
}
|
||||
|
||||
$rowKeys = array_keys($matrix);
|
||||
$columnKeys = @array_keys($matrix[$rowKeys[0]]);
|
||||
$columnKeys = @array_keys($matrix[$rowKeys[0]]); //* @phpstan-ignore-line
|
||||
|
||||
if ($columnNum > count($columnKeys)) {
|
||||
return ExcelError::REF();
|
||||
@@ -124,10 +143,15 @@ class Matrix
|
||||
);
|
||||
}
|
||||
$rowNum = $rowKeys[--$rowNum];
|
||||
/** @var mixed[][] $matrix */
|
||||
|
||||
return $matrix[$rowNum][$columnNum];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $matrix
|
||||
* @param mixed[] $rowKeys
|
||||
*/
|
||||
private static function extractRowValue(array $matrix, array $rowKeys, int $rowNum): mixed
|
||||
{
|
||||
if ($rowNum === 0) {
|
||||
|
||||
@@ -7,6 +7,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Cell;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Validations;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
|
||||
class Offset
|
||||
@@ -24,28 +25,32 @@ class Offset
|
||||
* @param null|string $cellAddress The reference from which you want to base the offset.
|
||||
* Reference must refer to a cell or range of adjacent cells;
|
||||
* otherwise, OFFSET returns the #VALUE! error value.
|
||||
* @param mixed $rows The number of rows, up or down, that you want the upper-left cell to refer to.
|
||||
* @param int $rows The number of rows, up or down, that you want the upper-left cell to refer to.
|
||||
* Using 5 as the rows argument specifies that the upper-left cell in the
|
||||
* reference is five rows below reference. Rows can be positive (which means
|
||||
* below the starting reference) or negative (which means above the starting
|
||||
* reference).
|
||||
* @param mixed $columns The number of columns, to the left or right, that you want the upper-left cell
|
||||
* @param int $columns The number of columns, to the left or right, that you want the upper-left cell
|
||||
* of the result to refer to. Using 5 as the cols argument specifies that the
|
||||
* upper-left cell in the reference is five columns to the right of reference.
|
||||
* Cols can be positive (which means to the right of the starting reference)
|
||||
* or negative (which means to the left of the starting reference).
|
||||
* @param mixed $height The height, in number of rows, that you want the returned reference to be.
|
||||
* @param ?int $height The height, in number of rows, that you want the returned reference to be.
|
||||
* Height must be a positive number.
|
||||
* @param mixed $width The width, in number of columns, that you want the returned reference to be.
|
||||
* @param ?int $width The width, in number of columns, that you want the returned reference to be.
|
||||
* Width must be a positive number.
|
||||
*
|
||||
* @return array|string An array containing a cell or range of cells, or a string on error
|
||||
* @return array<mixed>|string An array containing a cell or range of cells, or a string on error
|
||||
*/
|
||||
public static function OFFSET(?string $cellAddress = null, mixed $rows = 0, mixed $columns = 0, mixed $height = null, mixed $width = null, ?Cell $cell = null): string|array
|
||||
public static function OFFSET(?string $cellAddress = null, $rows = 0, $columns = 0, $height = null, $width = null, ?Cell $cell = null): string|array
|
||||
{
|
||||
/** @var int */
|
||||
$rows = Functions::flattenSingleValue($rows);
|
||||
/** @var int */
|
||||
$columns = Functions::flattenSingleValue($columns);
|
||||
/** @var int */
|
||||
$height = Functions::flattenSingleValue($height);
|
||||
/** @var int */
|
||||
$width = Functions::flattenSingleValue($width);
|
||||
|
||||
if ($cellAddress === null || $cellAddress === '') {
|
||||
@@ -55,6 +60,10 @@ class Offset
|
||||
if (!is_object($cell)) {
|
||||
return ExcelError::REF();
|
||||
}
|
||||
$sheet = $cell->getParent()?->getParent(); // worksheet
|
||||
if ($sheet !== null) {
|
||||
$cellAddress = Validations::definedNameToCoordinate($cellAddress, $sheet);
|
||||
}
|
||||
|
||||
[$cellAddress, $worksheet] = self::extractWorksheet($cellAddress, $cell);
|
||||
|
||||
@@ -62,12 +71,11 @@ class Offset
|
||||
if (strpos($cellAddress, ':')) {
|
||||
[$startCell, $endCell] = explode(':', $cellAddress);
|
||||
}
|
||||
[$startCellColumn, $startCellRow] = Coordinate::coordinateFromString($startCell);
|
||||
[$endCellColumn, $endCellRow] = Coordinate::coordinateFromString($endCell);
|
||||
[$startCellColumn, $startCellRow] = Coordinate::indexesFromString($startCell);
|
||||
[, $endCellRow, $endCellColumn] = Coordinate::indexesFromString($endCell);
|
||||
|
||||
$startCellRow += $rows;
|
||||
$startCellColumn = Coordinate::columnIndexFromString($startCellColumn) - 1;
|
||||
$startCellColumn += $columns;
|
||||
$startCellColumn += $columns - 1;
|
||||
|
||||
if (($startCellRow <= 0) || ($startCellColumn < 0)) {
|
||||
return ExcelError::REF();
|
||||
@@ -91,20 +99,21 @@ class Offset
|
||||
return self::extractRequiredCells($worksheet, $cellAddress);
|
||||
}
|
||||
|
||||
/** @return mixed[] */
|
||||
private static function extractRequiredCells(?Worksheet $worksheet, string $cellAddress): array
|
||||
{
|
||||
return Calculation::getInstance($worksheet !== null ? $worksheet->getParent() : null)
|
||||
->extractCellRange($cellAddress, $worksheet, false);
|
||||
}
|
||||
|
||||
/** @return array{string, ?Worksheet} */
|
||||
private static function extractWorksheet(?string $cellAddress, Cell $cell): array
|
||||
{
|
||||
$cellAddress = self::assessCellAddress($cellAddress ?? '', $cell);
|
||||
|
||||
$sheetName = '';
|
||||
if (str_contains($cellAddress, '!')) {
|
||||
[$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
|
||||
$sheetName = trim($sheetName, "'");
|
||||
[$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true, true);
|
||||
}
|
||||
|
||||
$worksheet = ($sheetName !== '')
|
||||
@@ -123,7 +132,11 @@ class Offset
|
||||
return $cellAddress;
|
||||
}
|
||||
|
||||
private static function adjustEndCellColumnForWidth(string $endCellColumn, mixed $width, int $startCellColumn, mixed $columns): int
|
||||
/**
|
||||
* @param null|object|scalar $width
|
||||
* @param scalar $columns
|
||||
*/
|
||||
private static function adjustEndCellColumnForWidth(string $endCellColumn, $width, int $startCellColumn, $columns): int
|
||||
{
|
||||
$endCellColumn = Coordinate::columnIndexFromString($endCellColumn) - 1;
|
||||
if (($width !== null) && (!is_object($width))) {
|
||||
@@ -135,7 +148,11 @@ class Offset
|
||||
return $endCellColumn;
|
||||
}
|
||||
|
||||
private static function adustEndCellRowForHeight(mixed $height, int $startCellRow, mixed $rows, mixed $endCellRow): int
|
||||
/**
|
||||
* @param null|object|scalar $height
|
||||
* @param scalar $rows
|
||||
*/
|
||||
private static function adustEndCellRowForHeight($height, int $startCellRow, $rows, int $endCellRow): int
|
||||
{
|
||||
if (($height !== null) && (!is_object($height))) {
|
||||
$endCellRow = $startCellRow + (int) $height - 1;
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ErrorValue;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Cell;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Exception as SpreadsheetException;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
|
||||
class RowColumnInformation
|
||||
@@ -13,7 +15,7 @@ class RowColumnInformation
|
||||
/**
|
||||
* Test if cellAddress is null or whitespace string.
|
||||
*
|
||||
* @param null|array|string $cellAddress A reference to a range of cells
|
||||
* @param null|mixed[]|string $cellAddress A reference to a range of cells
|
||||
*/
|
||||
private static function cellAddressNullOrWhitespace($cellAddress): bool
|
||||
{
|
||||
@@ -38,11 +40,11 @@ class RowColumnInformation
|
||||
* Excel Function:
|
||||
* =COLUMN([cellAddress])
|
||||
*
|
||||
* @param null|array|string $cellAddress A reference to a range of cells for which you want the column numbers
|
||||
* @param null|mixed[]|string $cellAddress A reference to a range of cells for which you want the column numbers
|
||||
*
|
||||
* @return int|int[]
|
||||
* @return int|int[]|string
|
||||
*/
|
||||
public static function COLUMN($cellAddress = null, ?Cell $cell = null): int|array
|
||||
public static function COLUMN($cellAddress = null, ?Cell $cell = null): int|string|array
|
||||
{
|
||||
if (self::cellAddressNullOrWhitespace($cellAddress)) {
|
||||
return self::cellColumn($cell);
|
||||
@@ -79,7 +81,11 @@ class RowColumnInformation
|
||||
|
||||
$cellAddress = (string) preg_replace('/[^a-z]/i', '', $cellAddress);
|
||||
|
||||
return Coordinate::columnIndexFromString($cellAddress);
|
||||
try {
|
||||
return Coordinate::columnIndexFromString($cellAddress);
|
||||
} catch (SpreadsheetException) {
|
||||
return ExcelError::NAME();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,7 +96,7 @@ class RowColumnInformation
|
||||
* Excel Function:
|
||||
* =COLUMNS(cellAddress)
|
||||
*
|
||||
* @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells
|
||||
* @param null|mixed[]|string $cellAddress An array or array formula, or a reference to a range of cells
|
||||
* for which you want the number of columns
|
||||
*
|
||||
* @return int|string The number of columns in cellAddress, or a string if arguments are invalid
|
||||
@@ -100,6 +106,9 @@ class RowColumnInformation
|
||||
if (self::cellAddressNullOrWhitespace($cellAddress)) {
|
||||
return 1;
|
||||
}
|
||||
if (is_string($cellAddress) && ErrorValue::isError($cellAddress)) {
|
||||
return $cellAddress;
|
||||
}
|
||||
if (!is_array($cellAddress)) {
|
||||
return ExcelError::VALUE();
|
||||
}
|
||||
@@ -115,9 +124,18 @@ class RowColumnInformation
|
||||
return $columns;
|
||||
}
|
||||
|
||||
private static function cellRow(?Cell $cell): int
|
||||
private static function cellRow(?Cell $cell): int|string
|
||||
{
|
||||
return ($cell !== null) ? $cell->getRow() : 1;
|
||||
return ($cell !== null) ? self::convert0ToName($cell->getRow()) : 1;
|
||||
}
|
||||
|
||||
private static function convert0ToName(int|string $result): int|string
|
||||
{
|
||||
if (is_int($result) && ($result <= 0 || $result > 1048576)) {
|
||||
return ExcelError::NAME();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,11 +151,11 @@ class RowColumnInformation
|
||||
* Excel Function:
|
||||
* =ROW([cellAddress])
|
||||
*
|
||||
* @param null|array|string $cellAddress A reference to a range of cells for which you want the row numbers
|
||||
* @param null|mixed[][]|string $cellAddress A reference to a range of cells for which you want the row numbers
|
||||
*
|
||||
* @return int|mixed[]
|
||||
* @return int|mixed[]|string
|
||||
*/
|
||||
public static function ROW($cellAddress = null, ?Cell $cell = null): int|array
|
||||
public static function ROW($cellAddress = null, ?Cell $cell = null): int|string|array
|
||||
{
|
||||
if (self::cellAddressNullOrWhitespace($cellAddress)) {
|
||||
return self::cellRow($cell);
|
||||
@@ -172,7 +190,7 @@ class RowColumnInformation
|
||||
}
|
||||
[$cellAddress] = explode(':', $cellAddress);
|
||||
|
||||
return (int) preg_replace('/\D/', '', $cellAddress);
|
||||
return self::convert0ToName((int) preg_replace('/\D/', '', $cellAddress));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -183,7 +201,7 @@ class RowColumnInformation
|
||||
* Excel Function:
|
||||
* =ROWS(cellAddress)
|
||||
*
|
||||
* @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells
|
||||
* @param null|mixed[]|string $cellAddress An array or array formula, or a reference to a range of cells
|
||||
* for which you want the number of rows
|
||||
*
|
||||
* @return int|string The number of rows in cellAddress, or a string if arguments are invalid
|
||||
@@ -193,6 +211,9 @@ class RowColumnInformation
|
||||
if (self::cellAddressNullOrWhitespace($cellAddress)) {
|
||||
return 1;
|
||||
}
|
||||
if (is_string($cellAddress) && ErrorValue::isError($cellAddress)) {
|
||||
return $cellAddress;
|
||||
}
|
||||
if (!is_array($cellAddress)) {
|
||||
return ExcelError::VALUE();
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ class Sort extends LookupRefValidations
|
||||
return $sortArray;
|
||||
}
|
||||
|
||||
/** @var mixed[][] */
|
||||
$sortArray = self::enumerateArrayKeys($sortArray);
|
||||
|
||||
$byColumn = (bool) $byColumn;
|
||||
@@ -43,7 +44,7 @@ class Sort extends LookupRefValidations
|
||||
|
||||
try {
|
||||
// If $sortIndex and $sortOrder are scalars, then convert them into arrays
|
||||
if (is_scalar($sortIndex)) {
|
||||
if (!is_array($sortIndex)) {
|
||||
$sortIndex = [$sortIndex];
|
||||
$sortOrder = is_scalar($sortOrder) ? [$sortOrder] : $sortOrder;
|
||||
}
|
||||
@@ -55,7 +56,11 @@ class Sort extends LookupRefValidations
|
||||
}
|
||||
|
||||
// We want a simple, enumrated array of arrays where we can reference column by its index number.
|
||||
$sortArray = array_values(array_map('array_values', $sortArray));
|
||||
/** @var callable(mixed): mixed */
|
||||
$temp = 'array_values';
|
||||
/** @var array<int> $sortOrder */
|
||||
$sortArray = array_values(array_map($temp, $sortArray));
|
||||
/** @var int[] $sortIndex */
|
||||
|
||||
return ($byColumn === true)
|
||||
? self::sortByColumn($sortArray, $sortIndex, $sortOrder)
|
||||
@@ -104,6 +109,11 @@ class Sort extends LookupRefValidations
|
||||
return self::processSortBy($sortArray, $sortBy, $sortOrder);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $sortArray
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function enumerateArrayKeys(array $sortArray): array
|
||||
{
|
||||
array_walk(
|
||||
@@ -133,6 +143,7 @@ class Sort extends LookupRefValidations
|
||||
$sortOrder = self::validateSortOrder($sortOrder);
|
||||
}
|
||||
|
||||
/** @return mixed[] */
|
||||
private static function validateSortVector(mixed $sortVector, int $sortArraySize): array
|
||||
{
|
||||
if (!is_array($sortVector)) {
|
||||
@@ -158,6 +169,7 @@ class Sort extends LookupRefValidations
|
||||
return $sortOrder;
|
||||
}
|
||||
|
||||
/** @param mixed[] $sortIndex */
|
||||
private static function validateArrayArgumentsForSort(array &$sortIndex, mixed &$sortOrder, int $sortArraySize): void
|
||||
{
|
||||
// It doesn't matter if they're row or column vectors, it works either way
|
||||
@@ -184,6 +196,11 @@ class Sort extends LookupRefValidations
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $sortVector
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function prepareSortVectorValues(array $sortVector): array
|
||||
{
|
||||
// Strings should be sorted case-insensitive; with booleans converted to locale-strings
|
||||
@@ -202,14 +219,19 @@ class Sort extends LookupRefValidations
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array[] $sortIndex
|
||||
* @param mixed[] $sortArray
|
||||
* @param mixed[] $sortIndex
|
||||
* @param int[] $sortOrder
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function processSortBy(array $sortArray, array $sortIndex, array $sortOrder): array
|
||||
{
|
||||
$sortArguments = [];
|
||||
/** @var mixed[] */
|
||||
$sortData = [];
|
||||
foreach ($sortIndex as $index => $sortValues) {
|
||||
/** @var mixed[] $sortValues */
|
||||
$sortData[] = $sortValues;
|
||||
$sortArguments[] = self::prepareSortVectorValues($sortValues);
|
||||
$sortArguments[] = $sortOrder[$index] === self::ORDER_ASCENDING ? SORT_ASC : SORT_DESC;
|
||||
@@ -221,8 +243,11 @@ class Sort extends LookupRefValidations
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $sortArray
|
||||
* @param int[] $sortIndex
|
||||
* @param int[] $sortOrder
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function sortByRow(array $sortArray, array $sortIndex, array $sortOrder): array
|
||||
{
|
||||
@@ -232,8 +257,11 @@ class Sort extends LookupRefValidations
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $sortArray
|
||||
* @param int[] $sortIndex
|
||||
* @param int[] $sortOrder
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function sortByColumn(array $sortArray, array $sortIndex, array $sortOrder): array
|
||||
{
|
||||
@@ -244,8 +272,11 @@ class Sort extends LookupRefValidations
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $sortArray
|
||||
* @param int[] $sortIndex
|
||||
* @param int[] $sortOrder
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function buildVectorForSort(array $sortArray, array $sortIndex, array $sortOrder): array
|
||||
{
|
||||
@@ -263,6 +294,12 @@ class Sort extends LookupRefValidations
|
||||
return $sortData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $sortData
|
||||
* @param mixed[] $sortArguments
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function executeVectorSortQuery(array $sortData, array $sortArguments): array
|
||||
{
|
||||
$sortData = Matrix::transpose($sortData);
|
||||
@@ -287,6 +324,12 @@ class Sort extends LookupRefValidations
|
||||
return $sortedData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $sortArray
|
||||
* @param mixed[] $sortVector
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function sortLookupArrayFromVector(array $sortArray, array $sortVector): array
|
||||
{
|
||||
// Building a new array in the correct (sorted) order works; but may be memory heavy for larger arrays
|
||||
|
||||
@@ -33,17 +33,31 @@ class Unique
|
||||
: self::uniqueByRow($lookupVector, $exactlyOnce);
|
||||
}
|
||||
|
||||
/** @param mixed[] $lookupVector */
|
||||
private static function uniqueByRow(array $lookupVector, bool $exactlyOnce): mixed
|
||||
{
|
||||
// When not $byColumn, we count whole rows or values, not individual values
|
||||
// so implode each row into a single string value
|
||||
array_walk(
|
||||
$lookupVector,
|
||||
//* @phpstan-ignore-next-line
|
||||
function (array &$value): void {
|
||||
$value = implode(chr(0x00), $value);
|
||||
$valuex = '';
|
||||
$separator = '';
|
||||
$numericIndicator = "\x01";
|
||||
foreach ($value as $cellValue) {
|
||||
/** @var scalar $cellValue */
|
||||
$valuex .= $separator . $cellValue;
|
||||
$separator = "\x00";
|
||||
if (is_int($cellValue) || is_float($cellValue)) {
|
||||
$valuex .= $numericIndicator;
|
||||
}
|
||||
}
|
||||
$value = $valuex;
|
||||
}
|
||||
);
|
||||
|
||||
/** @var string[] $lookupVector */
|
||||
$result = self::countValuesCaseInsensitive($lookupVector);
|
||||
|
||||
if ($exactlyOnce === true) {
|
||||
@@ -60,15 +74,24 @@ class Unique
|
||||
array_walk(
|
||||
$result,
|
||||
function (string &$value): void {
|
||||
$value = explode(chr(0x00), $value);
|
||||
$value = explode("\x00", $value);
|
||||
foreach ($value as &$stringValue) {
|
||||
if (str_ends_with($stringValue, "\x01")) {
|
||||
// x01 should only end a string which is otherwise a float or int,
|
||||
// so phpstan is technically correct but what it fears should not happen.
|
||||
$stringValue = 0 + substr($stringValue, 0, -1); //@phpstan-ignore-line
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return (count($result) === 1) ? array_pop($result) : $result;
|
||||
}
|
||||
|
||||
/** @param mixed[] $lookupVector */
|
||||
private static function uniqueByColumn(array $lookupVector, bool $exactlyOnce): mixed
|
||||
{
|
||||
/** @var string[] */
|
||||
$flattenedLookupVector = Functions::flattenArray($lookupVector);
|
||||
|
||||
if (count($lookupVector, COUNT_RECURSIVE) > count($flattenedLookupVector, COUNT_RECURSIVE) + 1) {
|
||||
@@ -94,6 +117,11 @@ class Unique
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $caseSensitiveLookupValues
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function countValuesCaseInsensitive(array $caseSensitiveLookupValues): array
|
||||
{
|
||||
$caseInsensitiveCounts = array_count_values(
|
||||
@@ -121,6 +149,11 @@ class Unique
|
||||
return $caseSensitiveCounts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $values
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function exactlyOnceFilter(array $values): array
|
||||
{
|
||||
return array_filter(
|
||||
|
||||
@@ -17,14 +17,14 @@ class VLookup extends LookupBase
|
||||
* in the same row based on the index_number.
|
||||
*
|
||||
* @param mixed $lookupValue The value that you want to match in lookup_array
|
||||
* @param mixed $lookupArray The range of cells being searched
|
||||
* @param mixed $indexNumber The column number in table_array from which the matching value must be returned.
|
||||
* @param mixed[] $lookupArray The range of cells being searched
|
||||
* @param array<mixed>|float|int|string $indexNumber The column number in table_array from which the matching value must be returned.
|
||||
* The first column is 1.
|
||||
* @param mixed $notExactMatch determines if you are looking for an exact match based on lookup_value
|
||||
*
|
||||
* @return mixed The value of the found cell
|
||||
*/
|
||||
public static function lookup(mixed $lookupValue, mixed $lookupArray, mixed $indexNumber, mixed $notExactMatch = true): mixed
|
||||
public static function lookup(mixed $lookupValue, $lookupArray, mixed $indexNumber, mixed $notExactMatch = true): mixed
|
||||
{
|
||||
if (is_array($lookupValue) || is_array($indexNumber)) {
|
||||
return self::evaluateArrayArgumentsIgnore([self::class, __FUNCTION__], 1, $lookupValue, $lookupArray, $indexNumber, $notExactMatch);
|
||||
@@ -54,6 +54,7 @@ class VLookup extends LookupBase
|
||||
uasort($lookupArray, $callable);
|
||||
}
|
||||
|
||||
/** @var string[][] $lookupArray */
|
||||
$rowNumber = self::vLookupSearch($lookupValue, $lookupArray, $firstColumn, $notExactMatch);
|
||||
|
||||
if ($rowNumber !== null) {
|
||||
@@ -64,6 +65,10 @@ class VLookup extends LookupBase
|
||||
return ExcelError::NA();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param scalar[] $a
|
||||
* @param scalar[] $b
|
||||
*/
|
||||
private static function vlookupSort(array $a, array $b): int
|
||||
{
|
||||
reset($a);
|
||||
@@ -80,16 +85,17 @@ class VLookup extends LookupBase
|
||||
|
||||
/**
|
||||
* @param mixed $lookupValue The value that you want to match in lookup_array
|
||||
* @param string[][] $lookupArray
|
||||
* @param int|string $column
|
||||
*/
|
||||
private static function vLookupSearch(mixed $lookupValue, array $lookupArray, $column, bool $notExactMatch): ?int
|
||||
{
|
||||
$lookupLower = StringHelper::strToLower((string) $lookupValue);
|
||||
$lookupLower = StringHelper::strToLower(StringHelper::convertToString($lookupValue));
|
||||
|
||||
$rowNumber = null;
|
||||
foreach ($lookupArray as $rowKey => $rowData) {
|
||||
$bothNumeric = is_numeric($lookupValue) && is_numeric($rowData[$column]);
|
||||
$bothNotNumeric = !is_numeric($lookupValue) && !is_numeric($rowData[$column]);
|
||||
$bothNumeric = self::numeric($lookupValue) && self::numeric($rowData[$column]);
|
||||
$bothNotNumeric = !self::numeric($lookupValue) && !self::numeric($rowData[$column]);
|
||||
$cellDataLower = StringHelper::strToLower((string) $rowData[$column]);
|
||||
|
||||
// break if we have passed possible keys
|
||||
@@ -114,4 +120,9 @@ class VLookup extends LookupBase
|
||||
|
||||
return $rowNumber;
|
||||
}
|
||||
|
||||
private static function numeric(mixed $value): bool
|
||||
{
|
||||
return is_int($value) || is_float($value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ class Absolute
|
||||
*
|
||||
* @param mixed $number Should be numeric, or can be an array of numbers
|
||||
*
|
||||
* @return array|float|int|string rounded number
|
||||
* @return array<mixed>|float|int|string rounded number
|
||||
* If an array of numbers is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user