From c88d4cadcbf8b425fbe16ecd72545b3e9a3759f3 Mon Sep 17 00:00:00 2001 From: Julian Mourell <30490143+jmourell@users.noreply.github.com> Date: Tue, 6 Apr 2021 19:12:48 +0900 Subject: [PATCH] Dev 1049 upgrade php spreadsheet (#3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * xdebug isn't built into the nightly PHP, so we'll get an error if we try to remove it; nightly should also be allow failures untilany identified issues are resolved * <>/!= * Update to versions of complex and matrix libraries * Set formula attribute in the Xlsx reader * Calculation/DateTime Failure With PHP8 (#1661) The following code generates an error with PHP8: if ($t1[1] > 29) { $t1[1] += 1900; Under the "right" conditions, PHP8 evaluates the condition as true when PHP8 is a non-numeric string and then generates an error trying to perform += on that non-numeric string. Adding a numeric test eliminates the problem. All unit tests involving this code now succeed with both PHP7 and PHP8. * Update to versions of complex and matrix libraries * Reader/Gnumeric Failure with PHP8 (#1662) * Reader/Gnumeric Failure with PHP8 An explicit cast from SimpleXML to int is now needed with PHP8, where it was performed implicitly with PHP7. Unit tests run correctly for both PHP7 and PHP8 on corrected code. * ReverseSort bug, exposed but not caused by PHP8 (#1660) Some tests of ReferenceHelper functions columnReverseSort and cellReverseSort which passed with PHP7 fail with PHP8. Both functions use the following construction: return 1 - strcasecmp(whatever); The "1" seems very mysterious. I believe that the correct code should be: return -strcasecmp(whatever); It appears in particular that PHP7 strcasecmp was never returning a value of 1 for the tests in question, but PHP8 strcasecmp does so. With the corrected code, the tests pass in both PHP7 and PHP8. * Drop $this->spreadSheet null check from Xlsx Writer (#1646) $this->spreadSheet cannot be null * Update index.md (#1620) * Bug setting Superscript/Subscript to false (#1567) If font style Superscript is set to true, Subscript is set to false. Likewise, setting Subscript to true sets Superscript to false. Both of these are working as they should. However, setting Superscript to false causes Subscript to be set to true, and setting Subscript to false causes Superscript to be set to true. I believe that is an error in both cases. This change fixes it. There seem to be no existing tests for Font styles. I added the tests necessary to validate this change. I will put adding more on my to-do list. * Update CHANGELOG * Sync README * Separate compoer.json for PHP8 to enforce phpunit ^9.3 * We don't want the composer lock file for PHP8 * Work with existing composer file; but force a fresh install to require new dependency versions * Allow failures on nightly * Allow failures on nightly fails if we remove it from the grid * fixed php8 deprecation warning for libxml_disable_entity_loader() (#1625) * fixed php8 deprecation warning for libxml_disable_entity_loader() * Use ZipArchive methods as zip_* functions are deprecated in PHP8 * Remove tests that include tcPDF and DomPDF libraries when running against PHP8, because neither library is yet PHP8-ready * Let's try to figure out why we're having an issue with a temp csv file * Need to add `application/csv` to the list of valid CSV mime types * Locale-specific float to string is changed in PHP as per https://wiki.php.net/rfc/locale_independent_float_to_string * Codestyle clean-ups * Switch from using poser badges to shields.io because poser wasn't picking up the correct license * Update composer suggestions * Introduce GitHub Actions The plan is to keep Travis for a short while, until we are confident that GitHub Actions work well enough for us. And after that we can remove Travis entirely. There is a bunch of duplicated things but it allows us to maximize parallelismt to have results as soon as possible. API documentation generation is still missing. * Annotate problems in code * Publish API docs via GitHub Actions * Update composer suggestions * Test PHP 8 on GitHub Actions * Improving Coverage for Excel2003 XML Reader (#1557) * Improving Coverage for Excel2003 XML Reader Reader/Xml is now 100% covered. File templates/Excel2003XMLTest.xml, used in some tests, is *not* readable by a current version of Excel. I have substituted a new file excel2003.xml to be used in its place. I have not deleted the original in case someone in future (possibly me) wants to see what it needs to make it usable. There are minimal code changes. - Unused protected functions pixel2WidthUnits and widthUnits2Pixel are deleted. - One regex looking to convert hex characters is changed from a-z to a-f, and made case insensitive. - No calculation performed for "error" cell (previously calculation was attempted and threw exception). - Empty relative row/cell is now handled correctly. - Style applied to empty cell when appropriate. - Support added for textRotation. - Support added for border styles. - Support added for diagonal borders. - Support added for superscript and subscript. - Support added for fill patterns. In theory, encodings other than UTF-8 were supported. In fact, I was unable to get SecurityScanner to pass *any* xml which is not UTF-8. Eliminating the assumption that strings might not be UTF-8 allowed much of the code to be greatly simplified. After that, I added some code that would permit the use of some ASCII-compatible encodings (there is a test of ISO-8859-1). It would be more difficult to handle other encodings (such as UTF-16). I am not convinced that even the ISO-8859 effort is worth it, but am willing to investigate either expanding or eliminating non-UTF8 support. I added a number of tests, creating an Xml directory, and moving XmlTest to that directory. Pull Request had problems reading old invalid sample in the code coverage phase, not in any of the other test phases, and not in the code coverage phase on my local machine. As it turns out, aside from being invalid, the sample is much larger than any of the other samples. Tests have been adjusted accordingly. * Smaller Test File Should eliminate need to avoid test during xml coverage. * Break Up Style Test into Multiple Tests Per suggestion from Mark Baker. * Integrate AddressHelper Change The introduction of AddressHelper introduced a conflict which needed to be resolved. I wanted to test it locally before resolving. This required me to add (unchanged) AddressHelper to my local copy. I hope this is an okay manner of resolving the conflict. * Weird Travis Error XmlOddTest works just fine on my local machine, but Travis failed it. Even worse, the lines which Travis flags don't even make any sense (one was the empty line between two methods!). This test is not essential to the rest of the change. I am removing it from the package, and will attempt to re-add it when I have a chance to sync up my fork with the main project. * Update CHANGELOG * 1.15.0 * Remove coverage from Travis Coverage data were not correctly uploaded for a long time now, and it now is uploaded via GitHub Actions * Prepare for next version * Ensure that the list of shared formulae is maintained while chunk-reading Xlsx Files (#1680) * Ensure that the list of shared formulae is maintained while chunk-reading Xlsx files * Prevent notice during accessing "cached magnification factor" offset Sheet View Settings Block should be 8-14 bytes long in BIFF8 Excel 97 according to the open office file format documentation. However access to byte 10 and 12 is not possible when record data is malformed, so getUInt2d throws notice. * Refresh lock files * Add exportArray Method for Styles (#1580) Issue #580 has gone stale since I started work on this. Nevertheless, this implements an exportArray function as an exact counterpart of applyFromArry. I chose the name exportArray to avoid confusion with the existing method getStyleArray, which does something completely different. This change also increases coverage for all the Style classes to 100%, with the exception of Style.php itself. There were several (unchanged) places in Style.php where I did not have sufficient understanding of what was supposed to be happening, so could not create tests. All properties used by applyFromArray are exported by this method. Note that conditional styles are not covered; this is consistent with the fact that they are not covered by applyFromArray. The method is implemented as a final public function in Style/Supervisor, which calls abstract protected function exportArray1, which is implemented in each of the subclasses, and which calls final protected function exportArray2 in Style/Supervisor. So exportArray is usable for any of the subclasses as well. The new method is added to the documentation. The existing documentation for applyFromArray was alphabetized to make it easier to follow. One property (Style quotePrefix) was added to the documentation. Some Borders pseudo-properties (vertical, horizontal, and outline) were documented as usable by applyFromArray, but aren't actually supported - they were removed. The documentation of the properties seemed to use setProperty and getProperty fairly randomly - it now uses setProperty exclusively. New constants were added for the textRotation "angles" used to create a "stacked" cell. I felt that changing the readers and writers to use these constants was beyond the scope of this change, but it is on my to-do list. * Updating a misspelling of a function name. (#1695) This will update the function name DCOUNTA from the misspelling of DCOUNT. * Make DefinedNames Samples Consistent With Other Samples (#1707) All other Samples write to temporary directory. DefinedNames samples write to main directory, which (a) means they aren't stored with others, and (b) they aren't ignored by git so look like changed files. The tests are also simplified by requiring Header rather than Bootstrap, making use of Helper. * Resolve XSS Vulnerability in the HTML Writer (#1719) Resolve XSS Vulnerability in the HTML Writer * Drop Travis * Automatic GitHub releases from git tags * Improve Coverage in src/PhpSpreadsheet There are no changes to code. Additional tests are added, so that the following 6 items now have 100% test coverage: - Comment - DefinedName - DocumentGenerator - IOFactory - NamedFormula - NamedRange * Changes for Scrutinizer Two changes to fix minor problems reported by Scrutinizer. * Spelling: Tou -> You * Fix for 1735 (Incorrect activeSheetIndex after RemoveSheetByIndex) (#1743) This is a fix for issue #1735. It adds tests for this situation, and similar situations involving adding new sheets and accessing existing ones. Coverage for Spreadsheet.php increases from 69% to 75% as a result. * Update change log * Fix for 3 Issues Involving ReadXlsx and NamedRange (#1742) * Fix for 3 Issues Involving ReadXlsx and NamedRange Issues #1686 and #1723, which provide sample spreadsheets, are probably solved by this ticket. Issue #1730 is also probably solved, but I have no way to verify. There are two problems with how PhpSpreadsheet is handling things now. Although the first problem is much less severe, and isn't really a factor in the issues named above, it is helpful to get it out of the way first. If you define a named range in Excel, and then delete the sheet where the range exists, Excel saves the range as #REF!. If there is a cell which references the range, it will similarly have the value #REF! when you open the Excel file. Currently, PhpSpreadsheet discards the #REF! definition, so a cell which references the range will appear as #NAME? rather than #REF!. This PR changes the behavior so that PhpSpreadsheet retains the #REF! definition, and cells which reference it will appear as #REF!. The second problem is the more severe, and is, I believe, responsible for the 3 issues identified above. If you define a named range and the sheet on which the range is defined does not exist at the time, Excel will save the range as something like: '[1]Unknown Sheet'!$A$1 If a cell references such a range, Excel will again display #REF!. PhpSpreadsheet currently throws an Exception when it encounters such a definition while reading the file. This PR changes the behavior so that PhpSpreadsheet saves the definition as #REF!, and cells which reference it will behave similarly. For the record, I will note that Excel does not magically recalculate when a missing sheet is subsequently added, despite the fact that the reference might now become resolvable. PhpSpreadsheet behaves likewise. * Remove Dead Code in Test Identified it after push but before merge. * Update change log * Apply Column and Row Styles to Existing Cells (#1721) * Apply Column and Row Styles to Existing Cells This is a fix for issue #1712. When a style is applied to an entire row or column, it is currently only effective for cells which don't already contain a value. The code needs to iterate through existing cells in the row/column in order to apply the style to them. This could be considered a breaking change, however, I believe that the change makes things operate as users would expect, and that the existing implementation is incomplete. The change also removes protected element conditionalStyles from the Style class. That element is an unused remnant, and can no longer be set or retrieved - methods getConditionalStyles and setConditionalStyles actually act on an element in the Worksheet class. Finally, additional tests are added so that Style, and in fact the entire Style directory, now has 100% test coverage. * Scrutinizer Changes Scrutinizer flagged 6 statements. 5 can be easily corrected. One is absolutely wrong (it thinks iterating through cells in column can return null). Let's see if we can satisfy it. * Remove Exception For CellIterator on Empty Row/Column For my first attempt at this change, which corrects a bug by updating styles for non-empty cells when a style is set on a row or column, I wished to make things more efficient by using setIterateOnlyExistingCells, something which the existing documentation recommends. This caused an exception to be generated when the row or column is empty. So I removed that part of the change while I researched what was going on. I have completed that research. The existing code does throw an exception when the row/column is empty and iterateOnlyExistingCells is true. However, that does not seem like a reasonable action. This situation is analagous to iterating over an empty array, and that action is legal and does not throw. The same should apply here. There were no tests for this situation, and now there are. I have added additional tests, and coverage for all of RowCellIterator, ColumnCellIterator, and CellIterator are all now 100%. Some of my new tests were added in new members, because the existing tests all relied on mocking, which was not the best choice for the new tests. One of the existing tests for RowCellIteratorTest (testSeekOutOfRange) was wrong; it issued the expected exception, but for the wrong reason. I have added an additional test to ensure that it fails "correctly". The existing documentation says that the default value for IterateOnlyExistingCells is true. In fact, the default value is false. I have corrected the documentation. * More Scrutinizer I believe its analysis is incorrect, but this should silence it. * DocBlock Correction ColumnCellIterator DocBlock for current indicated it could return null or Cell, but it can really return only Cell. This had caused Scrutinizer to complain earlier. * PHP8 Environment Appears to be Fixed Cosmetic change to Doc member. I suspect there is a way to rerun all the tests without another push, but I have been unable to figure out how. * Update change log * TextData Coverage and Minor Bug Fixes (#1744) This had been intended to get 100% coverage for TextData functions, and it does that. However, some minor bugs requiring source changes arose during testing. - the Excel CHAR function restricts its argument to 1-255. PhpSpreadsheet CHARACTER had been allowing 0+. Also, there is no need to test if iconv exists, since it is part of Composer requirements. - The DOLLAR function had been returning NUM for invalid arguments. Excel returns VALUE. Also, negative amounts were not being handled correctly. - The FIXEDFORMAT function had been returning NUM for invalid arguments. Excel FIXED returns VALUE. * Replace anti-xss with html purifier (#1751) * Replace voku/anti-xss with ezyang/htmlpurifier. Despite anti-xss being a smaller footprint dependency, an a better license fit with our MIT license, there are issues with it's automatic it sanitisation of global variables causing side effects * Additional unit tests for xss in html writer cell comments * Fix bug #1626 where values of 0 were "rounded" up/down as if they were not 0 (#1627) * Fix bug where values of 0 were "rounded" up/down as if they were not 0 * Update change log * Fix for #1612 - SLK Long File Name (#1706) Issue has been marked stale, but ... Sylk read sets worksheet title to filename (minus .slk). If that is >31 characters, PhpSpreadsheet throws Exception. This change truncates sheet title, as Excel does, to 31 characters. * Update change log * worksheet: fix if cellValue does not exist (#1727) The condition is FALSE if the cell does not exist in the flipped table, but anyway, it is sent in to a method requiring 'string' type, causing it to fail. * fixes #1655 issue (#1656) Resolve problem with incorrectly defined hyperlinks * Add 'ps' suffix to printer settings resources IDs (#1690) * Add 'ps' suffix to printer settings resources IDs * Update change log * Fix pixelsToPoints conversion (for HTML col width) (#1733) * DocBlock Change in Styles/Conditional (#1697) Scrutinizer reported a minor error in a test involving a module which I was not changing. Styles/Conditional function setConditions can take a scalar or an array as a parameter, but DocBlock says it only expects array. I did not wish to add the extra module to my PR, but made a note to self to fix that after PR was installed. That has now happened, and it makes for a good case for me to see all the PHP8/Composer2/etc. changes that have happened recently. * Merge pull request #1698 * Merge pull request #4 from PHPOffice/master * Restore Omitted Read XML Test * Fix for bug #1592 (UPDATED) (#1623) * Fix for Xls when BIFF8 SST (FCh) has bad Shared string length * Update change log * Add nightly PHP 8.1 dev to github actions (#1763) * Fix compatibility with ext-gd on php 8 (#1762) * CSV - Guess Encoding, Handle Null-string Escape (#1717) * CSV - Guess Encoding, Handle Null-string Escape This is in response to issue #1647 (detect CSV character encoding). First, my tests with mb_detect_encoding indicate that it doesn't work well enough; regardless, users can always do that on their own if they deem it useful. Rolling my own is also troublesome, but I can at least: a. Check for BOM (UTF-8, UTF-16BE, UTF-16LE, UTF-32BE, UTF-32LE). b. Do some heuristic tests for each of the above encodings. c. Fallback to a user-specified encoding (default CP1252) if a and b don't yield result. I think this is probably useful enough to include, and relatively easy to expand if other potential encodings should be considered. Starting with PHP7.4, fgetcsv allows specification of null string as escape character in fgetcsv. This is a much better choice than the PHP (and PhpSpreadsheet) default of backslash in that it handles the file in the same manner as Excel does. There is one statement in Reader/CSV which would be adversely affected if the caller so specified (building a regular expression under the assumption that escape character is a single character). Fix that statement appropriately and add tests. * Update changelog * Update Units of Measure supported by the CONVERT() function (#1768) Now supports all current UoM in all categories, with both 1- and 2-character multiplier prefixes, and binary multiplier prefixes, including the new Temperature scales * Changelog for 1.16.0 release * Fix date tests withut specified year for current year 2021 (#1774) * Mrand of zero to any multiple should return 0 (#1773) * Problems Using Builtin PHP Functions Directly As Excel Functions (#1799) * Problems Using Builtin PHP Functions Directly As Excel Functions This fixes issue #1789. As originally reported, stricter typing was causing PHP8 to throw an exception when a non-numeric value was passed to the Round function. Previous releases of PHP did not see this problem, however, on further analysis, they were also incorrect in returning 0 as the result in the erroneous situation, when they should have been returning a VALUE error. Yet more analysis showed that other functions would also have problems, and, in addition, might not handle invalid input (e.g. a negative length passed to REPT) or output (e.g. NAN in the case of ACOS(2)) correctly. The following MathTrig functions are affected: ABS, ACOS, ACOSH, ASIN, ASINH, ATAN, ATANH, COS, COSH, DEGREES (rad2deg), EXP, LN (log), LOG10, RADIANS (deg2rad), REPT (str_repeat), SIN, SINH, SQRT, TAN, TANH. One TextData function (REPT) is also affected. This change lets PhpSpreadsheet validate the input for each of these functions before passing control to the builtin, and handle the output afterwards. There were no explicit tests for any of these functions, a fact made easy to ignore by the fact that PhpSpreadsheet delegated the heavy lifting to PHP itself for these cases. A full suite of tests is now added for each of the affected functions. * Scrutinizer Recommendations Only in 3 modules which are part of this PR. * Improved Handling of Tan(PI/2) Return DIV0 error for TAN when COS is very small. * Additional Trig Tests Results which should be infinity, i.e. DIV/0 error. * Delete Temporary Files In XssVulnerabilityTest (#1800) * Delete Temporary Files In XssVulnerabilityTest They need not exist after the test. Some of them are placed in current directory, which means Git thinks they are needed. * Update Changelog for switch from built-in PHP functions to Excel class methods (to handle typechecking for stricter PHP8 typing) * Fix For #1772 Null Exception on ODS Read (#1776) Fix for #1772. Header and Footer Properties may be omitted in Page Setting Style Set. Code changed to allow for this possibility, and tests added. * Additional method call/return typing * Update Changelog * PHPCS Resolutions * Fix Xlsx reader overriding manually set number format with builtin number format (#1805) * Update Changelog * Named formula fix for empty formula value * Do not swallow previous exception (#1778) The previous exception will be included when loading the content as DOM Document fails. This makes debugging the reason behind the failure much easier. * Support DataBar of conditional formatting rule (#1754) Implemented the databar of Conditional Type for XLSX Files. - DataBar can be read, written, and added for basic use. - Supports reading, writing and adding using "extLst". About "extLst" - https://docs.microsoft.com/en-us/openspecs/office_standards/ms-xlsx/07d607af-5618-4ca2-b683-6a78dc0d9627 The following setting items on the Excel setting screen can be read, written, and added. - (minimum, maximum)type: Automatic, LowestValue, Number, Percent, Formula, Percentile - Direction: context, leftToRight, rightToLeft (show data bar only) - Fills Solid, Gradient - FillColor: PositiveValues, NegativeValues - Borders: Solid, None - BorderColor: PositiveValues, NegativeValues - Axis position: Automatic, Midpoint, None - Axis color * formatAsDate strip language metadata (#1618) * Revert "Fix cant get right format chinese date format error" This reverts commit 8c58385d6c103d23f6a5b3d1899a5e7cc8f66e92. * formatAsDate strip language metadata (fixes #1616) Co-authored-by: Mark Baker * Additional method call/return typing * Composer fixes * PHPCS Fixes * Additional unit tests for statistical functions, with a fix to ordering for RANK() (#1813) * Additional unit tests for statistical functions, with a fix to ordering for RANK() * Unhappy path unit tests (#1814) * Unhappy path unit tests * Fix unhappy error for BETADIST and BETAINV min/max range * Additional unit tests for previously untested financial functions (#1815) * Additional unit tests for previously untested financial functions, and some additions to follow untested paths * Start splitting Financial function tests out from the large FinancialTests class into individual test classes for each function * Extract remaining Excel function unit tests into separate test classes for each function (#1817) * Extract remaining Financial function unit tests into separate test classes for each function This makes it easier to manage unit tests if they are individual files rather than all in a single file It also provides a stepping stone toward making it easier to test Excel functions when Excel errors no longer return a string, but an actual Excel exception that can be handled more cleanly * Additional statistical unit tests (#1818) * Inherited Scrutinizer Recommendations - 1 of 3 (#1806) * Make DefinedNames Samples Consistent With Other Samples (#1707) All other Samples write to temporary directory. DefinedNames samples write to main directory, which (a) means they aren't stored with others, and (b) they aren't ignored by git so look like changed files. The tests are also simplified by requiring Header rather than Bootstrap, making use of Helper. * Resolve XSS Vulnerability in the HTML Writer (#1719) Resolve XSS Vulnerability in the HTML Writer * Drop Travis * Automatic GitHub releases from git tags * Improve Coverage in src/PhpSpreadsheet There are no changes to code. Additional tests are added, so that the following 6 items now have 100% test coverage: - Comment - DefinedName - DocumentGenerator - IOFactory - NamedFormula - NamedRange * Changes for Scrutinizer Two changes to fix minor problems reported by Scrutinizer. * Spelling: Tou -> You * Fix for 1735 (Incorrect activeSheetIndex after RemoveSheetByIndex) (#1743) This is a fix for issue #1735. It adds tests for this situation, and similar situations involving adding new sheets and accessing existing ones. Coverage for Spreadsheet.php increases from 69% to 75% as a result. * Update change log * Fix for 3 Issues Involving ReadXlsx and NamedRange (#1742) * Fix for 3 Issues Involving ReadXlsx and NamedRange Issues #1686 and #1723, which provide sample spreadsheets, are probably solved by this ticket. Issue #1730 is also probably solved, but I have no way to verify. There are two problems with how PhpSpreadsheet is handling things now. Although the first problem is much less severe, and isn't really a factor in the issues named above, it is helpful to get it out of the way first. If you define a named range in Excel, and then delete the sheet where the range exists, Excel saves the range as #REF!. If there is a cell which references the range, it will similarly have the value #REF! when you open the Excel file. Currently, PhpSpreadsheet discards the #REF! definition, so a cell which references the range will appear as #NAME? rather than #REF!. This PR changes the behavior so that PhpSpreadsheet retains the #REF! definition, and cells which reference it will appear as #REF!. The second problem is the more severe, and is, I believe, responsible for the 3 issues identified above. If you define a named range and the sheet on which the range is defined does not exist at the time, Excel will save the range as something like: '[1]Unknown Sheet'!$A$1 If a cell references such a range, Excel will again display #REF!. PhpSpreadsheet currently throws an Exception when it encounters such a definition while reading the file. This PR changes the behavior so that PhpSpreadsheet saves the definition as #REF!, and cells which reference it will behave similarly. For the record, I will note that Excel does not magically recalculate when a missing sheet is subsequently added, despite the fact that the reference might now become resolvable. PhpSpreadsheet behaves likewise. * Remove Dead Code in Test Identified it after push but before merge. * Update change log * Apply Column and Row Styles to Existing Cells (#1721) * Apply Column and Row Styles to Existing Cells This is a fix for issue #1712. When a style is applied to an entire row or column, it is currently only effective for cells which don't already contain a value. The code needs to iterate through existing cells in the row/column in order to apply the style to them. This could be considered a breaking change, however, I believe that the change makes things operate as users would expect, and that the existing implementation is incomplete. The change also removes protected element conditionalStyles from the Style class. That element is an unused remnant, and can no longer be set or retrieved - methods getConditionalStyles and setConditionalStyles actually act on an element in the Worksheet class. Finally, additional tests are added so that Style, and in fact the entire Style directory, now has 100% test coverage. * Scrutinizer Changes Scrutinizer flagged 6 statements. 5 can be easily corrected. One is absolutely wrong (it thinks iterating through cells in column can return null). Let's see if we can satisfy it. * Remove Exception For CellIterator on Empty Row/Column For my first attempt at this change, which corrects a bug by updating styles for non-empty cells when a style is set on a row or column, I wished to make things more efficient by using setIterateOnlyExistingCells, something which the existing documentation recommends. This caused an exception to be generated when the row or column is empty. So I removed that part of the change while I researched what was going on. I have completed that research. The existing code does throw an exception when the row/column is empty and iterateOnlyExistingCells is true. However, that does not seem like a reasonable action. This situation is analagous to iterating over an empty array, and that action is legal and does not throw. The same should apply here. There were no tests for this situation, and now there are. I have added additional tests, and coverage for all of RowCellIterator, ColumnCellIterator, and CellIterator are all now 100%. Some of my new tests were added in new members, because the existing tests all relied on mocking, which was not the best choice for the new tests. One of the existing tests for RowCellIteratorTest (testSeekOutOfRange) was wrong; it issued the expected exception, but for the wrong reason. I have added an additional test to ensure that it fails "correctly". The existing documentation says that the default value for IterateOnlyExistingCells is true. In fact, the default value is false. I have corrected the documentation. * More Scrutinizer I believe its analysis is incorrect, but this should silence it. * DocBlock Correction ColumnCellIterator DocBlock for current indicated it could return null or Cell, but it can really return only Cell. This had caused Scrutinizer to complain earlier. * PHP8 Environment Appears to be Fixed Cosmetic change to Doc member. I suspect there is a way to rerun all the tests without another push, but I have been unable to figure out how. * Update change log * TextData Coverage and Minor Bug Fixes (#1744) This had been intended to get 100% coverage for TextData functions, and it does that. However, some minor bugs requiring source changes arose during testing. - the Excel CHAR function restricts its argument to 1-255. PhpSpreadsheet CHARACTER had been allowing 0+. Also, there is no need to test if iconv exists, since it is part of Composer requirements. - The DOLLAR function had been returning NUM for invalid arguments. Excel returns VALUE. Also, negative amounts were not being handled correctly. - The FIXEDFORMAT function had been returning NUM for invalid arguments. Excel FIXED returns VALUE. * Replace anti-xss with html purifier (#1751) * Replace voku/anti-xss with ezyang/htmlpurifier. Despite anti-xss being a smaller footprint dependency, an a better license fit with our MIT license, there are issues with it's automatic it sanitisation of global variables causing side effects * Additional unit tests for xss in html writer cell comments * Fix bug #1626 where values of 0 were "rounded" up/down as if they were not 0 (#1627) * Fix bug where values of 0 were "rounded" up/down as if they were not 0 * Update change log * Fix for #1612 - SLK Long File Name (#1706) Issue has been marked stale, but ... Sylk read sets worksheet title to filename (minus .slk). If that is >31 characters, PhpSpreadsheet throws Exception. This change truncates sheet title, as Excel does, to 31 characters. * Update change log * worksheet: fix if cellValue does not exist (#1727) The condition is FALSE if the cell does not exist in the flipped table, but anyway, it is sent in to a method requiring 'string' type, causing it to fail. * fixes #1655 issue (#1656) Resolve problem with incorrectly defined hyperlinks * Add 'ps' suffix to printer settings resources IDs (#1690) * Add 'ps' suffix to printer settings resources IDs * Update change log * Fix pixelsToPoints conversion (for HTML col width) (#1733) * DocBlock Change in Styles/Conditional (#1697) Scrutinizer reported a minor error in a test involving a module which I was not changing. Styles/Conditional function setConditions can take a scalar or an array as a parameter, but DocBlock says it only expects array. I did not wish to add the extra module to my PR, but made a note to self to fix that after PR was installed. That has now happened, and it makes for a good case for me to see all the PHP8/Composer2/etc. changes that have happened recently. * Merge pull request #1698 * Merge pull request #4 from PHPOffice/master * Restore Omitted Read XML Test * Fix for bug #1592 (UPDATED) (#1623) * Fix for Xls when BIFF8 SST (FCh) has bad Shared string length * Update change log * Add nightly PHP 8.1 dev to github actions (#1763) * Fix compatibility with ext-gd on php 8 (#1762) * CSV - Guess Encoding, Handle Null-string Escape (#1717) * CSV - Guess Encoding, Handle Null-string Escape This is in response to issue #1647 (detect CSV character encoding). First, my tests with mb_detect_encoding indicate that it doesn't work well enough; regardless, users can always do that on their own if they deem it useful. Rolling my own is also troublesome, but I can at least: a. Check for BOM (UTF-8, UTF-16BE, UTF-16LE, UTF-32BE, UTF-32LE). b. Do some heuristic tests for each of the above encodings. c. Fallback to a user-specified encoding (default CP1252) if a and b don't yield result. I think this is probably useful enough to include, and relatively easy to expand if other potential encodings should be considered. Starting with PHP7.4, fgetcsv allows specification of null string as escape character in fgetcsv. This is a much better choice than the PHP (and PhpSpreadsheet) default of backslash in that it handles the file in the same manner as Excel does. There is one statement in Reader/CSV which would be adversely affected if the caller so specified (building a regular expression under the assumption that escape character is a single character). Fix that statement appropriately and add tests. * Update changelog * Update Units of Measure supported by the CONVERT() function (#1768) Now supports all current UoM in all categories, with both 1- and 2-character multiplier prefixes, and binary multiplier prefixes, including the new Temperature scales * Changelog for 1.16.0 release * Fix date tests withut specified year for current year 2021 (#1774) * Mrand of zero to any multiple should return 0 (#1773) * WIP Inherited Scrutinizer Recommendations - 1 of 3 I tried to sync my fork with the main project, as I have done several times before. However, the GitHub interface to do this had changed, and it appears that I did not make the optimal selection when I had a choice. Consequently, all the merges that happened to base between the last time I synchronized and this time appear to be part of any PR that I push. "Files changed" remains correct for my new PRs, but there appear to be many more commits involved than is actually the case. I will, at some point, delete and re-create my fork, and pay much closer attention in future when I want to sync my fork with the main project. Because of this set-up, Scrutinizer reports flaws in code that I haven't actually changed in my PRs #1799 and #1800. It still passes them, but, as long as I'm aware of the problems, I may as well attempt to correct them. The following are not part of those PRs: - 5 problems spread over 4 different members - 12 problems in Calculation/Engineering - 15 problems in Reader/XML I shall attempt to resolve these via 3 separate PRs, of which this is the first. * Try Hyperlink Again Scrutinizer still didn't like it as fixed. * Another Crack at Hyperlink This should work. Co-authored-by: Mark Baker Co-authored-by: Adrien Crivelli Co-authored-by: Ryan McAllen Co-authored-by: Flinsch <220455+Flinsch@users.noreply.github.com> Co-authored-by: Jan Sverre Riksfjord Co-authored-by: Max Kalyabin Co-authored-by: Sébastien Despont Co-authored-by: Guilliam Xavier Co-authored-by: Gianluca Giovinazzo Co-authored-by: Alexander M. Turek Co-authored-by: Martins Sipenko * Inherited Scrutinizer Recommendations - 2 of 3 (#1807) * Make DefinedNames Samples Consistent With Other Samples (#1707) All other Samples write to temporary directory. DefinedNames samples write to main directory, which (a) means they aren't stored with others, and (b) they aren't ignored by git so look like changed files. The tests are also simplified by requiring Header rather than Bootstrap, making use of Helper. * Resolve XSS Vulnerability in the HTML Writer (#1719) Resolve XSS Vulnerability in the HTML Writer * Drop Travis * Automatic GitHub releases from git tags * Improve Coverage in src/PhpSpreadsheet There are no changes to code. Additional tests are added, so that the following 6 items now have 100% test coverage: - Comment - DefinedName - DocumentGenerator - IOFactory - NamedFormula - NamedRange * Changes for Scrutinizer Two changes to fix minor problems reported by Scrutinizer. * Spelling: Tou -> You * Fix for 1735 (Incorrect activeSheetIndex after RemoveSheetByIndex) (#1743) This is a fix for issue #1735. It adds tests for this situation, and similar situations involving adding new sheets and accessing existing ones. Coverage for Spreadsheet.php increases from 69% to 75% as a result. * Update change log * Fix for 3 Issues Involving ReadXlsx and NamedRange (#1742) * Fix for 3 Issues Involving ReadXlsx and NamedRange Issues #1686 and #1723, which provide sample spreadsheets, are probably solved by this ticket. Issue #1730 is also probably solved, but I have no way to verify. There are two problems with how PhpSpreadsheet is handling things now. Although the first problem is much less severe, and isn't really a factor in the issues named above, it is helpful to get it out of the way first. If you define a named range in Excel, and then delete the sheet where the range exists, Excel saves the range as #REF!. If there is a cell which references the range, it will similarly have the value #REF! when you open the Excel file. Currently, PhpSpreadsheet discards the #REF! definition, so a cell which references the range will appear as #NAME? rather than #REF!. This PR changes the behavior so that PhpSpreadsheet retains the #REF! definition, and cells which reference it will appear as #REF!. The second problem is the more severe, and is, I believe, responsible for the 3 issues identified above. If you define a named range and the sheet on which the range is defined does not exist at the time, Excel will save the range as something like: '[1]Unknown Sheet'!$A$1 If a cell references such a range, Excel will again display #REF!. PhpSpreadsheet currently throws an Exception when it encounters such a definition while reading the file. This PR changes the behavior so that PhpSpreadsheet saves the definition as #REF!, and cells which reference it will behave similarly. For the record, I will note that Excel does not magically recalculate when a missing sheet is subsequently added, despite the fact that the reference might now become resolvable. PhpSpreadsheet behaves likewise. * Remove Dead Code in Test Identified it after push but before merge. * Update change log * Apply Column and Row Styles to Existing Cells (#1721) * Apply Column and Row Styles to Existing Cells This is a fix for issue #1712. When a style is applied to an entire row or column, it is currently only effective for cells which don't already contain a value. The code needs to iterate through existing cells in the row/column in order to apply the style to them. This could be considered a breaking change, however, I believe that the change makes things operate as users would expect, and that the existing implementation is incomplete. The change also removes protected element conditionalStyles from the Style class. That element is an unused remnant, and can no longer be set or retrieved - methods getConditionalStyles and setConditionalStyles actually act on an element in the Worksheet class. Finally, additional tests are added so that Style, and in fact the entire Style directory, now has 100% test coverage. * Scrutinizer Changes Scrutinizer flagged 6 statements. 5 can be easily corrected. One is absolutely wrong (it thinks iterating through cells in column can return null). Let's see if we can satisfy it. * Remove Exception For CellIterator on Empty Row/Column For my first attempt at this change, which corrects a bug by updating styles for non-empty cells when a style is set on a row or column, I wished to make things more efficient by using setIterateOnlyExistingCells, something which the existing documentation recommends. This caused an exception to be generated when the row or column is empty. So I removed that part of the change while I researched what was going on. I have completed that research. The existing code does throw an exception when the row/column is empty and iterateOnlyExistingCells is true. However, that does not seem like a reasonable action. This situation is analagous to iterating over an empty array, and that action is legal and does not throw. The same should apply here. There were no tests for this situation, and now there are. I have added additional tests, and coverage for all of RowCellIterator, ColumnCellIterator, and CellIterator are all now 100%. Some of my new tests were added in new members, because the existing tests all relied on mocking, which was not the best choice for the new tests. One of the existing tests for RowCellIteratorTest (testSeekOutOfRange) was wrong; it issued the expected exception, but for the wrong reason. I have added an additional test to ensure that it fails "correctly". The existing documentation says that the default value for IterateOnlyExistingCells is true. In fact, the default value is false. I have corrected the documentation. * More Scrutinizer I believe its analysis is incorrect, but this should silence it. * DocBlock Correction ColumnCellIterator DocBlock for current indicated it could return null or Cell, but it can really return only Cell. This had caused Scrutinizer to complain earlier. * PHP8 Environment Appears to be Fixed Cosmetic change to Doc member. I suspect there is a way to rerun all the tests without another push, but I have been unable to figure out how. * Update change log * TextData Coverage and Minor Bug Fixes (#1744) This had been intended to get 100% coverage for TextData functions, and it does that. However, some minor bugs requiring source changes arose during testing. - the Excel CHAR function restricts its argument to 1-255. PhpSpreadsheet CHARACTER had been allowing 0+. Also, there is no need to test if iconv exists, since it is part of Composer requirements. - The DOLLAR function had been returning NUM for invalid arguments. Excel returns VALUE. Also, negative amounts were not being handled correctly. - The FIXEDFORMAT function had been returning NUM for invalid arguments. Excel FIXED returns VALUE. * Replace anti-xss with html purifier (#1751) * Replace voku/anti-xss with ezyang/htmlpurifier. Despite anti-xss being a smaller footprint dependency, an a better license fit with our MIT license, there are issues with it's automatic it sanitisation of global variables causing side effects * Additional unit tests for xss in html writer cell comments * Fix bug #1626 where values of 0 were "rounded" up/down as if they were not 0 (#1627) * Fix bug where values of 0 were "rounded" up/down as if they were not 0 * Update change log * Fix for #1612 - SLK Long File Name (#1706) Issue has been marked stale, but ... Sylk read sets worksheet title to filename (minus .slk). If that is >31 characters, PhpSpreadsheet throws Exception. This change truncates sheet title, as Excel does, to 31 characters. * Update change log * worksheet: fix if cellValue does not exist (#1727) The condition is FALSE if the cell does not exist in the flipped table, but anyway, it is sent in to a method requiring 'string' type, causing it to fail. * fixes #1655 issue (#1656) Resolve problem with incorrectly defined hyperlinks * Add 'ps' suffix to printer settings resources IDs (#1690) * Add 'ps' suffix to printer settings resources IDs * Update change log * Fix pixelsToPoints conversion (for HTML col width) (#1733) * DocBlock Change in Styles/Conditional (#1697) Scrutinizer reported a minor error in a test involving a module which I was not changing. Styles/Conditional function setConditions can take a scalar or an array as a parameter, but DocBlock says it only expects array. I did not wish to add the extra module to my PR, but made a note to self to fix that after PR was installed. That has now happened, and it makes for a good case for me to see all the PHP8/Composer2/etc. changes that have happened recently. * Merge pull request #1698 * Merge pull request #4 from PHPOffice/master * Restore Omitted Read XML Test * Fix for bug #1592 (UPDATED) (#1623) * Fix for Xls when BIFF8 SST (FCh) has bad Shared string length * Update change log * Add nightly PHP 8.1 dev to github actions (#1763) * Fix compatibility with ext-gd on php 8 (#1762) * CSV - Guess Encoding, Handle Null-string Escape (#1717) * CSV - Guess Encoding, Handle Null-string Escape This is in response to issue #1647 (detect CSV character encoding). First, my tests with mb_detect_encoding indicate that it doesn't work well enough; regardless, users can always do that on their own if they deem it useful. Rolling my own is also troublesome, but I can at least: a. Check for BOM (UTF-8, UTF-16BE, UTF-16LE, UTF-32BE, UTF-32LE). b. Do some heuristic tests for each of the above encodings. c. Fallback to a user-specified encoding (default CP1252) if a and b don't yield result. I think this is probably useful enough to include, and relatively easy to expand if other potential encodings should be considered. Starting with PHP7.4, fgetcsv allows specification of null string as escape character in fgetcsv. This is a much better choice than the PHP (and PhpSpreadsheet) default of backslash in that it handles the file in the same manner as Excel does. There is one statement in Reader/CSV which would be adversely affected if the caller so specified (building a regular expression under the assumption that escape character is a single character). Fix that statement appropriately and add tests. * Update changelog * Update Units of Measure supported by the CONVERT() function (#1768) Now supports all current UoM in all categories, with both 1- and 2-character multiplier prefixes, and binary multiplier prefixes, including the new Temperature scales * Changelog for 1.16.0 release * Fix date tests withut specified year for current year 2021 (#1774) * Mrand of zero to any multiple should return 0 (#1773) * Inherited Scrutinizer Recommendations - 2 of 3 I tried to sync my fork with the main project, as I have done several times before. However, the GitHub interface to do this had changed, and it appears that I did not make the optimal selection when I had a choice. Consequently, all the merges that happened to base between the last time I synchronized and this time appear to be part of any PR that I push. "Files changed" remains correct for my new PRs, but there appear to be many more commits involved than is actually the case. I will, at some point, delete and re-create my fork, and pay much closer attention in future when I want to sync my fork with the main project. Because of this set-up, Scrutinizer reports flaws in code that I haven't actually changed in my PRs #1799 and #1800. It still passes them, but, as long as I'm aware of the problems, I may as well attempt to correct them. The following are not part of those PRs: - 5 problems spread over 4 different members - 12 problems in Calculation/Engineering - 15 problems in Reader/XML I shall attempt to resolve these via 3 separate PRs, of which this is the second. * Fixed Most of the Problems, But Some New Ones Cropped Up Trying once more to satisfy Scrutinizer. Co-authored-by: Mark Baker Co-authored-by: Adrien Crivelli Co-authored-by: Ryan McAllen Co-authored-by: Flinsch <220455+Flinsch@users.noreply.github.com> Co-authored-by: Jan Sverre Riksfjord Co-authored-by: Max Kalyabin Co-authored-by: Sébastien Despont Co-authored-by: Guilliam Xavier Co-authored-by: Gianluca Giovinazzo Co-authored-by: Alexander M. Turek Co-authored-by: Martins Sipenko * Inherited Scrutinizer Recommendations - 3 of 3 (#1808) * Make DefinedNames Samples Consistent With Other Samples (#1707) All other Samples write to temporary directory. DefinedNames samples write to main directory, which (a) means they aren't stored with others, and (b) they aren't ignored by git so look like changed files. The tests are also simplified by requiring Header rather than Bootstrap, making use of Helper. * Resolve XSS Vulnerability in the HTML Writer (#1719) Resolve XSS Vulnerability in the HTML Writer * Drop Travis * Automatic GitHub releases from git tags * Improve Coverage in src/PhpSpreadsheet There are no changes to code. Additional tests are added, so that the following 6 items now have 100% test coverage: - Comment - DefinedName - DocumentGenerator - IOFactory - NamedFormula - NamedRange * Changes for Scrutinizer Two changes to fix minor problems reported by Scrutinizer. * Spelling: Tou -> You * Fix for 1735 (Incorrect activeSheetIndex after RemoveSheetByIndex) (#1743) This is a fix for issue #1735. It adds tests for this situation, and similar situations involving adding new sheets and accessing existing ones. Coverage for Spreadsheet.php increases from 69% to 75% as a result. * Update change log * Fix for 3 Issues Involving ReadXlsx and NamedRange (#1742) * Fix for 3 Issues Involving ReadXlsx and NamedRange Issues #1686 and #1723, which provide sample spreadsheets, are probably solved by this ticket. Issue #1730 is also probably solved, but I have no way to verify. There are two problems with how PhpSpreadsheet is handling things now. Although the first problem is much less severe, and isn't really a factor in the issues named above, it is helpful to get it out of the way first. If you define a named range in Excel, and then delete the sheet where the range exists, Excel saves the range as #REF!. If there is a cell which references the range, it will similarly have the value #REF! when you open the Excel file. Currently, PhpSpreadsheet discards the #REF! definition, so a cell which references the range will appear as #NAME? rather than #REF!. This PR changes the behavior so that PhpSpreadsheet retains the #REF! definition, and cells which reference it will appear as #REF!. The second problem is the more severe, and is, I believe, responsible for the 3 issues identified above. If you define a named range and the sheet on which the range is defined does not exist at the time, Excel will save the range as something like: '[1]Unknown Sheet'!$A$1 If a cell references such a range, Excel will again display #REF!. PhpSpreadsheet currently throws an Exception when it encounters such a definition while reading the file. This PR changes the behavior so that PhpSpreadsheet saves the definition as #REF!, and cells which reference it will behave similarly. For the record, I will note that Excel does not magically recalculate when a missing sheet is subsequently added, despite the fact that the reference might now become resolvable. PhpSpreadsheet behaves likewise. * Remove Dead Code in Test Identified it after push but before merge. * Update change log * Apply Column and Row Styles to Existing Cells (#1721) * Apply Column and Row Styles to Existing Cells This is a fix for issue #1712. When a style is applied to an entire row or column, it is currently only effective for cells which don't already contain a value. The code needs to iterate through existing cells in the row/column in order to apply the style to them. This could be considered a breaking change, however, I believe that the change makes things operate as users would expect, and that the existing implementation is incomplete. The change also removes protected element conditionalStyles from the Style class. That element is an unused remnant, and can no longer be set or retrieved - methods getConditionalStyles and setConditionalStyles actually act on an element in the Worksheet class. Finally, additional tests are added so that Style, and in fact the entire Style directory, now has 100% test coverage. * Scrutinizer Changes Scrutinizer flagged 6 statements. 5 can be easily corrected. One is absolutely wrong (it thinks iterating through cells in column can return null). Let's see if we can satisfy it. * Remove Exception For CellIterator on Empty Row/Column For my first attempt at this change, which corrects a bug by updating styles for non-empty cells when a style is set on a row or column, I wished to make things more efficient by using setIterateOnlyExistingCells, something which the existing documentation recommends. This caused an exception to be generated when the row or column is empty. So I removed that part of the change while I researched what was going on. I have completed that research. The existing code does throw an exception when the row/column is empty and iterateOnlyExistingCells is true. However, that does not seem like a reasonable action. This situation is analagous to iterating over an empty array, and that action is legal and does not throw. The same should apply here. There were no tests for this situation, and now there are. I have added additional tests, and coverage for all of RowCellIterator, ColumnCellIterator, and CellIterator are all now 100%. Some of my new tests were added in new members, because the existing tests all relied on mocking, which was not the best choice for the new tests. One of the existing tests for RowCellIteratorTest (testSeekOutOfRange) was wrong; it issued the expected exception, but for the wrong reason. I have added an additional test to ensure that it fails "correctly". The existing documentation says that the default value for IterateOnlyExistingCells is true. In fact, the default value is false. I have corrected the documentation. * More Scrutinizer I believe its analysis is incorrect, but this should silence it. * DocBlock Correction ColumnCellIterator DocBlock for current indicated it could return null or Cell, but it can really return only Cell. This had caused Scrutinizer to complain earlier. * PHP8 Environment Appears to be Fixed Cosmetic change to Doc member. I suspect there is a way to rerun all the tests without another push, but I have been unable to figure out how. * Update change log * TextData Coverage and Minor Bug Fixes (#1744) This had been intended to get 100% coverage for TextData functions, and it does that. However, some minor bugs requiring source changes arose during testing. - the Excel CHAR function restricts its argument to 1-255. PhpSpreadsheet CHARACTER had been allowing 0+. Also, there is no need to test if iconv exists, since it is part of Composer requirements. - The DOLLAR function had been returning NUM for invalid arguments. Excel returns VALUE. Also, negative amounts were not being handled correctly. - The FIXEDFORMAT function had been returning NUM for invalid arguments. Excel FIXED returns VALUE. * Replace anti-xss with html purifier (#1751) * Replace voku/anti-xss with ezyang/htmlpurifier. Despite anti-xss being a smaller footprint dependency, an a better license fit with our MIT license, there are issues with it's automatic it sanitisation of global variables causing side effects * Additional unit tests for xss in html writer cell comments * Fix bug #1626 where values of 0 were "rounded" up/down as if they were not 0 (#1627) * Fix bug where values of 0 were "rounded" up/down as if they were not 0 * Update change log * Fix for #1612 - SLK Long File Name (#1706) Issue has been marked stale, but ... Sylk read sets worksheet title to filename (minus .slk). If that is >31 characters, PhpSpreadsheet throws Exception. This change truncates sheet title, as Excel does, to 31 characters. * Update change log * worksheet: fix if cellValue does not exist (#1727) The condition is FALSE if the cell does not exist in the flipped table, but anyway, it is sent in to a method requiring 'string' type, causing it to fail. * fixes #1655 issue (#1656) Resolve problem with incorrectly defined hyperlinks * Add 'ps' suffix to printer settings resources IDs (#1690) * Add 'ps' suffix to printer settings resources IDs * Update change log * Fix pixelsToPoints conversion (for HTML col width) (#1733) * DocBlock Change in Styles/Conditional (#1697) Scrutinizer reported a minor error in a test involving a module which I was not changing. Styles/Conditional function setConditions can take a scalar or an array as a parameter, but DocBlock says it only expects array. I did not wish to add the extra module to my PR, but made a note to self to fix that after PR was installed. That has now happened, and it makes for a good case for me to see all the PHP8/Composer2/etc. changes that have happened recently. * Merge pull request #1698 * Merge pull request #4 from PHPOffice/master * Restore Omitted Read XML Test * Fix for bug #1592 (UPDATED) (#1623) * Fix for Xls when BIFF8 SST (FCh) has bad Shared string length * Update change log * Add nightly PHP 8.1 dev to github actions (#1763) * Fix compatibility with ext-gd on php 8 (#1762) * CSV - Guess Encoding, Handle Null-string Escape (#1717) * CSV - Guess Encoding, Handle Null-string Escape This is in response to issue #1647 (detect CSV character encoding). First, my tests with mb_detect_encoding indicate that it doesn't work well enough; regardless, users can always do that on their own if they deem it useful. Rolling my own is also troublesome, but I can at least: a. Check for BOM (UTF-8, UTF-16BE, UTF-16LE, UTF-32BE, UTF-32LE). b. Do some heuristic tests for each of the above encodings. c. Fallback to a user-specified encoding (default CP1252) if a and b don't yield result. I think this is probably useful enough to include, and relatively easy to expand if other potential encodings should be considered. Starting with PHP7.4, fgetcsv allows specification of null string as escape character in fgetcsv. This is a much better choice than the PHP (and PhpSpreadsheet) default of backslash in that it handles the file in the same manner as Excel does. There is one statement in Reader/CSV which would be adversely affected if the caller so specified (building a regular expression under the assumption that escape character is a single character). Fix that statement appropriately and add tests. * Update changelog * Update Units of Measure supported by the CONVERT() function (#1768) Now supports all current UoM in all categories, with both 1- and 2-character multiplier prefixes, and binary multiplier prefixes, including the new Temperature scales * Changelog for 1.16.0 release * Fix date tests withut specified year for current year 2021 (#1774) * Mrand of zero to any multiple should return 0 (#1773) * Inherited Scrutinizer Recommendations - 3 of 3 I tried to sync my fork with the main project, as I have done several times before. However, the GitHub interface to do this had changed, and it appears that I did not make the optimal selection when I had a choice. Consequently, all the merges that happened to base between the last time I synchronized and this time appear to be part of any PR that I push. "Files changed" remains correct for my new PRs, but there appear to be many more commits involved than is actually the case. I will, at some point, delete and re-create my fork, and pay much closer attention in future when I want to sync my fork with the main project. Because of this set-up, Scrutinizer reports flaws in code that I haven't actually changed in my PRs #1799 and #1800. It still passes them, but, as long as I'm aware of the problems, I may as well attempt to correct them. The following are not part of those PRs: - 5 problems spread over 4 different members - 12 problems in Calculation/Engineering - 15 problems in Reader/XML I shall attempt to resolve these via 3 separate PRs, of which this is the third. * The Usual Fixed some Scrutinizer problems, some new ones popped up. * More Scrutinizer I think it's wrong in a lot of these cases. Although I am working around all of them, I intend to file a bug report with them. Co-authored-by: Mark Baker Co-authored-by: Adrien Crivelli Co-authored-by: Ryan McAllen Co-authored-by: Flinsch <220455+Flinsch@users.noreply.github.com> Co-authored-by: Jan Sverre Riksfjord Co-authored-by: Max Kalyabin Co-authored-by: Sébastien Despont Co-authored-by: Guilliam Xavier Co-authored-by: Gianluca Giovinazzo Co-authored-by: Alexander M. Turek Co-authored-by: Martins Sipenko * Fix/1674 (#1688) * Treat inline strings like strings in Open Document because it has no specific inline-string format * implement data-type error Co-authored-by: Mark Baker * Fixed reading XSLS style alignments from XML (#1710) The attribute `$alignmentXml` given to the private method `readAlignmentStyle` is the alignment XML tag that contains the alignment data itself. But inside that method all data are read from another `alignment` XML tag within that given tag. This redundant child-node access resulted in the following error- / notice-message: `PHP Notice: Trying to access array offset on value of type null in /foo/bar/baz/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Styles.php on line 146` These changes remove the redundant child-node access in the method `readAlignmentStyle`. * Update change log * Fix/sheets xlsx chart (#1761) * Add support for Google Sheets Exported XLSX Charts Google Sheets XLSX charts use oneCellAnchor positioning and the data series do not have the *Cache elements with cached values. * update CHANGELOG * Add support for Google Sheets Exported XLSX Charts Google Sheets XLSX charts use oneCellAnchor positioning and the data series do not have the *Cache elements with cached values. Because the reader had been assuming *Cache elements existed as children of strRef and numRef, errors about the node being deleted were thrown when reading Xlsx exported from Google Sheets. Co-authored-by: Darren Maczka * Fix/chart axis titles (#1760) * use axPos value to determine whether an axis title is mapped to the XaxisLabel or YaxisLabel * update changelog * Fix php-cs-fixer violations Co-authored-by: Darren Maczka Co-authored-by: Mark Baker * Update change log * Cell alignment for ods Writer (#1819) * Cell alignment for ods Writer * Refactoring of ODS Writer to break codebase into smaller components (#1820) * Refactoring of ODS Writer to break codebase into smaller components * Fix XLSX reader when having a corrupt numeric cell data type (#1664) * fix for read xlsx with somewhat corrupt cell data type * Update changelog * Update changelog * Remove leftover of Sami * Stacked Alignment - Use Class Constant Rather than Literal (#1716) * Stacked Alignment - Use Class Constant Rather than Literal PR #1580 defined constants for "stacked" alignment in cells. Using those constants outside of Style/Alignment was beyond the scope of the original PR, but I said I would get to it. This PR replaces all uses of literal -165, and appropriate uses of literal 255, with the named constants, and adds tests to make sure that the changed code is covered in the test suite. * Substitute a literal dot inside quotes within number format masks to prevent it being mistaken for a decimal separator (#1830) * Substitute a literal dot inside quotes within number format masks to prevent it being mistaken for a decimal separator * Fix case where mergeComplexNumberFormatMasks would get stuck in endless-loop (#1793) * Fix case where mergeComplexNumberFormatMasks would get stuck in endless-loop if $numbers had many decimals * Update change log * Attempt to provide allow failure for PHP8.1 unit tests (#1847) * Attempt to provide allow failure for PHP8.1 unit tests PHP8.1 Tests show as passed despite the errors, and it requires checking the actual output from the run to see what the rea result is; but I can live with that until github provides functionality for a proper allow_failure option * Move Bessel function calculations from the Engineering class to a dedicated Engineering\Bessel classes (#1846) * Move Bessel function calculations from the Engineering class to a dedicated Engineering\Bessel class Retain the original methods in the Engineering class as stubs for BC, but deprecate them. They will be removed for PHPSpreadsheet v2 * Some refactoring of the Bessel calculation logic * Fix callable for ConvertUOM() * Update PPMT & IPMT implementation to better reflect excel behaviour. Update CUMPRINC & CUMIPMT implementation to prevent a crash while trying to add a string to a number. Update AMORLINC & AMORDEGRC to prevent crash when trying to multiply a string by a number. Update related unit tests. Update changelog to describe what we fixed. (#1840) Co-authored-by: Obmecha * Fix docblocks * Extract all BitWise functions from the Engineering class into a dedicated BitWise class (#1848) * Extract all BitWise functions from the Engineering class into a dedicated BitWise class Retain the original methods in the Engineering class as stubs for BC, but deprecate them. They will be removed for PHPSpreadsheet v2 Note that unit tests still point to the Engineering class stubs; these should be modified to use the bessel classes directly when the stubs are removed * Extract all Base Conversion functions from the Engineering class into dedicated Base Conversion classes (#1849) * Extract all Base Conversion functions from the Engineering class into a dedicated Convert classes extending from a common ConvertBase class Retain the original methods in the Engineering class as stubs for BC, but deprecate them. They will be removed for PHPSpreadsheet v2 Note that unit tests still point to the Engineering class stubs; these should be modified to use the Convert classes directly when the stubs are removed * Split out into separate base conversion classes, with a ConvertBase class for common methods * Extract all Error functions from the Engineering class into a dedicated Erf and ErfC classes (#1850) * Extract all Error functions from the Engineering class into a dedicated Erf and ErfC classes Retain the original methods in the Engineering class as stubs for BC, but deprecate them. They will be removed for PHPSpreadsheet v2 Note that unit tests still point to the Engineering class stubs; these should be modified to use the Erf and ErfC classes directly when the stubs are removed * Reminder that ERF is used (either directly or Indirectly) in some of the statistical functions as well * Update Changelog * Extract Permutation functions from the Statistical class into a dedicated Permutations class (#1851) * Extract Permutation functions from the Statistical class into a dedicated Permutations class Retain the original methods in the Statistical class as stubs for BC, but deprecate them. They will be removed for PHPSpreadsheet v2 Note that unit tests still point to the Statistical class stubs; these should be modified to use the Permutations class directly when the stubs are removed Also provided a basic implementationof the PERMUTATIONA() Function * Update docs * Extract DELTA() and GESTEP() functions from the Engineering class into a dedicated Comparison classes (#1853) * Extract DELTA() and GESTEP() functions from the Engineering class into a dedicated Comparison classes Retain the original methods in the Engineering class as stubs for BC, but deprecate them. They will be removed for PHPSpreadsheet v2 Note that unit tests still point to the Engineering class stubs; these should be modified to use the Erf and ErfC classes directly when the stubs are removed * ROUND Accepts null, false, and true as First Parameter (#1837) * ROUND Accepts null, false, and true as First Parameter Issue #1789 was addressed by PR #1799. In a follow-up discussion, it came to light that ROUND was not handling the unexpected case where the first parameter is an empty cell in the same manner that Excel does. Subsequent investigation showed that a boolean first parameter is permitted. I broadened my investigation to include the following related functions. - ROUNDUP - ROUNDDOWN - MROUND - TRUNC - INT - FLOOR - FLOOR.MATH - FLOOR.PRECISE - CEILING - CEILING.MATH - CEILING.PRECISE All of these allow a NULL first parameter, and all except MROUND allow boolean. For completeness, I will note that all treat null string as invalid. I suspect there are other functions which permit similarly unexpected parameters, but I consider them out of scope for this PR. CEILING.MATH and CEILING.PRECISE were unimplemented, and are now supported as part of this PR. The tests for each of these functions have been re-coded, though all the original test data is still included in the test cases, plus several new cases for each. The new tests now take place as a user would invoke the functions, through a spreadsheet cell rather than a direct call to the appropriate function within Calculation/MathTrig. Aside from being more realistic, the new tests are also more complete. For example, FLOOR.MATH can take from 1-3 arguments, and the existing tests confirmed that the function in Calculation could handle a single argument. However, the function list in Calculation.php erroneously set the number of arguments for FLOOR.MATH to exactly 3, so, if a user tried to get the calculated result of a cell containing FLOOR.MATH(1.2), the result would be an Exception. Aside from the parameter support, there are a few minor code changes. Ods, as well as Gnumeric, allows the omission of the second parameter for FLOAT and CEILING; Excel does not. A potential divide-by-zero error is avoided in CEILING, FLOOR, and FLOORMATH. I will note that it would probably be beneficial in terms of maintainability to break MathTrig up into many individual modules. The same would hold for the other Calculation modules. I would be willing to look into this if you agree that it would be worthwhile. * Support 'Forms' for ROMAN Function (#1828) * Support 'Forms' for ROMAN Function This seems like an exceptionally silly thing for MS to have implemented (Wikipedia on Roman Numerals: "There is no indication this is anything other than an invention by the programmer"). Nevertheless, we can, and therefore probably should, implement it. Not that I can implement it by an algorithm - Excel describes the various extra styles as "more concise", "more concise", "more concise", and "simplified". Nevertheless, since the universe of potential calls is relatively small, it can be implemented as a table of values where the new forms would return a different value than "classic". This table is relatively large, so I have put it its own member to avoid overhead when the function is needed. * Move ROMAN To Its Own Class See discussion in PR #1837 * PHP 8.1 Deprecations PHP8.1 Unit tests failed. 1 line fixes are available for - Shared/Font - Shared/XMLWriter - Style/Color - Writer/HTML The problem is that an error is also reported for a strcmp at line 272 of Cell/Cell. Not only does that line not invoke strcmp, there is no strcmp in all of Cell/Cell, so I don't know what to make of the error message. Oh well, let's fix what can be fixed. Still dealing with the mysterious PHP8.1 unit test failure in Cell\Cell, which seems to have something to do with strcmp. The only uses of strcmp that I can find in src/ are in Calculation. I can't find any use of it in test/ or samples/. So, if this doesn't fix the problem, I may have to give up. * Additional Office 365 functions for Excel added in 2020 * Start extracting Logical Excel functions into separate groups in dedicated classes (#1855) * Start extracting Logical Excel functions into separate groups in dedicated classes * Extracting remaining Logical Excel functions into separate groups in dedicated classes * Update deprecation notices to reflect deprecated from the next release (#1856) * Update deprecation notices to reflect deprecated from the next release * Fix coding standards issue * Fix coding standards issue * Use DateTime Rather than gmmktime in Sample Template (#1827) This avoids a potential Y2038 problem on 32-bit systems - see issue #1826. * Make Documentation Updates Easier and More Accurate (#1573) I made a documentation change and noticed that the result when browsed locally did not quite match what is seen when browsing from the web. After some research, I found https://github.com/mkdocs/mkdocs/issues/2028 That described my situation well and suggested adding an extra javascript script to the configuration. This worked exactly as desired on my local machine. This accounts for the presence of extrajs.js and mkdocs.yml in this request. In addition to the display problem, "mkdocs build" generates the documentation into a directory which is not ignored by git. I added that directory to .gitignore as part of this request. Finally, since I don't know how exactly the documentation makes it to production, I made an insignificant change to one doc file as a sanity check. * Let's see what Scrutinizer makes of these changes (#1859) * Let's see what Scrutinizer makes of these changes * Advanced Value Binder Improvements (#1862) Advanced Value Binder - Improved format checking/setting for fractions; - Better percentage checking; - Some minor refactoring; - Improved unit testing * Advanced Value Binder improvements (#1863) * Refactor times, and add unit tests * Additional Unit Test Cases for Convert UoM (#1864) * Additional Unit Test Cases * Bugfix #1858; `getCell()` method should support named cells correctly (#1865) Resolution for Issue [#1858](https://github.com/PHPOffice/PhpSpreadsheet/issues/1858) * Bugfix #1858; Apply stricter scoping rules to named range/cell access (#1866) * Apply stricter scoping rules to named range/cell access via Worksheet object * Additional unit tests * Refactor the Excel Database functions; and rewrite the query building (#1871) * Refactor the Excel Database functions; and rewrite the query building to fix a bug with complex multi-criteria queries that involve both AND and OR conditions * Fix handling for empty cells and NULL values in searches * Expand unit tests; and add TODOs for dates, percentages, and wildcard text comparisons * Enable support for dates and percentages in Excel Database functions (#1875) * Enable support for dates and percentages in Excel Database functions, and CountIf/AverageIf/etc * Enable support for booleans in Excel Database functions * Enable support for wildcard text searches in Excel Database functions (#1876) * Enable support for wildcard text searches in Excel Database functions * Avoid Duplicate Titles When Reading Multiple HTML Files (#1829) This issue arose while researching issue #1823. The issue was not a bug; it just required clarification to the author of how to use the software. But, while researching, I discovered that loading html into 2 sheets of a spreadsheet has a problem if the html title tag is the same for the 2 sheets. PhpSpreadsheet would be able to save the resulting file, but Excel would not be able to read it properly because of the duplicate title. The worksheet setTitle method allows for disambiguation is such a circumstance. The html reader passed a parameter indicating "don't disambiguate", but I can't see any harm in changing that to "disambiguate". An extremely simple fix, with tests to back it up. * Update change log * Initial experiments using the new Database query logic with Conditional Statistical Functions (#1880) - Refactoring of the Statistical Conditional functions (`AVERAGEIF()`, `AVERAGEIFS()`, `COUNTIF()`, `COUNTIFS()`, `MAXIFS()` and `MINIFS()` to use the new Database functions codebase. - Extended unit testing - Fix handling for null values - Fixes to wildcard text searches There's still scope for further improvements to memory usage and performance; but for now the code is stable with all unit tests passing * 100% Coverage for Calculation/DateTime (#1870) * 100% Coverage for Calculation/DateTime The code in DateTime is now completely covered. Along the way, some errors were discovered and corrected. - The tests which have had to be changed at the start of every year are replaced by more robust equivalents which do not require annual changes. - Several places in the code where Gnumeric and OpenOffice were thought to differ from Excel do not appear to have had any justification. I have left a comment where such code has been removed. - Use DateTime when possible rather than date, time, or strftime functions to avoid potential Y2038 problems. - Some impossible code has been removed, replaced by an explanatory comment. - NETWORKDAYS had a bug when the start date was Sunday. There had been no tests of this condition. - Some functions allow boolean and null arguments where a number is expected. This is more complicated than the equivalent situations in MathTrig because the initial date for these calculations can be Day 1 rather than Day 0. - More testing for dates from 1900-01-01 through the fictitious everywhere-but-Excel 1900-01-29. - This showed that there is an additional Excel bug - Excel evaluates WEEKNUM(emptycell) as 0, which is not a valid result for WEEKNUM without a second argument. PhpSpreadsheet now duplicates this bug. - There is a similar and even worse bug for 1904-01-01 in 1904 calculations. Weeknum returns 0 for this, but returns the correct value for arguments of 0 or null. - DATEVALUE should accept 1900-02-29 (sigh) and relatives. PhpSpreadsheet now duplicates this bug. - Testing bootstrap sets default timezone. This appears to be a relic from the releases of PHP where the unwise decision, subsequenly reversed, was made to issue messages for "no default timezone is set" rather than just use a sensible default. This was a disruptive setting for some of the tests I added. There is only one test in the entire suite which is default-timezone-dependent. Setting and resetting of default timezone is moved to that test (Reader/ODS/ODSTest), and out of bootstrap. - There had been no testing of NOW() function. - DATEVALUE test had no tests for 1904 calendar and needs some. - DATE test changed 1900/1904 calendar in use without restoring it. - WEEKDAY test had no tests for 1904 calendar and needs some. - Which revealed a bug in Shared/Date (excelToDateTimeObject was not recognizing 1904-01-01 as valid when 1904 calendar is in use). - And an additional bug in that legal 1904-calendar values in the 0.0-1.0 range yielded the same "wrong" answers as 1900-calendar (see "One note" below). Also the comment for one of the calendar-1904 tests was wrong in attempting to identify what time of day the fraction represented. I had wanted to break this up into a set of smaller modules, a process already started for Engineering and MathTrig. However the number of source code changes was sufficient that I wanted a clean delta for this request. If it is merged, I will work on breaking it up afterwards. One note - Shared/Date/excelToDateTimeObject, when calendar-1900 is in use, returns an unexpected result if its argument is between 0 and 1, which is nominally invalid for that calendar. It uses a base-1970 calendar in that instance. That check is not justifiable for calendar-1904, where values in that range are legal, so I made the check specific to calendar-1900, and adjusted 3 1904 unit test results accordingly. However, I have to admit that I don't understand why that check should be made even for calendar-1900. It certainly doesn't match anything that Excel does. I would recommend scrapping that code altogether. If agreed, I would do this as part of the break-up into smaller modules. Another note - more controversially, it is clear that PhpSpreadsheet needs to support the Excel and PHP date formats. Although it requires further study, I am not convinced that it needs to support Unix timestamp format. Since that is a potential source of Y2038 problems on 32-bit systems, I would like to open a PR to deprecate the use of that format. Please let me know if you are aware of a valid reason to continue to support it. * Avoid the performance/memory overheads of "clone on modify" of $args (#1884) * Avoid the performance/memory overheads of "clone on modify" of $args when building the condition set/database for AVERAGEIFS(), MAXIFS() and MINIFS() * Avoid the performance/memory overheads of "clone on modify" of $args when building the condition set/database for COUNTIFS() * Additional conditionals from math trig (#1885) * Use our new Conditional logic to implement the SUMIF() and SUMIFS() Mathematical functions * Initial Formula Translation tests (#1886) * Initial Formula Translation tests * Pdf Writer strtoupper() fix (#1629) * _setPageSize's strtoupper() on array argument PhpSpreadsheet/Writer/Pdf.php Class defines a protected static mixed array called $paperSizes, this array contains string values along with array values. 'strtoupper() expects parameter 1 to be string, array given' error happens due to array passed to $paperSize variable from that $paperSizes mixed array on the Mpdf Class where Pdf extends Examples of cases, when a 'Letter' paper size is chosen, then no problem occurs since the index in that value for the array is a string value, but when 'Tabloid' paper size is chosen the value in the index for that paper size is an array, that's when the strtoupper() error happens * _setPageSize's strtoupper() on array argument PhpSpreadsheet/Writer/Pdf.php Class defines a protected static mixed array called $paperSizes, this array contains string values along with array values. 'strtoupper() expects parameter 1 to be string, array given' error happens due to array passed to $paperSize variable from that $paperSizes mixed array on the Dompdf Class where Pdf extends Examples of cases; when a 'Letter' paper size is chosen, then no problem occurs since the index in the array for that value a string, but when 'Tabloid' paper size is chosen the value in the index for that paper size is an array, that's when the strtoupper() error happens. * Update ReferenceHelper.php (#1873) * Update ReferenceHelper for Defined Names * Start splitting some of the basic Statistical functions out into separate classes (#1888) * Start splitting some of the basic Statistical functions out into separate classes containing just a few similar functions * Splitting some of the basic Statistical functions out into separate classes containing just a few similar functions - MAX(), MAXA(), MIN() and MINA() * Splitting some more of the basic Statistical functions out into separate classes containing just a few similar functions - StandardDeviations and Variances * Statistics more unit tests (#1889) * Additional unit tests * Changelog * Changelog * Fix Two 32-bit Timestamp Problems, and Minor getFormattedValue Bug (#1891) I ran the test suite using 32-bit PHP. There were 2 places where changes were needed due to 32-bit timestamps. Reader\\Xml.php was using strtotime as an intermediate step in converting a string timestamp to an Excel timestamp. The XML file type stores pure timestamps (i.e. no date portion) as, e.g., 1899-12-31T02:30:00.000, and that value causes an error using strtotime on a 32-bit system. However, it is sufficient to use that value in a DateTime constructor, and that will work for 32- and 64-bit. There was no test for that particular cell, so I added one to the XML read test. And that's when I discovered the getFormattedValue bug. The cell's format is `hh":"mm":"ss`. The quotes around the colons are disrupting the formatting. PhpSpreadsheet formats the cell by converting the Excel format to a Php Date format, in this case `H\:m\:s`. That's a problem, since Excel thinks 'm' means *minutes*, but PHP thinks it means *months*. This is not a problem when the colon is not quoted; there are ample tests for that. I added my best guess as to how to recognize this situation, changing `\:m` to `:i`. The XML read test now succeeds, and no other tests were broken by this change. Test Shared\\DateTest had one test where the expected result of converting to a Unix timestamp exceeds 2**32. Since a Unix timestamp is strictly an int, that test fails on a 32-bit system. In the discussion regarding recently merged PR #1870, it was felt that the user base might still be using the functions that convert to and from a timestamp. So, we should not drop this test, but, since it cannot succeed on a 32-bit system, I changed it to be skipped whenever the expected result exceeded PHP_INT_MAX. There are 3 "toTimestamp" functions within that test. Only one of these had been affected, but I thought it was a good idea to add additional tests to the others to demonstrate this condition. In the course of testing, I also discovered some 32-bit problems with bitwise and base-conversion functions. I am preparing separate PRs to deal with those. * Move the trend functions from Statistical and into their own group class (#1890) * Move the trend functions from Statistical and into their own group class * Additional LINEST()/LOGEST() tests, and fix for the returned array * Writing defined names without a worksheet reference in Xlsx (#1892) * Refactoring * Minor Refactoring * Reverted Scrutinzer fix in Xslx Reader listWorksheetInfo (#1895) * Statistical trends additional functions and unit tests (#1896) * PEARSON() and CORREL() are identical functions * Unit tests for GROWTH() function * Move GROWTH() function into Statistical\Trends Class * Statistical refactoring - Confidence() and Trend() (#1898) - Move TREND() functions into the Statistical Trends class - Unit tests for TREND() - Create Confidence class for Statistical Confidence functions, and the CONFIDENCE() method * Trend unit tests (#1899) - Move TREND() functions into the Statistical Trends class - Unit tests for TREND() - Create Confidence class for Statistical Confidence functions * Minor scrutinizer improvements (#1906) * Minor scrutinizer improvements * Minor typing improvements * Additional argument validation for LEFT(), MID() and RIGHT() text functions (#1909) * Additional argument validation for LEFT(), MID() and RIGHT() text functions * Start refactoring the Lookup and Reference functions (#1912) * Start refactoring the Lookup and Reference functions - COLUMN(), COLUMNS(), ROW() and ROWS() - LOOKUP(), VLOOKUP() and HLOOKUP() - Refactor TRANSPOSE() and ADDRESS() functions into their own classes * Additional unit tests - LOOKUP() - TRANSPOSE() - ADDRESS() * Fix for Issue #1887 - Lose Track of Selected Cells After Save (#1908) * Fix for Issue #1887 - Lose Track of Selected Cells After Save Issue #1887 reports that selected cells are lost after saving Xlsx. Testing indicates that this applies to the object in memory, though not to the saved spreadsheet. Xlsx writer tries to save calculated values for cells which contain formulas. Calculation::_calculateFormulaValue issues a getStyle call merely to retrieve the quotePrefix property, which, if set, indicates that the cell does not contain a formula even though it looks like one. A side-effect of calls to getStyle is that selectedCell is updated. That is clearly accidental, and highly undesirable, in this case. Code is changed to save selectedCell before getStyle call and restore it afterwards. The problem was reported only for Xlsx save. To be on the safe side, test is made for output formats of Xlsx, Xls, Ods, Html (which basically includes Pdf), and Csv. For all of those, the object in memory is tested after the save. For Xlsx and Xls, the saved file is also tested. It does not make sense to test the saved file for Csv and Html. It does make sense to test it for Ods, but the necessary support is not yet present in either the Ods Reader or Ods Writer - a project for another day. * Move Logic Out of Calculation, Add Support for Ods ActiveSheet and SelectedCells Mark Baker thought logic belonged in Worksheet, not Calculation. I couldn't get it to work in Worksheet, but doing it in Cell works, and that has already been used to preserve ActiveSheet over call to getCalculatedValue, so this just extends that idea to SelectedCells. Original tests could not completely support Ods because of a lack of support for ActiveSheet and SelectedCells in Ods Reader and Writer. There's a lot missing in Ods support, but a journey of 1000 miles ... Those two particular concepts are now supported for Ods. * Update ChangeLog * Unhappy path tests for FORMULATEXT() Function (#1915) * Unhappy path tests * Lookup ref further tests and examples (#1918) * Extract LookupRef\INDEX() into index() method of LookupRef\Matrix class Additional tests * Bugfix for returning a column using INDEX() * Some improvements to ROW() and COLUMN() * Simplify some of the INDEX() logic, eliminating redundant code * Replace manual wildcard logic in MATCH() function with the new WildcardMatch methods (#1919) * Replace manual wildcard logic in MATCH() function with the new WildcardMatch methods * Additional unit tests * Refactor input validations * Refactor actual search logic into dedicated methods * Eliminate redundant code * Fix SpreadsheetML (xml) detection (#1917) * Fix SpreadsheetML (xml) detection (#1916) Replace the unrequired product signature by the required namespace definition for XML Spreadsheet. * Add summary to changelog (#1916) Co-authored-by: Christof Bachmann * Continue MathTrig Breakup - Trig Functions (#1905) * Continue MathTrig Breakup - Trig Functions Continuing the process of breaking MathTrip.php up into smaller classes. This round takes care of the trig and hyperbolic functions, plus a few others. - COS, COSH, ACOS, ACOSH - COT, COTH, ACOT, ACOTH - CSC, CSCH - SEC, SECH - SIN, SINH, ASIN, ASINH - TAN, TANH, ATAN, ATANH, ATAN2 - EVEN - ODD - SIGN There are no bug fixes in this PR, except that boolean arguments are now accepted for all these functions, as they are for Excel. Taking a cue from what has been done in Engineering, the parameter validation now happens in a routine which issues Exceptions for invalid values; this simplifies the code in the functions themselves. Consistent with earlier changes of this nature, the versions in the MathTrig class remain, with a doc block indicating deprecation, and a stub call to the new routines. I think several more iterations will be needed to break up MathTrig completely. * Add null typehint to Worksheet::getColumnDimension() since it returns null (#1914) * Add null typehint to `Worksheet::getColumnDimension()` since it can return null * `getColumnDimensionByColumn()` and `getRowDimension()` * Update the fopen mode for writer * Add test case for excel with media * Refactor xlsx writer * Move file handler creation and file addition to the end * Update PHPCS changes * First step extracting INDIRECT() and OFFSET() to their own classes (#1921) * First step extracting INDIRECT() and OFFSET() to their own classes * Start building unit tests for OFFSET() and INDEX() * Named ranges should be handled by the Calculation Engine, not by the implementation of the Excel INDIRECT() function * When calling the calculation engine to get the range of cells to return, INDIRECT() and OFFSET() should use the instance of the calculation engine for the current workbook to benefit from cached results in that range There's a couple of minor bugfixes in here; but it's basically just refactoring of the INDIRECT() and OFFSET() Excel functions into their own classes - still needs a lot of work on unit testing; and there's a lot more that could be improved in the code itself (including handling of the a1 flag for R1C1 format in INDIRECT() * Coverage for Helper/Samples (#1920) * Coverage for Helper/Samples I was perplexed by the fact that Helper/Samples seemed to be entirely uncovered when running the test suite, since I know all the samples are run as part of the test. I think that what must be happening is that the Helper code is invoked mostly as part of a Data Provider (and therefore not counted), not as part of the test proper (which would count). So, this change adds a small number of tests which result in Samples being 100% covered. Covering one statement was tricky - simulating the inability to create a test directory. Mocking, a technique I have not used before, solves this problem admirably. * Suggestions From Mark Baker Tests changed from assertEquals to assertSame. Added @covers annotation to test class. Validate parameter for method being mocked. * Improve Coverage of BIN2DEC etc. (#1902) * Improve Coverage of BIN2DEC etc. The following functions have some special handling depending on the Calculation mode: - BIN2DEC - BIN2HEX - BIN2OCT - DEC2BIN - DEC2HEX - DEC2OCT - HEX2BIN - HEX2DEC - HEX2OCT - OCT2BIN - OCT2DEC - OCT2HEX Ods accepts boolean for its numeric argument. This had already been coded, but there were no tests for it. Gnumeric allows the use of non-integer argument where Excel/Ods do not. The existing code allowed this for certain functions but not for others. Gnumeric consistently allows it, so there is no need for parameter gnumericCheck in convertBase::ValidateValue. Again, there were no tests for this. There were some minor changes needed: - In functions where you are allowed to specify the numnber of "places" in the result, there is an upper bound of 10 which had not been enforced. - Negative values were not handled correctly in some cases. - There was at least one (avoidable) error on a 32-bit system. - Some upper and lower bounds were not being enforced. In addition to enforcing those, the bounds are now defined as class constants in ConvertDecimal. Many tests have been added, so that Engineering is now almost 100% covered. The exception is some BESSEL code. There have been some recent changes to BESSEL which are not yet part of my fork, so I could not address those now. However, I freely admit that, when I looked at the uncovered portion, it seemed like it might be a difficult task, so I probably wouldn't have tackled it anyhow. In particular, the uncovered code seemed to deal with very large numbers, and, although PhpSpreadsheet and Excel both give very large results for these conditions, their answers are not particularly close to each other. I think we're dealing with resuts approaching infinity. More study is needed. * Bitwise Functions and 32-bit (#1900) * Bitwise Functions and 32-bit When running the test suite with 32-bit PHP, a failure was reported in BITLSHIFT. In fact, all of the following are vulnerable to problems, and didn't report any failures only because of a scarcity of tests: - BITAND - BITOR - BITXOR - BITRSHIFT - BITLSHIFT Those last 2 can be resolved fairly easily by using multiplication by a power of 2 rather than shifting. The first 3 are a tougher nut to crack, and I will continue to think how they might best be approached. For now, I have added skippable tests for each of them, which at least documents the problem. Aside from adding many new tests, some bugs were correctd: - The function list in Calculation.php pointed BITXOR to BITOR. - All 5 functions allow null/false/true parameters. - BIT*SHIFT shift amount must be numeric, can be negative, allows decimal portion (which is truncated to integer), and has an absolute value limit of 53. - Because BITRSHIFT allows negative shift amount, its result can overflow (in which case return NAN). - All 5 functions disallow negative parameters (except ...SHIFT second parameter). This was coded, but the code had been thwarted by an earlier is_int test. * Full Support for AND/OR/XOR on 32-bit Previous version did not support operands 2**32 through 2**48. * Some minor refactoring (#1923) * Some minor refactoring * jpgraph seems to be finally dying with PHP. (#1926) * jpgraph seems to be finally dying with PHP. Until we have a valid alternative, disabling this run for PHP because it errors https://github.com/HuasoFoundries/jpgraph looks like a natural successor, but it isn't BC so it will require some work to integrate * Resolve Deprecated setMethods() call when Mocking for tests (#1925) Resolve Deprecated `setMethods()` calls when Mocking for tests, using `onlyMethods()` and `addMethods()` instead * Fix error with a single byte being removed after the _ spacing character when rendering number formats (#1927) * Fix error with a single byte being removed after the _ spacing character when rendering number formats * First step in some refactoring of the NumberFormat class (#1928) * Refactoring of the NumberFormat class; separate the cell numberformat properties from the actually code used to format a value, leaving just a callthrough stub * Resolve issue with percentage formatter, and provide support for ? placeholders in percentage formatting * Update change log * Initial unit tests for Document Properties (#1932) * Initial unit tests for Document Properties * Typehinting in the document properties class * Fix reference to deprecated transpose in lookup (#1935) * Fix reference to the deprecated `TRANSPOSE()` function in `LOOKUP()`, pointing to the new `transpose()` method in the `LookupRef\Matrix` class instead * Final part of breaking down the Engineering class for Excel Engineering functions into smaller individual/group classes (#1940) * Final breaking down the Engineering class for Excel Engineering functions into smaller individual/group classes * Additional unhappy path tests for Complex Number functions * Fix return docblocks for floats to allow for error strings * Start work on breaking down some of the Financial Excel functions (#1941) * Start work on breaking down some of the Financial Excel functions * Unhappy path unit tests for Treasury Bill functions * Codebase for Treasury Bills includes logic for a different days between settlement and maturity calculation for OpenOffice; but Open/Libre Office now uses the Excel days calculation, so this discrepancy between packages is no longer required * We've already converted the Settlement and Maturity dates to Excel timestamps, so there's no need to try doing it again when calculating the days between Settlement and Maturity * Add Unit Tests for the Days per Year helper function * Extract Interest Rate functions - EFFECT() and NOMINAL() - with additional validation, and unhappy path unit tests * First pass at extracting the Coupon Excel functions * Simplify the validation methods * Extended unit tests to cover all combinations of frequency and basis, including leap years Fix for COUPDAYSNC() when basis is US 360 and settlement date is the last day of the month * Ensure that all Financial function code uses the new Helpers class for Days Per Year * First pass at extracting Financial Price functions for Securities (#1942) * Extracting Financial Price functions for Securities - PRICE(), PRICEMAT(), PRICEDISC() * Additional unit tests for PRICEDISC() invalid arguments * Additional unit tests for PRICEMAT() invalid arguments * Add docblock for PRICE() * Clarification on validation checks for <= 0 and < 0 * Complete Breakup Of Calculation/DateTime Functions (#1937) * Complete Breakup Of Calculation/DateTime Functions In conjunction with parallel breakups happening in other areas of Calculation, this change breaks up all the DateTime functions into their own classes. All methods remaining in DateTime itself have a doc block deprecation notice, and consist only of stub code to call the replacement methods. Coverage of DateTime itself and all the replacement methods is 100%. There is only one substantive change to the code (see next paragraph). Among the non-substantive changes, it now adopts the same parsing technique (throwing and catching exceptions) already in use in Engineering and MathTrig. Boolean parameters are allowed in lieu of numbers when Excel allows them. Most of the code changes involve refactoring due to the need to avoid Scrutinizer "complexity" failures in what it will consider to be new methods. Issue #1936 was opened just as I was staging this. It is now fixed. One existing WORKDAY test was wrong (noted in a comment in the test data file), and a bunch of new tests are added. I found it confusing to use DateTime as a node of the the class name since most of the methods invoke native DateTime methods. So, everything is moved to directory DateTimeExcel, and that is what is used in the class names. There are several follow-up activities that I am planning to undertake if this PR is merged. - ODS supports dates well before 1900. There are exactly 2 assertions for this functionality. More are needed (and some functions might have to change to accept this). - WEEKDAY has some poorly documented extra options for "style" which are not yet implemented. - Most tests have been changed to use a formula as entered on a spreadsheet rather than a direct call to the method which implements the formula. There are 3 exceptions at this time. WORKDAY and NETWORKDAYS, which include arrays as part of their parameters, are more complicated than most. YEARFRAC was just too large to deal with now. - There are direct calls to the now-deprecated methods in both source code and tests, mostly in Financial code, but possibly in others as well. These need to be changed. - Some constants, none "officially" documented, remain in the original class. These should be either deleted or marked deprecated. I wasn't sure if deprecation was even possible (or desirable), and did not want that to be something which would cause Scrutinizer to fail the change. * Deprecate Now-unused Constants, Fix Yearfrac bug, Change 3 Tests Add new DateTime/Constants class, initially populated with constants used in Weeknum. MS has another inconsistency with how it handles null cells in Yearfrac. Change PhpSpreadsheet to behave compatibly with this bug. I have modified YearFrac, WorkDay, and NetworkDays tests to be more to my liking. Many tests added to YearFrac because of the bug above. Only minor modifications to the existing tests for the others. * Financial functions next stage of refactoring (#1943) * First steps splitting out the Amortization and Deprecation Excel functions from Financials * Verify which methods allow negative values for arguments * Additional unit tests for SLN() and SYD() * Additional unit tests for DDB() * Additional unit tests for DB() * Verify Amortization cases where salvage is greater than cost * More unit tests for Amortization * Resolve broken test in AMORLINC() and extract amortizationCoefficient calculation * verify amortizationCoefficient calculation * Extract YIELDDISC() and YIELDMAT() to Financial\Securities * Additional validation for Securities Yield functions * First phase of refactoring the Excel Text functions (#1945) * Refactoring the Excel Text functions * More unit tests for utf-8 handling, for edge cases, and for argument validations * New Bessel Algorithm, providing a higher degree of accuracy and precision (#1946) * New Bessel Algorithm, providing a higher degree of precision (12 decimal places) and still matching/exceeding MS Excel's precision across the range of values * Csv reader refactor infer delimiter (#1948) * Refactor delimiter inference for CSV file reading into a separate class * First steps toward refactoring Excel's Statistical Distributions (#1949) * First steps toward refactoring Statistical Distributions into smaller classes: BETA() and GAMMA() (and related functions) to start with... they all need a lot of tidying up, and more testing; but it's a start * Add basic datatype validations to Beta and Gamma Excel function implementations * Switch to using a trait with the validation methods to provide easier sharing between distribution classes * Additional unit tests for Beta and Gamma functions, including unhappy path for validations * Extract ChiSquared functions * Additional argument validation checks with unit tests for Chi Squared functions * Extract Fisher * Move MEDIAN() and MODE() to the Averages class * Extract filters for Median and Mode for common usage * Extract Poisson distribution into its own class (#1953) * Continue MathTrig Breakup - Problem Children (#1954) Continuing the process of breaking MathTrip.php up into smaller classes. This round takes care of all functions which might be an impediment to installing due to either uncovered code or "complexity": - BASE - FACT - LCM - MDETERM, MINVERSE, MMULT - MULTINOMIAL - PRODUCT - QUOTIENT - SERIESSUM - SUM - SUMPRODUCT MathTrig and the members in directory MathTrig are now 100% covered. Many tests have been added, and some edge-case bugs are corrected. Some cases where PhpSpreadsheet had rejected numeric values stored as strings have been changed to accept them whenever Excel does; there had been no tests for that condition. Boolean arguments are now accepted as arguments wherever Excel accpets them. Taking a cue from what has been done in Engineering, the parameter validation now happens in a routine which issues Exceptions for invalid values; this simplifies the code in the functions themselves. Thank you for doing that; I did not foresee how useful that was when I first looked at it. Consistent with earlier changes of this nature, the versions in the MathTrig class remain, with a doc block indicating deprecation, and a stub call to the new routines. All tests except for MINVERSE and MMULT are now handled in the context of a spreadsheet rather than a direct call to the calculation function which implements it. PhpSpreadsheet would need to handle dynamic arrays in order to test MINVERSE and MMULT in a spreadsheet context. Implementing that looks like it might be *very* challenging. It is not something I plan to look at, at least not in the near future. One parsing problem turned up in the test conversion. It is in one of the SUMIF tests. It takes me to an area in Calculation where the comment says "I don't even want to know what you did to get here". It did not show up in the previous incarnation because, by using a direct call, the previous test managed to bypass the parsing. I have confirmed that this problem shows up in earlier releases of PhpSpreadsheet, so the changes in this PR did not cause it - they merely exposed it. I have left the test intact, but marked it "incomplete" for documentation purposes. I have not been able to get a handle on what's going wrong yet. I will probably open an issue on it if I can't resolve it soon. However, the test in question isn't a "real world" issue, and the error wasn't caused by this change, so I see no reason to delay this pending a resolution of the problem. SUM had an idiosyncratic moment of its own. It had been ignoring non-numeric values, but Excel returns VALUE in that situation. So I changed it and wrote some new tests, which worked, but ... SUMIF uses several levels of indirection to get to SUM, and SUMIF *does* ignore non-numeric values, so a SUMIF test broke. SUM is a really simple function; the most practical approach seemed to be to clone it, with the string-accepting version being used by the Legacy version (which is called by SUMIF), and the non-string-accepting version being used in the Calculation Function table. That seems far easier and more practical than, for instance, adding a boolean parameter to the variable parameter list. As a follow-up, I will change SUMIF to explicitly call the appropriate new version, but I did not want to add that to this already large change. SUM again - although it was fully covered beforehand, there was not a specific test member for it. There is now. FACT had been coded to fail Gnumeric requests where the numeric argument has a decimal portion. However, Gnumeric does accept such an argument, and, unlike Excel and ODS, does not truncate it, but returns the result of a Gamma function call instead. This has been corrected. When LCM included arguments which contained both 0 and a negative number, it returned 0 or NUM, whichever it found first. It is changed to always return NUM in that circumstance, as Excel does. QUOTIENT had been documented as taking a variadic list of arguments. In fact, it takes exactly 2 - numerator and denominator - and the docblock and signature is fixed, even in the deprecated version. The SERIESSUM docbock and signature are more accurate, even in the deprecated version. It is changed to ignore nulls, as Excel does, rather than return VALUE, and is one of the routines which previously rejected numbers in string form. SUBTOTAL tests had used mocking for some reason. These are replaced with normal tests. And SUBTOTAL had a big surprise in store. That part of it which deals with hidden cells cares only whether the row is hidden, and doesn't care about the column's visibility. I struggled with whether it should be SubTotal or Subtotal. I think the latter is correct, so that's how I proceeded. I don't think there are likely to be any other capitalization controversies. * Switch calls to deprecated function methods to the equivalent new methods (#1957) * Extract ACCRINT() and ACCRINTM() Financial functions into their own class (#1956) * Extract ACCRINT() and ACCRINTM() Financial functions into their own class Implement additional validations, with additional unit tests Add support for the new calculation method argument for ACCRINT() * Additional tests for Amortization functions * Start implementing Newton-Raphson for the inverse of Statistical Distributions (#1958) * Start implementing Newton-Raphson for the inverse of Statistical Distributions, starting with the two-tailed Student-T * Additional unit tests and validations * Use the new Newton Raphson class for calculating the Inverse of ChiSquared * Extract Weibull distribution, and provide unit tests * Difference in variance calculations between Excel/Gnumeric and Open/LibreOffice (#1959) * Difference in variance calculations between Excel/Gnumeric and Open/LibreOffice * Simplify STDEV() function logic by remembering that STDEV() is simply the square root of VAR(), so we can simply use the VAR() calculaion rather than duplicating the basic logic... and also allow for the differences between Excel/Gnumeric and Open/LibreOffice * Implementation of the CHITEST() statistical function (#1960) * Implementation of the CHITEST() statistical function * A couple of additional edge case tests (rows = 1, columns = 1) * Update PHP deps Simplify our constraints thanks to PHPUnit 8.5 that supports PHP 8+ * Implemented the CHISQ.DIST() Statistical function. (#1961) * Implementation of the CHISQ.DIST() statistical function for left tail distribution * Chi squared inverse left tailed (#1964) * Implementation of the CHISQ.INV() method for ChiSquared distribution left-tail * Update remaining references in the Financial Functions code to avoid calling deprecated methods (#1965) * Update remaining references in the Financial Functions code to bypass deprecated date methods and use the new date function methods directly * Extract Percentile-type functions from Statistics (#1966) * Extract Percentile-type functions from Statistics (e.g. PERCENTILE(), PERCENTRANK(), QUARTILE(), and RANK()) * Unit test for PERCENTILE() with an empty (of numbers) dataset * Document release process * Extract Binomial Distribution functions from Statistical (#1974) * Extract Binomial Distribution functions from Statistical Replace the old MS algorithm for CRITBINOM() (which has now been replaced with te BINOM.INV() function) with a brute force approach - I'll look to refine it later. The MS algorithm is no longer documented, and the implementation produced erroneous results anyway * Exract the NEGBINOMDIST() function as well; still need to add a cumulative flag to support the additional argument for the newer NEGBINOM.DIST() function * Rationalise validation of probability arguments * Extract a few more Distribution functions from Statistical (#1975) * Extract a few more Distribution functions from Statistical; this time EXPONDIST() and HYPGEOMDIST() * Extract the F Distribution (although only F.DIST() is implemented so far * Updae docblocks * PHPCS * Resolution for [#Issue 1972](https://github.com/PHPOffice/PhpSpreadsheet/issues/1972) (#1978) * Resolution for [#Issue 1972](https://github.com/PHPOffice/PhpSpreadsheet/issues/1972) where format masks with a leading and trailing quote were always treated as literal strings, even when they masks containing quoted characters. Also resolves issue with colour name case-sensitivity * Continue MathTrig Breakup - Penultimate? (#1973) * Continue MathTrig Breakup - Penultimate? Continuing the process of breaking MathTrip.php up into smaller classes. This round takes care of about half of what is left, so perhaps one round after this one will finish the job: - ARABIC - COMBIN; also implemented COMBINA - FACTDOUBLE - GCD (which accepts and ignores empty cells as arguments, but returns VALUE if all the arguments are that way; LCM does the same) - LOG_BASE, LOG10, LN - implemented MUNIT - MOD - POWER - RAND, RANDBETWEEN (RANDARRAY is too complicated to implement with this ticket) As you can see from the description, there are some functions which were combined in a single class. When not combined, I adopted PowerKiki's suggestion of using "execute" as the function name. Co-authored-by: Mark Baker * Extract Normal and Standard Normal Distributions from the Statistical Class (#1981) * Extract Normal and Standard Normal Distributions from the Statistical Class * Extract ZTest from the Statistical Class, and move it to the Standard Normal Distribution class Additional unit tests for NORMINV() * Extract LogNormal distribution functions from Statistical * Introduce PHPStan To improve the feedback loop on code quality with a process that can be run locally by the developers, instead of only on Scrutinizer. * Let's start with some appeasements to phpstan, just to reduce the baseline (#1983) * Let's start with some appeasements to phpstan, just to reduce the baseline * Appeasements to phpstan, taking the number of reported errors down to just 61 * PHPStan stuff (#1984) * Financial start refactoring cash flow functions (#1986) * Start extracting CashFlow functions from Financial, beginning with the simple Single Rate flows * Extracting Variable Periodic and NonPeriodic CashFlow functions from Financial * Some more unit tests for exception cases * PHPStan Level 2 * Drop obsolete code * Move original file to temporary file * Continue MathTrig Breakup - Completion! (#1985) * Continue MathTrig Breakup - Completion! Continuing the process of breaking MathTrip.php up into smaller classes. This round takes care of everything that was left: - ABS - DEGREES - EXP - RADIANS - SQRT - SQRTPI - SUMSQ, SUMX2MY2, SUMX2PY2, SUMXMY2 The only notable logic change was that the 3 SUMX* functions had accepted arrays of unlike length; in that condition, they now return N/A, as Excel does. There had been no tests for this condition. All the functions in MathTrig.php are now deprecated. Except for COMBIN, the test suite executes them only from MathTrig MovedFunctionsTest. COMBIN is still directly called by some Statistics Binomial functions which have not yet had the opportunity to be re-coded for the new location. Co-authored-by: Mark Baker * Unlink temporary file * Change pName to regexp sheet name match * Fix potential nullable error * Remove code coverage upload from test file Co-authored-by: MarkBaker Co-authored-by: Gislain Harding Co-authored-by: oleibman Co-authored-by: Simon Podlipsky Co-authored-by: Arman Hosseini Co-authored-by: Adrien Crivelli Co-authored-by: Roland Eigelsreiter Co-authored-by: Jan-Simon Winkelmann Co-authored-by: CoryHrycko <40903917+CoryHrycko@users.noreply.github.com> Co-authored-by: Ryan McAllen Co-authored-by: Flinsch <220455+Flinsch@users.noreply.github.com> Co-authored-by: Jan Sverre Riksfjord Co-authored-by: Max Kalyabin Co-authored-by: Sébastien Despont Co-authored-by: Guilliam Xavier Co-authored-by: Gianluca Giovinazzo Co-authored-by: Alexander M. Turek Co-authored-by: Martins Sipenko Co-authored-by: Richard van Velzen Co-authored-by: Jasper Zonneveld Co-authored-by: もりもと たかひろ Co-authored-by: SheetJSDev Co-authored-by: Alexander Gunkel Co-authored-by: Gerrit Addiks Co-authored-by: Darren Maczka Co-authored-by: Darren Maczka Co-authored-by: Mats Sibelius Co-authored-by: ElPopcorn <33669386+ElPopcorn@users.noreply.github.com> Co-authored-by: Obmecha Co-authored-by: adancabanas Co-authored-by: Ivan Stanojevic Co-authored-by: Patrick Brouwers Co-authored-by: christof-b Co-authored-by: Christof Bachmann Co-authored-by: Vivek Kumar --- .gitattributes | 2 - .github/workflows/github-pages.yml | 29 + .github/workflows/main.yml | 180 + .gitignore | 3 + .php_cs.dist | 2 +- .phpcs.xml.dist | 22 + .scrutinizer.yml | 2 +- .travis.yml | 51 - CHANGELOG.md | 263 +- CONTRIBUTING.md | 9 + README.md | 11 +- composer.json | 45 +- composer.lock | 2258 +++++++++---- docs/extra/extrajs.js | 5 + docs/faq.md | 2 +- docs/index.md | 7 +- docs/references/function-list-by-category.md | 272 +- docs/references/function-list-by-name.md | 993 +++--- docs/topics/accessing-cells.md | 52 +- docs/topics/architecture.md | 4 +- docs/topics/autofilters.md | 38 +- docs/topics/calculation-engine.md | 98 +- docs/topics/creating-spreadsheet.md | 6 +- docs/topics/defined-names.md | 593 ++++ .../10-databar-of-conditional-formatting.png | Bin 0 -> 143616 bytes docs/topics/memory_saving.md | 2 +- docs/topics/migration-from-PHPExcel.md | 2 +- docs/topics/reading-and-writing-to-file.md | 162 +- docs/topics/reading-files.md | 36 +- docs/topics/recipes.md | 437 ++- docs/topics/settings.md | 21 +- docs/topics/worksheets.md | 14 +- mkdocs.yml | 2 + phpstan.neon.dist | 16 + samples/Basic/07_Reader.php | 1 + samples/Basic/13_Calculation.php | 6 +- .../Basic/13_CalculationCyclicFormulae.php | 6 +- samples/Basic/16_Csv.php | 12 +- samples/Basic/17b_Html.php | 20 + samples/Basic/20_Read_Excel2003XML.php | 2 +- samples/Basic/20_Read_Xls.php | 1 + samples/Basic/24_Readfilter.php | 4 +- samples/Basic/26_Utf8.php | 10 +- samples/Basic/28_Iterator.php | 9 +- samples/Basic/30_Templatebiff5.php | 43 + samples/Basic/40_Duplicate_style.php | 2 +- samples/Basic/42_RichText.php | 2 +- samples/Basic/43_Merge_workbooks.php | 4 + samples/Basic/44_Worksheet_info.php | 2 + .../Basic/45_Quadratic_equation_solver.php | 1 + samples/Calculations/LookupRef/ADDRESS.php | 22 + samples/Calculations/LookupRef/COLUMN.php | 23 + samples/Calculations/LookupRef/COLUMNS.php | 21 + samples/Calculations/LookupRef/INDEX.php | 39 + samples/Calculations/LookupRef/INDIRECT.php | 33 + samples/Calculations/LookupRef/OFFSET.php | 33 + samples/Calculations/LookupRef/ROW.php | 20 + samples/Calculations/LookupRef/ROWS.php | 20 + samples/Chart/34_Chart_update.php | 12 +- samples/Chart/35_Chart_render.php | 5 + samples/DefinedNames/AbsoluteNamedRange.php | 54 + .../CrossWorksheetNamedFormula.php | 90 + .../DefinedNames/NamedFormulaeAndRanges.php | 65 + samples/DefinedNames/RelativeNamedRange.php | 57 + samples/DefinedNames/RelativeNamedRange2.php | 60 + .../RelativeNamedRangeAsFunction.php | 63 + samples/DefinedNames/ScopedNamedRange.php | 72 + samples/DefinedNames/ScopedNamedRange2.php | 89 + samples/DefinedNames/SimpleNamedFormula.php | 43 + samples/DefinedNames/SimpleNamedRange.php | 37 + samples/Pdf/21a_Pdf.php | 25 + samples/Pdf/21b_Pdf.php | 55 + samples/Reader/sampleData/example1xls | Bin 0 -> 22528 bytes samples/images/bmp.bmp | Bin 0 -> 30186 bytes samples/images/gif.gif | Bin 0 -> 1578 bytes samples/templates/30template.xls | Bin 39424 -> 51200 bytes samples/templates/30templatebiff5.xls | Bin 0 -> 338944 bytes samples/templates/GnumericTest.gnumeric | Bin 7823 -> 8064 bytes samples/templates/SylkTest.slk | 3 +- .../templates/excel2003.short.bad.xml | 26 +- samples/templates/excel2003.xml | 944 ++++++ samples/templates/old.gnumeric | Bin 0 -> 1276 bytes samples/templates/sampleSpreadsheet.php | 4 +- .../Calculation/Calculation.php | 1547 ++++++--- src/PhpSpreadsheet/Calculation/Category.php | 1 + src/PhpSpreadsheet/Calculation/Database.php | 318 +- .../Calculation/Database/DAverage.php | 45 + .../Calculation/Database/DCount.php | 43 + .../Calculation/Database/DCountA.php | 42 + .../Calculation/Database/DGet.php | 51 + .../Calculation/Database/DMax.php | 46 + .../Calculation/Database/DMin.php | 46 + .../Calculation/Database/DProduct.php | 45 + .../Calculation/Database/DStDev.php | 46 + .../Calculation/Database/DStDevP.php | 46 + .../Calculation/Database/DSum.php | 45 + .../Calculation/Database/DVar.php | 46 + .../Calculation/Database/DVarP.php | 46 + .../Calculation/Database/DatabaseAbstract.php | 174 + src/PhpSpreadsheet/Calculation/DateTime.php | 1287 ++----- .../Calculation/DateTimeExcel/Constants.php | 37 + .../Calculation/DateTimeExcel/DateDif.php | 146 + .../Calculation/DateTimeExcel/DateValue.php | 151 + .../Calculation/DateTimeExcel/Datefunc.php | 168 + .../Calculation/DateTimeExcel/Day.php | 61 + .../Calculation/DateTimeExcel/Days.php | 51 + .../Calculation/DateTimeExcel/Days360.php | 106 + .../Calculation/DateTimeExcel/EDate.php | 45 + .../Calculation/DateTimeExcel/EoMonth.php | 47 + .../Calculation/DateTimeExcel/Helpers.php | 275 ++ .../Calculation/DateTimeExcel/Hour.php | 44 + .../Calculation/DateTimeExcel/IsoWeekNum.php | 55 + .../Calculation/DateTimeExcel/Minute.php | 44 + .../Calculation/DateTimeExcel/Month.php | 40 + .../Calculation/DateTimeExcel/NetworkDays.php | 102 + .../Calculation/DateTimeExcel/Now.php | 34 + .../Calculation/DateTimeExcel/Second.php | 44 + .../Calculation/DateTimeExcel/Time.php | 116 + .../Calculation/DateTimeExcel/TimeValue.php | 61 + .../Calculation/DateTimeExcel/Today.php | 34 + .../Calculation/DateTimeExcel/WeekDay.php | 80 + .../Calculation/DateTimeExcel/WeekNum.php | 130 + .../Calculation/DateTimeExcel/WorkDay.php | 184 + .../Calculation/DateTimeExcel/Year.php | 40 + .../Calculation/DateTimeExcel/YearFrac.php | 120 + .../Calculation/Engine/Logger.php | 14 + .../Calculation/Engineering.php | 2011 ++--------- .../Engineering/BaseValidations.php | 27 + .../Calculation/Engineering/BesselI.php | 139 + .../Calculation/Engineering/BesselJ.php | 174 + .../Calculation/Engineering/BesselK.php | 113 + .../Calculation/Engineering/BesselY.php | 120 + .../Calculation/Engineering/BitWise.php | 227 ++ .../Calculation/Engineering/Compare.php | 72 + .../Calculation/Engineering/Complex.php | 101 + .../Engineering/ComplexFunctions.php | 513 +++ .../Engineering/ComplexOperations.php | 120 + .../Calculation/Engineering/Constants.php | 11 + .../Calculation/Engineering/ConvertBase.php | 65 + .../Calculation/Engineering/ConvertBinary.php | 134 + .../Engineering/ConvertDecimal.php | 183 + .../Calculation/Engineering/ConvertHex.php | 146 + .../Calculation/Engineering/ConvertOctal.php | 145 + .../Calculation/Engineering/ConvertUOM.php | 684 ++++ .../Calculation/Engineering/Erf.php | 91 + .../Calculation/Engineering/ErfC.php | 68 + src/PhpSpreadsheet/Calculation/Financial.php | 1493 ++------- .../Calculation/Financial/Amortization.php | 190 ++ .../Calculation/Financial/BaseValidations.php | 72 + .../Calculation/Financial/CashFlow/Single.php | 111 + .../CashFlow/Variable/NonPeriodic.php | 234 ++ .../Financial/CashFlow/Variable/Periodic.php | 160 + .../Calculation/Financial/Coupons.php | 374 +++ .../Calculation/Financial/Depreciation.php | 265 ++ .../Calculation/Financial/Dollar.php | 80 + .../Calculation/Financial/Helpers.php | 65 + .../Calculation/Financial/InterestRate.php | 74 + .../Financial/Securities/AccruedInterest.php | 141 + .../Financial/Securities/BaseValidations.php | 139 + .../Financial/Securities/Constants.php | 10 + .../Financial/Securities/Price.php | 201 ++ .../Financial/Securities/Yields.php | 138 + .../Calculation/Financial/TreasuryBill.php | 155 + .../Calculation/FormulaParser.php | 36 +- src/PhpSpreadsheet/Calculation/Functions.php | 43 +- .../Calculation/Internal/MakeMatrix.php | 11 + .../Calculation/Internal/WildcardMatch.php | 39 + src/PhpSpreadsheet/Calculation/Logical.php | 267 +- .../Calculation/Logical/Boolean.php | 36 + .../Calculation/Logical/Conditional.php | 183 + .../Calculation/Logical/Operations.php | 198 ++ src/PhpSpreadsheet/Calculation/LookupRef.php | 786 +---- .../Calculation/LookupRef/Address.php | 97 + .../Calculation/LookupRef/ExcelMatch.php | 198 ++ .../Calculation/LookupRef/HLookup.php | 86 + .../Calculation/LookupRef/Indirect.php | 75 + .../Calculation/LookupRef/Lookup.php | 105 + .../Calculation/LookupRef/LookupBase.php | 48 + .../Calculation/LookupRef/Matrix.php | 104 + .../Calculation/LookupRef/Offset.php | 136 + .../LookupRef/RowColumnInformation.php | 173 + .../Calculation/LookupRef/VLookup.php | 105 + src/PhpSpreadsheet/Calculation/MathTrig.php | 1683 ++++------ .../Calculation/MathTrig/Absolute.php | 28 + .../Calculation/MathTrig/Acos.php | 28 + .../Calculation/MathTrig/Acosh.php | 28 + .../Calculation/MathTrig/Acot.php | 28 + .../Calculation/MathTrig/Acoth.php | 30 + .../Calculation/MathTrig/Arabic.php | 95 + .../Calculation/MathTrig/Asin.php | 28 + .../Calculation/MathTrig/Asinh.php | 28 + .../Calculation/MathTrig/Atan.php | 28 + .../Calculation/MathTrig/Atan2.php | 46 + .../Calculation/MathTrig/Atanh.php | 28 + .../Calculation/MathTrig/Base.php | 49 + .../Calculation/MathTrig/Ceiling.php | 66 + .../Calculation/MathTrig/CeilingMath.php | 50 + .../Calculation/MathTrig/CeilingPrecise.php | 38 + .../Calculation/MathTrig/Combinations.php | 74 + .../Calculation/MathTrig/Cos.php | 28 + .../Calculation/MathTrig/Cosh.php | 28 + .../Calculation/MathTrig/Cot.php | 28 + .../Calculation/MathTrig/Coth.php | 28 + .../Calculation/MathTrig/Csc.php | 28 + .../Calculation/MathTrig/Csch.php | 28 + .../Calculation/MathTrig/Degrees.php | 28 + .../Calculation/MathTrig/Even.php | 35 + .../Calculation/MathTrig/Exp.php | 28 + .../Calculation/MathTrig/Fact.php | 47 + .../Calculation/MathTrig/FactDouble.php | 39 + .../Calculation/MathTrig/Floor.php | 69 + .../Calculation/MathTrig/FloorMath.php | 64 + .../Calculation/MathTrig/FloorPrecise.php | 51 + .../Calculation/MathTrig/Gcd.php | 69 + .../Calculation/MathTrig/Helpers.php | 129 + .../Calculation/MathTrig/IntClass.php | 31 + .../Calculation/MathTrig/Lcm.php | 110 + .../Calculation/MathTrig/Logarithms.php | 79 + .../Calculation/MathTrig/MatrixFunctions.php | 138 + .../Calculation/MathTrig/Mod.php | 36 + .../Calculation/MathTrig/Mround.php | 40 + .../Calculation/MathTrig/Multinomial.php | 41 + .../Calculation/MathTrig/Odd.php | 38 + .../Calculation/MathTrig/Power.php | 42 + .../Calculation/MathTrig/Product.php | 47 + .../Calculation/MathTrig/Quotient.php | 35 + .../Calculation/MathTrig/Radians.php | 28 + .../Calculation/MathTrig/Random.php | 39 + .../Calculation/MathTrig/Roman.php | 839 +++++ .../Calculation/MathTrig/Round.php | 30 + .../Calculation/MathTrig/RoundDown.php | 38 + .../Calculation/MathTrig/RoundUp.php | 38 + .../Calculation/MathTrig/Sec.php | 28 + .../Calculation/MathTrig/Sech.php | 28 + .../Calculation/MathTrig/SeriesSum.php | 46 + .../Calculation/MathTrig/Sign.php | 29 + .../Calculation/MathTrig/Sin.php | 28 + .../Calculation/MathTrig/Sinh.php | 28 + .../Calculation/MathTrig/Sqrt.php | 28 + .../Calculation/MathTrig/SqrtPi.php | 29 + .../Calculation/MathTrig/Subtotal.php | 99 + .../Calculation/MathTrig/Sum.php | 68 + .../Calculation/MathTrig/SumProduct.php | 49 + .../Calculation/MathTrig/SumSquares.php | 142 + .../Calculation/MathTrig/Tan.php | 28 + .../Calculation/MathTrig/Tanh.php | 28 + .../Calculation/MathTrig/Trunc.php | 39 + .../Calculation/Statistical.php | 2982 ++++------------- .../Calculation/Statistical/AggregateBase.php | 50 + .../Calculation/Statistical/Averages.php | 259 ++ .../Statistical/BaseValidations.php | 27 + .../Calculation/Statistical/Conditional.php | 304 ++ .../Calculation/Statistical/Confidence.php | 43 + .../Calculation/Statistical/Counts.php | 95 + .../Distributions/BaseValidations.php | 47 + .../Statistical/Distributions/Beta.php | 259 ++ .../Statistical/Distributions/Binomial.php | 201 ++ .../Statistical/Distributions/ChiSquared.php | 301 ++ .../Statistical/Distributions/Exponential.php | 49 + .../Statistical/Distributions/F.php | 58 + .../Statistical/Distributions/Fisher.php | 63 + .../Statistical/Distributions/Gamma.php | 129 + .../Statistical/Distributions/GammaBase.php | 381 +++ .../Distributions/HyperGeometric.php | 56 + .../Statistical/Distributions/LogNormal.php | 121 + .../Distributions/NewtonRaphson.php | 62 + .../Statistical/Distributions/Normal.php | 168 + .../Statistical/Distributions/Poisson.php | 55 + .../Distributions/StandardNormal.php | 90 + .../Statistical/Distributions/StudentT.php | 127 + .../Statistical/Distributions/Weibull.php | 51 + .../Calculation/Statistical/MaxMinBase.php | 17 + .../Calculation/Statistical/Maximum.php | 78 + .../Calculation/Statistical/Minimum.php | 78 + .../Calculation/Statistical/Percentiles.php | 207 ++ .../Calculation/Statistical/Permutations.php | 75 + .../Statistical/StandardDeviations.php | 95 + .../Calculation/Statistical/Trends.php | 423 +++ .../Calculation/Statistical/VarianceBase.php | 28 + .../Calculation/Statistical/Variances.php | 185 + src/PhpSpreadsheet/Calculation/TextData.php | 518 +-- .../Calculation/TextData/CaseConvert.php | 64 + .../Calculation/TextData/CharacterConvert.php | 69 + .../Calculation/TextData/Concatenate.php | 82 + .../Calculation/TextData/Extract.php | 77 + .../Calculation/TextData/Format.php | 196 ++ .../Calculation/TextData/Replace.php | 64 + .../Calculation/TextData/Search.php | 80 + .../Calculation/TextData/Text.php | 59 + .../Calculation/TextData/Trim.php | 58 + src/PhpSpreadsheet/Calculation/Web.php | 53 + .../Calculation/functionlist.txt | 6 + src/PhpSpreadsheet/Cell/AddressHelper.php | 134 + .../Cell/AdvancedValueBinder.php | 151 +- src/PhpSpreadsheet/Cell/Cell.php | 6 + src/PhpSpreadsheet/Cell/Coordinate.php | 94 +- .../Cell/DefaultValueBinder.php | 30 +- src/PhpSpreadsheet/Chart/Axis.php | 18 +- src/PhpSpreadsheet/Chart/Chart.php | 2 +- src/PhpSpreadsheet/Chart/GridLines.php | 6 +- src/PhpSpreadsheet/Chart/PlotArea.php | 4 +- src/PhpSpreadsheet/Chart/Renderer/JpGraph.php | 8 +- .../Chart/Renderer/Polyfill.php | 9 - src/PhpSpreadsheet/DefinedName.php | 263 ++ src/PhpSpreadsheet/Document/Properties.php | 238 +- src/PhpSpreadsheet/HashTable.php | 19 +- src/PhpSpreadsheet/Helper/Html.php | 6 +- src/PhpSpreadsheet/Helper/Sample.php | 13 +- src/PhpSpreadsheet/IOFactory.php | 2 +- src/PhpSpreadsheet/NamedFormula.php | 45 + src/PhpSpreadsheet/NamedRange.php | 232 +- src/PhpSpreadsheet/Reader/BaseReader.php | 23 +- src/PhpSpreadsheet/Reader/Csv.php | 191 +- src/PhpSpreadsheet/Reader/Csv/Delimiter.php | 144 + src/PhpSpreadsheet/Reader/Gnumeric.php | 1101 +++--- .../Reader/Gnumeric/PageSetup.php | 143 + src/PhpSpreadsheet/Reader/Html.php | 649 ++-- src/PhpSpreadsheet/Reader/Ods.php | 269 +- .../Reader/Ods/PageSettings.php | 139 + src/PhpSpreadsheet/Reader/Ods/Properties.php | 6 +- .../Reader/Security/XmlScanner.php | 4 +- src/PhpSpreadsheet/Reader/Slk.php | 608 ++-- src/PhpSpreadsheet/Reader/Xls.php | 209 +- src/PhpSpreadsheet/Reader/Xls/Color.php | 2 +- src/PhpSpreadsheet/Reader/Xls/MD5.php | 27 +- src/PhpSpreadsheet/Reader/Xlsx.php | 186 +- src/PhpSpreadsheet/Reader/Xlsx/Chart.php | 67 +- .../Reader/Xlsx/ColumnAndRowAttributes.php | 12 +- .../Reader/Xlsx/ConditionalStyles.php | 85 +- src/PhpSpreadsheet/Reader/Xlsx/Hyperlinks.php | 6 +- src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php | 33 +- .../Reader/Xlsx/SheetViewOptions.php | 30 +- src/PhpSpreadsheet/Reader/Xlsx/Styles.php | 20 +- src/PhpSpreadsheet/Reader/Xml.php | 439 +-- .../Reader/Xml/PageSettings.php | 130 + src/PhpSpreadsheet/ReferenceHelper.php | 205 +- src/PhpSpreadsheet/RichText/ITextElement.php | 2 +- src/PhpSpreadsheet/RichText/TextElement.php | 2 +- src/PhpSpreadsheet/Settings.php | 59 + src/PhpSpreadsheet/Shared/CodePage.php | 191 +- src/PhpSpreadsheet/Shared/Date.php | 67 +- src/PhpSpreadsheet/Shared/Drawing.php | 10 +- src/PhpSpreadsheet/Shared/Font.php | 64 +- .../Shared/JAMA/CholeskyDecomposition.php | 4 +- .../Shared/JAMA/EigenvalueDecomposition.php | 21 +- .../Shared/JAMA/LUDecomposition.php | 6 +- src/PhpSpreadsheet/Shared/JAMA/Matrix.php | 13 +- .../Shared/JAMA/QRDecomposition.php | 68 +- .../JAMA/SingularValueDecomposition.php | 3 +- .../Shared/JAMA/utils/Maths.php | 1 + src/PhpSpreadsheet/Shared/OLE.php | 85 +- .../Shared/OLE/ChainedBlockStream.php | 6 +- src/PhpSpreadsheet/Shared/OLE/PPS.php | 17 +- src/PhpSpreadsheet/Shared/OLE/PPS/Root.php | 32 +- src/PhpSpreadsheet/Shared/OLERead.php | 10 +- src/PhpSpreadsheet/Shared/PasswordHasher.php | 69 +- src/PhpSpreadsheet/Shared/StringHelper.php | 4 +- src/PhpSpreadsheet/Shared/TimeZone.php | 6 +- src/PhpSpreadsheet/Shared/Trend/BestFit.php | 65 +- .../Shared/Trend/ExponentialBestFit.php | 21 +- .../Shared/Trend/LinearBestFit.php | 5 +- .../Shared/Trend/LogarithmicBestFit.php | 23 +- .../Shared/Trend/PolynomialBestFit.php | 3 +- .../Shared/Trend/PowerBestFit.php | 35 +- src/PhpSpreadsheet/Shared/Trend/Trend.php | 13 +- src/PhpSpreadsheet/Shared/XMLWriter.php | 2 +- src/PhpSpreadsheet/Shared/Xls.php | 3 +- src/PhpSpreadsheet/Spreadsheet.php | 229 +- src/PhpSpreadsheet/Style/Alignment.php | 31 +- src/PhpSpreadsheet/Style/Border.php | 36 +- src/PhpSpreadsheet/Style/Borders.php | 28 +- src/PhpSpreadsheet/Style/Color.php | 54 +- src/PhpSpreadsheet/Style/Conditional.php | 31 +- .../ConditionalDataBar.php | 102 + .../ConditionalDataBarExtension.php | 290 ++ .../ConditionalFormatValueObject.php | 78 + .../ConditionalFormattingRuleExtension.php | 195 ++ src/PhpSpreadsheet/Style/Fill.php | 11 + src/PhpSpreadsheet/Style/Font.php | 38 +- src/PhpSpreadsheet/Style/NumberFormat.php | 478 +-- .../Style/NumberFormat/BaseFormatter.php | 12 + .../Style/NumberFormat/DateFormatter.php | 129 + .../Style/NumberFormat/Formatter.php | 162 + .../Style/NumberFormat/FractionFormatter.php | 45 + .../Style/NumberFormat/NumberFormatter.php | 182 + .../NumberFormat/PercentageFormatter.php | 42 + src/PhpSpreadsheet/Style/Protection.php | 9 + src/PhpSpreadsheet/Style/Style.php | 155 +- src/PhpSpreadsheet/Style/Supervisor.php | 41 + src/PhpSpreadsheet/Worksheet/AutoFilter.php | 53 +- .../Worksheet/AutoFilter/Column/Rule.php | 12 +- src/PhpSpreadsheet/Worksheet/BaseDrawing.php | 2 +- src/PhpSpreadsheet/Worksheet/CellIterator.php | 8 +- src/PhpSpreadsheet/Worksheet/Column.php | 4 +- .../Worksheet/ColumnCellIterator.php | 54 +- .../Worksheet/ColumnDimension.php | 30 +- .../Worksheet/ColumnIterator.php | 22 +- src/PhpSpreadsheet/Worksheet/Dimension.php | 38 +- src/PhpSpreadsheet/Worksheet/Iterator.php | 8 +- .../Worksheet/MemoryDrawing.php | 8 +- src/PhpSpreadsheet/Worksheet/PageMargins.php | 30 + src/PhpSpreadsheet/Worksheet/PageSetup.php | 24 +- src/PhpSpreadsheet/Worksheet/Protection.php | 111 +- src/PhpSpreadsheet/Worksheet/Row.php | 8 +- .../Worksheet/RowCellIterator.php | 33 +- src/PhpSpreadsheet/Worksheet/RowDimension.php | 24 +- src/PhpSpreadsheet/Worksheet/RowIterator.php | 18 +- src/PhpSpreadsheet/Worksheet/Worksheet.php | 153 +- src/PhpSpreadsheet/Writer/Csv.php | 42 +- src/PhpSpreadsheet/Writer/Html.php | 85 +- src/PhpSpreadsheet/Writer/Ods.php | 120 +- src/PhpSpreadsheet/Writer/Ods/Cell/Style.php | 178 + src/PhpSpreadsheet/Writer/Ods/Content.php | 124 +- src/PhpSpreadsheet/Writer/Ods/Formula.php | 119 + src/PhpSpreadsheet/Writer/Ods/Meta.php | 9 +- src/PhpSpreadsheet/Writer/Ods/MetaInf.php | 2 +- src/PhpSpreadsheet/Writer/Ods/Mimetype.php | 6 +- .../Writer/Ods/NamedExpressions.php | 128 + src/PhpSpreadsheet/Writer/Ods/Settings.php | 56 +- src/PhpSpreadsheet/Writer/Ods/Styles.php | 5 +- src/PhpSpreadsheet/Writer/Ods/Thumbnails.php | 6 +- src/PhpSpreadsheet/Writer/Ods/WriterPart.php | 2 + src/PhpSpreadsheet/Writer/Pdf/Dompdf.php | 2 +- src/PhpSpreadsheet/Writer/Pdf/Mpdf.php | 2 +- src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php | 2 +- src/PhpSpreadsheet/Writer/Xls.php | 351 +- src/PhpSpreadsheet/Writer/Xls/Escher.php | 8 +- src/PhpSpreadsheet/Writer/Xls/Parser.php | 95 +- src/PhpSpreadsheet/Writer/Xls/Workbook.php | 91 +- src/PhpSpreadsheet/Writer/Xls/Worksheet.php | 218 +- src/PhpSpreadsheet/Writer/Xls/Xf.php | 160 +- src/PhpSpreadsheet/Writer/Xlsx.php | 649 ++-- src/PhpSpreadsheet/Writer/Xlsx/Chart.php | 13 +- src/PhpSpreadsheet/Writer/Xlsx/Comments.php | 5 +- .../Writer/Xlsx/DefinedNames.php | 228 ++ src/PhpSpreadsheet/Writer/Xlsx/Drawing.php | 17 +- src/PhpSpreadsheet/Writer/Xlsx/Rels.php | 21 +- .../Writer/Xlsx/StringTable.php | 12 +- src/PhpSpreadsheet/Writer/Xlsx/Style.php | 24 +- src/PhpSpreadsheet/Writer/Xlsx/Theme.php | 14 +- src/PhpSpreadsheet/Writer/Xlsx/Workbook.php | 185 +- src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php | 214 +- src/PhpSpreadsheet/Writer/Xlsx/Xlfn.php | 7 + .../Calculation/CalculationTest.php | 23 +- .../DefinedNameConfusedForCellTest.php | 1 + .../DefinedNamesCalculationTest.php | 98 + .../Calculation/Engine/RangeTest.php | 170 + .../Calculation/FinancialTest.php | 628 ---- .../Calculation/FormulaAsStringTest.php | 4 +- .../Functions/Database/DAverageTest.php | 110 + .../Functions/Database/DCountATest.php | 103 + .../Functions/Database/DCountTest.php | 136 + .../Functions/Database/DGetTest.php | 115 + .../Functions/Database/DMaxTest.php | 105 + .../Functions/Database/DMinTest.php | 101 + .../Functions/Database/DProductTest.php | 102 + .../Functions/Database/DStDevPTest.php | 101 + .../Functions/Database/DStDevTest.php | 101 + .../Functions/Database/DSumTest.php | 123 + .../Functions/Database/DVarPTest.php | 101 + .../Functions/Database/DVarTest.php | 101 + .../Functions/DateTime/AllSetupTeardown.php | 71 + .../Functions/DateTime/DateDifTest.php | 26 +- .../Functions/DateTime/DateTest.php | 46 +- .../Functions/DateTime/DateValueTest.php | 55 +- .../Functions/DateTime/DayTest.php | 49 +- .../Functions/DateTime/Days360Test.php | 27 +- .../Functions/DateTime/DaysTest.php | 43 +- .../Functions/DateTime/EDateTest.php | 33 +- .../Functions/DateTime/EoMonthTest.php | 36 +- .../Functions/DateTime/HourTest.php | 24 +- .../Functions/DateTime/IsoWeekNumTest.php | 44 +- .../Functions/DateTime/MinuteTest.php | 24 +- .../Functions/DateTime/MonthTest.php | 24 +- .../Functions/DateTime/MovedFunctionsTest.php | 63 + .../Functions/DateTime/NetworkDaysTest.php | 50 +- .../Functions/DateTime/NowTest.php | 34 + .../Functions/DateTime/SecondTest.php | 24 +- .../Functions/DateTime/TimeTest.php | 46 +- .../Functions/DateTime/TimeValueTest.php | 30 +- .../Functions/DateTime/TodayTest.php | 34 + .../Functions/DateTime/WeekDayTest.php | 31 +- .../Functions/DateTime/WeekNumTest.php | 43 +- .../Functions/DateTime/WorkDayTest.php | 50 +- .../Functions/DateTime/YearFracTest.php | 42 +- .../Functions/DateTime/YearTest.php | 24 +- .../Functions/Engineering/BesselITest.php | 2 +- .../Functions/Engineering/BesselKTest.php | 2 +- .../Functions/Engineering/BesselYTest.php | 2 +- .../Functions/Engineering/Bin2DecTest.php | 67 +- .../Functions/Engineering/Bin2HexTest.php | 67 +- .../Functions/Engineering/Bin2OctTest.php | 67 +- .../Functions/Engineering/BitAndTest.php | 21 +- .../Functions/Engineering/BitLShiftTest.php | 21 +- .../Functions/Engineering/BitOrTest.php | 21 +- .../Functions/Engineering/BitRShiftTest.php | 21 +- .../Functions/Engineering/BitXorTest.php | 21 +- .../Functions/Engineering/ConvertUoMTest.php | 6 + .../Functions/Engineering/Dec2BinTest.php | 67 +- .../Functions/Engineering/Dec2HexTest.php | 73 +- .../Functions/Engineering/Dec2OctTest.php | 67 +- .../Functions/Engineering/Hex2BinTest.php | 70 +- .../Functions/Engineering/Hex2DecTest.php | 70 +- .../Functions/Engineering/Hex2OctTest.php | 67 +- .../Engineering/MovedBitwiseTest.php | 24 + .../Engineering/MovedFunctionsTest.php | 31 + .../Functions/Engineering/Oct2BinTest.php | 67 +- .../Functions/Engineering/Oct2DecTest.php | 67 +- .../Functions/Engineering/Oct2HexTest.php | 67 +- .../Functions/Financial/AccrintMTest.php | 2 +- .../Functions/Financial/AccrintTest.php | 2 +- .../Functions/Financial/AmorDegRcTest.php | 31 + .../Functions/Financial/AmorLincTest.php | 31 + .../Functions/Financial/CoupDayBsTest.php | 31 + .../Functions/Financial/CoupDaysNcTest.php | 31 + .../Functions/Financial/CoupDaysTest.php | 31 + .../Functions/Financial/CoupNcdTest.php | 31 + .../Functions/Financial/CoupNumTest.php | 31 + .../Functions/Financial/CoupPcdTest.php | 31 + .../Functions/Financial/CumIpmtTest.php | 31 + .../Functions/Financial/CumPrincTest.php | 31 + .../Functions/Financial/DbTest.php | 31 + .../Functions/Financial/DdbTest.php | 31 + .../Functions/Financial/DiscTest.php | 31 + .../Functions/Financial/DollarDeTest.php | 31 + .../Functions/Financial/DollarFrTest.php | 31 + .../Functions/Financial/EffectTest.php | 33 + .../Functions/Financial/FvScheduleTest.php | 31 + .../Functions/Financial/FvTest.php | 31 + .../Functions/Financial/HelpersTest.php | 27 + .../Functions/Financial/IPmtTest.php | 31 + .../Functions/Financial/IntRateTest.php | 31 + .../Functions/Financial/IrrTest.php | 31 + .../Functions/Financial/IsPmtTest.php | 31 + .../Functions/Financial/MirrTest.php | 31 + .../Functions/Financial/NPerTest.php | 31 + .../Functions/Financial/NominalTest.php | 33 + .../Functions/Financial/NpvTest.php | 31 + .../Functions/Financial/PDurationTest.php | 31 + .../Functions/Financial/PriceDiscTest.php | 31 + .../Functions/Financial/PriceMatTest.php | 31 + .../Functions/Financial/PriceTest.php | 50 + .../Functions/Financial/PvTest.php | 31 + .../Functions/Financial/RateTest.php | 31 + .../Functions/Financial/ReceivedTest.php | 31 + .../Functions/Financial/RriTest.php | 31 + .../Functions/Financial/SlnTest.php | 31 + .../Functions/Financial/SydTest.php | 31 + .../Functions/Financial/TBillEqTest.php | 31 + .../Functions/Financial/TBillPriceTest.php | 31 + .../Functions/Financial/TBillYieldTest.php | 31 + .../Functions/Financial/XNpvTest.php | 40 + .../Functions/Financial/XirrTest.php | 40 + .../Functions/Financial/YieldDiscTest.php | 31 + .../Functions/Financial/YieldMatTest.php | 31 + .../Functions/Logical/IfErrorTest.php | 4 +- .../Functions/Logical/IfNaTest.php | 4 +- .../Calculation/Functions/Logical/IfsTest.php | 32 + .../Functions/LookupRef/AddressTest.php | 31 + .../Functions/LookupRef/ColumnTest.php | 46 + .../Functions/LookupRef/IndexTest.php | 1 + .../Functions/LookupRef/IndirectTest.php | 55 + .../Functions/LookupRef/OffsetTest.php | 32 + .../Functions/LookupRef/RowTest.php | 46 + .../Functions/LookupRef/TransposeTest.php | 32 + .../Functions/MathTrig/AbsTest.php | 31 + .../Functions/MathTrig/AcosTest.php | 26 + .../Functions/MathTrig/AcoshTest.php | 26 + .../Functions/MathTrig/AcotTest.php | 22 +- .../Functions/MathTrig/AcothTest.php | 22 +- .../Functions/MathTrig/AllSetupTeardown.php | 67 + .../Functions/MathTrig/ArabicTest.php | 19 +- .../Functions/MathTrig/AsinTest.php | 26 + .../Functions/MathTrig/AsinhTest.php | 26 + .../Functions/MathTrig/Atan2Test.php | 24 +- .../Functions/MathTrig/AtanTest.php | 26 + .../Functions/MathTrig/AtanhTest.php | 26 + .../Functions/MathTrig/BaseTest.php | 38 +- .../Functions/MathTrig/CeilingMathTest.php | 30 + .../Functions/MathTrig/CeilingPreciseTest.php | 30 + .../Functions/MathTrig/CeilingTest.php | 50 +- .../Functions/MathTrig/CombinATest.php | 33 + .../Functions/MathTrig/CombinTest.php | 28 +- .../Functions/MathTrig/CosTest.php | 26 + .../Functions/MathTrig/CoshTest.php | 26 + .../Functions/MathTrig/CotTest.php | 22 +- .../Functions/MathTrig/CothTest.php | 22 +- .../Functions/MathTrig/CscTest.php | 22 +- .../Functions/MathTrig/CschTest.php | 22 +- .../Functions/MathTrig/DegreesTest.php | 31 + .../Functions/MathTrig/EvenTest.php | 20 +- .../Functions/MathTrig/ExpTest.php | 33 + .../Functions/MathTrig/FactDoubleTest.php | 21 +- .../Functions/MathTrig/FactTest.php | 57 +- .../Functions/MathTrig/FloorMathTest.php | 23 +- .../Functions/MathTrig/FloorPreciseTest.php | 23 +- .../Functions/MathTrig/FloorTest.php | 50 +- .../Functions/MathTrig/GcdTest.php | 27 +- .../Functions/MathTrig/IntTest.php | 26 +- .../Functions/MathTrig/LcmTest.php | 20 +- .../Calculation/Functions/MathTrig/LnTest.php | 33 + .../Functions/MathTrig/Log10Test.php | 33 + .../Functions/MathTrig/LogTest.php | 32 +- .../Functions/MathTrig/MInverseTest.php | 21 +- .../Functions/MathTrig/MMultTest.php | 30 +- .../Functions/MathTrig/MRoundTest.php | 23 +- .../Functions/MathTrig/MUnitTest.php | 23 + .../Functions/MathTrig/MdeTermTest.php | 26 +- .../Functions/MathTrig/ModTest.php | 32 +- .../Functions/MathTrig/MovedFunctionsTest.php | 111 + .../Functions/MathTrig/MultinomialTest.php | 25 +- .../Functions/MathTrig/OddTest.php | 20 +- .../Functions/MathTrig/PowerTest.php | 32 +- .../Functions/MathTrig/ProductTest.php | 20 +- .../Functions/MathTrig/QuotientTest.php | 34 +- .../Functions/MathTrig/RadiansTest.php | 31 + .../Functions/MathTrig/RandBetweenTest.php | 46 + .../Functions/MathTrig/RandTest.php | 24 + .../Functions/MathTrig/RomanTest.php | 20 +- .../Functions/MathTrig/RoundDownTest.php | 27 +- .../Functions/MathTrig/RoundTest.php | 30 + .../Functions/MathTrig/RoundUpTest.php | 27 +- .../Functions/MathTrig/SecTest.php | 22 +- .../Functions/MathTrig/SechTest.php | 22 +- .../Functions/MathTrig/SeriesSumTest.php | 35 +- .../Functions/MathTrig/SignTest.php | 23 +- .../Functions/MathTrig/SinTest.php | 26 + .../Functions/MathTrig/SinhTest.php | 26 + .../Functions/MathTrig/SqrtPiTest.php | 27 +- .../Functions/MathTrig/SqrtTest.php | 31 + .../Functions/MathTrig/SubTotalTest.php | 250 +- .../Functions/MathTrig/SumIfTest.php | 35 +- .../Functions/MathTrig/SumIfsTest.php | 13 +- .../Functions/MathTrig/SumProductTest.php | 28 +- .../Functions/MathTrig/SumSqTest.php | 25 +- .../Functions/MathTrig/SumTest.php | 29 + .../Functions/MathTrig/SumX2MY2Test.php | 30 +- .../Functions/MathTrig/SumX2PY2Test.php | 30 +- .../Functions/MathTrig/SumXMY2Test.php | 30 +- .../Functions/MathTrig/TanTest.php | 26 + .../Functions/MathTrig/TanhTest.php | 26 + .../Functions/MathTrig/TruncTest.php | 23 +- .../Functions/Statistical/AverageIfsTest.php | 31 + .../Statistical/BinomDistRangeTest.php | 31 + .../Functions/Statistical/BinomInvTest.php | 31 + .../Statistical/ChiDistLeftTailTest.php | 31 + ...iDistTest.php => ChiDistRightTailTest.php} | 4 +- .../Statistical/ChiInvLeftTailTest.php | 37 + .../Statistical/ChiInvRightTailTest.php | 37 + .../Functions/Statistical/ChiTestTest.php | 27 + .../Functions/Statistical/CorrelTest.php | 4 +- .../Functions/Statistical/CountTest.php | 8 + .../Functions/Statistical/FDistTest.php | 25 + .../Functions/Statistical/FisherInvTest.php | 2 +- .../Functions/Statistical/FisherTest.php | 2 +- .../Functions/Statistical/GammaLnTest.php | 2 +- .../Functions/Statistical/GammaTest.php | 26 + .../Functions/Statistical/GaussTest.php | 26 + .../Functions/Statistical/GrowthTest.php | 33 + .../Functions/Statistical/HypGeomDistTest.php | 25 + .../Functions/Statistical/KurtTest.php | 25 + .../Functions/Statistical/LargeTest.php | 27 + .../Functions/Statistical/LinEstTest.php | 33 + .../Functions/Statistical/LogEstTest.php | 33 + .../Functions/Statistical/LogInvTest.php | 25 + .../Statistical/LogNormDist2Test.php | 25 + .../Functions/Statistical/LogNormDistTest.php | 25 + .../Functions/Statistical/MaxATest.php | 25 + .../Functions/Statistical/MaxTest.php | 25 + .../Functions/Statistical/MinATest.php | 25 + .../Functions/Statistical/MinTest.php | 25 + .../Statistical/NegBinomDistTest.php | 31 + .../Functions/Statistical/NormDistTest.php | 25 + .../Functions/Statistical/NormInvTest.php | 25 + .../Functions/Statistical/NormSDist2Test.php | 25 + .../Functions/Statistical/NormSDistTest.php | 26 + .../Functions/Statistical/NormSInvTest.php | 26 + .../Functions/Statistical/PercentRankTest.php | 34 + .../Functions/Statistical/PercentileTest.php | 31 + .../Statistical/PermutationATest.php | 31 + .../Functions/Statistical/PoissonTest.php | 31 + .../Functions/Statistical/QuartileTest.php | 31 + .../Functions/Statistical/RankTest.php | 34 + .../{ChiInvTest.php => SkewTest.php} | 12 +- .../Functions/Statistical/SmallTest.php | 27 + .../Functions/Statistical/StDevATest.php | 51 + .../Functions/Statistical/StDevPATest.php | 51 + .../Functions/Statistical/StDevPTest.php | 51 + .../Functions/Statistical/StDevTest.php | 51 + .../Functions/Statistical/StandardizeTest.php | 25 + .../Functions/Statistical/TDistTest.php | 28 + .../Functions/Statistical/TinvTest.php | 27 + .../Functions/Statistical/TrendTest.php | 33 + .../Functions/Statistical/TrimMeanTest.php | 32 + .../Functions/Statistical/VarATest.php | 51 + .../Functions/Statistical/VarPATest.php | 51 + .../Functions/Statistical/VarPTest.php | 51 + .../Functions/Statistical/VarTest.php | 51 + .../Functions/Statistical/WeibullTest.php | 29 + .../Functions/Statistical/ZTestTest.php | 28 + .../Functions/TextData/CharTest.php | 20 +- .../Functions/TextData/CleanTest.php | 20 +- .../Functions/TextData/CodeTest.php | 20 +- .../Functions/TextData/ConcatenateTest.php | 18 - .../Functions/TextData/DollarTest.php | 18 - .../Functions/TextData/ExactTest.php | 22 - .../Functions/TextData/FindTest.php | 18 - .../Functions/TextData/FixedTest.php | 18 - .../Functions/TextData/LeftTest.php | 52 +- .../Functions/TextData/LenTest.php | 20 +- .../Functions/TextData/LowerTest.php | 53 +- .../Functions/TextData/MidTest.php | 53 +- .../Functions/TextData/NumberValueTest.php | 18 - .../Functions/TextData/ProperTest.php | 53 +- .../Functions/TextData/ReplaceTest.php | 18 - .../Functions/TextData/ReptTest.php | 58 + .../Functions/TextData/RightTest.php | 52 +- .../Functions/TextData/SearchTest.php | 18 - .../Functions/TextData/SubstituteTest.php | 18 - .../Calculation/Functions/TextData/TTest.php | 20 +- .../Functions/TextData/TextJoinTest.php | 18 - .../Functions/TextData/TextTest.php | 23 - .../Functions/TextData/TrimTest.php | 20 +- .../Functions/TextData/UpperTest.php | 53 +- .../Functions/TextData/ValueTest.php | 23 +- .../Functions/Web/WebServiceTest.php | 77 + .../Calculation/FunctionsTest.php | 10 +- .../Calculation/LookupRefTest.php | 6 + .../Calculation/TranslationTest.php | 51 + .../Cell/AddressHelperTest.php | 112 + .../Cell/AdvancedValueBinderTest.php | 249 +- tests/PhpSpreadsheetTests/Cell/CellTest.php | 19 + .../Cell/CoordinateTest.php | 84 +- .../Cell/DefaultValueBinderTest.php | 9 +- ...ueBinderWithOverriddenDataTypeForValue.php | 4 +- .../Collection/CellsTest.php | 2 +- .../DefinedNameFormulaTest.php | 188 ++ tests/PhpSpreadsheetTests/DefinedNameTest.php | 205 ++ .../Document/PropertiesTest.php | 183 + .../DocumentGeneratorTest.php | 15 + .../Functional/ActiveSheetTest.php | 7 + .../Functional/ColumnWidthTest.php | 2 - .../Functional/CommentsTest.php | 17 +- .../Functional/PrintAreaTest.php | 13 +- .../Functional/ReadFilterTest.php | 10 +- .../Functional/StreamTest.php | 13 +- .../Helper/SampleCoverageTest.php | 39 + .../PhpSpreadsheetTests/Helper/SampleTest.php | 16 +- tests/PhpSpreadsheetTests/IOFactoryTest.php | 46 +- .../PhpSpreadsheetTests/NamedFormulaTest.php | 126 + tests/PhpSpreadsheetTests/NamedRangeTest.php | 126 + .../Reader/CsvContiguousTest.php | 8 +- tests/PhpSpreadsheetTests/Reader/CsvTest.php | 99 +- .../Reader/Gnumeric/GnumericFilter.php | 14 + .../Reader/Gnumeric/GnumericInfoTest.php | 47 + .../Reader/Gnumeric/GnumericLoadTest.php | 162 + .../Reader/Gnumeric/GnumericStylesTest.php | 269 ++ .../Reader/Gnumeric/PageSetupTest.php | 147 + .../Reader/Html/HtmlBorderTest.php | 110 + .../Reader/Html/HtmlHelper.php | 28 + .../Reader/Html/HtmlImageTest.php | 84 + .../Reader/Html/HtmlLoadStringTest.php | 121 + .../Reader/Html/HtmlTagsTest.php | 236 ++ .../Reader/{ => Html}/HtmlTest.php | 281 +- .../Reader/Ods/OdsInfoTest.php | 110 + .../Reader/{ => Ods}/OdsTest.php | 90 +- .../Reader/Ods/PageSetupBug1772Test.php | 98 + .../Reader/Ods/PageSetupTest.php | 149 + .../Reader/Security/XmlScannerTest.php | 27 +- .../Reader/SheetsXlsxChartTest.php | 51 + tests/PhpSpreadsheetTests/Reader/SlkTest.php | 158 + .../Reader/Xls/PageSetupTest.php | 149 + tests/PhpSpreadsheetTests/Reader/XlsTest.php | 65 +- .../Reader/Xlsx/ChartsTitleTest.php | 64 + .../ConditionalFormattingDataBarXlsxTest.php | 325 ++ .../Reader/Xlsx/NamedRangeTest.php | 22 + .../Reader/Xlsx/PageSetupTest.php | 149 + .../PhpSpreadsheetTests/Reader/Xlsx2Test.php | 10 +- tests/PhpSpreadsheetTests/Reader/XlsxTest.php | 21 +- .../Reader/Xml/PageSetupTest.php | 149 + .../Reader/Xml/XmlFilter.php | 14 + .../Reader/Xml/XmlInfoTest.php | 75 + .../Reader/Xml/XmlLoadTest.php | 99 + .../Reader/Xml/XmlOddTest.php | 70 + .../Reader/Xml/XmlStyleCoverageTest.php | 113 + .../Reader/Xml/XmlStylesTest.php | 135 + .../Reader/{ => Xml}/XmlTest.php | 18 +- .../ReferenceHelperTest.php | 34 + tests/PhpSpreadsheetTests/SettingsTest.php | 22 +- .../Shared/CodePageTest.php | 21 +- tests/PhpSpreadsheetTests/Shared/DateTest.php | 104 +- tests/PhpSpreadsheetTests/Shared/FontTest.php | 28 +- .../Shared/StringHelperTest.php | 16 + .../Shared/TimeZoneTest.php | 58 +- .../Shared/Trend/ExponentialBestFitTest.php | 49 + .../Shared/Trend/LinearBestFitTest.php | 49 + tests/PhpSpreadsheetTests/SpreadsheetTest.php | 146 +- .../Style/AlignmentTest.php | 94 + .../PhpSpreadsheetTests/Style/BorderTest.php | 223 +- tests/PhpSpreadsheetTests/Style/ColorTest.php | 32 +- .../Style/ConditionalTest.php | 52 + .../Style/ExportArrayTest.php | 161 + tests/PhpSpreadsheetTests/Style/FillTest.php | 22 + tests/PhpSpreadsheetTests/Style/FontTest.php | 42 + .../Style/NumberFormatBuiltinTest.php | 35 + .../Style/NumberFormatTest.php | 16 + tests/PhpSpreadsheetTests/Style/StyleTest.php | 160 + .../Worksheet/ColumnCellIterator2Test.php | 77 + .../Worksheet/ColumnCellIteratorTest.php | 12 +- .../Worksheet/ColumnIteratorTest.php | 24 + .../Worksheet/PageMarginsTest.php | 89 + .../Worksheet/ProtectionTest.php | 39 + .../Worksheet/RowCellIterator2Test.php | 77 + .../Worksheet/RowCellIteratorTest.php | 15 +- .../Worksheet/RowIteratorTest.php | 24 + .../Worksheet/WorksheetNamedRangesTest.php | 143 + .../Writer/Csv/CsvEnclosureTest.php | 216 ++ .../Writer/Csv/CsvWriteTest.php | 4 +- .../Writer/Html/CallbackTest.php | 53 + .../Html/ExtendForChartsAndImagesTest.php | 94 + .../Writer/Html/HtmlNumberFormatTest.php | 5 + .../Writer/Html/ImagesRootTest.php | 14 +- .../Writer/Html/InvalidFileNameTest.php | 13 +- .../Writer/Html/XssVulnerabilityTest.php | 90 + .../Writer/Ods/ContentTest.php | 4 + .../Writer/RetainSelectedCellsTest.php | 77 + .../Writer/Xls/FormulaErrTest.php | 11 +- .../Writer/Xls/WorkbookTest.php | 2 +- .../Writer/Xls/XlsGifBmpTest.php | 84 + .../Writer/Xlsx/DrawingsTest.php | 78 + .../Writer/Xlsx/FloatsRetainedTest.php | 1 + .../Writer/Xlsx/LocaleFloatsTest.php | 1 + .../Writer/Xlsx/StartsWithHashTest.php | 62 + .../Writer/Xlsx/UnparsedDataCloneTest.php | 103 + tests/bootstrap.php | 2 +- tests/data/Calculation/DateTime/DATE.php | 393 +-- tests/data/Calculation/DateTime/DATEDIF.php | 516 +-- tests/data/Calculation/DateTime/DATEVALUE.php | 368 +- tests/data/Calculation/DateTime/DAY.php | 66 +- .../Calculation/DateTime/DAYOpenOffice.php | 19 + tests/data/Calculation/DateTime/DAYS.php | 97 +- tests/data/Calculation/DateTime/DAYS360.php | 174 +- tests/data/Calculation/DateTime/EDATE.php | 80 +- tests/data/Calculation/DateTime/EOMONTH.php | 92 +- tests/data/Calculation/DateTime/HOUR.php | 65 +- .../data/Calculation/DateTime/ISOWEEKNUM.php | 63 +- .../Calculation/DateTime/ISOWEEKNUM1904.php | 33 + tests/data/Calculation/DateTime/MINUTE.php | 65 +- tests/data/Calculation/DateTime/MONTH.php | 68 +- .../data/Calculation/DateTime/NETWORKDAYS.php | 31 +- tests/data/Calculation/DateTime/SECOND.php | 65 +- tests/data/Calculation/DateTime/TIME.php | 112 +- tests/data/Calculation/DateTime/TIMEVALUE.php | 70 +- tests/data/Calculation/DateTime/WEEKDAY.php | 149 +- tests/data/Calculation/DateTime/WEEKNUM.php | 248 +- .../data/Calculation/DateTime/WEEKNUM1904.php | 92 + tests/data/Calculation/DateTime/WORKDAY.php | 44 +- tests/data/Calculation/DateTime/YEAR.php | 62 +- tests/data/Calculation/DateTime/YEARFRAC.php | 53 +- .../DefinedNames/NamedFormulae.xlsx | Bin 0 -> 9216 bytes .../Calculation/DefinedNames/NamedRanges.xlsx | Bin 0 -> 9221 bytes .../data/Calculation/Engineering/BESSELI.php | 286 +- .../data/Calculation/Engineering/BESSELJ.php | 330 +- .../data/Calculation/Engineering/BESSELK.php | 206 +- .../data/Calculation/Engineering/BESSELY.php | 193 +- .../data/Calculation/Engineering/BIN2DEC.php | 67 +- .../data/Calculation/Engineering/BIN2HEX.php | 101 +- .../data/Calculation/Engineering/BIN2OCT.php | 106 +- tests/data/Calculation/Engineering/BITAND.php | 45 +- .../Calculation/Engineering/BITLSHIFT.php | 46 +- tests/data/Calculation/Engineering/BITOR.php | 50 +- .../Calculation/Engineering/BITRSHIFT.php | 43 +- tests/data/Calculation/Engineering/BITXOR.php | 48 +- .../data/Calculation/Engineering/COMPLEX.php | 14 +- .../Calculation/Engineering/CONVERTUOM.php | 238 +- .../data/Calculation/Engineering/DEC2BIN.php | 119 +- .../data/Calculation/Engineering/DEC2HEX.php | 104 +- .../data/Calculation/Engineering/DEC2OCT.php | 76 +- tests/data/Calculation/Engineering/DELTA.php | 5 + tests/data/Calculation/Engineering/GESTEP.php | 5 + .../data/Calculation/Engineering/HEX2BIN.php | 96 +- .../data/Calculation/Engineering/HEX2DEC.php | 86 +- .../data/Calculation/Engineering/HEX2OCT.php | 80 +- tests/data/Calculation/Engineering/IMABS.php | 6 + .../Calculation/Engineering/IMAGINARY.php | 6 + .../Calculation/Engineering/IMARGUMENT.php | 6 + .../Calculation/Engineering/IMCONJUGATE.php | 6 + tests/data/Calculation/Engineering/IMCOS.php | 6 + tests/data/Calculation/Engineering/IMCOSH.php | 6 + tests/data/Calculation/Engineering/IMCOT.php | 6 + tests/data/Calculation/Engineering/IMCSC.php | 6 + tests/data/Calculation/Engineering/IMCSCH.php | 6 + tests/data/Calculation/Engineering/IMDIV.php | 9 +- tests/data/Calculation/Engineering/IMEXP.php | 6 + tests/data/Calculation/Engineering/IMLN.php | 8 +- .../data/Calculation/Engineering/IMLOG10.php | 8 +- tests/data/Calculation/Engineering/IMLOG2.php | 8 +- .../data/Calculation/Engineering/IMPOWER.php | 9 +- .../Calculation/Engineering/IMPRODUCT.php | 9 +- tests/data/Calculation/Engineering/IMREAL.php | 6 + tests/data/Calculation/Engineering/IMSEC.php | 6 + tests/data/Calculation/Engineering/IMSECH.php | 6 + tests/data/Calculation/Engineering/IMSIN.php | 6 + tests/data/Calculation/Engineering/IMSINH.php | 6 + tests/data/Calculation/Engineering/IMSQRT.php | 6 + tests/data/Calculation/Engineering/IMSUB.php | 9 +- tests/data/Calculation/Engineering/IMSUM.php | 11 +- tests/data/Calculation/Engineering/IMTAN.php | 6 + .../data/Calculation/Engineering/OCT2BIN.php | 84 +- .../data/Calculation/Engineering/OCT2DEC.php | 53 +- .../data/Calculation/Engineering/OCT2HEX.php | 60 +- tests/data/Calculation/Financial/ACCRINT.php | 113 +- tests/data/Calculation/Financial/ACCRINTM.php | 59 +- .../data/Calculation/Financial/AMORDEGRC.php | 72 +- tests/data/Calculation/Financial/AMORLINC.php | 36 +- .../data/Calculation/Financial/COUPDAYBS.php | 163 +- tests/data/Calculation/Financial/COUPDAYS.php | 156 +- .../data/Calculation/Financial/COUPDAYSNC.php | 177 +- tests/data/Calculation/Financial/COUPNCD.php | 151 +- tests/data/Calculation/Financial/COUPNUM.php | 156 +- tests/data/Calculation/Financial/COUPPCD.php | 151 +- tests/data/Calculation/Financial/CUMIPMT.php | 9 + tests/data/Calculation/Financial/CUMPRINC.php | 9 + tests/data/Calculation/Financial/DB.php | 121 +- tests/data/Calculation/Financial/DDB.php | 113 + .../Calculation/Financial/DaysPerYear.php | 15 + tests/data/Calculation/Financial/EFFECT.php | 21 +- .../data/Calculation/Financial/FVSCHEDULE.php | 18 + tests/data/Calculation/Financial/IPMT.php | 2 +- tests/data/Calculation/Financial/IRR.php | 35 +- tests/data/Calculation/Financial/MIRR.php | 32 +- tests/data/Calculation/Financial/NOMINAL.php | 21 +- tests/data/Calculation/Financial/NPV.php | 8 +- .../data/Calculation/Financial/PDURATION.php | 36 +- .../data/Calculation/Financial/PRICEDISC.php | 32 + tests/data/Calculation/Financial/PRICEMAT.php | 44 + tests/data/Calculation/Financial/RECEIVED.php | 16 + tests/data/Calculation/Financial/RRI.php | 34 +- tests/data/Calculation/Financial/SLN.php | 34 +- tests/data/Calculation/Financial/SYD.php | 52 + tests/data/Calculation/Financial/TBILLEQ.php | 46 + .../data/Calculation/Financial/TBILLPRICE.php | 54 + .../data/Calculation/Financial/TBILLYIELD.php | 38 + .../data/Calculation/Financial/YIELDDISC.php | 36 + tests/data/Calculation/Financial/YIELDMAT.php | 40 + tests/data/Calculation/Logical/IFS.php | 50 + tests/data/Calculation/LookupRef/ADDRESS.php | 56 + tests/data/Calculation/LookupRef/COLUMN.php | 32 + tests/data/Calculation/LookupRef/HLOOKUP.php | 33 + tests/data/Calculation/LookupRef/INDEX.php | 72 +- tests/data/Calculation/LookupRef/INDIRECT.php | 16 + tests/data/Calculation/LookupRef/LOOKUP.php | 42 +- tests/data/Calculation/LookupRef/MATCH.php | 109 +- tests/data/Calculation/LookupRef/OFFSET.php | 8 + tests/data/Calculation/LookupRef/ROW.php | 35 + .../data/Calculation/LookupRef/TRANSPOSE.php | 32 + tests/data/Calculation/LookupRef/VLOOKUP.php | 29 +- tests/data/Calculation/MathTrig/ABS.php | 18 + tests/data/Calculation/MathTrig/ACOS.php | 14 + tests/data/Calculation/MathTrig/ACOSH.php | 14 + tests/data/Calculation/MathTrig/ACOT.php | 75 +- tests/data/Calculation/MathTrig/ACOTH.php | 75 +- tests/data/Calculation/MathTrig/ASIN.php | 14 + tests/data/Calculation/MathTrig/ASINH.php | 16 + tests/data/Calculation/MathTrig/ATAN.php | 16 + tests/data/Calculation/MathTrig/ATAN2.php | 84 +- tests/data/Calculation/MathTrig/ATANH.php | 15 + tests/data/Calculation/MathTrig/BASE.php | 6 + tests/data/Calculation/MathTrig/CEILING.php | 130 +- .../data/Calculation/MathTrig/CEILINGMATH.php | 32 + .../Calculation/MathTrig/CEILINGPRECISE.php | 26 + tests/data/Calculation/MathTrig/COMBIN.php | 9 + tests/data/Calculation/MathTrig/COMBINA.php | 135 + tests/data/Calculation/MathTrig/COS.php | 16 + tests/data/Calculation/MathTrig/COSH.php | 11 + tests/data/Calculation/MathTrig/COT.php | 57 +- tests/data/Calculation/MathTrig/COTH.php | 76 +- tests/data/Calculation/MathTrig/CSC.php | 57 +- tests/data/Calculation/MathTrig/CSCH.php | 75 +- tests/data/Calculation/MathTrig/DEGREES.php | 12 + tests/data/Calculation/MathTrig/EVEN.php | 88 +- tests/data/Calculation/MathTrig/EXP.php | 11 + tests/data/Calculation/MathTrig/FACT.php | 4 +- .../Calculation/MathTrig/FACTGNUMERIC.php | 44 + tests/data/Calculation/MathTrig/FLOOR.php | 73 +- tests/data/Calculation/MathTrig/FLOORMATH.php | 113 +- .../Calculation/MathTrig/FLOORPRECISE.php | 75 +- tests/data/Calculation/MathTrig/GCD.php | 4 + tests/data/Calculation/MathTrig/INT.php | 101 +- tests/data/Calculation/MathTrig/LCM.php | 7 + tests/data/Calculation/MathTrig/LN.php | 11 + tests/data/Calculation/MathTrig/LOG.php | 1 + tests/data/Calculation/MathTrig/LOG10.php | 12 + tests/data/Calculation/MathTrig/MDETERM.php | 52 +- tests/data/Calculation/MathTrig/MINVERSE.php | 72 +- tests/data/Calculation/MathTrig/MMULT.php | 22 + tests/data/Calculation/MathTrig/MOD.php | 9 +- tests/data/Calculation/MathTrig/MROUND.php | 78 +- .../data/Calculation/MathTrig/MULTINOMIAL.php | 6 + tests/data/Calculation/MathTrig/ODD.php | 68 +- tests/data/Calculation/MathTrig/POWER.php | 2 + tests/data/Calculation/MathTrig/PRODUCT.php | 2 + tests/data/Calculation/MathTrig/QUOTIENT.php | 8 + tests/data/Calculation/MathTrig/RADIANS.php | 14 + .../data/Calculation/MathTrig/RANDBETWEEN.php | 19 + tests/data/Calculation/MathTrig/ROMAN.php | 89 +- tests/data/Calculation/MathTrig/ROUND.php | 34 + tests/data/Calculation/MathTrig/ROUNDDOWN.php | 120 +- tests/data/Calculation/MathTrig/ROUNDUP.php | 120 +- tests/data/Calculation/MathTrig/SEC.php | 77 +- tests/data/Calculation/MathTrig/SECH.php | 75 +- tests/data/Calculation/MathTrig/SERIESSUM.php | 12 + tests/data/Calculation/MathTrig/SIGN.php | 71 +- tests/data/Calculation/MathTrig/SIN.php | 15 + tests/data/Calculation/MathTrig/SINH.php | 14 + tests/data/Calculation/MathTrig/SQRT.php | 16 + tests/data/Calculation/MathTrig/SQRTPI.php | 4 + tests/data/Calculation/MathTrig/SUBTOTAL.php | 85 +- .../Calculation/MathTrig/SUBTOTALHIDDEN.php | 81 +- .../Calculation/MathTrig/SUBTOTALNESTED.php | 18 - tests/data/Calculation/MathTrig/SUM.php | 8 + tests/data/Calculation/MathTrig/SUMIF.php | 36 +- tests/data/Calculation/MathTrig/SUMIFS.php | 19 + .../data/Calculation/MathTrig/SUMPRODUCT.php | 10 + tests/data/Calculation/MathTrig/SUMSQ.php | 9 + tests/data/Calculation/MathTrig/SUMX2MY2.php | 10 + tests/data/Calculation/MathTrig/SUMX2PY2.php | 10 + tests/data/Calculation/MathTrig/SUMXMY2.php | 10 + tests/data/Calculation/MathTrig/TAN.php | 17 + tests/data/Calculation/MathTrig/TANH.php | 14 + tests/data/Calculation/MathTrig/TRUNC.php | 124 +- tests/data/Calculation/Statistical/AVEDEV.php | 4 + .../data/Calculation/Statistical/AVERAGE.php | 4 + .../data/Calculation/Statistical/AVERAGEA.php | 8 + .../Calculation/Statistical/AVERAGEIF.php | 35 +- .../Calculation/Statistical/AVERAGEIFS.php | 45 + .../data/Calculation/Statistical/BETADIST.php | 56 + .../data/Calculation/Statistical/BETAINV.php | 56 + .../Calculation/Statistical/BINOMDIST.php | 32 + .../Statistical/BINOMDISTRANGE.php | 72 + .../data/Calculation/Statistical/BINOMINV.php | 60 + .../Statistical/CHIDISTLeftTail.php | 64 + .../{CHIDIST.php => CHIDISTRightTail.php} | 16 + tests/data/Calculation/Statistical/CHIINV.php | 36 - .../Statistical/CHIINVLeftTail.php | 64 + .../Statistical/CHIINVRightTail.php | 64 + .../data/Calculation/Statistical/CHITEST.php | 39 + .../Calculation/Statistical/CONFIDENCE.php | 12 + tests/data/Calculation/Statistical/CORREL.php | 15 + .../data/Calculation/Statistical/COUNTIF.php | 47 +- .../data/Calculation/Statistical/COUNTIFS.php | 26 +- tests/data/Calculation/Statistical/COVAR.php | 10 + .../Calculation/Statistical/CRITBINOM.php | 24 - .../Calculation/Statistical/EXPONDIST.php | 28 + tests/data/Calculation/Statistical/FDIST.php | 80 + tests/data/Calculation/Statistical/FISHER.php | 16 + .../Calculation/Statistical/FISHERINV.php | 8 + .../data/Calculation/Statistical/FORECAST.php | 18 + tests/data/Calculation/Statistical/GAMMA.php | 15 + .../Calculation/Statistical/GAMMADIST.php | 48 + .../data/Calculation/Statistical/GAMMAINV.php | 40 + .../data/Calculation/Statistical/GAMMALN.php | 16 + tests/data/Calculation/Statistical/GAUSS.php | 10 + .../data/Calculation/Statistical/GEOMEAN.php | 7 + tests/data/Calculation/Statistical/GROWTH.php | 25 + .../data/Calculation/Statistical/HARMEAN.php | 11 + .../Calculation/Statistical/HYPGEOMDIST.php | 68 + .../Calculation/Statistical/INTERCEPT.php | 10 + tests/data/Calculation/Statistical/KURT.php | 12 + tests/data/Calculation/Statistical/LARGE.php | 34 + tests/data/Calculation/Statistical/LINEST.php | 105 + tests/data/Calculation/Statistical/LOGEST.php | 72 + tests/data/Calculation/Statistical/LOGINV.php | 40 + .../Calculation/Statistical/LOGNORMDIST.php | 11 + .../Calculation/Statistical/LOGNORMDIST2.php | 26 + tests/data/Calculation/Statistical/MAX.php | 16 + tests/data/Calculation/Statistical/MAXA.php | 28 + tests/data/Calculation/Statistical/MAXIFS.php | 31 + tests/data/Calculation/Statistical/MIN.php | 16 + tests/data/Calculation/Statistical/MINA.php | 28 + tests/data/Calculation/Statistical/MINIFS.php | 31 + .../Calculation/Statistical/NEGBINOMDIST.php | 52 + .../data/Calculation/Statistical/NORMDIST.php | 14 + .../data/Calculation/Statistical/NORMINV.php | 15 + .../Calculation/Statistical/NORMSDIST.php | 12 + .../Calculation/Statistical/NORMSDIST2.php | 18 + .../data/Calculation/Statistical/NORMSINV.php | 11 + .../Calculation/Statistical/PERCENTILE.php | 60 + .../Calculation/Statistical/PERCENTRANK.php | 73 + tests/data/Calculation/Statistical/PERMUT.php | 12 + .../Calculation/Statistical/PERMUTATIONA.php | 36 + .../data/Calculation/Statistical/POISSON.php | 40 + .../data/Calculation/Statistical/QUARTILE.php | 52 + tests/data/Calculation/Statistical/RANK.php | 56 + tests/data/Calculation/Statistical/RSQ.php | 10 + tests/data/Calculation/Statistical/SKEW.php | 20 + tests/data/Calculation/Statistical/SLOPE.php | 10 + tests/data/Calculation/Statistical/SMALL.php | 34 + .../Calculation/Statistical/STANDARDIZE.php | 12 + tests/data/Calculation/Statistical/STDEV.php | 20 + tests/data/Calculation/Statistical/STDEVA.php | 20 + .../Calculation/Statistical/STDEVA_ODS.php | 20 + tests/data/Calculation/Statistical/STDEVP.php | 20 + .../data/Calculation/Statistical/STDEVPA.php | 20 + .../Calculation/Statistical/STDEVPA_ODS.php | 20 + .../Calculation/Statistical/STDEVP_ODS.php | 20 + .../Calculation/Statistical/STDEV_ODS.php | 20 + tests/data/Calculation/Statistical/STEYX.php | 10 + tests/data/Calculation/Statistical/TDIST.php | 56 + tests/data/Calculation/Statistical/TINV.php | 32 + tests/data/Calculation/Statistical/TREND.php | 34 + .../data/Calculation/Statistical/TRIMMEAN.php | 34 + tests/data/Calculation/Statistical/VAR.php | 16 + tests/data/Calculation/Statistical/VARA.php | 16 + .../data/Calculation/Statistical/VARA_ODS.php | 16 + tests/data/Calculation/Statistical/VARP.php | 16 + tests/data/Calculation/Statistical/VARPA.php | 16 + .../Calculation/Statistical/VARPA_ODS.php | 16 + .../data/Calculation/Statistical/VARP_ODS.php | 16 + .../data/Calculation/Statistical/VAR_ODS.php | 16 + .../data/Calculation/Statistical/WEIBULL.php | 48 + tests/data/Calculation/Statistical/ZTEST.php | 42 + tests/data/Calculation/TextData/CHAR.php | 28 +- tests/data/Calculation/TextData/CLEAN.php | 4 + tests/data/Calculation/TextData/DOLLAR.php | 18 +- tests/data/Calculation/TextData/FIND.php | 35 + tests/data/Calculation/TextData/FIXED.php | 32 +- tests/data/Calculation/TextData/LEFT.php | 30 + tests/data/Calculation/TextData/LOWER.php | 12 + tests/data/Calculation/TextData/MID.php | 40 +- tests/data/Calculation/TextData/PROPER.php | 12 + tests/data/Calculation/TextData/REPLACE.php | 28 + tests/data/Calculation/TextData/REPT.php | 14 + tests/data/Calculation/TextData/RIGHT.php | 30 + tests/data/Calculation/TextData/SEARCH.php | 40 + .../data/Calculation/TextData/SUBSTITUTE.php | 34 + tests/data/Calculation/TextData/TEXTJOIN.php | 12 + tests/data/Calculation/TextData/UPPER.php | 12 + tests/data/Calculation/Translations.php | 48 + tests/data/Calculation/Web/WEBSERVICE.php | 30 + .../data/Cell/A1ConversionToR1C1Absolute.php | 15 + .../data/Cell/A1ConversionToR1C1Exception.php | 5 + .../data/Cell/A1ConversionToR1C1Relative.php | 19 + tests/data/Cell/DefaultValueBinder.php | 4 + tests/data/Cell/IndexesFromString.php | 74 + .../data/Cell/R1C1ConversionToA1Absolute.php | 14 + .../data/Cell/R1C1ConversionToA1Exception.php | 9 + .../data/Cell/R1C1ConversionToA1Relative.php | 14 + tests/data/CellCoordinates.php | 28 + .../CellExtractAllCellReferencesInRange.php | 25 +- tests/data/Reader/CSV/escape.csv | 4 + .../Reader/CSV/line_break_escaped_32le.csv | Bin 0 -> 2120 bytes ...break_in_enclosure_with_escaped_quotes.csv | 32 +- tests/data/Reader/CSV/premiere.utf16be.csv | Bin 0 -> 112 bytes tests/data/Reader/CSV/premiere.utf16bebom.csv | Bin 0 -> 114 bytes tests/data/Reader/CSV/premiere.utf16le.csv | Bin 0 -> 112 bytes tests/data/Reader/CSV/premiere.utf16lebom.csv | Bin 0 -> 114 bytes tests/data/Reader/CSV/premiere.utf32be.csv | Bin 0 -> 224 bytes tests/data/Reader/CSV/premiere.utf32bebom.csv | Bin 0 -> 228 bytes tests/data/Reader/CSV/premiere.utf32le.csv | Bin 0 -> 224 bytes tests/data/Reader/CSV/premiere.utf32lebom.csv | Bin 0 -> 228 bytes tests/data/Reader/CSV/premiere.utf8.csv | 2 + tests/data/Reader/CSV/premiere.utf8bom.csv | 2 + tests/data/Reader/CSV/premiere.win1252.csv | 2 + tests/data/Reader/Gnumeric/PageSetup.gnumeric | Bin 0 -> 2347 bytes tests/data/Reader/HTML/badhtml.html | 1 + tests/data/Reader/Ods/PageSetup.ods | Bin 0 -> 4814 bytes tests/data/Reader/Ods/bug1772.ods | Bin 0 -> 11652 bytes tests/data/Reader/Ods/corruptMeta.ods | Bin 0 -> 7196 bytes tests/data/Reader/Ods/data.ods | Bin 11764 -> 12182 bytes tests/data/Reader/Ods/nomimetype.ods | Bin 0 -> 7058 bytes tests/data/Reader/XLS/PageSetup.xls | Bin 0 -> 34816 bytes tests/data/Reader/XLS/bug1505.xls | Bin 0 -> 20992 bytes tests/data/Reader/XLS/bug1592.xls | Bin 0 -> 20992 bytes tests/data/Reader/XLSX/PageSetup.xlsx | Bin 0 -> 15225 bytes tests/data/Reader/XLSX/bug1686b.xlsx | Bin 0 -> 11740 bytes .../conditionalFormattingDataBarTest.xlsx | Bin 0 -> 10443 bytes tests/data/Reader/XLSX/excelChartsTest.xlsx | Bin 0 -> 32243 bytes tests/data/Reader/XLSX/sheetsChartsTest.xlsx | Bin 0 -> 12042 bytes tests/data/Reader/Xml/PageSetup.xml | 250 ++ tests/data/Reader/Xml/excel2003.iso8859-1.xml | 67 + tests/data/ReferenceHelperFormulaUpdates.php | 74 + ...renceHelperFormulaUpdatesMultipleSheet.php | 88 + tests/data/Shared/CodePage.php | 65 +- .../data/Shared/Date/ExcelToTimestamp1900.php | 7 + .../data/Shared/Date/ExcelToTimestamp1904.php | 13 +- tests/data/Shared/Date/FormatCodes.php | 12 + tests/data/Shared/PasswordHashes.php | 26 + .../data/Shared/Trend/ExponentialBestFit.php | 12 + tests/data/Shared/Trend/LinearBestFit.php | 20 + tests/data/Style/NumberFormat.php | 155 +- tests/data/Style/NumberFormatDates.php | 27 +- tests/data/Worksheet/namedRangeTest.xlsx | Bin 0 -> 9812 bytes tests/data/Writer/Ods/content-empty.xml | 3 +- tests/data/Writer/Ods/content-with-data.xml | 137 +- .../data/Writer/XLSX/drawing_on_2nd_page.xlsx | Bin 0 -> 16265 bytes .../XLSX/saving_drawing_with_same_path.xlsx | Bin 0 -> 7936 bytes 1197 files changed, 63716 insertions(+), 23529 deletions(-) create mode 100644 .github/workflows/github-pages.yml create mode 100644 .github/workflows/main.yml create mode 100644 .phpcs.xml.dist delete mode 100644 .travis.yml create mode 100644 docs/extra/extrajs.js create mode 100644 docs/topics/defined-names.md create mode 100644 docs/topics/images/10-databar-of-conditional-formatting.png create mode 100644 phpstan.neon.dist create mode 100644 samples/Basic/17b_Html.php create mode 100644 samples/Basic/30_Templatebiff5.php create mode 100644 samples/Calculations/LookupRef/ADDRESS.php create mode 100644 samples/Calculations/LookupRef/COLUMN.php create mode 100644 samples/Calculations/LookupRef/COLUMNS.php create mode 100644 samples/Calculations/LookupRef/INDEX.php create mode 100644 samples/Calculations/LookupRef/INDIRECT.php create mode 100644 samples/Calculations/LookupRef/OFFSET.php create mode 100644 samples/Calculations/LookupRef/ROW.php create mode 100644 samples/Calculations/LookupRef/ROWS.php create mode 100644 samples/DefinedNames/AbsoluteNamedRange.php create mode 100644 samples/DefinedNames/CrossWorksheetNamedFormula.php create mode 100644 samples/DefinedNames/NamedFormulaeAndRanges.php create mode 100644 samples/DefinedNames/RelativeNamedRange.php create mode 100644 samples/DefinedNames/RelativeNamedRange2.php create mode 100644 samples/DefinedNames/RelativeNamedRangeAsFunction.php create mode 100644 samples/DefinedNames/ScopedNamedRange.php create mode 100644 samples/DefinedNames/ScopedNamedRange2.php create mode 100644 samples/DefinedNames/SimpleNamedFormula.php create mode 100644 samples/DefinedNames/SimpleNamedRange.php create mode 100644 samples/Pdf/21a_Pdf.php create mode 100644 samples/Pdf/21b_Pdf.php create mode 100644 samples/Reader/sampleData/example1xls create mode 100644 samples/images/bmp.bmp create mode 100644 samples/images/gif.gif create mode 100644 samples/templates/30templatebiff5.xls rename tests/data/Reader/Xml/WithoutStyle.xml => samples/templates/excel2003.short.bad.xml (66%) create mode 100644 samples/templates/excel2003.xml create mode 100644 samples/templates/old.gnumeric create mode 100644 src/PhpSpreadsheet/Calculation/Database/DAverage.php create mode 100644 src/PhpSpreadsheet/Calculation/Database/DCount.php create mode 100644 src/PhpSpreadsheet/Calculation/Database/DCountA.php create mode 100644 src/PhpSpreadsheet/Calculation/Database/DGet.php create mode 100644 src/PhpSpreadsheet/Calculation/Database/DMax.php create mode 100644 src/PhpSpreadsheet/Calculation/Database/DMin.php create mode 100644 src/PhpSpreadsheet/Calculation/Database/DProduct.php create mode 100644 src/PhpSpreadsheet/Calculation/Database/DStDev.php create mode 100644 src/PhpSpreadsheet/Calculation/Database/DStDevP.php create mode 100644 src/PhpSpreadsheet/Calculation/Database/DSum.php create mode 100644 src/PhpSpreadsheet/Calculation/Database/DVar.php create mode 100644 src/PhpSpreadsheet/Calculation/Database/DVarP.php create mode 100644 src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php create mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/Constants.php create mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/DateDif.php create mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php create mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/Datefunc.php create mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/Day.php create mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php create mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php create mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/EDate.php create mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/EoMonth.php create mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php create mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/Hour.php create mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/IsoWeekNum.php create mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/Minute.php create mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php create mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php create mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/Now.php create mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/Second.php create mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php create mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php create mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/Today.php create mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/WeekDay.php create mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/WeekNum.php create mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php create mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/Year.php create mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php create mode 100644 src/PhpSpreadsheet/Calculation/Engineering/BaseValidations.php create mode 100644 src/PhpSpreadsheet/Calculation/Engineering/BesselI.php create mode 100644 src/PhpSpreadsheet/Calculation/Engineering/BesselJ.php create mode 100644 src/PhpSpreadsheet/Calculation/Engineering/BesselK.php create mode 100644 src/PhpSpreadsheet/Calculation/Engineering/BesselY.php create mode 100644 src/PhpSpreadsheet/Calculation/Engineering/BitWise.php create mode 100644 src/PhpSpreadsheet/Calculation/Engineering/Compare.php create mode 100644 src/PhpSpreadsheet/Calculation/Engineering/Complex.php create mode 100644 src/PhpSpreadsheet/Calculation/Engineering/ComplexFunctions.php create mode 100644 src/PhpSpreadsheet/Calculation/Engineering/ComplexOperations.php create mode 100644 src/PhpSpreadsheet/Calculation/Engineering/Constants.php create mode 100644 src/PhpSpreadsheet/Calculation/Engineering/ConvertBase.php create mode 100644 src/PhpSpreadsheet/Calculation/Engineering/ConvertBinary.php create mode 100644 src/PhpSpreadsheet/Calculation/Engineering/ConvertDecimal.php create mode 100644 src/PhpSpreadsheet/Calculation/Engineering/ConvertHex.php create mode 100644 src/PhpSpreadsheet/Calculation/Engineering/ConvertOctal.php create mode 100644 src/PhpSpreadsheet/Calculation/Engineering/ConvertUOM.php create mode 100644 src/PhpSpreadsheet/Calculation/Engineering/Erf.php create mode 100644 src/PhpSpreadsheet/Calculation/Engineering/ErfC.php create mode 100644 src/PhpSpreadsheet/Calculation/Financial/Amortization.php create mode 100644 src/PhpSpreadsheet/Calculation/Financial/BaseValidations.php create mode 100644 src/PhpSpreadsheet/Calculation/Financial/CashFlow/Single.php create mode 100644 src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php create mode 100644 src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php create mode 100644 src/PhpSpreadsheet/Calculation/Financial/Coupons.php create mode 100644 src/PhpSpreadsheet/Calculation/Financial/Depreciation.php create mode 100644 src/PhpSpreadsheet/Calculation/Financial/Dollar.php create mode 100644 src/PhpSpreadsheet/Calculation/Financial/Helpers.php create mode 100644 src/PhpSpreadsheet/Calculation/Financial/InterestRate.php create mode 100644 src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php create mode 100644 src/PhpSpreadsheet/Calculation/Financial/Securities/BaseValidations.php create mode 100644 src/PhpSpreadsheet/Calculation/Financial/Securities/Constants.php create mode 100644 src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php create mode 100644 src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php create mode 100644 src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php create mode 100644 src/PhpSpreadsheet/Calculation/Internal/MakeMatrix.php create mode 100644 src/PhpSpreadsheet/Calculation/Internal/WildcardMatch.php create mode 100644 src/PhpSpreadsheet/Calculation/Logical/Boolean.php create mode 100644 src/PhpSpreadsheet/Calculation/Logical/Conditional.php create mode 100644 src/PhpSpreadsheet/Calculation/Logical/Operations.php create mode 100644 src/PhpSpreadsheet/Calculation/LookupRef/Address.php create mode 100644 src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php create mode 100644 src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php create mode 100644 src/PhpSpreadsheet/Calculation/LookupRef/Indirect.php create mode 100644 src/PhpSpreadsheet/Calculation/LookupRef/Lookup.php create mode 100644 src/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php create mode 100644 src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php create mode 100644 src/PhpSpreadsheet/Calculation/LookupRef/Offset.php create mode 100644 src/PhpSpreadsheet/Calculation/LookupRef/RowColumnInformation.php create mode 100644 src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Absolute.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Acos.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Acosh.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Acot.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Acoth.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Arabic.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Asin.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Asinh.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Atan.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Atan2.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Atanh.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Base.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Ceiling.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/CeilingMath.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/CeilingPrecise.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Combinations.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Cos.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Cosh.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Cot.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Coth.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Csc.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Csch.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Degrees.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Even.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Exp.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Fact.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/FactDouble.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Floor.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/FloorMath.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/FloorPrecise.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Gcd.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Helpers.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/IntClass.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Lcm.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Logarithms.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/MatrixFunctions.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Mod.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Mround.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Multinomial.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Odd.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Power.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Product.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Quotient.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Radians.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Random.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Roman.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Round.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/RoundDown.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/RoundUp.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Sec.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Sech.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/SeriesSum.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Sign.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Sin.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Sinh.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Sqrt.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/SqrtPi.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Sum.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/SumProduct.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/SumSquares.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Tan.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Tanh.php create mode 100644 src/PhpSpreadsheet/Calculation/MathTrig/Trunc.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/AggregateBase.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Averages.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/BaseValidations.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Conditional.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Confidence.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Counts.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Distributions/BaseValidations.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Distributions/Beta.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Distributions/Binomial.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Distributions/Exponential.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Distributions/F.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Distributions/Fisher.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Distributions/Gamma.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Distributions/GammaBase.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Distributions/HyperGeometric.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Distributions/LogNormal.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Distributions/NewtonRaphson.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Distributions/Normal.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Distributions/Poisson.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Distributions/StandardNormal.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Distributions/StudentT.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Distributions/Weibull.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/MaxMinBase.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Maximum.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Minimum.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Percentiles.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Permutations.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/StandardDeviations.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Trends.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/VarianceBase.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Variances.php create mode 100644 src/PhpSpreadsheet/Calculation/TextData/CaseConvert.php create mode 100644 src/PhpSpreadsheet/Calculation/TextData/CharacterConvert.php create mode 100644 src/PhpSpreadsheet/Calculation/TextData/Concatenate.php create mode 100644 src/PhpSpreadsheet/Calculation/TextData/Extract.php create mode 100644 src/PhpSpreadsheet/Calculation/TextData/Format.php create mode 100644 src/PhpSpreadsheet/Calculation/TextData/Replace.php create mode 100644 src/PhpSpreadsheet/Calculation/TextData/Search.php create mode 100644 src/PhpSpreadsheet/Calculation/TextData/Text.php create mode 100644 src/PhpSpreadsheet/Calculation/TextData/Trim.php create mode 100644 src/PhpSpreadsheet/Calculation/Web.php create mode 100644 src/PhpSpreadsheet/Cell/AddressHelper.php delete mode 100644 src/PhpSpreadsheet/Chart/Renderer/Polyfill.php create mode 100644 src/PhpSpreadsheet/DefinedName.php create mode 100644 src/PhpSpreadsheet/NamedFormula.php create mode 100644 src/PhpSpreadsheet/Reader/Csv/Delimiter.php create mode 100644 src/PhpSpreadsheet/Reader/Gnumeric/PageSetup.php create mode 100644 src/PhpSpreadsheet/Reader/Ods/PageSettings.php create mode 100644 src/PhpSpreadsheet/Reader/Xml/PageSettings.php create mode 100644 src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBar.php create mode 100644 src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBarExtension.php create mode 100644 src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormatValueObject.php create mode 100644 src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php create mode 100644 src/PhpSpreadsheet/Style/NumberFormat/BaseFormatter.php create mode 100644 src/PhpSpreadsheet/Style/NumberFormat/DateFormatter.php create mode 100644 src/PhpSpreadsheet/Style/NumberFormat/Formatter.php create mode 100644 src/PhpSpreadsheet/Style/NumberFormat/FractionFormatter.php create mode 100644 src/PhpSpreadsheet/Style/NumberFormat/NumberFormatter.php create mode 100644 src/PhpSpreadsheet/Style/NumberFormat/PercentageFormatter.php create mode 100644 src/PhpSpreadsheet/Writer/Ods/Cell/Style.php create mode 100644 src/PhpSpreadsheet/Writer/Ods/Formula.php create mode 100644 src/PhpSpreadsheet/Writer/Ods/NamedExpressions.php create mode 100644 src/PhpSpreadsheet/Writer/Xlsx/DefinedNames.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/DefinedNamesCalculationTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Engine/RangeTest.php delete mode 100644 tests/PhpSpreadsheetTests/Calculation/FinancialTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Database/DAverageTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Database/DCountATest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Database/DCountTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Database/DGetTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Database/DMaxTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Database/DMinTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Database/DProductTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Database/DStDevPTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Database/DStDevTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Database/DSumTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Database/DVarPTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Database/DVarTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/AllSetupTeardown.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/MovedFunctionsTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/NowTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/TodayTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Engineering/MovedBitwiseTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Engineering/MovedFunctionsTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/AmorDegRcTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/AmorLincTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/CoupDayBsTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/CoupDaysNcTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/CoupDaysTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/CoupNcdTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/CoupNumTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/CoupPcdTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/CumIpmtTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/CumPrincTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/DbTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/DdbTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/DiscTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/DollarDeTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/DollarFrTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/EffectTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/FvScheduleTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/FvTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/HelpersTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/IPmtTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/IntRateTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/IrrTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/IsPmtTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/MirrTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/NPerTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/NominalTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/NpvTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/PDurationTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/PriceDiscTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/PriceMatTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/PriceTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/PvTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/RateTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/ReceivedTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/RriTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/SlnTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/SydTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/TBillEqTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/TBillPriceTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/TBillYieldTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/XNpvTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/XirrTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/YieldDiscTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Financial/YieldMatTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Logical/IfsTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/AddressTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/ColumnTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/IndirectTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/OffsetTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/RowTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/TransposeTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/AbsTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/AcosTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/AcoshTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/AllSetupTeardown.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/AsinTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/AsinhTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/AtanTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/AtanhTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/CeilingMathTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/CeilingPreciseTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/CombinATest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/CosTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/CoshTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/DegreesTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/ExpTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/LnTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/Log10Test.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/MUnitTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/MovedFunctionsTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/RadiansTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/RandBetweenTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/RandTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/RoundTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/SinTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/SinhTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/SqrtTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/SumTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/TanTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/TanhTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/AverageIfsTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/BinomDistRangeTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/BinomInvTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/ChiDistLeftTailTest.php rename tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/{ChiDistTest.php => ChiDistRightTailTest.php} (92%) create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/ChiInvLeftTailTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/ChiInvRightTailTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/ChiTestTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/FDistTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/GammaTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/GaussTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/GrowthTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/HypGeomDistTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/KurtTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/LargeTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/LinEstTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/LogEstTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/LogInvTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/LogNormDist2Test.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/LogNormDistTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/MaxATest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/MaxTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/MinATest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/MinTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/NegBinomDistTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/NormDistTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/NormInvTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/NormSDist2Test.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/NormSDistTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/NormSInvTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/PercentRankTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/PercentileTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/PermutationATest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/PoissonTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/QuartileTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/RankTest.php rename tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/{ChiInvTest.php => SkewTest.php} (63%) create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/SmallTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/StDevATest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/StDevPATest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/StDevPTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/StDevTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/StandardizeTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/TDistTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/TinvTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/TrendTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/TrimMeanTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/VarATest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/VarPATest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/VarPTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/VarTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/WeibullTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/ZTestTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/TextData/ReptTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Web/WebServiceTest.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/TranslationTest.php create mode 100644 tests/PhpSpreadsheetTests/Cell/AddressHelperTest.php create mode 100644 tests/PhpSpreadsheetTests/DefinedNameFormulaTest.php create mode 100644 tests/PhpSpreadsheetTests/DefinedNameTest.php create mode 100644 tests/PhpSpreadsheetTests/Document/PropertiesTest.php create mode 100644 tests/PhpSpreadsheetTests/Helper/SampleCoverageTest.php create mode 100644 tests/PhpSpreadsheetTests/NamedFormulaTest.php create mode 100644 tests/PhpSpreadsheetTests/NamedRangeTest.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Gnumeric/GnumericFilter.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Gnumeric/GnumericInfoTest.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Gnumeric/GnumericLoadTest.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Gnumeric/GnumericStylesTest.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Gnumeric/PageSetupTest.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Html/HtmlBorderTest.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Html/HtmlHelper.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Html/HtmlImageTest.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Html/HtmlLoadStringTest.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Html/HtmlTagsTest.php rename tests/PhpSpreadsheetTests/Reader/{ => Html}/HtmlTest.php (55%) create mode 100644 tests/PhpSpreadsheetTests/Reader/Ods/OdsInfoTest.php rename tests/PhpSpreadsheetTests/Reader/{ => Ods}/OdsTest.php (76%) create mode 100644 tests/PhpSpreadsheetTests/Reader/Ods/PageSetupBug1772Test.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Ods/PageSetupTest.php create mode 100644 tests/PhpSpreadsheetTests/Reader/SheetsXlsxChartTest.php create mode 100644 tests/PhpSpreadsheetTests/Reader/SlkTest.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Xls/PageSetupTest.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Xlsx/ChartsTitleTest.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Xlsx/ConditionalFormattingDataBarXlsxTest.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Xlsx/NamedRangeTest.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Xlsx/PageSetupTest.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Xml/PageSetupTest.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Xml/XmlFilter.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Xml/XmlInfoTest.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Xml/XmlLoadTest.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Xml/XmlOddTest.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Xml/XmlStyleCoverageTest.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Xml/XmlStylesTest.php rename tests/PhpSpreadsheetTests/Reader/{ => Xml}/XmlTest.php (68%) create mode 100644 tests/PhpSpreadsheetTests/Shared/Trend/ExponentialBestFitTest.php create mode 100644 tests/PhpSpreadsheetTests/Shared/Trend/LinearBestFitTest.php create mode 100644 tests/PhpSpreadsheetTests/Style/AlignmentTest.php create mode 100644 tests/PhpSpreadsheetTests/Style/ConditionalTest.php create mode 100644 tests/PhpSpreadsheetTests/Style/ExportArrayTest.php create mode 100644 tests/PhpSpreadsheetTests/Style/FillTest.php create mode 100644 tests/PhpSpreadsheetTests/Style/FontTest.php create mode 100644 tests/PhpSpreadsheetTests/Style/NumberFormatBuiltinTest.php create mode 100644 tests/PhpSpreadsheetTests/Style/StyleTest.php create mode 100644 tests/PhpSpreadsheetTests/Worksheet/ColumnCellIterator2Test.php create mode 100644 tests/PhpSpreadsheetTests/Worksheet/PageMarginsTest.php create mode 100644 tests/PhpSpreadsheetTests/Worksheet/ProtectionTest.php create mode 100644 tests/PhpSpreadsheetTests/Worksheet/RowCellIterator2Test.php create mode 100644 tests/PhpSpreadsheetTests/Worksheet/WorksheetNamedRangesTest.php create mode 100644 tests/PhpSpreadsheetTests/Writer/Csv/CsvEnclosureTest.php create mode 100644 tests/PhpSpreadsheetTests/Writer/Html/CallbackTest.php create mode 100644 tests/PhpSpreadsheetTests/Writer/Html/ExtendForChartsAndImagesTest.php create mode 100644 tests/PhpSpreadsheetTests/Writer/Html/XssVulnerabilityTest.php create mode 100644 tests/PhpSpreadsheetTests/Writer/RetainSelectedCellsTest.php create mode 100644 tests/PhpSpreadsheetTests/Writer/Xls/XlsGifBmpTest.php create mode 100644 tests/PhpSpreadsheetTests/Writer/Xlsx/DrawingsTest.php create mode 100644 tests/PhpSpreadsheetTests/Writer/Xlsx/StartsWithHashTest.php create mode 100644 tests/PhpSpreadsheetTests/Writer/Xlsx/UnparsedDataCloneTest.php create mode 100644 tests/data/Calculation/DateTime/DAYOpenOffice.php create mode 100644 tests/data/Calculation/DateTime/ISOWEEKNUM1904.php create mode 100644 tests/data/Calculation/DateTime/WEEKNUM1904.php create mode 100644 tests/data/Calculation/DefinedNames/NamedFormulae.xlsx create mode 100644 tests/data/Calculation/DefinedNames/NamedRanges.xlsx create mode 100644 tests/data/Calculation/Financial/DaysPerYear.php create mode 100644 tests/data/Calculation/Financial/PRICEMAT.php create mode 100644 tests/data/Calculation/Financial/RECEIVED.php create mode 100644 tests/data/Calculation/Financial/TBILLEQ.php create mode 100644 tests/data/Calculation/Financial/TBILLPRICE.php create mode 100644 tests/data/Calculation/Financial/TBILLYIELD.php create mode 100644 tests/data/Calculation/Financial/YIELDDISC.php create mode 100644 tests/data/Calculation/Financial/YIELDMAT.php create mode 100644 tests/data/Calculation/Logical/IFS.php create mode 100644 tests/data/Calculation/LookupRef/ADDRESS.php create mode 100644 tests/data/Calculation/LookupRef/COLUMN.php create mode 100644 tests/data/Calculation/LookupRef/INDIRECT.php create mode 100644 tests/data/Calculation/LookupRef/OFFSET.php create mode 100644 tests/data/Calculation/LookupRef/ROW.php create mode 100644 tests/data/Calculation/LookupRef/TRANSPOSE.php create mode 100644 tests/data/Calculation/MathTrig/ABS.php create mode 100644 tests/data/Calculation/MathTrig/ACOS.php create mode 100644 tests/data/Calculation/MathTrig/ACOSH.php create mode 100644 tests/data/Calculation/MathTrig/ASIN.php create mode 100644 tests/data/Calculation/MathTrig/ASINH.php create mode 100644 tests/data/Calculation/MathTrig/ATAN.php create mode 100644 tests/data/Calculation/MathTrig/ATANH.php create mode 100644 tests/data/Calculation/MathTrig/CEILINGMATH.php create mode 100644 tests/data/Calculation/MathTrig/CEILINGPRECISE.php create mode 100644 tests/data/Calculation/MathTrig/COMBINA.php create mode 100644 tests/data/Calculation/MathTrig/COS.php create mode 100644 tests/data/Calculation/MathTrig/COSH.php create mode 100644 tests/data/Calculation/MathTrig/DEGREES.php create mode 100644 tests/data/Calculation/MathTrig/EXP.php create mode 100644 tests/data/Calculation/MathTrig/FACTGNUMERIC.php create mode 100644 tests/data/Calculation/MathTrig/LN.php create mode 100644 tests/data/Calculation/MathTrig/LOG10.php create mode 100644 tests/data/Calculation/MathTrig/RADIANS.php create mode 100644 tests/data/Calculation/MathTrig/RANDBETWEEN.php create mode 100644 tests/data/Calculation/MathTrig/ROUND.php create mode 100644 tests/data/Calculation/MathTrig/SIN.php create mode 100644 tests/data/Calculation/MathTrig/SINH.php create mode 100644 tests/data/Calculation/MathTrig/SQRT.php delete mode 100644 tests/data/Calculation/MathTrig/SUBTOTALNESTED.php create mode 100644 tests/data/Calculation/MathTrig/SUM.php create mode 100644 tests/data/Calculation/MathTrig/TAN.php create mode 100644 tests/data/Calculation/MathTrig/TANH.php create mode 100644 tests/data/Calculation/Statistical/AVERAGEIFS.php create mode 100644 tests/data/Calculation/Statistical/BINOMDISTRANGE.php create mode 100644 tests/data/Calculation/Statistical/BINOMINV.php create mode 100644 tests/data/Calculation/Statistical/CHIDISTLeftTail.php rename tests/data/Calculation/Statistical/{CHIDIST.php => CHIDISTRightTail.php} (66%) delete mode 100644 tests/data/Calculation/Statistical/CHIINV.php create mode 100644 tests/data/Calculation/Statistical/CHIINVLeftTail.php create mode 100644 tests/data/Calculation/Statistical/CHIINVRightTail.php create mode 100644 tests/data/Calculation/Statistical/CHITEST.php delete mode 100644 tests/data/Calculation/Statistical/CRITBINOM.php create mode 100644 tests/data/Calculation/Statistical/FDIST.php create mode 100644 tests/data/Calculation/Statistical/GAMMA.php create mode 100644 tests/data/Calculation/Statistical/GAUSS.php create mode 100644 tests/data/Calculation/Statistical/GROWTH.php create mode 100644 tests/data/Calculation/Statistical/HYPGEOMDIST.php create mode 100644 tests/data/Calculation/Statistical/KURT.php create mode 100644 tests/data/Calculation/Statistical/LARGE.php create mode 100644 tests/data/Calculation/Statistical/LINEST.php create mode 100644 tests/data/Calculation/Statistical/LOGEST.php create mode 100644 tests/data/Calculation/Statistical/LOGINV.php create mode 100644 tests/data/Calculation/Statistical/LOGNORMDIST.php create mode 100644 tests/data/Calculation/Statistical/LOGNORMDIST2.php create mode 100644 tests/data/Calculation/Statistical/MAX.php create mode 100644 tests/data/Calculation/Statistical/MAXA.php create mode 100644 tests/data/Calculation/Statistical/MIN.php create mode 100644 tests/data/Calculation/Statistical/MINA.php create mode 100644 tests/data/Calculation/Statistical/NEGBINOMDIST.php create mode 100644 tests/data/Calculation/Statistical/NORMDIST.php create mode 100644 tests/data/Calculation/Statistical/NORMINV.php create mode 100644 tests/data/Calculation/Statistical/NORMSDIST.php create mode 100644 tests/data/Calculation/Statistical/NORMSDIST2.php create mode 100644 tests/data/Calculation/Statistical/NORMSINV.php create mode 100644 tests/data/Calculation/Statistical/PERCENTILE.php create mode 100644 tests/data/Calculation/Statistical/PERCENTRANK.php create mode 100644 tests/data/Calculation/Statistical/PERMUTATIONA.php create mode 100644 tests/data/Calculation/Statistical/POISSON.php create mode 100644 tests/data/Calculation/Statistical/QUARTILE.php create mode 100644 tests/data/Calculation/Statistical/RANK.php create mode 100644 tests/data/Calculation/Statistical/SKEW.php create mode 100644 tests/data/Calculation/Statistical/SMALL.php create mode 100644 tests/data/Calculation/Statistical/STANDARDIZE.php create mode 100644 tests/data/Calculation/Statistical/STDEV.php create mode 100644 tests/data/Calculation/Statistical/STDEVA.php create mode 100644 tests/data/Calculation/Statistical/STDEVA_ODS.php create mode 100644 tests/data/Calculation/Statistical/STDEVP.php create mode 100644 tests/data/Calculation/Statistical/STDEVPA.php create mode 100644 tests/data/Calculation/Statistical/STDEVPA_ODS.php create mode 100644 tests/data/Calculation/Statistical/STDEVP_ODS.php create mode 100644 tests/data/Calculation/Statistical/STDEV_ODS.php create mode 100644 tests/data/Calculation/Statistical/TDIST.php create mode 100644 tests/data/Calculation/Statistical/TINV.php create mode 100644 tests/data/Calculation/Statistical/TREND.php create mode 100644 tests/data/Calculation/Statistical/TRIMMEAN.php create mode 100644 tests/data/Calculation/Statistical/VAR.php create mode 100644 tests/data/Calculation/Statistical/VARA.php create mode 100644 tests/data/Calculation/Statistical/VARA_ODS.php create mode 100644 tests/data/Calculation/Statistical/VARP.php create mode 100644 tests/data/Calculation/Statistical/VARPA.php create mode 100644 tests/data/Calculation/Statistical/VARPA_ODS.php create mode 100644 tests/data/Calculation/Statistical/VARP_ODS.php create mode 100644 tests/data/Calculation/Statistical/VAR_ODS.php create mode 100644 tests/data/Calculation/Statistical/WEIBULL.php create mode 100644 tests/data/Calculation/Statistical/ZTEST.php create mode 100644 tests/data/Calculation/TextData/REPT.php create mode 100644 tests/data/Calculation/Translations.php create mode 100644 tests/data/Calculation/Web/WEBSERVICE.php create mode 100644 tests/data/Cell/A1ConversionToR1C1Absolute.php create mode 100644 tests/data/Cell/A1ConversionToR1C1Exception.php create mode 100644 tests/data/Cell/A1ConversionToR1C1Relative.php create mode 100644 tests/data/Cell/IndexesFromString.php create mode 100644 tests/data/Cell/R1C1ConversionToA1Absolute.php create mode 100644 tests/data/Cell/R1C1ConversionToA1Exception.php create mode 100644 tests/data/Cell/R1C1ConversionToA1Relative.php create mode 100644 tests/data/Reader/CSV/escape.csv create mode 100644 tests/data/Reader/CSV/line_break_escaped_32le.csv create mode 100644 tests/data/Reader/CSV/premiere.utf16be.csv create mode 100644 tests/data/Reader/CSV/premiere.utf16bebom.csv create mode 100644 tests/data/Reader/CSV/premiere.utf16le.csv create mode 100644 tests/data/Reader/CSV/premiere.utf16lebom.csv create mode 100644 tests/data/Reader/CSV/premiere.utf32be.csv create mode 100644 tests/data/Reader/CSV/premiere.utf32bebom.csv create mode 100644 tests/data/Reader/CSV/premiere.utf32le.csv create mode 100644 tests/data/Reader/CSV/premiere.utf32lebom.csv create mode 100644 tests/data/Reader/CSV/premiere.utf8.csv create mode 100644 tests/data/Reader/CSV/premiere.utf8bom.csv create mode 100644 tests/data/Reader/CSV/premiere.win1252.csv create mode 100644 tests/data/Reader/Gnumeric/PageSetup.gnumeric create mode 100644 tests/data/Reader/HTML/badhtml.html create mode 100644 tests/data/Reader/Ods/PageSetup.ods create mode 100644 tests/data/Reader/Ods/bug1772.ods create mode 100644 tests/data/Reader/Ods/corruptMeta.ods create mode 100644 tests/data/Reader/Ods/nomimetype.ods create mode 100644 tests/data/Reader/XLS/PageSetup.xls create mode 100644 tests/data/Reader/XLS/bug1505.xls create mode 100644 tests/data/Reader/XLS/bug1592.xls create mode 100644 tests/data/Reader/XLSX/PageSetup.xlsx create mode 100644 tests/data/Reader/XLSX/bug1686b.xlsx create mode 100644 tests/data/Reader/XLSX/conditionalFormattingDataBarTest.xlsx create mode 100644 tests/data/Reader/XLSX/excelChartsTest.xlsx create mode 100755 tests/data/Reader/XLSX/sheetsChartsTest.xlsx create mode 100644 tests/data/Reader/Xml/PageSetup.xml create mode 100644 tests/data/Reader/Xml/excel2003.iso8859-1.xml create mode 100644 tests/data/ReferenceHelperFormulaUpdates.php create mode 100644 tests/data/ReferenceHelperFormulaUpdatesMultipleSheet.php create mode 100644 tests/data/Shared/Trend/ExponentialBestFit.php create mode 100644 tests/data/Shared/Trend/LinearBestFit.php create mode 100644 tests/data/Worksheet/namedRangeTest.xlsx create mode 100644 tests/data/Writer/XLSX/drawing_on_2nd_page.xlsx create mode 100644 tests/data/Writer/XLSX/saving_drawing_with_same_path.xlsx diff --git a/.gitattributes b/.gitattributes index 0186deae8c..9f2b2f6e57 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,9 +3,7 @@ /.github export-ignore /.gitignore export-ignore /.php_cs.dist export-ignore -/.sami.php export-ignore /.scrutinizer.yml export-ignore -/.travis.yml export-ignore /CHANGELOG.PHPExcel.md export-ignore /bin export-ignore /composer.lock export-ignore diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml new file mode 100644 index 0000000000..3bdeea1d62 --- /dev/null +++ b/.github/workflows/github-pages.yml @@ -0,0 +1,29 @@ +name: GithHub Pages +on: + push: + tags: + - '*' + +jobs: + github-pages: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP, with composer and extensions + uses: shivammathur/setup-php@v2 + with: + php-version: 7.4 + coverage: none # remove xdebug + + - name: Build API documentation + run: | + curl -LO https://github.com/phpDocumentor/phpDocumentor/releases/download/v3.0.0-rc/phpDocumentor.phar + php phpDocumentor.phar --directory src/ --target docs/api + + - name: Deploy to GithHub Pages + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./docs/api diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000000..7d7cf5e640 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,180 @@ +name: main +on: [ push, pull_request ] +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + experimental: + - false + php-version: + - '7.2' + - '7.3' + - '7.4' + - '8.0' + + include: + - php-version: '8.1' + experimental: true + + name: PHP ${{ matrix.php-version }} + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP, with composer and extensions + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + extensions: ctype, dom, gd, iconv, fileinfo, libxml, mbstring, simplexml, xml, xmlreader, xmlwriter, zip, zlib + coverage: none + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache composer dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Delete composer lock file + id: composer-lock + if: ${{ matrix.php-version == '8.1' }} + run: | + rm composer.lock + echo "::set-output name=flags::--ignore-platform-reqs" + + - name: Install dependencies + run: composer update --no-progress --prefer-dist --optimize-autoloader ${{ steps.composer-lock.outputs.flags }} + + - name: Setup problem matchers for PHP + run: echo "::add-matcher::${{ runner.tool_cache }}/php.json" + + - name: Setup problem matchers for PHPUnit + run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + + - name: "Run PHPUnit tests (Experimental: ${{ matrix.experimental }})" + env: + FAILURE_ACTION: "${{ matrix.experimental == true }}" + run: vendor/bin/phpunit --verbose || $FAILURE_ACTION + + php-cs-fixer: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP, with composer and extensions + uses: shivammathur/setup-php@v2 + with: + php-version: 7.4 + extensions: ctype, dom, gd, iconv, fileinfo, libxml, mbstring, simplexml, xml, xmlreader, xmlwriter, zip, zlib + coverage: none + tools: cs2pr + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache composer dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --no-progress --prefer-dist --optimize-autoloader + + - name: Code style with PHP-CS-Fixer + run: ./vendor/bin/php-cs-fixer fix --format=checkstyle | cs2pr + + phpcs: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP, with composer and extensions + uses: shivammathur/setup-php@v2 + with: + php-version: 7.4 + extensions: ctype, dom, gd, iconv, fileinfo, libxml, mbstring, simplexml, xml, xmlreader, xmlwriter, zip, zlib + coverage: none + tools: cs2pr + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache composer dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --no-progress --prefer-dist --optimize-autoloader + + - name: Code style with PHP_CodeSniffer + run: ./vendor/bin/phpcs -q --report=checkstyle | cs2pr + + phpstan: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP, with composer and extensions + uses: shivammathur/setup-php@v2 + with: + php-version: 7.4 + extensions: ctype, dom, gd, iconv, fileinfo, libxml, mbstring, simplexml, xml, xmlreader, xmlwriter, zip, zlib + coverage: none + tools: cs2pr + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache composer dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --no-progress --prefer-dist --optimize-autoloader + + - name: Static analysis with PHPStan + run: ./vendor/bin/phpstan analyse + + + + release: + runs-on: ubuntu-latest + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + steps: + - uses: actions/checkout@v2 + with: + ref: ${{ github.ref }} # Otherwise our annotated tag is not fetched and we cannot get correct version + + # Create release + - name: Get release info + id: release-info + run: | + echo "::set-output name=subject::$(git tag --format '%(contents:subject)' --points-at)" + git tag --format '%(contents:body)' --points-at > release-body.txt + - uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token + with: + tag_name: ${{ github.ref }} + release_name: ${{ steps.release-info.outputs.subject }} + body_path: release-body.txt diff --git a/.gitignore b/.gitignore index 0723541d7a..eac08567d9 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,6 @@ *.project /.settings /.idea + +## mkdocs output +/site diff --git a/.php_cs.dist b/.php_cs.dist index f8797e88de..1a6464203b 100644 --- a/.php_cs.dist +++ b/.php_cs.dist @@ -160,7 +160,7 @@ return PhpCsFixer\Config::create() 'php_unit_test_annotation' => true, 'php_unit_test_case_static_method_calls' => ['call_type' => 'self'], 'php_unit_test_class_requires_covers' => false, // We don't care as much as we should about coverage - 'phpdoc_add_missing_param_annotation' => true, + 'phpdoc_add_missing_param_annotation' => false, // Don't add things that bring no value 'phpdoc_align' => false, // Waste of time 'phpdoc_annotation_without_dot' => true, 'phpdoc_indent' => true, diff --git a/.phpcs.xml.dist b/.phpcs.xml.dist new file mode 100644 index 0000000000..3eafb6ca19 --- /dev/null +++ b/.phpcs.xml.dist @@ -0,0 +1,22 @@ + + + + samples + src + tests + + samples/Header.php + */tests/Core/*/*Test\.(inc|css|js)$ + + + + + + + + + + + + diff --git a/.scrutinizer.yml b/.scrutinizer.yml index 748f3ac3fa..08c6125539 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -18,7 +18,7 @@ build: tools: external_code_coverage: - timeout: 3600 + timeout: 600 build_failure_conditions: - 'elements.rating(<= C).new.exists' # No new classes/methods with a rating of C or worse allowed diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 31ae804ea2..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,51 +0,0 @@ -language: php -dist: bionic - -php: - - 7.2 - - 7.3 - - 7.4 - -cache: - directories: - - vendor - - $HOME/.composer/cache - -before_script: - # Deactivate xdebug - - phpenv config-rm xdebug.ini - - composer install --ignore-platform-reqs - -script: - - ./vendor/bin/phpunit - -jobs: - include: - - - stage: Code style - php: 7.4 - script: - - ./vendor/bin/php-cs-fixer fix --diff --verbose --dry-run - - ./vendor/bin/phpcs --report-width=200 samples/ src/ tests/ --ignore=samples/Header.php --standard=PSR2 -n - - - stage: Coverage - php: 7.4 - script: - - pecl install pcov - - ./vendor/bin/phpunit --coverage-clover coverage-clover.xml - after_script: - - wget https://scrutinizer-ci.com/ocular.phar - - php ocular.phar code-coverage:upload --format=php-clover tests/coverage-clover.xml - - - stage: API documentations - if: tag is present AND branch = master - php: 7.4 - before_script: - - curl -LO https://github.com/phpDocumentor/phpDocumentor/releases/download/v3.0.0-rc/phpDocumentor.phar - script: - - php phpDocumentor.phar --directory src/ --target docs/api - deploy: - provider: pages - skip-cleanup: true - local-dir: docs/api - github-token: $GITHUB_TOKEN diff --git a/CHANGELOG.md b/CHANGELOG.md index 27d72197f3..69d1652a2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,222 @@ 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). -## [Unreleased] +## Unreleased - TBD + +### Added + +- Implemented the CHITEST(), CHISQ.DIST() and CHISQ.INV() and equivalent Statistical functions, for both left- and right-tailed distributions. +- Support for ActiveSheet and SelectedCells in the ODS Reader and Writer. [PR #1908](https://github.com/PHPOffice/PhpSpreadsheet/pull/1908) + +### Changed + +- Nothing. + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- Fixed issue with quoted strings in number format mask rendered with toFormattedString() [Issue 1972#](https://github.com/PHPOffice/PhpSpreadsheet/issues/1972) [PR #1978](https://github.com/PHPOffice/PhpSpreadsheet/pull/1978) +- Fixed issue with percentage formats in number format mask rendered with toFormattedString() [Issue 1929#](https://github.com/PHPOffice/PhpSpreadsheet/issues/1929) [PR #1928](https://github.com/PHPOffice/PhpSpreadsheet/pull/1928) +- Fixed issue with _ spacing character in number format mask corrupting output from toFormattedString() [Issue 1924#](https://github.com/PHPOffice/PhpSpreadsheet/issues/1924) [PR #1927](https://github.com/PHPOffice/PhpSpreadsheet/pull/1927) +- Fix for [Issue #1887](https://github.com/PHPOffice/PhpSpreadsheet/issues/1887) - Lose Track of Selected Cells After Save +- Fixed issue with Xlsx@listWorksheetInfo not returning any data +- Fixed invalid arguments triggering mb_substr() error in LEFT(), MID() and RIGHT() text functions. [Issue #640](https://github.com/PHPOffice/PhpSpreadsheet/issues/640) +- Fix for [Issue #1916](https://github.com/PHPOffice/PhpSpreadsheet/issues/1916) - Invalid signature check for XML files + +## 1.17.1 - 2021-03-01 + +### Added + +- Implementation of the Excel `AVERAGEIFS()` functions as part of a restructuring of Database functions and Conditional Statistical functions. +- Support for date values and percentages in query parameters for Database functions, and the IF expressions in functions like COUNTIF() and AVERAGEIF(). [#1875](https://github.com/PHPOffice/PhpSpreadsheet/pull/1875) +- Support for booleans, and for wildcard text search in query parameters for Database functions, and the IF expressions in functions like COUNTIF() and AVERAGEIF(). [#1876](https://github.com/PHPOffice/PhpSpreadsheet/pull/1876) +- Implemented DataBar for conditional formatting in Xlsx, providing read/write and creation of (type, value, direction, fills, border, axis position, color settings) as DataBar options in Excel. [#1754](https://github.com/PHPOffice/PhpSpreadsheet/pull/1754) +- Alignment for ODS Writer [#1796](https://github.com/PHPOffice/PhpSpreadsheet/issues/1796) +- Basic implementation of the PERMUTATIONA() Statistical Function + +### Changed + +- Formula functions that previously called PHP functions directly are now processed through the Excel Functions classes; resolving issues with PHP8 stricter typing. [#1789](https://github.com/PHPOffice/PhpSpreadsheet/issues/1789) + + The following MathTrig functions are affected: + `ABS()`, `ACOS()`, `ACOSH()`, `ASIN()`, `ASINH()`, `ATAN()`, `ATANH()`, + `COS()`, `COSH()`, `DEGREES()` (rad2deg), `EXP()`, `LN()` (log), `LOG10()`, + `RADIANS()` (deg2rad), `SIN()`, `SINH()`, `SQRT()`, `TAN()`, `TANH()`. + + One TextData function is also affected: `REPT()` (str_repeat). +- `formatAsDate` correctly matches language metadata, reverting c55272e +- Formulae that previously crashed on sub function call returning excel error value now return said value. + The following functions are affected `CUMPRINC()`, `CUMIPMT()`, `AMORLINC()`, + `AMORDEGRC()`. +- Adapt some function error return value to match excel's error. + The following functions are affected `PPMT()`, `IPMT()`. + +### Deprecated + +- Calling many of the Excel formula functions directly rather than through the Calculation Engine. + + The logic for these Functions is now being moved out of the categorised `Database`, `DateTime`, `Engineering`, `Financial`, `Logical`, `LookupRef`, `MathTrig`, `Statistical`, `TextData` and `Web` classes into small, dedicated classes for individual functions or related groups of functions. + + This makes the logic in these classes easier to maintain; and will reduce the memory footprint required to execute formulae when calling these functions. + +### Removed + +- Nothing. + +### Fixed + +- Avoid Duplicate Titles When Reading Multiple HTML Files.[Issue #1823](https://github.com/PHPOffice/PhpSpreadsheet/issues/1823) [PR #1829](https://github.com/PHPOffice/PhpSpreadsheet/pull/1829) +- Fixed issue with Worksheet's `getCell()` method when trying to get a cell by defined name. [#1858](https://github.com/PHPOffice/PhpSpreadsheet/issues/1858) +- Fix possible endless loop in NumberFormat Masks [#1792](https://github.com/PHPOffice/PhpSpreadsheet/issues/1792) +- Fix problem resulting from literal dot inside quotes in number format masks. [PR #1830](https://github.com/PHPOffice/PhpSpreadsheet/pull/1830) +- Resolve Google Sheets Xlsx charts issue. Google Sheets uses oneCellAnchor positioning and does not include *Cache values in the exported Xlsx. [PR #1761](https://github.com/PHPOffice/PhpSpreadsheet/pull/1761) +- Fix for Xlsx Chart axis titles mapping to correct X or Y axis label when only one is present. [PR #1760](https://github.com/PHPOffice/PhpSpreadsheet/pull/1760) +- Fix For Null Exception on ODS Read of Page Settings. [#1772](https://github.com/PHPOffice/PhpSpreadsheet/issues/1772) +- Fix Xlsx reader overriding manually set number format with builtin number format. [PR #1805](https://github.com/PHPOffice/PhpSpreadsheet/pull/1805) +- Fix Xlsx reader cell alignment. [PR #1710](https://github.com/PHPOffice/PhpSpreadsheet/pull/1710) +- Fix for not yet implemented data-types in Open Document writer [Issue #1674](https://github.com/PHPOffice/PhpSpreadsheet/issues/1674) +- Fix XLSX reader when having a corrupt numeric cell data type [PR #1664](https://github.com/phpoffice/phpspreadsheet/pull/1664) +- Fix on `CUMPRINC()`, `CUMIPMT()`, `AMORLINC()`, `AMORDEGRC()` usage. When those functions called one of `YEARFRAC()`, `PPMT()`, `IPMT()` and they would get back an error value (represented as a string), trying to use numeral operands (`+`, `/`, `-`, `*`) on said return value and a number (`float or `int`) would fail. + +## 1.16.0 - 2020-12-31 + +### Added + +- CSV Reader - Best Guess for Encoding, and Handle Null-string Escape [#1647](https://github.com/PHPOffice/PhpSpreadsheet/issues/1647) + +### Changed + +- Updated the CONVERT() function to support all current MS Excel categories and Units of Measure. + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- Fix for Xls Reader when SST has a bad length [#1592](https://github.com/PHPOffice/PhpSpreadsheet/issues/1592) +- Resolve Xlsx loader issue whe hyperlinks don't have a destination +- Resolve issues when printer settings resources IDs clash with drawing IDs +- Resolve issue with SLK long filenames [#1612](https://github.com/PHPOffice/PhpSpreadsheet/issues/1612) +- ROUNDUP and ROUNDDOWN return incorrect results for values of 0 [#1627](https://github.com/phpoffice/phpspreadsheet/pull/1627) +- Apply Column and Row Styles to Existing Cells [#1712](https://github.com/PHPOffice/PhpSpreadsheet/issues/1712) [PR #1721](https://github.com/PHPOffice/PhpSpreadsheet/pull/1721) +- Resolve issues with defined names where worksheet doesn't exist (#1686)[https://github.com/PHPOffice/PhpSpreadsheet/issues/1686] and [#1723](https://github.com/PHPOffice/PhpSpreadsheet/issues/1723) - [PR #1742](https://github.com/PHPOffice/PhpSpreadsheet/pull/1742) +- Fix for issue [#1735](https://github.com/PHPOffice/PhpSpreadsheet/issues/1735) Incorrect activeSheetIndex after RemoveSheetByIndex - [PR #1743](https://github.com/PHPOffice/PhpSpreadsheet/pull/1743) +- Ensure that the list of shared formulae is maintained when an xlsx file is chunked with readFilter[Issue #169](https://github.com/PHPOffice/PhpSpreadsheet/issues/1669). +- Fix for notice during accessing "cached magnification factor" offset [#1354](https://github.com/PHPOffice/PhpSpreadsheet/pull/1354) +- Fix compatibility with ext-gd on php 8 + +### Security Fix (CVE-2020-7776) + +- Prevent XSS through cell comments in the HTML Writer. + +## 1.15.0 - 2020-10-11 + +### Added + +- Implemented Page Order for Xlsx and Xls Readers, and provided Page Settings (Orientation, Scale, Horizontal/Vertical Centering, Page Order, Margins) support for Ods, Gnumeric and Xls Readers [#1559](https://github.com/PHPOffice/PhpSpreadsheet/pull/1559) +- Implementation of the Excel `LOGNORM.DIST()`, `NORM.S.DIST()`, `GAMMA()` and `GAUSS()` functions. [#1588](https://github.com/PHPOffice/PhpSpreadsheet/pull/1588) +- Named formula implementation, and improved handling of Defined Names generally [#1535](https://github.com/PHPOffice/PhpSpreadsheet/pull/1535) + - Defined Names are now case-insensitive + - Distinction between named ranges and named formulae + - Correct handling of union and intersection operators in named ranges + - Correct evaluation of named range operators in calculations + - fix resolution of relative named range values in the calculation engine; previously all named range values had been treated as absolute. + - Calculation support for named formulae + - Support for nested ranges and formulae (named ranges and formulae that reference other named ranges/formulae) in calculations + - Introduction of a helper to convert address formats between R1C1 and A1 (and the reverse) + - Proper support for both named ranges and named formulae in all appropriate Readers + - **Xlsx** (Previously only simple named ranges were supported) + - **Xls** (Previously only simple named ranges were supported) + - **Gnumeric** (Previously neither named ranges nor formulae were supported) + - **Ods** (Previously neither named ranges nor formulae were supported) + - **Xml** (Previously neither named ranges nor formulae were supported) + - Proper support for named ranges and named formulae in all appropriate Writers + - **Xlsx** (Previously only simple named ranges were supported) + - **Xls** (Previously neither named ranges nor formulae were supported) - Still not supported, but some parser issues resolved that previously failed to differentiate between a defined name and a function name + - **Ods** (Previously neither named ranges nor formulae were supported) +- Support for PHP 8.0 + +### Changed + +- Improve Coverage for ODS Reader [#1545](https://github.com/phpoffice/phpspreadsheet/pull/1545) +- Named formula implementation, and improved handling of Defined Names generally [#1535](https://github.com/PHPOffice/PhpSpreadsheet/pull/1535) +- fix resolution of relative named range values in the calculation engine; previously all named range values had been treated as absolute. +- Drop $this->spreadSheet null check from Xlsx Writer [#1646](https://github.com/phpoffice/phpspreadsheet/pull/1646) +- Improving Coverage for Excel2003 XML Reader [#1557](https://github.com/phpoffice/phpspreadsheet/pull/1557) + +### Deprecated + +- **IMPORTANT NOTE:** This Introduces a **BC break** in the handling of named ranges. Previously, a named range cell reference of `B2` would be treated identically to a named range cell reference of `$B2` or `B$2` or `$B$2` because the calculation engine treated then all as absolute references. These changes "fix" that, so the calculation engine now handles relative references in named ranges correctly. + This change that resolves previously incorrect behaviour in the calculation may affect users who have dynamically defined named ranges using relative references when they should have used absolute references. + +### Removed + +- Nothing. + +### Fixed + +- PrintArea causes exception [#1544](https://github.com/phpoffice/phpspreadsheet/pull/1544) +- Calculation/DateTime Failure With PHP8 [#1661](https://github.com/phpoffice/phpspreadsheet/pull/1661) +- Reader/Gnumeric Failure with PHP8 [#1662](https://github.com/phpoffice/phpspreadsheet/pull/1662) +- ReverseSort bug, exposed but not caused by PHP8 [#1660](https://github.com/phpoffice/phpspreadsheet/pull/1660) +- Bug setting Superscript/Subscript to false [#1567](https://github.com/phpoffice/phpspreadsheet/pull/1567) + +## 1.14.1 - 2020-07-19 + +### Added + +- nothing + +### Fixed + +- WEBSERVICE is HTTP client agnostic and must be configured via `Settings::setHttpClient()` [#1562](https://github.com/PHPOffice/PhpSpreadsheet/issues/1562) +- Borders were not complete on rowspanned columns using HTML reader [#1473](https://github.com/PHPOffice/PhpSpreadsheet/pull/1473) + +### Changed + +## 1.14.0 - 2020-06-29 + +### Added + +- Add support for IFS() logical function [#1442](https://github.com/PHPOffice/PhpSpreadsheet/pull/1442) +- Add Cell Address Helper to provide conversions between the R1C1 and A1 address formats [#1558](https://github.com/PHPOffice/PhpSpreadsheet/pull/1558) +- Add ability to edit Html/Pdf before saving [#1499](https://github.com/PHPOffice/PhpSpreadsheet/pull/1499) +- Add ability to set codepage explicitly for BIFF5 [#1018](https://github.com/PHPOffice/PhpSpreadsheet/issues/1018) +- Added support for the WEBSERVICE function [#1409](https://github.com/PHPOffice/PhpSpreadsheet/pull/1409) + +### Fixed + +- Resolve evaluation of utf-8 named ranges in calculation engine [#1522](https://github.com/PHPOffice/PhpSpreadsheet/pull/1522) +- Fix HLOOKUP on single row [#1512](https://github.com/PHPOffice/PhpSpreadsheet/pull/1512) +- Fix MATCH when comparing different numeric types [#1521](https://github.com/PHPOffice/PhpSpreadsheet/pull/1521) +- Fix exact MATCH on ranges with empty cells [#1520](https://github.com/PHPOffice/PhpSpreadsheet/pull/1520) +- Fix for Issue [#1516](https://github.com/PHPOffice/PhpSpreadsheet/issues/1516) (Cloning worksheet makes corrupted Xlsx) [#1530](https://github.com/PHPOffice/PhpSpreadsheet/pull/1530) +- Fix For Issue [#1509](https://github.com/PHPOffice/PhpSpreadsheet/issues/1509) (Can not set empty enclosure for CSV) [#1518](https://github.com/PHPOffice/PhpSpreadsheet/pull/1518) +- Fix for Issue [#1505](https://github.com/PHPOffice/PhpSpreadsheet/issues/1505) (TypeError : Argument 4 passed to PhpOffice\PhpSpreadsheet\Writer\Xlsx\Worksheet::writeAttributeIf() must be of the type string) [#1525](https://github.com/PHPOffice/PhpSpreadsheet/pull/1525) +- Fix for Issue [#1495](https://github.com/PHPOffice/PhpSpreadsheet/issues/1495) (Sheet index being changed when multiple sheets are used in formula) [#1500]((https://github.com/PHPOffice/PhpSpreadsheet/pull/1500)) +- Fix for Issue [#1533](https://github.com/PHPOffice/PhpSpreadsheet/issues/1533) (A reference to a cell containing a string starting with "#" leads to errors in the generated xlsx.) [#1534](https://github.com/PHPOffice/PhpSpreadsheet/pull/1534) +- Xls Writer - Correct Timestamp Bug [#1493](https://github.com/PHPOffice/PhpSpreadsheet/pull/1493) +- Don't ouput row and columns without any cells in HTML writer [#1235](https://github.com/PHPOffice/PhpSpreadsheet/issues/1235) + +## 1.13.0 - 2020-05-31 ### Added - Support writing to streams in all writers [#1292](https://github.com/PHPOffice/PhpSpreadsheet/issues/1292) - Support CSV files with data wrapping a lot of lines [#1468](https://github.com/PHPOffice/PhpSpreadsheet/pull/1468) +- Support protection of worksheet by a specific hash algorithm [#1485](https://github.com/PHPOffice/PhpSpreadsheet/pull/1485) ### Fixed @@ -19,6 +229,10 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Fix RATE, PRICE, XIRR, and XNPV Functions [#1456](https://github.com/PHPOffice/PhpSpreadsheet/pull/1456) - Save Excel 2010+ functions properly in XLSX [#1461](https://github.com/PHPOffice/PhpSpreadsheet/pull/1461) - Several improvements in HTML writer [#1464](https://github.com/PHPOffice/PhpSpreadsheet/pull/1464) +- Fix incorrect behaviour when saving XLSX file with drawings [#1462](https://github.com/PHPOffice/PhpSpreadsheet/pull/1462), +- Fix Crash while trying setting a cell the value "123456\n" [#1476](https://github.com/PHPOffice/PhpSpreadsheet/pull/1481) +- Improved DATEDIF() function and reduced errors for Y and YM units [#1466](https://github.com/PHPOffice/PhpSpreadsheet/pull/1466) +- Stricter typing for mergeCells [#1494](https://github.com/PHPOffice/PhpSpreadsheet/pull/1494) ### Changed @@ -26,7 +240,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Drop partial migration tool in favor of complete migration via RectorPHP [#1445](https://github.com/PHPOffice/PhpSpreadsheet/issues/1445) - Limit composer package to `src/` [#1424](https://github.com/PHPOffice/PhpSpreadsheet/pull/1424) -## [1.12.0] - 2020-04-27 +## 1.12.0 - 2020-04-27 ### Added @@ -42,7 +256,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - MATCH with a static array should return the position of the found value based on the values submitted [#1332](https://github.com/PHPOffice/PhpSpreadsheet/pull/1332) - Fix Xlsx Reader's handling of undefined fill color [#1353](https://github.com/PHPOffice/PhpSpreadsheet/pull/1353) -## [1.11.0] - 2020-03-02 +## 1.11.0 - 2020-03-02 ### Added @@ -61,7 +275,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Fix XLSX file loading with autofilter containing '$' [#1326](https://github.com/PHPOffice/PhpSpreadsheet/pull/1326) - PHPDoc - Use `@return $this` for fluent methods [#1362](https://github.com/PHPOffice/PhpSpreadsheet/pull/1362) -## [1.10.1] - 2019-12-02 +## 1.10.1 - 2019-12-02 ### Changed @@ -76,7 +290,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - ODS writer prevent invalid numeric value if locale decimal separator is comma [#1268](https://github.com/PHPOffice/PhpSpreadsheet/pull/1268) - Xlsx writer actually writes plotVisOnly and dispBlanksAs from chart properties [#1266](https://github.com/PHPOffice/PhpSpreadsheet/pull/1266) -## [1.10.0] - 2019-11-18 +## 1.10.0 - 2019-11-18 ### Changed @@ -101,7 +315,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Validate XIRR inputs and return correct error values [#1120](https://github.com/PHPOffice/PhpSpreadsheet/issues/1120) - Allow to read xlsx files with exotic workbook names like "workbook2.xml" [#1183](https://github.com/PHPOffice/PhpSpreadsheet/pull/1183) -## [1.9.0] - 2019-08-17 +## 1.9.0 - 2019-08-17 ### Changed @@ -132,20 +346,20 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Fix `getCalculatedValue()` error with more than two INDIRECT [#1115](https://github.com/PHPOffice/PhpSpreadsheet/pull/1115) - Writer\Html did not hide columns [#985](https://github.com/PHPOffice/PhpSpreadsheet/pull/985) -## [1.8.2] - 2019-07-08 +## 1.8.2 - 2019-07-08 ### Fixed - Uncaught error when opening ods file and properties aren't defined [#1047](https://github.com/PHPOffice/PhpSpreadsheet/issues/1047) - Xlsx Reader Cell datavalidations bug [#1052](https://github.com/PHPOffice/PhpSpreadsheet/pull/1052) -## [1.8.1] - 2019-07-02 +## 1.8.1 - 2019-07-02 ### Fixed - Allow nullable theme for Xlsx Style Reader class [#1043](https://github.com/PHPOffice/PhpSpreadsheet/issues/1043) -## [1.8.0] - 2019-07-01 +## 1.8.0 - 2019-07-01 ### Security Fix (CVE-2019-12331) @@ -168,7 +382,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Fix a SUMIF warning with some versions of PHP when having different length of arrays provided as input [#873](https://github.com/PHPOffice/PhpSpreadsheet/pull/873) - Fix incorrectly handled backslash-escaped space characters in number format -## [1.7.0] - 2019-05-26 +## 1.7.0 - 2019-05-26 - Added support for inline styles in Html reader (borders, alignment, width, height) - QuotedText cells no longer treated as formulae if the content begins with a `=` @@ -182,7 +396,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Fix VLOOKUP - Fix return type hint -## [1.6.0] - 2019-01-02 +## 1.6.0 - 2019-01-02 ### Added @@ -210,13 +424,13 @@ and this project adheres to [Semantic Versioning](https://semver.org). - `master` is the new default branch, `develop` does not exist anymore -## [1.5.2] - 2018-11-25 +## 1.5.2 - 2018-11-25 ### Security - Improvements to the design of the XML Security Scanner [#771](https://github.com/PHPOffice/PhpSpreadsheet/issues/771) -## [1.5.1] - 2018-11-20 +## 1.5.1 - 2018-11-20 ### Security @@ -236,7 +450,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Mpdf export can exceed pcre.backtrack_limit [#637](https://github.com/PHPOffice/PhpSpreadsheet/issues/637) - Fix index overflow on data values array [#748](https://github.com/PHPOffice/PhpSpreadsheet/pull/748) -## [1.5.0] - 2018-10-21 +## 1.5.0 - 2018-10-21 ### Added @@ -251,7 +465,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - OFFSET should allow omitted height and width [#561](https://github.com/PHPOffice/PhpSpreadsheet/issues/561) - Correctly determine delimiter when CSV contains line breaks inside enclosures [#716](https://github.com/PHPOffice/PhpSpreadsheet/issues/716) -## [1.4.1] - 2018-09-30 +## 1.4.1 - 2018-09-30 ### Fixed @@ -260,7 +474,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Fix warning when reading xlsx without styles [#631](https://github.com/PHPOffice/PhpSpreadsheet/pull/631) - Fix broken sample links on windows due to $baseDir having backslash [#653](https://github.com/PHPOffice/PhpSpreadsheet/pull/653) -## [1.4.0] - 2018-08-06 +## 1.4.0 - 2018-08-06 ### Added @@ -324,13 +538,13 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Migration tool keep variables containing $PHPExcel untouched [#598](https://github.com/PHPOffice/PhpSpreadsheet/issues/598) - Rowspans/colspans were incorrect when adding worksheet using loadIntoExisting [#619](https://github.com/PHPOffice/PhpSpreadsheet/issues/619) -## [1.3.1] - 2018-06-12 +## 1.3.1 - 2018-06-12 ### Fixed - Ranges across Z and AA columns incorrectly threw an exception [#545](https://github.com/PHPOffice/PhpSpreadsheet/issues/545) -## [1.3.0] - 2018-06-10 +## 1.3.0 - 2018-06-10 ### Added @@ -349,13 +563,13 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Fixed parsing of conditionals in COUNTIF functions [#526](https://github.com/PHPOffice/PhpSpreadsheet/issues/526) - Corruption errors for saved Xlsx docs with frozen panes [#532](https://github.com/PHPOffice/PhpSpreadsheet/issues/532) -## [1.2.1] - 2018-04-10 +## 1.2.1 - 2018-04-10 ### Fixed - Plain text and richtext mixed in same cell can be read [#442](https://github.com/PHPOffice/PhpSpreadsheet/issues/442) -## [1.2.0] - 2018-03-04 +## 1.2.0 - 2018-03-04 ### Added @@ -373,7 +587,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Select correct cell when calling freezePane [#389](https://github.com/PHPOffice/PhpSpreadsheet/issues/389) - `setStrikethrough()` did not set the font [#403](https://github.com/PHPOffice/PhpSpreadsheet/issues/403) -## [1.1.0] - 2018-01-28 +## 1.1.0 - 2018-01-28 ### Added @@ -391,7 +605,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Support XML file without styles [#331](https://github.com/PHPOffice/PhpSpreadsheet/pull/331) - Cell coordinates which are already a range cause an exception [#319](https://github.com/PHPOffice/PhpSpreadsheet/issues/319) -## [1.0.0] - 2017-12-25 +## 1.0.0 - 2017-12-25 ### Added @@ -410,7 +624,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Constant `TYPE_DOUGHTNUTCHART` is now `TYPE_DOUGHNUTCHART`. -## [1.0.0-beta2] - 2017-11-26 +## 1.0.0-beta2 - 2017-11-26 ### Added @@ -448,7 +662,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - `PhpSpreadsheet\Style` => `PhpSpreadsheet\Style\Style` - `PhpSpreadsheet\Worksheet` => `PhpSpreadsheet\Worksheet\Worksheet` -## [1.0.0-beta] - 2017-08-17 +## 1.0.0-beta - 2017-08-17 ### Added @@ -468,6 +682,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Ignore inlineStr type if formula element exists - @ncrypthic [#570](https://github.com/PHPOffice/PHPExcel/issues/570) - Excel 2007 Reader freezes because of conditional formatting - @rentalhost [#575](https://github.com/PHPOffice/PHPExcel/issues/575) - Readers will now parse files containing worksheet titles over 31 characters [#176](https://github.com/PHPOffice/PhpSpreadsheet/pull/176) +- Fixed PHP8 deprecation warning for libxml_disable_entity_loader() [#1625](https://github.com/phpoffice/phpspreadsheet/pull/1625) ### General diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index aed13fe2db..f59535331c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,3 +9,12 @@ If you would like to contribute, here are some notes and guidelines: - All code changes must be validated by `composer check` - [Helpful article about forking](https://help.github.com/articles/fork-a-repo/ "Forking a GitHub repository") - [Helpful article about pull requests](https://help.github.com/articles/using-pull-requests/ "Pull Requests") + +## How to release + +1. Complete CHANGELOG.md and commit +2. Create an annotated tag + 1. `git tag -a 1.2.3` + 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 tag with `git push --tags`, GitHub Actions will create a GitHub release automatically diff --git a/README.md b/README.md index 893b3784b1..2a94e0d3d5 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,15 @@ # PhpSpreadsheet -[![Build Status](https://travis-ci.org/PHPOffice/PhpSpreadsheet.svg?branch=master)](https://travis-ci.org/PHPOffice/PhpSpreadsheet) +[![Build Status](https://github.com/PHPOffice/PhpSpreadsheet/workflows/main/badge.svg)](https://github.com/PHPOffice/PhpSpreadsheet/actions) [![Code Quality](https://scrutinizer-ci.com/g/PHPOffice/PhpSpreadsheet/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/PHPOffice/PhpSpreadsheet/?branch=master) [![Code Coverage](https://scrutinizer-ci.com/g/PHPOffice/PhpSpreadsheet/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/PHPOffice/PhpSpreadsheet/?branch=master) -[![Total Downloads](https://poser.pugx.org/phpoffice/phpspreadsheet/downloads.png)](https://packagist.org/packages/phpoffice/phpspreadsheet) -[![Latest Stable Version](https://poser.pugx.org/phpoffice/phpspreadsheet/v/stable.png)](https://packagist.org/packages/phpoffice/phpspreadsheet) -[![License](https://poser.pugx.org/phpoffice/phpspreadsheet/license.png)](https://packagist.org/packages/phpoffice/phpspreadsheet) +[![Total Downloads](https://img.shields.io/packagist/dt/PHPOffice/PhpSpreadsheet)](https://packagist.org/packages/phpoffice/phpspreadsheet) +[![Latest Stable Version](https://img.shields.io/github/v/release/PHPOffice/PhpSpreadsheet)](https://packagist.org/packages/phpoffice/phpspreadsheet) +[![License](https://img.shields.io/github/license/PHPOffice/PhpSpreadsheet)](https://packagist.org/packages/phpoffice/phpspreadsheet) [![Join the chat at https://gitter.im/PHPOffice/PhpSpreadsheet](https://img.shields.io/badge/GITTER-join%20chat-green.svg)](https://gitter.im/PHPOffice/PhpSpreadsheet) -PhpSpreadsheet is a library written in pure PHP and providing a set of classes that allow you to read from and to write to different spreadsheet file formats, like Excel and LibreOffice Calc. +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. ## Documentation diff --git a/composer.json b/composer.json index 6ce3a72830..d0c3a16daf 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,19 @@ { "name": "phpoffice/phpspreadsheet", "description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine", - "keywords": ["PHP", "OpenXML", "Excel", "xlsx", "xls", "ods", "gnumeric", "spreadsheet"], + "keywords": [ + "PHP", + "OpenXML", + "Excel", + "xlsx", + "xls", + "ods", + "gnumeric", + "spreadsheet" + ], + "config": { + "sort-packages": true + }, "homepage": "https://github.com/PHPOffice/PhpSpreadsheet", "type": "library", "license": "MIT", @@ -28,8 +40,9 @@ "scripts": { "check": [ "php-cs-fixer fix --ansi --dry-run --diff", - "phpcs --report-width=200 samples/ src/ tests/ --ignore=samples/Header.php --standard=PSR2 -n", - "phpunit --color=always" + "phpcs", + "phpunit --color=always", + "phpstan analyse --ansi" ], "fix": [ "php-cs-fixer fix --ansi" @@ -39,39 +52,43 @@ ] }, "require": { - "php": "^7.2", + "php": "^7.2 || ^8.0", + "ext-simplexml": "*", "ext-ctype": "*", "ext-dom": "*", + "ext-fileinfo": "*", "ext-gd": "*", "ext-iconv": "*", - "ext-fileinfo": "*", "ext-libxml": "*", "ext-mbstring": "*", - "ext-SimpleXML": "*", "ext-xml": "*", "ext-xmlreader": "*", "ext-xmlwriter": "*", "ext-zip": "*", "ext-zlib": "*", - "markbaker/complex": "^1.4", - "markbaker/matrix": "^1.2", - "psr/simple-cache": "^1.0", - "maennchen/zipstream-php": "^2.0" + "ezyang/htmlpurifier": "^4.13", + "maennchen/zipstream-php": "^2.1", + "markbaker/complex": "^2.0", + "markbaker/matrix": "^2.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0", + "psr/simple-cache": "^1.0" }, "require-dev": { - "dompdf/dompdf": "^0.8.5", - "friendsofphp/php-cs-fixer": "^2.16", + "dompdf/dompdf": "^1.0", + "friendsofphp/php-cs-fixer": "^2.18", "jpgraph/jpgraph": "^4.0", "mpdf/mpdf": "^8.0", "phpcompatibility/php-compatibility": "^9.3", + "phpstan/phpstan": "^0.12.82", "phpunit/phpunit": "^8.5", "squizlabs/php_codesniffer": "^3.5", "tecnickcom/tcpdf": "^6.3" }, "suggest": { "mpdf/mpdf": "Option for rendering PDF with PDF Writer", - "dompdf/dompdf": "Option for rendering PDF with PDF Writer", - "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer", + "dompdf/dompdf": "Option for rendering PDF with PDF Writer (doesn't yet support PHP8)", + "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer (doesn't yet support PHP8)", "jpgraph/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers" }, "autoload": { diff --git a/composer.lock b/composer.lock index 41ec1b9b75..e4060972b4 100644 --- a/composer.lock +++ b/composer.lock @@ -4,27 +4,81 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ab06908c3ff8187971def16c578f1ced", + "content-hash": "89b62d75519340c289a3a763245f1ca0", "packages": [ + { + "name": "ezyang/htmlpurifier", + "version": "v4.13.0", + "source": { + "type": "git", + "url": "https://github.com/ezyang/htmlpurifier.git", + "reference": "08e27c97e4c6ed02f37c5b2b20488046c8d90d75" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/08e27c97e4c6ed02f37c5b2b20488046c8d90d75", + "reference": "08e27c97e4c6ed02f37c5b2b20488046c8d90d75", + "shasum": "" + }, + "require": { + "php": ">=5.2" + }, + "require-dev": { + "simpletest/simpletest": "dev-master#72de02a7b80c6bb8864ef9bf66d41d2f58f826bd" + }, + "type": "library", + "autoload": { + "psr-0": { + "HTMLPurifier": "library/" + }, + "files": [ + "library/HTMLPurifier.composer.php" + ], + "exclude-from-classmap": [ + "/library/HTMLPurifier/Language/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-or-later" + ], + "authors": [ + { + "name": "Edward Z. Yang", + "email": "admin@htmlpurifier.org", + "homepage": "http://ezyang.com" + } + ], + "description": "Standards compliant HTML filter written in PHP", + "homepage": "http://htmlpurifier.org/", + "keywords": [ + "html" + ], + "support": { + "issues": "https://github.com/ezyang/htmlpurifier/issues", + "source": "https://github.com/ezyang/htmlpurifier/tree/master" + }, + "time": "2020-06-29T00:56:53+00:00" + }, { "name": "maennchen/zipstream-php", - "version": "2.0.0", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/maennchen/ZipStream-PHP.git", - "reference": "9ceee828f9620b2e5c075e551ec7ed8a7035ac95" + "reference": "c4c5803cc1f93df3d2448478ef79394a5981cc58" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/9ceee828f9620b2e5c075e551ec7ed8a7035ac95", - "reference": "9ceee828f9620b2e5c075e551ec7ed8a7035ac95", + "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/c4c5803cc1f93df3d2448478ef79394a5981cc58", + "reference": "c4c5803cc1f93df3d2448478ef79394a5981cc58", "shasum": "" }, "require": { - "ext-mbstring": "*", "myclabs/php-enum": "^1.5", "php": ">= 7.1", - "psr/http-message": "^1.0" + "psr/http-message": "^1.0", + "symfony/polyfill-mbstring": "^1.0" }, "require-dev": { "ext-zip": "*", @@ -65,34 +119,44 @@ "stream", "zip" ], - "time": "2020-02-23T01:48:39+00:00" + "support": { + "issues": "https://github.com/maennchen/ZipStream-PHP/issues", + "source": "https://github.com/maennchen/ZipStream-PHP/tree/master" + }, + "funding": [ + { + "url": "https://opencollective.com/zipstream", + "type": "open_collective" + } + ], + "time": "2020-05-30T13:11:16+00:00" }, { "name": "markbaker/complex", - "version": "1.4.8", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/MarkBaker/PHPComplex.git", - "reference": "8eaa40cceec7bf0518187530b2e63871be661b72" + "reference": "9999f1432fae467bc93c53f357105b4c31bb994c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/8eaa40cceec7bf0518187530b2e63871be661b72", - "reference": "8eaa40cceec7bf0518187530b2e63871be661b72", + "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/9999f1432fae467bc93c53f357105b4c31bb994c", + "reference": "9999f1432fae467bc93c53f357105b4c31bb994c", "shasum": "" }, "require": { - "php": "^5.6.0|^7.0.0" + "php": "^7.2 || ^8.0" }, "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.5.0", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", "phpcompatibility/php-compatibility": "^9.0", "phpdocumentor/phpdocumentor": "2.*", - "phploc/phploc": "2.*", + "phploc/phploc": "^4.0", "phpmd/phpmd": "2.*", - "phpunit/phpunit": "^4.8.35|^5.4.0", - "sebastian/phpcpd": "2.*", - "squizlabs/php_codesniffer": "^3.4.0" + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.3", + "sebastian/phpcpd": "^4.0", + "squizlabs/php_codesniffer": "^3.4" }, "type": "library", "autoload": { @@ -160,33 +224,38 @@ "complex", "mathematics" ], - "time": "2020-03-11T20:15:49+00:00" + "support": { + "issues": "https://github.com/MarkBaker/PHPComplex/issues", + "source": "https://github.com/MarkBaker/PHPComplex/tree/PHP8" + }, + "time": "2020-08-26T10:42:07+00:00" }, { "name": "markbaker/matrix", - "version": "1.2.0", + "version": "2.1.2", "source": { "type": "git", "url": "https://github.com/MarkBaker/PHPMatrix.git", - "reference": "5348c5a67e3b75cd209d70103f916a93b1f1ed21" + "reference": "361c0f545c3172ee26c3d596a0aa03f0cef65e6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/5348c5a67e3b75cd209d70103f916a93b1f1ed21", - "reference": "5348c5a67e3b75cd209d70103f916a93b1f1ed21", + "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/361c0f545c3172ee26c3d596a0aa03f0cef65e6a", + "reference": "361c0f545c3172ee26c3d596a0aa03f0cef65e6a", "shasum": "" }, "require": { - "php": "^5.6.0|^7.0.0" + "php": "^7.1 || ^8.0" }, "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "dev-master", - "phpcompatibility/php-compatibility": "dev-master", - "phploc/phploc": "^4", - "phpmd/phpmd": "dev-master", - "phpunit/phpunit": "^5.7", - "sebastian/phpcpd": "^3.0", - "squizlabs/php_codesniffer": "^3.0@dev" + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", + "phpcompatibility/php-compatibility": "^9.0", + "phpdocumentor/phpdocumentor": "2.*", + "phploc/phploc": "^4.0", + "phpmd/phpmd": "2.*", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.3", + "sebastian/phpcpd": "^4.0", + "squizlabs/php_codesniffer": "^3.4" }, "type": "library", "autoload": { @@ -194,22 +263,22 @@ "Matrix\\": "classes/src/" }, "files": [ - "classes/src/functions/adjoint.php", - "classes/src/functions/antidiagonal.php", - "classes/src/functions/cofactors.php", - "classes/src/functions/determinant.php", - "classes/src/functions/diagonal.php", - "classes/src/functions/identity.php", - "classes/src/functions/inverse.php", - "classes/src/functions/minors.php", - "classes/src/functions/trace.php", - "classes/src/functions/transpose.php", - "classes/src/operations/add.php", - "classes/src/operations/directsum.php", - "classes/src/operations/subtract.php", - "classes/src/operations/multiply.php", - "classes/src/operations/divideby.php", - "classes/src/operations/divideinto.php" + "classes/src/Functions/adjoint.php", + "classes/src/Functions/antidiagonal.php", + "classes/src/Functions/cofactors.php", + "classes/src/Functions/determinant.php", + "classes/src/Functions/diagonal.php", + "classes/src/Functions/identity.php", + "classes/src/Functions/inverse.php", + "classes/src/Functions/minors.php", + "classes/src/Functions/trace.php", + "classes/src/Functions/transpose.php", + "classes/src/Operations/add.php", + "classes/src/Operations/directsum.php", + "classes/src/Operations/subtract.php", + "classes/src/Operations/multiply.php", + "classes/src/Operations/divideby.php", + "classes/src/Operations/divideinto.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -219,7 +288,7 @@ "authors": [ { "name": "Mark Baker", - "email": "mark@lange.demon.co.uk" + "email": "mark@demon-angel.eu" } ], "description": "PHP Class for working with matrices", @@ -229,20 +298,24 @@ "matrix", "vector" ], - "time": "2019-10-06T11:29:25+00:00" + "support": { + "issues": "https://github.com/MarkBaker/PHPMatrix/issues", + "source": "https://github.com/MarkBaker/PHPMatrix/tree/2.1.2" + }, + "time": "2021-01-23T16:37:31+00:00" }, { "name": "myclabs/php-enum", - "version": "1.7.6", + "version": "1.7.7", "source": { "type": "git", "url": "https://github.com/myclabs/php-enum.git", - "reference": "5f36467c7a87e20fbdc51e524fd8f9d1de80187c" + "reference": "d178027d1e679832db9f38248fcc7200647dc2b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/php-enum/zipball/5f36467c7a87e20fbdc51e524fd8f9d1de80187c", - "reference": "5f36467c7a87e20fbdc51e524fd8f9d1de80187c", + "url": "https://api.github.com/repos/myclabs/php-enum/zipball/d178027d1e679832db9f38248fcc7200647dc2b7", + "reference": "d178027d1e679832db9f38248fcc7200647dc2b7", "shasum": "" }, "require": { @@ -275,7 +348,128 @@ "keywords": [ "enum" ], - "time": "2020-02-14T08:15:52+00:00" + "support": { + "issues": "https://github.com/myclabs/php-enum/issues", + "source": "https://github.com/myclabs/php-enum/tree/1.7.7" + }, + "funding": [ + { + "url": "https://github.com/mnapoli", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/php-enum", + "type": "tidelift" + } + ], + "time": "2020-11-14T18:14:52+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client/tree/master" + }, + "time": "2020-06-29T06:28:15+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "shasum": "" + }, + "require": { + "php": ">=7.0.0", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory/tree/master" + }, + "time": "2019-04-30T12:38:16+00:00" }, { "name": "psr/http-message", @@ -325,6 +519,9 @@ "request", "response" ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/master" + }, "time": "2016-08-06T14:39:51+00:00" }, { @@ -373,34 +570,118 @@ "psr-16", "simple-cache" ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/master" + }, "time": "2017-10-23T01:57:42+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "5232de97ee3b75b0360528dae24e73db49566ab1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/5232de97ee3b75b0360528dae24e73db49566ab1", + "reference": "5232de97ee3b75b0360528dae24e73db49566ab1", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.22.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-01-22T09:19:47+00:00" } ], "packages-dev": [ { "name": "composer/semver", - "version": "1.5.1", + "version": "3.2.4", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "c6bea70230ef4dd483e6bbcab6005f682ed3a8de" + "reference": "a02fdf930a3c1c3ed3a49b5f63859c0c20e10464" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/c6bea70230ef4dd483e6bbcab6005f682ed3a8de", - "reference": "c6bea70230ef4dd483e6bbcab6005f682ed3a8de", + "url": "https://api.github.com/repos/composer/semver/zipball/a02fdf930a3c1c3ed3a49b5f63859c0c20e10464", + "reference": "a02fdf930a3c1c3ed3a49b5f63859c0c20e10464", "shasum": "" }, "require": { - "php": "^5.3.2 || ^7.0" + "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^4.5 || ^5.0.5" + "phpstan/phpstan": "^0.12.54", + "symfony/phpunit-bridge": "^4.2 || ^5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-main": "3.x-dev" } }, "autoload": { @@ -436,20 +717,39 @@ "validation", "versioning" ], - "time": "2020-01-13T12:06:48+00:00" + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.2.4" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2020-11-13T08:59:24+00:00" }, { "name": "composer/xdebug-handler", - "version": "1.4.1", + "version": "1.4.6", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "1ab9842d69e64fb3a01be6b656501032d1b78cb7" + "reference": "f27e06cd9675801df441b3656569b328e04aa37c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/1ab9842d69e64fb3a01be6b656501032d1b78cb7", - "reference": "1ab9842d69e64fb3a01be6b656501032d1b78cb7", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/f27e06cd9675801df441b3656569b328e04aa37c", + "reference": "f27e06cd9675801df441b3656569b328e04aa37c", "shasum": "" }, "require": { @@ -457,7 +757,8 @@ "psr/log": "^1.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8" + "phpstan/phpstan": "^0.12.55", + "symfony/phpunit-bridge": "^4.2 || ^5" }, "type": "library", "autoload": { @@ -480,43 +781,53 @@ "Xdebug", "performance" ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/1.4.6" + }, "funding": [ { "url": "https://packagist.com", "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" } ], - "time": "2020-03-01T12:26:26+00:00" + "time": "2021-03-25T17:01:18+00:00" }, { "name": "doctrine/annotations", - "version": "1.10.2", + "version": "1.12.1", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "b9d758e831c70751155c698c2f7df4665314a1cb" + "reference": "b17c5014ef81d212ac539f07a1001832df1b6d3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/b9d758e831c70751155c698c2f7df4665314a1cb", - "reference": "b9d758e831c70751155c698c2f7df4665314a1cb", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/b17c5014ef81d212ac539f07a1001832df1b6d3b", + "reference": "b17c5014ef81d212ac539f07a1001832df1b6d3b", "shasum": "" }, "require": { "doctrine/lexer": "1.*", "ext-tokenizer": "*", - "php": "^7.1" + "php": "^7.1 || ^8.0" }, "require-dev": { "doctrine/cache": "1.*", - "phpunit/phpunit": "^7.5" + "doctrine/coding-standard": "^6.0 || ^8.1", + "phpstan/phpstan": "^0.12.20", + "phpunit/phpunit": "^7.5 || ^9.1.5" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.9.x-dev" - } - }, "autoload": { "psr-4": { "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" @@ -549,46 +860,45 @@ } ], "description": "Docblock Annotations Parser", - "homepage": "http://www.doctrine-project.org", + "homepage": "https://www.doctrine-project.org/projects/annotations.html", "keywords": [ "annotations", "docblock", "parser" ], - "time": "2020-04-20T09:18:32+00:00" + "support": { + "issues": "https://github.com/doctrine/annotations/issues", + "source": "https://github.com/doctrine/annotations/tree/1.12.1" + }, + "time": "2021-02-21T21:00:45+00:00" }, { "name": "doctrine/instantiator", - "version": "1.3.0", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "ae466f726242e637cebdd526a7d991b9433bacf1" + "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/ae466f726242e637cebdd526a7d991b9433bacf1", - "reference": "ae466f726242e637cebdd526a7d991b9433bacf1", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b", + "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^6.0", + "doctrine/coding-standard": "^8.0", "ext-pdo": "*", "ext-phar": "*", - "phpbench/phpbench": "^0.13", - "phpstan/phpstan-phpunit": "^0.11", - "phpstan/phpstan-shim": "^0.11", - "phpunit/phpunit": "^7.0" + "phpbench/phpbench": "^0.13 || 1.0.0-alpha2", + "phpstan/phpstan": "^0.12", + "phpstan/phpstan-phpunit": "^0.12", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - }, "autoload": { "psr-4": { "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" @@ -602,7 +912,7 @@ { "name": "Marco Pivetta", "email": "ocramius@gmail.com", - "homepage": "http://ocramius.github.com/" + "homepage": "https://ocramius.github.io/" } ], "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", @@ -611,24 +921,42 @@ "constructor", "instantiate" ], - "time": "2019-10-21T16:45:58+00:00" + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.4.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2020-11-10T18:47:58+00:00" }, { "name": "doctrine/lexer", - "version": "1.2.0", + "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6" + "reference": "e864bbf5904cb8f5bb334f99209b48018522f042" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6", - "reference": "5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/e864bbf5904cb8f5bb334f99209b48018522f042", + "reference": "e864bbf5904cb8f5bb334f99209b48018522f042", "shasum": "" }, "require": { - "php": "^7.2" + "php": "^7.2 || ^8.0" }, "require-dev": { "doctrine/coding-standard": "^6.0", @@ -673,37 +1001,57 @@ "parser", "php" ], - "time": "2019-10-30T14:39:59+00:00" + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/1.2.1" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2020-05-25T17:44:05+00:00" }, { "name": "dompdf/dompdf", - "version": "v0.8.5", + "version": "v1.0.2", "source": { "type": "git", "url": "https://github.com/dompdf/dompdf.git", - "reference": "6782abfc090b132134cd6cea0ec6d76f0fce2c56" + "reference": "8768448244967a46d6e67b891d30878e0e15d25c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dompdf/dompdf/zipball/6782abfc090b132134cd6cea0ec6d76f0fce2c56", - "reference": "6782abfc090b132134cd6cea0ec6d76f0fce2c56", + "url": "https://api.github.com/repos/dompdf/dompdf/zipball/8768448244967a46d6e67b891d30878e0e15d25c", + "reference": "8768448244967a46d6e67b891d30878e0e15d25c", "shasum": "" }, "require": { "ext-dom": "*", "ext-mbstring": "*", - "phenx/php-font-lib": "^0.5.1", + "phenx/php-font-lib": "^0.5.2", "phenx/php-svg-lib": "^0.3.3", - "php": "^7.1" + "php": "^7.1 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^7.5", + "mockery/mockery": "^1.3", + "phpunit/phpunit": "^7.5 || ^8 || ^9", "squizlabs/php_codesniffer": "^3.5" }, "suggest": { "ext-gd": "Needed to process images", "ext-gmagick": "Improves image processing performance", - "ext-imagick": "Improves image processing performance" + "ext-imagick": "Improves image processing performance", + "ext-zlib": "Needed for pdf stream compression" }, "type": "library", "extra": { @@ -739,31 +1087,35 @@ ], "description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter", "homepage": "https://github.com/dompdf/dompdf", - "time": "2020-02-20T03:52:51+00:00" + "support": { + "issues": "https://github.com/dompdf/dompdf/issues", + "source": "https://github.com/dompdf/dompdf/tree/v1.0.2" + }, + "time": "2021-01-08T14:18:52+00:00" }, { "name": "friendsofphp/php-cs-fixer", - "version": "v2.16.3", + "version": "v2.18.4", "source": { "type": "git", "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", - "reference": "83baf823a33a1cbd5416c8626935cf3f843c10b0" + "reference": "06f764e3cb6d60822d8f5135205f9d32b5508a31" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/83baf823a33a1cbd5416c8626935cf3f843c10b0", - "reference": "83baf823a33a1cbd5416c8626935cf3f843c10b0", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/06f764e3cb6d60822d8f5135205f9d32b5508a31", + "reference": "06f764e3cb6d60822d8f5135205f9d32b5508a31", "shasum": "" }, "require": { - "composer/semver": "^1.4", + "composer/semver": "^1.4 || ^2.0 || ^3.0", "composer/xdebug-handler": "^1.2", "doctrine/annotations": "^1.2", "ext-json": "*", "ext-tokenizer": "*", - "php": "^5.6 || ^7.0", + "php": "^5.6 || ^7.0 || ^8.0", "php-cs-fixer/diff": "^1.3", - "symfony/console": "^3.4.17 || ^4.1.6 || ^5.0", + "symfony/console": "^3.4.43 || ^4.1.6 || ^5.0", "symfony/event-dispatcher": "^3.0 || ^4.0 || ^5.0", "symfony/filesystem": "^3.0 || ^4.0 || ^5.0", "symfony/finder": "^3.0 || ^4.0 || ^5.0", @@ -774,22 +1126,24 @@ "symfony/stopwatch": "^3.0 || ^4.0 || ^5.0" }, "require-dev": { - "johnkary/phpunit-speedtrap": "^1.1 || ^2.0 || ^3.0", "justinrainbow/json-schema": "^5.0", - "keradus/cli-executor": "^1.2", + "keradus/cli-executor": "^1.4", "mikey179/vfsstream": "^1.6", - "php-coveralls/php-coveralls": "^2.1", + "php-coveralls/php-coveralls": "^2.4.2", "php-cs-fixer/accessible-object": "^1.0", - "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.1", - "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.1", - "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.1", - "phpunitgoodpractices/traits": "^1.8", - "symfony/phpunit-bridge": "^4.3 || ^5.0", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.2", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.2.1", + "phpspec/prophecy-phpunit": "^1.1 || ^2.0", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.13 || ^9.5", + "phpunitgoodpractices/polyfill": "^1.5", + "phpunitgoodpractices/traits": "^1.9.1", + "sanmai/phpunit-legacy-adapter": "^6.4 || ^8.2.1", + "symfony/phpunit-bridge": "^5.2.1", "symfony/yaml": "^3.0 || ^4.0 || ^5.0" }, "suggest": { "ext-dom": "For handling output formats in XML", - "ext-mbstring": "For handling non-UTF8 characters in cache signature.", + "ext-mbstring": "For handling non-UTF8 characters.", "php-cs-fixer/phpunit-constraint-isidenticalstring": "For IsIdenticalString constraint.", "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "For XmlMatchesXsd constraint.", "symfony/polyfill-mbstring": "When enabling `ext-mbstring` is not possible." @@ -812,6 +1166,7 @@ "tests/Test/IntegrationCaseFactoryInterface.php", "tests/Test/InternalIntegrationCaseFactory.php", "tests/Test/IsIdenticalConstraint.php", + "tests/Test/TokensWithObservedTransformers.php", "tests/TestCase.php" ] }, @@ -830,13 +1185,17 @@ } ], "description": "A tool to automatically fix PHP code style", + "support": { + "issues": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/issues", + "source": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/tree/v2.18.4" + }, "funding": [ { "url": "https://github.com/keradus", "type": "github" } ], - "time": "2020-04-15T18:51:10+00:00" + "time": "2021-03-20T14:52:33+00:00" }, { "name": "jpgraph/jpgraph", @@ -876,35 +1235,40 @@ "jpgraph", "pie" ], + "support": { + "issues": "https://github.com/ztec/JpGraph/issues", + "source": "https://github.com/ztec/JpGraph/tree/4.x" + }, + "abandoned": true, "time": "2017-02-23T09:44:15+00:00" }, { "name": "mpdf/mpdf", - "version": "v8.0.5", + "version": "v8.0.10", "source": { "type": "git", "url": "https://github.com/mpdf/mpdf.git", - "reference": "bad32aa9cd5958175aef185c02e032ddbadc56ea" + "reference": "1333a962cd2f7ae1a127b7534b7734b58179186f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mpdf/mpdf/zipball/bad32aa9cd5958175aef185c02e032ddbadc56ea", - "reference": "bad32aa9cd5958175aef185c02e032ddbadc56ea", + "url": "https://api.github.com/repos/mpdf/mpdf/zipball/1333a962cd2f7ae1a127b7534b7734b58179186f", + "reference": "1333a962cd2f7ae1a127b7534b7734b58179186f", "shasum": "" }, "require": { "ext-gd": "*", "ext-mbstring": "*", "myclabs/deep-copy": "^1.7", - "paragonie/random_compat": "^1.4|^2.0|9.99.99", - "php": "^5.6 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0", + "paragonie/random_compat": "^1.4|^2.0|^9.99.99", + "php": "^5.6 || ^7.0 || ~8.0.0", "psr/log": "^1.0", "setasign/fpdi": "^2.1" }, "require-dev": { - "mockery/mockery": "^0.9.5", - "mpdf/qrcode": "^1.0.0", - "phpunit/phpunit": "^5.0", + "mockery/mockery": "^1.3.0", + "mpdf/qrcode": "^1.1.0", + "phpunit/phpunit": "^5.7", "squizlabs/php_codesniffer": "^3.5.0", "tracy/tracy": "^2.4" }, @@ -914,11 +1278,6 @@ "ext-zlib": "Needed for compression of embedded resources, such as fonts" }, "type": "library", - "extra": { - "branch-alias": { - "dev-development": "7.x-dev" - } - }, "autoload": { "psr-4": { "Mpdf\\": "src/" @@ -945,24 +1304,35 @@ "php", "utf-8" ], - "time": "2020-02-05T08:43:46+00:00" + "support": { + "docs": "http://mpdf.github.io", + "issues": "https://github.com/mpdf/mpdf/issues", + "source": "https://github.com/mpdf/mpdf" + }, + "funding": [ + { + "url": "https://www.paypal.me/mpdf", + "type": "custom" + } + ], + "time": "2021-01-08T14:59:28+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.9.5", + "version": "1.10.2", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "b2c28789e80a97badd14145fda39b545d83ca3ef" + "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/b2c28789e80a97badd14145fda39b545d83ca3ef", - "reference": "b2c28789e80a97badd14145fda39b545d83ca3ef", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220", + "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.1 || ^8.0" }, "replace": { "myclabs/deep-copy": "self.version" @@ -993,24 +1363,34 @@ "object", "object graph" ], - "time": "2020-01-17T21:11:47+00:00" + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2020-11-13T09:40:50+00:00" }, { "name": "paragonie/random_compat", - "version": "v9.99.99", + "version": "v9.99.100", "source": { "type": "git", "url": "https://github.com/paragonie/random_compat.git", - "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95" + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", - "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", "shasum": "" }, "require": { - "php": "^7" + "php": ">= 7" }, "require-dev": { "phpunit/phpunit": "4.*|5.*", @@ -1038,32 +1418,38 @@ "pseudorandom", "random" ], - "time": "2018-07-02T15:55:56+00:00" + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/random_compat/issues", + "source": "https://github.com/paragonie/random_compat" + }, + "time": "2020-10-15T08:29:30+00:00" }, { "name": "phar-io/manifest", - "version": "1.0.3", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" + "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", - "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/85265efd3af7ba3ca4b2a2c34dbfc5788dd29133", + "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133", "shasum": "" }, "require": { "ext-dom": "*", "ext-phar": "*", - "phar-io/version": "^2.0", - "php": "^5.6 || ^7.0" + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -1093,24 +1479,28 @@ } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "time": "2018-07-08T19:23:20+00:00" + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/master" + }, + "time": "2020-06-27T14:33:11+00:00" }, { "name": "phar-io/version", - "version": "2.0.1", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", - "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" + "reference": "bae7c545bef187884426f042434e561ab1ddb182" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", - "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "url": "https://api.github.com/repos/phar-io/version/zipball/bae7c545bef187884426f042434e561ab1ddb182", + "reference": "bae7c545bef187884426f042434e561ab1ddb182", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { @@ -1140,7 +1530,11 @@ } ], "description": "Library for handling version information and constraints", - "time": "2018-07-08T19:19:57+00:00" + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.1.0" + }, + "time": "2021-02-23T14:00:09+00:00" }, { "name": "phenx/php-font-lib", @@ -1177,6 +1571,10 @@ ], "description": "A library to read, parse, export and make subsets of different types of font files.", "homepage": "https://github.com/PhenX/php-font-lib", + "support": { + "issues": "https://github.com/PhenX/php-font-lib/issues", + "source": "https://github.com/PhenX/php-font-lib/tree/0.5.2" + }, "time": "2020-03-08T15:31:32+00:00" }, { @@ -1217,27 +1615,31 @@ ], "description": "A library to read, parse and export to PDF SVG files.", "homepage": "https://github.com/PhenX/php-svg-lib", + "support": { + "issues": "https://github.com/PhenX/php-svg-lib/issues", + "source": "https://github.com/PhenX/php-svg-lib/tree/master" + }, "time": "2019-09-11T20:02:13+00:00" }, { "name": "php-cs-fixer/diff", - "version": "v1.3.0", + "version": "v1.3.1", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/diff.git", - "reference": "78bb099e9c16361126c86ce82ec4405ebab8e756" + "reference": "dbd31aeb251639ac0b9e7e29405c1441907f5759" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/diff/zipball/78bb099e9c16361126c86ce82ec4405ebab8e756", - "reference": "78bb099e9c16361126c86ce82ec4405ebab8e756", + "url": "https://api.github.com/repos/PHP-CS-Fixer/diff/zipball/dbd31aeb251639ac0b9e7e29405c1441907f5759", + "reference": "dbd31aeb251639ac0b9e7e29405c1441907f5759", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^5.6 || ^7.0 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^5.7.23 || ^6.4.3", + "phpunit/phpunit": "^5.7.23 || ^6.4.3 || ^7.0", "symfony/process": "^3.3" }, "type": "library", @@ -1251,14 +1653,14 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, { "name": "SpacePossum" } @@ -1268,7 +1670,11 @@ "keywords": [ "diff" ], - "time": "2018-02-15T16:58:55+00:00" + "support": { + "issues": "https://github.com/PHP-CS-Fixer/diff/issues", + "source": "https://github.com/PHP-CS-Fixer/diff/tree/v1.3.1" + }, + "time": "2020-10-14T08:39:05+00:00" }, { "name": "phpcompatibility/php-compatibility", @@ -1326,29 +1732,33 @@ "phpcs", "standards" ], + "support": { + "issues": "https://github.com/PHPCompatibility/PHPCompatibility/issues", + "source": "https://github.com/PHPCompatibility/PHPCompatibility" + }, "time": "2019-12-27T09:44:58+00:00" }, { "name": "phpdocumentor/reflection-common", - "version": "2.1.0", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "6568f4687e5b41b054365f9ae03fcb1ed5f2069b" + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/6568f4687e5b41b054365f9ae03fcb1ed5f2069b", - "reference": "6568f4687e5b41b054365f9ae03fcb1ed5f2069b", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", "shasum": "" }, "require": { - "php": ">=7.1" + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.x-dev" + "dev-2.x": "2.x-dev" } }, "autoload": { @@ -1375,32 +1785,35 @@ "reflection", "static analysis" ], - "time": "2020-04-27T09:25:28+00:00" + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.1.0", + "version": "5.2.2", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e" + "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e", - "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/069a785b2141f5bcf49f3e353548dc1cce6df556", + "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556", "shasum": "" }, "require": { - "ext-filter": "^7.1", - "php": "^7.2", - "phpdocumentor/reflection-common": "^2.0", - "phpdocumentor/type-resolver": "^1.0", - "webmozart/assert": "^1" + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" }, "require-dev": { - "doctrine/instantiator": "^1", - "mockery/mockery": "^1" + "mockery/mockery": "~1.3.2" }, "type": "library", "extra": { @@ -1428,34 +1841,37 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2020-02-22T12:28:44+00:00" + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master" + }, + "time": "2020-09-03T19:13:55+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.1.0", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "7462d5f123dfc080dfdf26897032a6513644fc95" + "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/7462d5f123dfc080dfdf26897032a6513644fc95", - "reference": "7462d5f123dfc080dfdf26897032a6513644fc95", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", + "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", "shasum": "" }, "require": { - "php": "^7.2", + "php": "^7.2 || ^8.0", "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { - "ext-tokenizer": "^7.2", - "mockery/mockery": "~1" + "ext-tokenizer": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-1.x": "1.x-dev" } }, "autoload": { @@ -1474,37 +1890,41 @@ } ], "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "time": "2020-02-18T18:59:58+00:00" + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.4.0" + }, + "time": "2020-09-17T18:55:26+00:00" }, { "name": "phpspec/prophecy", - "version": "v1.10.3", + "version": "1.13.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "451c3cd1418cf640de218914901e51b064abb093" + "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093", - "reference": "451c3cd1418cf640de218914901e51b064abb093", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/be1996ed8adc35c3fd795488a653f4b518be70ea", + "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", - "sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0" + "doctrine/instantiator": "^1.2", + "php": "^7.2 || ~8.0, <8.1", + "phpdocumentor/reflection-docblock": "^5.2", + "sebastian/comparator": "^3.0 || ^4.0", + "sebastian/recursion-context": "^3.0 || ^4.0" }, "require-dev": { - "phpspec/phpspec": "^2.5 || ^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" + "phpspec/phpspec": "^6.0", + "phpunit/phpunit": "^8.0 || ^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.10.x-dev" + "dev-master": "1.11.x-dev" } }, "autoload": { @@ -1537,29 +1957,93 @@ "spy", "stub" ], - "time": "2020-03-05T15:02:03+00:00" + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/1.13.0" + }, + "time": "2021-03-17T13:42:18+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "0.12.82", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "3920f0fb0aff39263d3a4cb0bca120a67a1a6a11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/3920f0fb0aff39263d3a4cb0bca120a67a1a6a11", + "reference": "3920f0fb0aff39263d3a4cb0bca120a67a1a6a11", + "shasum": "" + }, + "require": { + "php": "^7.1|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.12-dev" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "support": { + "issues": "https://github.com/phpstan/phpstan/issues", + "source": "https://github.com/phpstan/phpstan/tree/0.12.82" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://www.patreon.com/phpstan", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", + "type": "tidelift" + } + ], + "time": "2021-03-19T06:08:17+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "7.0.10", + "version": "7.0.14", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "f1884187926fbb755a9aaf0b3836ad3165b478bf" + "reference": "bb7c9a210c72e4709cdde67f8b7362f672f2225c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f1884187926fbb755a9aaf0b3836ad3165b478bf", - "reference": "f1884187926fbb755a9aaf0b3836ad3165b478bf", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/bb7c9a210c72e4709cdde67f8b7362f672f2225c", + "reference": "bb7c9a210c72e4709cdde67f8b7362f672f2225c", "shasum": "" }, "require": { "ext-dom": "*", "ext-xmlwriter": "*", - "php": "^7.2", + "php": ">=7.2", "phpunit/php-file-iterator": "^2.0.2", "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^3.1.1", + "phpunit/php-token-stream": "^3.1.1 || ^4.0", "sebastian/code-unit-reverse-lookup": "^1.0.1", "sebastian/environment": "^4.2.2", "sebastian/version": "^2.0.1", @@ -1600,27 +2084,37 @@ "testing", "xunit" ], - "time": "2019-11-20T13:55:58+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/7.0.14" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-12-02T13:39:03+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "2.0.2", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "050bedf145a257b1ff02746c31894800e5122946" + "reference": "4b49fb70f067272b659ef0174ff9ca40fdaa6357" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946", - "reference": "050bedf145a257b1ff02746c31894800e5122946", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/4b49fb70f067272b659ef0174ff9ca40fdaa6357", + "reference": "4b49fb70f067272b659ef0174ff9ca40fdaa6357", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "^7.1" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { @@ -1650,7 +2144,17 @@ "filesystem", "iterator" ], - "time": "2018-09-13T20:33:42+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T08:25:21+00:00" }, { "name": "phpunit/php-text-template", @@ -1691,27 +2195,31 @@ "keywords": [ "template" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1" + }, "time": "2015-06-21T13:50:34+00:00" }, { "name": "phpunit/php-timer", - "version": "2.1.2", + "version": "2.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "1038454804406b0b5f5f520358e78c1c2f71501e" + "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/1038454804406b0b5f5f520358e78c1c2f71501e", - "reference": "1038454804406b0b5f5f520358e78c1c2f71501e", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/2454ae1765516d20c4ffe103d85a58a9a3bd5662", + "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { @@ -1740,25 +2248,35 @@ "keywords": [ "timer" ], - "time": "2019-06-07T04:22:29+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/2.1.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T08:20:02+00:00" }, { "name": "phpunit/php-token-stream", - "version": "3.1.1", + "version": "3.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff" + "reference": "472b687829041c24b25f475e14c2f38a09edf1c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/995192df77f63a59e47f025390d2d1fdf8f425ff", - "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/472b687829041c24b25f475e14c2f38a09edf1c2", + "reference": "472b687829041c24b25f475e14c2f38a09edf1c2", "shasum": "" }, "require": { "ext-tokenizer": "*", - "php": "^7.1" + "php": ">=7.1" }, "require-dev": { "phpunit/phpunit": "^7.0" @@ -1789,43 +2307,54 @@ "keywords": [ "tokenizer" ], - "time": "2019-09-17T06:23:10+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/php-token-stream/issues", + "source": "https://github.com/sebastianbergmann/php-token-stream/tree/3.1.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "abandoned": true, + "time": "2020-11-30T08:38:46+00:00" }, { "name": "phpunit/phpunit", - "version": "8.5.4", + "version": "8.5.15", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "8474e22d7d642f665084ba5ec780626cbd1efd23" + "reference": "038d4196d8e8cb405cd5e82cedfe413ad6eef9ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/8474e22d7d642f665084ba5ec780626cbd1efd23", - "reference": "8474e22d7d642f665084ba5ec780626cbd1efd23", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/038d4196d8e8cb405cd5e82cedfe413ad6eef9ef", + "reference": "038d4196d8e8cb405cd5e82cedfe413ad6eef9ef", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.2.0", + "doctrine/instantiator": "^1.3.1", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.9.1", - "phar-io/manifest": "^1.0.3", - "phar-io/version": "^2.0.1", - "php": "^7.2", - "phpspec/prophecy": "^1.8.1", - "phpunit/php-code-coverage": "^7.0.7", + "myclabs/deep-copy": "^1.10.0", + "phar-io/manifest": "^2.0.1", + "phar-io/version": "^3.0.2", + "php": ">=7.2", + "phpspec/prophecy": "^1.10.3", + "phpunit/php-code-coverage": "^7.0.12", "phpunit/php-file-iterator": "^2.0.2", "phpunit/php-text-template": "^1.2.1", "phpunit/php-timer": "^2.1.2", "sebastian/comparator": "^3.0.2", "sebastian/diff": "^3.0.2", - "sebastian/environment": "^4.2.2", - "sebastian/exporter": "^3.1.1", + "sebastian/environment": "^4.2.3", + "sebastian/exporter": "^3.1.2", "sebastian/global-state": "^3.0.0", "sebastian/object-enumerator": "^3.0.3", "sebastian/resource-operations": "^2.0.1", @@ -1872,6 +2401,10 @@ "testing", "xunit" ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.15" + }, "funding": [ { "url": "https://phpunit.de/donate.html", @@ -1882,31 +2415,26 @@ "type": "github" } ], - "time": "2020-04-23T04:39:42+00:00" + "time": "2021-03-17T07:27:54+00:00" }, { "name": "psr/container", - "version": "1.0.0", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=7.2.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, "autoload": { "psr-4": { "Psr\\Container\\": "src/" @@ -1919,7 +2447,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common Container Interface (PHP FIG PSR-11)", @@ -1931,7 +2459,11 @@ "container-interop", "psr" ], - "time": "2017-02-14T16:28:37+00:00" + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/1.1.1" + }, + "time": "2021-03-05T17:36:06+00:00" }, { "name": "psr/event-dispatcher", @@ -1977,6 +2509,10 @@ "psr", "psr-14" ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, "time": "2019-01-08T18:20:26+00:00" }, { @@ -2024,20 +2560,23 @@ "psr", "psr-3" ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.3" + }, "time": "2020-03-23T09:12:05+00:00" }, { "name": "sabberworm/php-css-parser", - "version": "8.3.0", + "version": "8.3.1", "source": { "type": "git", "url": "https://github.com/sabberworm/PHP-CSS-Parser.git", - "reference": "91bcc3e3fdb7386c9a2e0e0aa09ca75cc43f121f" + "reference": "d217848e1396ef962fb1997cf3e2421acba7f796" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sabberworm/PHP-CSS-Parser/zipball/91bcc3e3fdb7386c9a2e0e0aa09ca75cc43f121f", - "reference": "91bcc3e3fdb7386c9a2e0e0aa09ca75cc43f121f", + "url": "https://api.github.com/repos/sabberworm/PHP-CSS-Parser/zipball/d217848e1396ef962fb1997cf3e2421acba7f796", + "reference": "d217848e1396ef962fb1997cf3e2421acba7f796", "shasum": "" }, "require": { @@ -2069,27 +2608,31 @@ "parser", "stylesheet" ], - "time": "2019-02-22T07:42:52+00:00" + "support": { + "issues": "https://github.com/sabberworm/PHP-CSS-Parser/issues", + "source": "https://github.com/sabberworm/PHP-CSS-Parser/tree/8.3.1" + }, + "time": "2020-06-01T09:10:00+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" + "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619", + "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": ">=5.6" }, "require-dev": { - "phpunit/phpunit": "^5.7 || ^6.0" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { @@ -2114,29 +2657,39 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2017-03-04T06:30:41+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T08:15:22+00:00" }, { "name": "sebastian/comparator", - "version": "3.0.2", + "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" + "reference": "1071dfcef776a57013124ff35e1fc41ccd294758" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", - "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1071dfcef776a57013124ff35e1fc41ccd294758", + "reference": "1071dfcef776a57013124ff35e1fc41ccd294758", "shasum": "" }, "require": { - "php": "^7.1", + "php": ">=7.1", "sebastian/diff": "^3.0", "sebastian/exporter": "^3.1" }, "require-dev": { - "phpunit/phpunit": "^7.1" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { @@ -2154,6 +2707,10 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" @@ -2165,10 +2722,6 @@ { "name": "Bernhard Schussek", "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" } ], "description": "Provides the functionality to compare PHP values for equality", @@ -2178,24 +2731,34 @@ "compare", "equality" ], - "time": "2018-07-12T15:12:46+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T08:04:30+00:00" }, { "name": "sebastian/diff", - "version": "3.0.2", + "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29" + "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29", - "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/14f72dd46eaf2f2293cbe79c93cc0bc43161a211", + "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=7.1" }, "require-dev": { "phpunit/phpunit": "^7.5 || ^8.0", @@ -2217,13 +2780,13 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" } ], "description": "Diff implementation", @@ -2234,24 +2797,34 @@ "unidiff", "unified diff" ], - "time": "2019-02-04T06:01:07+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:59:04+00:00" }, { "name": "sebastian/environment", - "version": "4.2.3", + "version": "4.2.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368" + "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/464c90d7bdf5ad4e8a6aea15c091fec0603d4368", - "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", + "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=7.1" }, "require-dev": { "phpunit/phpunit": "^7.5" @@ -2287,24 +2860,34 @@ "environment", "hhvm" ], - "time": "2019-11-20T08:46:58+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/4.2.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:53:42+00:00" }, { "name": "sebastian/exporter", - "version": "3.1.2", + "version": "3.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e" + "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/68609e1261d215ea5b21b7987539cbfbe156ec3e", - "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/6b853149eab67d4da22291d36f5b0631c0fd856e", + "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e", "shasum": "" }, "require": { - "php": "^7.0", + "php": ">=7.0", "sebastian/recursion-context": "^3.0" }, "require-dev": { @@ -2354,24 +2937,34 @@ "export", "exporter" ], - "time": "2019-09-14T09:02:43+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:47:53+00:00" }, { "name": "sebastian/global-state", - "version": "3.0.0", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4" + "reference": "474fb9edb7ab891665d3bfc6317f42a0a150454b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4", - "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/474fb9edb7ab891665d3bfc6317f42a0a150454b", + "reference": "474fb9edb7ab891665d3bfc6317f42a0a150454b", "shasum": "" }, "require": { - "php": "^7.2", + "php": ">=7.2", "sebastian/object-reflector": "^1.1.1", "sebastian/recursion-context": "^3.0" }, @@ -2408,24 +3001,34 @@ "keywords": [ "global state" ], - "time": "2019-02-01T05:30:01+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:43:24+00:00" }, { "name": "sebastian/object-enumerator", - "version": "3.0.3", + "version": "3.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" + "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", + "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", "shasum": "" }, "require": { - "php": "^7.0", + "php": ">=7.0", "sebastian/object-reflector": "^1.1.1", "sebastian/recursion-context": "^3.0" }, @@ -2455,24 +3058,34 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-08-03T12:35:26+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:40:27+00:00" }, { "name": "sebastian/object-reflector", - "version": "1.1.1", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "773f97c67f28de00d397be301821b06708fca0be" + "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", - "reference": "773f97c67f28de00d397be301821b06708fca0be", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", + "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=7.0" }, "require-dev": { "phpunit/phpunit": "^6.0" @@ -2500,24 +3113,34 @@ ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "time": "2017-03-29T09:07:27+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:37:18+00:00" }, { "name": "sebastian/recursion-context", - "version": "3.0.0", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" + "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/367dcba38d6e1977be014dc4b22f47a484dac7fb", + "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=7.0" }, "require-dev": { "phpunit/phpunit": "^6.0" @@ -2538,14 +3161,14 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, { "name": "Adam Harvey", "email": "aharvey@php.net" @@ -2553,24 +3176,34 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2017-03-03T06:23:57+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:34:24+00:00" }, { "name": "sebastian/resource-operations", - "version": "2.0.1", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9" + "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9", - "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/31d35ca87926450c44eae7e2611d45a7a65ea8b3", + "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=7.1" }, "type": "library", "extra": { @@ -2595,24 +3228,34 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2018-10-04T04:07:39+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:30:19+00:00" }, { "name": "sebastian/type", - "version": "1.1.3", + "version": "1.1.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "3aaaa15fa71d27650d62a948be022fe3b48541a3" + "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/3aaaa15fa71d27650d62a948be022fe3b48541a3", - "reference": "3aaaa15fa71d27650d62a948be022fe3b48541a3", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/0150cfbc4495ed2df3872fb31b26781e4e077eb4", + "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4", "shasum": "" }, "require": { - "php": "^7.2" + "php": ">=7.2" }, "require-dev": { "phpunit/phpunit": "^8.2" @@ -2641,7 +3284,17 @@ ], "description": "Collection of value objects that represent the types of the PHP type system", "homepage": "https://github.com/sebastianbergmann/type", - "time": "2019-07-02T08:10:15+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/1.1.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:25:11+00:00" }, { "name": "sebastian/version", @@ -2684,25 +3337,29 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/master" + }, "time": "2016-10-03T07:35:21+00:00" }, { "name": "setasign/fpdi", - "version": "v2.3.2", + "version": "v2.3.6", "source": { "type": "git", "url": "https://github.com/Setasign/FPDI.git", - "reference": "527761458f504882ab844f15754523825647f291" + "reference": "6231e315f73e4f62d72b73f3d6d78ff0eed93c31" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Setasign/FPDI/zipball/527761458f504882ab844f15754523825647f291", - "reference": "527761458f504882ab844f15754523825647f291", + "url": "https://api.github.com/repos/Setasign/FPDI/zipball/6231e315f73e4f62d72b73f3d6d78ff0eed93c31", + "reference": "6231e315f73e4f62d72b73f3d6d78ff0eed93c31", "shasum": "" }, "require": { "ext-zlib": "*", - "php": "^5.6 || ^7.0" + "php": "^5.6 || ^7.0 || ^8.0" }, "conflict": { "setasign/tfpdf": "<1.31" @@ -2711,6 +3368,7 @@ "phpunit/phpunit": "~5.7", "setasign/fpdf": "~1.8", "setasign/tfpdf": "1.31", + "squizlabs/php_codesniffer": "^3.5", "tecnickcom/tcpdf": "~6.2" }, "suggest": { @@ -2745,26 +3403,30 @@ "fpdi", "pdf" ], + "support": { + "issues": "https://github.com/Setasign/FPDI/issues", + "source": "https://github.com/Setasign/FPDI/tree/v2.3.6" + }, "funding": [ { "url": "https://tidelift.com/funding/github/packagist/setasign/fpdi", "type": "tidelift" } ], - "time": "2020-03-23T15:53:59+00:00" + "time": "2021-02-11T11:37:01+00:00" }, { "name": "squizlabs/php_codesniffer", - "version": "3.5.5", + "version": "3.5.8", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "73e2e7f57d958e7228fce50dc0c61f58f017f9f6" + "reference": "9d583721a7157ee997f235f327de038e7ea6dac4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/73e2e7f57d958e7228fce50dc0c61f58f017f9f6", - "reference": "73e2e7f57d958e7228fce50dc0c61f58f017f9f6", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/9d583721a7157ee997f235f327de038e7ea6dac4", + "reference": "9d583721a7157ee997f235f327de038e7ea6dac4", "shasum": "" }, "require": { @@ -2802,30 +3464,38 @@ "phpcs", "standards" ], - "time": "2020-04-17T01:09:41+00:00" + "support": { + "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", + "source": "https://github.com/squizlabs/PHP_CodeSniffer", + "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + }, + "time": "2020-10-23T02:01:07+00:00" }, { "name": "symfony/console", - "version": "v5.0.7", + "version": "v5.2.5", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "5fa1caadc8cdaa17bcfb25219f3b53fe294a9935" + "reference": "938ebbadae1b0a9c9d1ec313f87f9708609f1b79" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/5fa1caadc8cdaa17bcfb25219f3b53fe294a9935", - "reference": "5fa1caadc8cdaa17bcfb25219f3b53fe294a9935", + "url": "https://api.github.com/repos/symfony/console/zipball/938ebbadae1b0a9c9d1ec313f87f9708609f1b79", + "reference": "938ebbadae1b0a9c9d1ec313f87f9708609f1b79", "shasum": "" }, "require": { - "php": "^7.2.5", + "php": ">=7.2.5", "symfony/polyfill-mbstring": "~1.0", "symfony/polyfill-php73": "^1.8", - "symfony/service-contracts": "^1.1|^2" + "symfony/polyfill-php80": "^1.15", + "symfony/service-contracts": "^1.1|^2", + "symfony/string": "^5.1" }, "conflict": { "symfony/dependency-injection": "<4.4", + "symfony/dotenv": "<5.1", "symfony/event-dispatcher": "<4.4", "symfony/lock": "<4.4", "symfony/process": "<4.4" @@ -2849,11 +3519,6 @@ "symfony/process": "" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Console\\": "" @@ -2876,8 +3541,84 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Console Component", + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v5.2.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-03-06T13:42:15+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v2.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5fa56b4074d1ae755beb55617ddafe6f5d78f665", + "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/master" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -2892,25 +3633,27 @@ "type": "tidelift" } ], - "time": "2020-03-30T11:42:42+00:00" + "time": "2020-09-07T11:33:47+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v5.0.7", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "24f40d95385774ed5c71dbf014edd047e2f2f3dc" + "reference": "d08d6ec121a425897951900ab692b612a61d6240" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/24f40d95385774ed5c71dbf014edd047e2f2f3dc", - "reference": "24f40d95385774ed5c71dbf014edd047e2f2f3dc", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d08d6ec121a425897951900ab692b612a61d6240", + "reference": "d08d6ec121a425897951900ab692b612a61d6240", "shasum": "" }, "require": { - "php": "^7.2.5", - "symfony/event-dispatcher-contracts": "^2" + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1", + "symfony/event-dispatcher-contracts": "^2", + "symfony/polyfill-php80": "^1.15" }, "conflict": { "symfony/dependency-injection": "<4.4" @@ -2923,6 +3666,7 @@ "psr/log": "~1.0", "symfony/config": "^4.4|^5.0", "symfony/dependency-injection": "^4.4|^5.0", + "symfony/error-handler": "^4.4|^5.0", "symfony/expression-language": "^4.4|^5.0", "symfony/http-foundation": "^4.4|^5.0", "symfony/service-contracts": "^1.1|^2", @@ -2933,11 +3677,6 @@ "symfony/http-kernel": "" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\EventDispatcher\\": "" @@ -2960,8 +3699,11 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony EventDispatcher Component", + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v5.2.4" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -2976,24 +3718,24 @@ "type": "tidelift" } ], - "time": "2020-03-27T16:56:45+00:00" + "time": "2021-02-18T17:12:37+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v2.0.1", + "version": "v2.2.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "af23c2584d4577d54661c434446fb8fbed6025dd" + "reference": "0ba7d54483095a198fa51781bc608d17e84dffa2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/af23c2584d4577d54661c434446fb8fbed6025dd", - "reference": "af23c2584d4577d54661c434446fb8fbed6025dd", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/0ba7d54483095a198fa51781bc608d17e84dffa2", + "reference": "0ba7d54483095a198fa51781bc608d17e84dffa2", "shasum": "" }, "require": { - "php": "^7.2.5", + "php": ">=7.2.5", "psr/event-dispatcher": "^1" }, "suggest": { @@ -3002,7 +3744,11 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "2.2-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -3034,35 +3780,172 @@ "interoperability", "standards" ], - "time": "2019-11-18T17:27:11+00:00" + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-09-07T11:33:47+00:00" }, { "name": "symfony/filesystem", - "version": "v5.0.7", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "ca3b87dd09fff9b771731637f5379965fbfab420" + "reference": "710d364200997a5afde34d9fe57bd52f3cc1e108" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/ca3b87dd09fff9b771731637f5379965fbfab420", - "reference": "ca3b87dd09fff9b771731637f5379965fbfab420", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/710d364200997a5afde34d9fe57bd52f3cc1e108", + "reference": "710d364200997a5afde34d9fe57bd52f3cc1e108", "shasum": "" }, "require": { - "php": "^7.2.5", + "php": ">=7.2.5", "symfony/polyfill-ctype": "~1.8" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v5.2.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-02-12T10:38:38+00:00" + }, + { + "name": "symfony/finder", + "version": "v5.2.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "0d639a0943822626290d169965804f79400e6a04" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/0d639a0943822626290d169965804f79400e6a04", + "reference": "0d639a0943822626290d169965804f79400e6a04", + "shasum": "" + }, + "require": { + "php": ">=7.2.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v5.2.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } + ], + "time": "2021-02-15T18:55:04+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "v5.2.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "5d0f633f9bbfcf7ec642a2b5037268e61b0a62ce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/5d0f633f9bbfcf7ec642a2b5037268e61b0a62ce", + "reference": "5d0f633f9bbfcf7ec642a2b5037268e61b0a62ce", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1", + "symfony/polyfill-php73": "~1.0", + "symfony/polyfill-php80": "^1.15" }, + "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\Filesystem\\": "" + "Symfony\\Component\\OptionsResolver\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -3082,8 +3965,16 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Filesystem Component", - "homepage": "https://symfony.com", + "description": "Provides an improved replacement for the array_replace PHP function", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "support": { + "source": "https://github.com/symfony/options-resolver/tree/v5.2.4" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3098,37 +3989,44 @@ "type": "tidelift" } ], - "time": "2020-03-27T16:56:45+00:00" + "time": "2021-01-27T12:56:27+00:00" }, { - "name": "symfony/finder", - "version": "v5.0.7", + "name": "symfony/polyfill-ctype", + "version": "v1.22.1", "source": { "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "600a52c29afc0d1caa74acbec8d3095ca7e9910d" + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "c6c942b1ac76c82448322025e084cadc56048b4e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/600a52c29afc0d1caa74acbec8d3095ca7e9910d", - "reference": "600a52c29afc0d1caa74acbec8d3095ca7e9910d", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/c6c942b1ac76c82448322025e084cadc56048b4e", + "reference": "c6c942b1ac76c82448322025e084cadc56048b4e", "shasum": "" }, "require": { - "php": "^7.2.5" + "php": ">=7.1" + }, + "suggest": { + "ext-ctype": "For best performance" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { "psr-4": { - "Symfony\\Component\\Finder\\": "" + "Symfony\\Polyfill\\Ctype\\": "" }, - "exclude-from-classmap": [ - "/Tests/" + "files": [ + "bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -3137,16 +4035,25 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Finder Component", + "description": "Symfony polyfill for ctype functions", "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.22.1" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3161,37 +4068,44 @@ "type": "tidelift" } ], - "time": "2020-03-27T16:56:45+00:00" + "time": "2021-01-07T16:49:33+00:00" }, { - "name": "symfony/options-resolver", - "version": "v5.0.7", + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.22.1", "source": { "type": "git", - "url": "https://github.com/symfony/options-resolver.git", - "reference": "09dccfffd24b311df7f184aa80ee7b61ad61ed8d" + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "5601e09b69f26c1828b13b6bb87cb07cddba3170" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/09dccfffd24b311df7f184aa80ee7b61ad61ed8d", - "reference": "09dccfffd24b311df7f184aa80ee7b61ad61ed8d", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/5601e09b69f26c1828b13b6bb87cb07cddba3170", + "reference": "5601e09b69f26c1828b13b6bb87cb07cddba3170", "shasum": "" }, "require": { - "php": "^7.2.5" + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { "psr-4": { - "Symfony\\Component\\OptionsResolver\\": "" + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" }, - "exclude-from-classmap": [ - "/Tests/" + "files": [ + "bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -3200,21 +4114,27 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony OptionsResolver Component", + "description": "Symfony polyfill for intl's grapheme_* functions", "homepage": "https://symfony.com", "keywords": [ - "config", - "configuration", - "options" + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.22.1" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3229,40 +4149,47 @@ "type": "tidelift" } ], - "time": "2020-03-27T16:56:45+00:00" + "time": "2021-01-22T09:19:47+00:00" }, { - "name": "symfony/polyfill-ctype", - "version": "v1.15.0", + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.22.1", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14" + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/4719fa9c18b0464d399f1a63bf624b42b6fa8d14", - "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/43a0283138253ed1d48d352ab6d0bdb3f809f248", + "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.1" }, "suggest": { - "ext-ctype": "For best performance" + "ext-intl": "For best performance" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.15-dev" + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" }, "files": [ "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -3271,22 +4198,27 @@ ], "authors": [ { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for ctype functions", + "description": "Symfony polyfill for intl's Normalizer class and related functions", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "ctype", + "intl", + "normalizer", "polyfill", - "portable" + "portable", + "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.22.1" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3301,41 +4233,34 @@ "type": "tidelift" } ], - "time": "2020-02-27T09:26:54+00:00" + "time": "2021-01-22T09:19:47+00:00" }, { - "name": "symfony/polyfill-mbstring", - "version": "v1.15.0", + "name": "symfony/polyfill-php70", + "version": "v1.20.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac" + "url": "https://github.com/symfony/polyfill-php70.git", + "reference": "5f03a781d984aae42cebd18e7912fa80f02ee644" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/81ffd3a9c6d707be22e3012b827de1c9775fc5ac", - "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/5f03a781d984aae42cebd18e7912fa80f02ee644", + "reference": "5f03a781d984aae42cebd18e7912fa80f02ee644", "shasum": "" }, "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-mbstring": "For best performance" + "php": ">=7.1" }, - "type": "library", + "type": "metapackage", "extra": { "branch-alias": { - "dev-master": "1.15-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" + "dev-main": "1.20-dev" }, - "files": [ - "bootstrap.php" - ] + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3351,15 +4276,17 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for the Mbstring extension", + "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "mbstring", "polyfill", "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-php70/tree/v1.20.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3374,41 +4301,41 @@ "type": "tidelift" } ], - "time": "2020-03-09T19:04:49+00:00" + "time": "2020-10-23T14:02:19+00:00" }, { - "name": "symfony/polyfill-php70", - "version": "v1.15.0", + "name": "symfony/polyfill-php72", + "version": "v1.22.1", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php70.git", - "reference": "2a18e37a489803559284416df58c71ccebe50bf0" + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/2a18e37a489803559284416df58c71ccebe50bf0", - "reference": "2a18e37a489803559284416df58c71ccebe50bf0", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9", + "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9", "shasum": "" }, "require": { - "paragonie/random_compat": "~1.0|~2.0|~9.99", - "php": ">=5.3.3" + "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.15-dev" + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Php70\\": "" + "Symfony\\Polyfill\\Php72\\": "" }, "files": [ "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -3425,7 +4352,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -3433,37 +4360,61 @@ "portable", "shim" ], - "time": "2020-02-27T09:26:54+00:00" + "support": { + "source": "https://github.com/symfony/polyfill-php72/tree/v1.22.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-01-07T16:49:33+00:00" }, { - "name": "symfony/polyfill-php72", - "version": "v1.15.0", + "name": "symfony/polyfill-php73", + "version": "v1.22.1", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "37b0976c78b94856543260ce09b460a7bc852747" + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/37b0976c78b94856543260ce09b460a7bc852747", - "reference": "37b0976c78b94856543260ce09b460a7bc852747", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/a678b42e92f86eca04b7fa4c0f6f19d097fb69e2", + "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.15-dev" + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Php72\\": "" + "Symfony\\Polyfill\\Php73\\": "" }, "files": [ "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -3480,7 +4431,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -3488,6 +4439,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.22.1" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3502,34 +4456,38 @@ "type": "tidelift" } ], - "time": "2020-02-27T09:26:54+00:00" + "time": "2021-01-07T16:49:33+00:00" }, { - "name": "symfony/polyfill-php73", - "version": "v1.15.0", + "name": "symfony/polyfill-php80", + "version": "v1.22.1", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7" + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7", - "reference": "0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dc3063ba22c2a1fd2f45ed856374d79114998f91", + "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.15-dev" + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Php73\\": "" + "Symfony\\Polyfill\\Php80\\": "" }, "files": [ "bootstrap.php" @@ -3543,6 +4501,10 @@ "MIT" ], "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, { "name": "Nicolas Grekas", "email": "p@tchwork.com" @@ -3552,7 +4514,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -3560,6 +4522,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.22.1" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3574,31 +4539,27 @@ "type": "tidelift" } ], - "time": "2020-02-27T09:26:54+00:00" + "time": "2021-01-07T16:49:33+00:00" }, { "name": "symfony/process", - "version": "v5.0.7", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "c5ca4a0fc16a0c888067d43fbcfe1f8a53d8e70e" + "reference": "313a38f09c77fbcdc1d223e57d368cea76a2fd2f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/c5ca4a0fc16a0c888067d43fbcfe1f8a53d8e70e", - "reference": "c5ca4a0fc16a0c888067d43fbcfe1f8a53d8e70e", + "url": "https://api.github.com/repos/symfony/process/zipball/313a38f09c77fbcdc1d223e57d368cea76a2fd2f", + "reference": "313a38f09c77fbcdc1d223e57d368cea76a2fd2f", "shasum": "" }, "require": { - "php": "^7.2.5" + "php": ">=7.2.5", + "symfony/polyfill-php80": "^1.15" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Process\\": "" @@ -3621,8 +4582,11 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Process Component", + "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v5.2.4" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3637,24 +4601,24 @@ "type": "tidelift" } ], - "time": "2020-03-27T16:56:45+00:00" + "time": "2021-01-27T10:15:41+00:00" }, { "name": "symfony/service-contracts", - "version": "v2.0.1", + "version": "v2.2.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "144c5e51266b281231e947b51223ba14acf1a749" + "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/144c5e51266b281231e947b51223ba14acf1a749", - "reference": "144c5e51266b281231e947b51223ba14acf1a749", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d15da7ba4957ffb8f1747218be9e1a121fd298a1", + "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1", "shasum": "" }, "require": { - "php": "^7.2.5", + "php": ">=7.2.5", "psr/container": "^1.0" }, "suggest": { @@ -3663,7 +4627,11 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "2.2-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -3695,32 +4663,44 @@ "interoperability", "standards" ], - "time": "2019-11-18T17:27:11+00:00" + "support": { + "source": "https://github.com/symfony/service-contracts/tree/master" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-09-07T11:33:47+00:00" }, { "name": "symfony/stopwatch", - "version": "v5.0.7", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "a1d86d30d4522423afc998f32404efa34fcf5a73" + "reference": "b12274acfab9d9850c52583d136a24398cdf1a0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/a1d86d30d4522423afc998f32404efa34fcf5a73", - "reference": "a1d86d30d4522423afc998f32404efa34fcf5a73", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/b12274acfab9d9850c52583d136a24398cdf1a0c", + "reference": "b12274acfab9d9850c52583d136a24398cdf1a0c", "shasum": "" }, "require": { - "php": "^7.2.5", + "php": ">=7.2.5", "symfony/service-contracts": "^1.0|^2" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Stopwatch\\": "" @@ -3743,8 +4723,94 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Stopwatch Component", + "description": "Provides a way to profile code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/stopwatch/tree/v5.2.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-01-27T10:15:41+00:00" + }, + { + "name": "symfony/string", + "version": "v5.2.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "4e78d7d47061fa183639927ec40d607973699609" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/4e78d7d47061fa183639927ec40d607973699609", + "reference": "4e78d7d47061fa183639927ec40d607973699609", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php80": "~1.15" + }, + "require-dev": { + "symfony/error-handler": "^4.4|^5.0", + "symfony/http-client": "^4.4|^5.0", + "symfony/translation-contracts": "^1.1|^2", + "symfony/var-exporter": "^4.4|^5.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "files": [ + "Resources/functions.php" + ], + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v5.2.4" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3759,20 +4825,20 @@ "type": "tidelift" } ], - "time": "2020-03-27T16:56:45+00:00" + "time": "2021-02-16T10:20:28+00:00" }, { "name": "tecnickcom/tcpdf", - "version": "6.3.5", + "version": "6.4.1", "source": { "type": "git", "url": "https://github.com/tecnickcom/TCPDF.git", - "reference": "19a535eaa7fb1c1cac499109deeb1a7a201b4549" + "reference": "5ba838befdb37ef06a16d9f716f35eb03cb1b329" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/19a535eaa7fb1c1cac499109deeb1a7a201b4549", - "reference": "19a535eaa7fb1c1cac499109deeb1a7a201b4549", + "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/5ba838befdb37ef06a16d9f716f35eb03cb1b329", + "reference": "5ba838befdb37ef06a16d9f716f35eb03cb1b329", "shasum": "" }, "require": { @@ -3821,27 +4887,37 @@ "pdf417", "qrcode" ], - "time": "2020-02-14T14:20:12+00:00" + "support": { + "issues": "https://github.com/tecnickcom/TCPDF/issues", + "source": "https://github.com/tecnickcom/TCPDF/tree/6.4.1" + }, + "funding": [ + { + "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_donations¤cy_code=GBP&business=paypal@tecnick.com&item_name=donation%20for%20tcpdf%20project", + "type": "custom" + } + ], + "time": "2021-03-27T16:00:33+00:00" }, { "name": "theseer/tokenizer", - "version": "1.1.3", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9" + "reference": "75a63c33a8577608444246075ea0af0d052e452a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9", - "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/75a63c33a8577608444246075ea0af0d052e452a", + "reference": "75a63c33a8577608444246075ea0af0d052e452a", "shasum": "" }, "require": { "ext-dom": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", - "php": "^7.0" + "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { @@ -3861,33 +4937,49 @@ } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "time": "2019-06-13T22:48:21+00:00" + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/master" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2020-07-12T23:59:07+00:00" }, { "name": "webmozart/assert", - "version": "1.8.0", + "version": "1.10.0", "source": { "type": "git", - "url": "https://github.com/webmozart/assert.git", - "reference": "ab2cb0b3b559010b75981b1bdce728da3ee90ad6" + "url": "https://github.com/webmozarts/assert.git", + "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/ab2cb0b3b559010b75981b1bdce728da3ee90ad6", - "reference": "ab2cb0b3b559010b75981b1bdce728da3ee90ad6", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25", + "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0", + "php": "^7.2 || ^8.0", "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "vimeo/psalm": "<3.9.1" + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" }, "require-dev": { - "phpunit/phpunit": "^4.8.36 || ^7.5.13" + "phpunit/phpunit": "^8.5.13" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, "autoload": { "psr-4": { "Webmozart\\Assert\\": "src/" @@ -3909,7 +5001,11 @@ "check", "validate" ], - "time": "2020-04-18T12:12:48+00:00" + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.10.0" + }, + "time": "2021-03-09T10:59:23+00:00" } ], "aliases": [], @@ -3918,15 +5014,15 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^7.2", + "php": "^7.2 || ^8.0", + "ext-simplexml": "*", "ext-ctype": "*", "ext-dom": "*", + "ext-fileinfo": "*", "ext-gd": "*", "ext-iconv": "*", - "ext-fileinfo": "*", "ext-libxml": "*", "ext-mbstring": "*", - "ext-simplexml": "*", "ext-xml": "*", "ext-xmlreader": "*", "ext-xmlwriter": "*", @@ -3934,5 +5030,5 @@ "ext-zlib": "*" }, "platform-dev": [], - "plugin-api-version": "1.1.0" + "plugin-api-version": "2.0.0" } diff --git a/docs/extra/extrajs.js b/docs/extra/extrajs.js new file mode 100644 index 0000000000..9ad135e838 --- /dev/null +++ b/docs/extra/extrajs.js @@ -0,0 +1,5 @@ +document.addEventListener("DOMContentLoaded", function() { + document.querySelectorAll("table").forEach(function(table) { + table.classList.add("docutils"); + }); +}); \ No newline at end of file diff --git a/docs/faq.md b/docs/faq.md index 19f5f8fc2f..ac69e415fa 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -23,7 +23,7 @@ When you make use of any of the worksheet protection features (e.g. cell range protection, prohibiting deleting rows, ...), make sure you enable worksheet security. This can for example be done like this: -``` php +```php $spreadsheet->getActiveSheet()->getProtection()->setSheet(true); ``` diff --git a/docs/index.md b/docs/index.md index c1a064595c..42acedf97b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -2,9 +2,8 @@ ![Logo](./assets/logo.svg) -PhpSpreadsheet is a library written in pure PHP and providing a set of -classes that allow you to read from and to write to different -spreadsheet file formats, like Excel and LibreOffice Calc. +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. ## File formats supported @@ -90,7 +89,7 @@ php vendor/phpoffice/phpspreadsheet/samples/Basic/01_Simple.php ## Learn by documentation -For more in-depth documentation, you may read about an [overview of the +For more documentation in depth, you may read about an [overview of the architecture](./topics/architecture.md), [creating a spreadsheet](./topics/creating-spreadsheet.md), [worksheets](./topics/worksheets.md), diff --git a/docs/references/function-list-by-category.md b/docs/references/function-list-by-category.md index 49bb66c0ed..6ac54cb7a1 100644 --- a/docs/references/function-list-by-category.md +++ b/docs/references/function-list-by-category.md @@ -14,7 +14,7 @@ CUBEVALUE | **Not yet Implemented** ## CATEGORY_DATABASE -Excel Function | PhpSpreadsheet Function +Excel Function | PhpSpreadsheet Function --------------------|------------------------------------------- DAVERAGE | \PhpOffice\PhpSpreadsheet\Calculation\Database::DAVERAGE DCOUNT | \PhpOffice\PhpSpreadsheet\Calculation\Database::DCOUNT @@ -213,7 +213,7 @@ FALSE | \PhpOffice\PhpSpreadsheet\Calculation\Logical::FALSE IF | \PhpOffice\PhpSpreadsheet\Calculation\Logical::statementIf IFERROR | \PhpOffice\PhpSpreadsheet\Calculation\Logical::IFERROR IFNA | \PhpOffice\PhpSpreadsheet\Calculation\Logical::IFNA -IFS | **Not yet Implemented** +IFS | \PhpOffice\PhpSpreadsheet\Calculation\Logical::IFS NOT | \PhpOffice\PhpSpreadsheet\Calculation\Logical::NOT OR | \PhpOffice\PhpSpreadsheet\Calculation\Logical::logicalOr SWITCH | \PhpOffice\PhpSpreadsheet\Calculation\Logical::statementSwitch @@ -229,6 +229,7 @@ AREAS | **Not yet Implemented** CHOOSE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::CHOOSE COLUMN | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::COLUMN COLUMNS | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::COLUMNS +FILTER | **Not yet Implemented** FORMULATEXT | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::FORMULATEXT GETPIVOTDATA | **Not yet Implemented** HLOOKUP | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::HLOOKUP @@ -241,8 +242,13 @@ OFFSET | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::OFFSET ROW | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::ROW ROWS | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::ROWS RTD | **Not yet Implemented** +SORT | **Not yet Implemented** +SORTBY | **Not yet Implemented** TRANSPOSE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::TRANSPOSE +UNIQUE | **Not yet Implemented** VLOOKUP | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::VLOOKUP +XLOOKUP | **Not yet Implemented** +XMATCH | **Not yet Implemented** ## CATEGORY_MATH_AND_TRIG @@ -253,6 +259,7 @@ ACOS | acos ACOSH | acosh ACOT | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::ACOT ACOTH | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::ACOTH +AGGREGATE | **Not yet Implemented** ARABIC | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::ARABIC ASIN | asin ASINH | asinh @@ -261,13 +268,17 @@ ATAN2 | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::ATAN2 ATANH | atanh BASE | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::BASE CEILING | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::CEILING +CEILING.MATH | **Not yet Implemented** +CEILING.PRECISE | **Not yet Implemented** COMBIN | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::COMBIN +COMBINA | **Not yet Implemented** COS | cos COSH | cosh COT | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::COT COTH | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::COTH CSC | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::CSC CSCH | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::CSCH +DECIMAL | **Not yet Implemented** DEGREES | rad2deg EVEN | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::EVEN EXP | exp @@ -278,6 +289,7 @@ FLOOR.MATH | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::FLOORMATH FLOOR.PRECISE | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::FLOORPRECISE GCD | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::GCD INT | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::INT +ISO.CEILING | **Not yet Implemented** LCM | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::LCM LN | log LOG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::logBase @@ -288,6 +300,7 @@ MMULT | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::MMULT MOD | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::MOD MROUND | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::MROUND MULTINOMIAL | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::MULTINOMIAL +MUNIT | **Not yet Implemented** ODD | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::ODD PI | pi POWER | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::POWER @@ -295,6 +308,7 @@ PRODUCT | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::PRODUCT QUOTIENT | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::QUOTIENT RADIANS | deg2rad RAND | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::RAND +RANDARRAY | **Not yet Implemented** RANDBETWEEN | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::RAND ROMAN | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::ROMAN ROUND | round @@ -303,6 +317,7 @@ ROUNDUP | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::ROUNDUP SEC | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SEC SECH | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SECH SERIESSUM | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SERIESSUM +SEQUENCE | **Not yet Implemented** SIGN | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SIGN SIN | sin SINH | sinh @@ -323,99 +338,157 @@ TRUNC | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::TRUNC ## CATEGORY_STATISTICAL -Excel Function | PhpSpreadsheet Function ---------------------|------------------------------------------- -AVEDEV | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::AVEDEV -AVERAGE | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::AVERAGE -AVERAGEA | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::AVERAGEA -AVERAGEIF | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::AVERAGEIF -AVERAGEIFS | **Not yet Implemented** -BETADIST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::BETADIST -BETAINV | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::BETAINV -BINOMDIST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::BINOMDIST -CHIDIST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::CHIDIST -CHIINV | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::CHIINV -CHITEST | **Not yet Implemented** -CONFIDENCE | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::CONFIDENCE -CORREL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::CORREL -COUNT | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::COUNT -COUNTA | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::COUNTA -COUNTBLANK | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::COUNTBLANK -COUNTIF | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::COUNTIF -COUNTIFS | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::COUNTIFS -COVAR | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::COVAR -CRITBINOM | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::CRITBINOM -DEVSQ | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::DEVSQ -EXPONDIST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::EXPONDIST -FDIST | **Not yet Implemented** -FINV | **Not yet Implemented** -FISHER | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::FISHER -FISHERINV | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::FISHERINV -FORECAST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::FORECAST -FREQUENCY | **Not yet Implemented** -FTEST | **Not yet Implemented** -GAMMADIST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::GAMMADIST -GAMMAINV | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::GAMMAINV -GAMMALN | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::GAMMALN -GEOMEAN | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::GEOMEAN -GROWTH | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::GROWTH -HARMEAN | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::HARMEAN -HYPGEOMDIST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::HYPGEOMDIST -INTERCEPT | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::INTERCEPT -KURT | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::KURT -LARGE | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::LARGE -LINEST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::LINEST -LOGEST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::LOGEST -LOGINV | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::LOGINV -LOGNORMDIST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::LOGNORMDIST -MAX | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MAX -MAXA | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MAXA -MAXIFS | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MAXIFS -MEDIAN | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MEDIAN -MEDIANIF | **Not yet Implemented** -MIN | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MIN -MINA | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MINA -MINIFS | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MINIFS -MODE | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MODE -MODE.SNGL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MODE -NEGBINOMDIST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::NEGBINOMDIST -NORMDIST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::NORMDIST -NORMINV | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::NORMINV -NORMSDIST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::NORMSDIST -NORMSINV | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::NORMSINV -PEARSON | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::CORREL -PERCENTILE | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::PERCENTILE -PERCENTRANK | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::PERCENTRANK -PERMUT | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::PERMUT -POISSON | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::POISSON -PROB | **Not yet Implemented** -QUARTILE | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::QUARTILE -RANK | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::RANK -RSQ | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::RSQ -SKEW | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::SKEW -SLOPE | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::SLOPE -SMALL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::SMALL -STANDARDIZE | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STANDARDIZE -STDEV | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STDEV -STDEV.P | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STDEVP -STDEV.S | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STDEV -STDEVA | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STDEVA -STDEVP | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STDEVP -STDEVPA | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STDEVPA -STEYX | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STEYX -TDIST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::TDIST -TINV | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::TINV -TREND | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::TREND -TRIMMEAN | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::TRIMMEAN -TTEST | **Not yet Implemented** -VAR | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::VARFunc -VAR.P | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::VARP -VAR.S | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::VARFunc -VARA | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::VARA -VARP | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::VARP -VARPA | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::VARPA -WEIBULL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::WEIBULL -ZTEST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::ZTEST +Excel Function | PhpSpreadsheet Function +-------------------------|------------------------------------------- +AVEDEV | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::AVEDEV +AVERAGE | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::AVERAGE +AVERAGEA | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::AVERAGEA +AVERAGEIF | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::AVERAGEIF +AVERAGEIFS | **Not yet Implemented** +BETADIST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::BETADIST +BETA.DIST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::BETADIST +BETAINV | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::BETAINV +BETA.INV | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::BETAINV +BINOMDIST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::BINOMDIST +BINOM.DIST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::BINOMDIST +BINOM.DIST.RANGE | **Not yet Implemented** +BINOM.INV | **Not yet Implemented** +CHIDIST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::CHIDIST +CHISQ.DIST | **Not yet Implemented** +CHISQ.DIST.RT | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::CHIDIST +CHIINV | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::CHIINV +CHISQ.INV | **Not yet Implemented** +CHISQ.INV.RT | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::CHIINV +CHITEST | **Not yet Implemented** +CHISQ.TEST | **Not yet Implemented** +CONFIDENCE | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::CONFIDENCE +CONFIDENCE.NORM | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::CONFIDENCE +CONFIDENCE.T | **Not yet Implemented** +CORREL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::CORREL +COUNT | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::COUNT +COUNTA | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::COUNTA +COUNTBLANK | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::COUNTBLANK +COUNTIF | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::COUNTIF +COUNTIFS | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::COUNTIFS +COVAR | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::COVAR +COVARIANCE.P | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::COVAR +COVARIANCE.S | **Not yet Implemented** +CRITBINOM | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::CRITBINOM +DEVSQ | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::DEVSQ +EXPONDIST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::EXPONDIST +EXPON.DIST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::EXPONDIST +FDIST | **Not yet Implemented** +F.DIST | **Not yet Implemented** +F.DIST.RT | **Not yet Implemented** +FINV | **Not yet Implemented** +F.INV | **Not yet Implemented** +F.INV.RT | **Not yet Implemented** +F.TEST | **Not yet Implemented** +FISHER | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::FISHER +FISHERINV | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::FISHERINV +FORECAST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::FORECAST +FORECAST.ETS | **Not yet Implemented** +FORECAST.ETS.CONFINT | **Not yet Implemented** +FORECAST.ETS.SEASONALITY | **Not yet Implemented** +FORECAST.ETS.STAT | **Not yet Implemented** +FORECAST.LINEAR | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::FORECAST +FREQUENCY | **Not yet Implemented** +FTEST | **Not yet Implemented** +GAMMA | **Not yet Implemented** +GAMMADIST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::GAMMADIST +GAMMA.DIST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::GAMMADIST +GAMMAINV | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::GAMMAINV +GAMMA.INV | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::GAMMAINV +GAMMALN | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::GAMMALN +GAMMALN.PRECISE | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::GAMMALN +GAUSS | **Not yet Implemented** +GEOMEAN | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::GEOMEAN +GROWTH | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::GROWTH +HARMEAN | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::HARMEAN +HYPGEOMDIST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::HYPGEOMDIST +INTERCEPT | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::INTERCEPT +KURT | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::KURT +LARGE | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::LARGE +LINEST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::LINEST +LOGEST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::LOGEST +LOGINV | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::LOGINV +LOGNORMDIST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::LOGNORMDIST +LOGNORM.DIST | **Not yet Implemented** +LOGNORM.INV | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::LOGINV +MAX | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MAX +MAXA | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MAXA +MAXIFS | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MAXIFS +MEDIAN | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MEDIAN +MEDIANIF | **Not yet Implemented** +MIN | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MIN +MINA | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MINA +MINIFS | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MINIFS +MODE | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MODE +MODE.MULT | **Not yet Implemented** +MODE.SNGL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MODE +NEGBINOMDIST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::NEGBINOMDIST +NEGBINOM.DIST | **Not yet Implemented** +NORMDIST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::NORMDIST +NORM.DIST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::NORMDIST +NORMINV | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::NORMINV +NORM.INV | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::NORMINV +NORMSDIST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::NORMSDIST +NORM.S.DIST | **Not yet Implemented** +NORMSINV | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::NORMSINV +NORM.S.INV | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::NORMSINV +PEARSON | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::CORREL +PERCENTILE | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::PERCENTILE +PERCENTILE.EXC | **Not yet Implemented** +PERCENTILE.INC | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::PERCENTILE +PERCENTRANK | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::PERCENTRANK +PERCENTRANK.EXC | **Not yet Implemented** +PERCENTRANK.INC | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::PERCENTRANK +PERMUT | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::PERMUT +PERMUTATIONA | **Not yet Implemented** +PHI | **Not yet Implemented** +POISSON | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::POISSON +POISSON.DIST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::POISSON +PROB | **Not yet Implemented** +QUARTILE | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::QUARTILE +QUARTILE.EXC | **Not yet Implemented** +QUARTILE.INC | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::QUARTILE +RANK | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::RANK +RANK.AVG | **Not yet Implemented** +RANK.EQ | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::RANK +RSQ | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::RSQ +SKEW | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::SKEW +SKEW.P | **Not yet Implemented** +SLOPE | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::SLOPE +SMALL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::SMALL +STANDARDIZE | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STANDARDIZE +STDEV | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STDEV +STDEV.P | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STDEVP +STDEV.S | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STDEV +STDEVA | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STDEVA +STDEVP | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STDEVP +STDEVPA | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STDEVPA +STEYX | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STEYX +TDIST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::TDIST +T.DIST | **Not yet Implemented** +T.DIST.2T | **Not yet Implemented** +T.DIST.RT | **Not yet Implemented** +TINV | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::TINV +T.INV | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::TINV +T.INV.2T | **Not yet Implemented** +TREND | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::TREND +TRIMMEAN | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::TRIMMEAN +TTEST | **Not yet Implemented** +T.TEST | **Not yet Implemented** +VAR | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::VARFunc +VAR.P | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::VARP +VAR.S | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::VARFunc +VARA | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::VARA +VARP | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::VARP +VARPA | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::VARPA +WEIBULL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::WEIBULL +WEIBULL.DIST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::WEIBULL +ZTEST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::ZTEST +Z.TEST | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::ZTEST ## CATEGORY_TEXT_AND_DATA @@ -428,6 +501,7 @@ CLEAN | \PhpOffice\PhpSpreadsheet\Calculation\TextData::TRIMNONPRI CODE | \PhpOffice\PhpSpreadsheet\Calculation\TextData::ASCIICODE CONCAT | \PhpOffice\PhpSpreadsheet\Calculation\TextData::CONCATENATE CONCATENATE | \PhpOffice\PhpSpreadsheet\Calculation\TextData::CONCATENATE +DBCS | **Not yet Implemented** DOLLAR | \PhpOffice\PhpSpreadsheet\Calculation\TextData::DOLLAR EXACT | \PhpOffice\PhpSpreadsheet\Calculation\TextData::EXACT FIND | \PhpOffice\PhpSpreadsheet\Calculation\TextData::SEARCHSENSITIVE @@ -460,3 +534,11 @@ UNICHAR | \PhpOffice\PhpSpreadsheet\Calculation\TextData::CHARACTER UNICODE | \PhpOffice\PhpSpreadsheet\Calculation\TextData::ASCIICODE UPPER | \PhpOffice\PhpSpreadsheet\Calculation\TextData::UPPERCASE VALUE | \PhpOffice\PhpSpreadsheet\Calculation\TextData::VALUE + +## CATEGORY_WEB + +Excel Function | PhpSpreadsheet Function +--------------------|------------------------------------------- +ENCODEURL | **Not yet Implemented** +FILTERXML | **Not yet Implemented** +WEBSERVICE | \PhpOffice\PhpSpreadsheet\Calculation\Web::WEBSERVICE diff --git a/docs/references/function-list-by-name.md b/docs/references/function-list-by-name.md index f5493d039c..2341ee5f3b 100644 --- a/docs/references/function-list-by-name.md +++ b/docs/references/function-list-by-name.md @@ -2,536 +2,613 @@ ## A -Excel Function | Category | PhpSpreadsheet Function ---------------------|--------------------------------|------------------------------------------- -ABS | CATEGORY_MATH_AND_TRIG | abs -ACCRINT | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::ACCRINT -ACCRINTM | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::ACCRINTM -ACOS | CATEGORY_MATH_AND_TRIG | acos -ACOSH | CATEGORY_MATH_AND_TRIG | acosh -ACOT | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::ACOT -ACOTH | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::ACOTH -ADDRESS | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::cellAddress -AMORDEGRC | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::AMORDEGRC -AMORLINC | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::AMORLINC -AND | CATEGORY_LOGICAL | \PhpOffice\PhpSpreadsheet\Calculation\Logical::logicalAnd -ARABIC | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::ARABIC -AREAS | CATEGORY_LOOKUP_AND_REFERENCE | **Not yet Implemented** -ASC | CATEGORY_TEXT_AND_DATA | **Not yet Implemented** -ASIN | CATEGORY_MATH_AND_TRIG | asin -ASINH | CATEGORY_MATH_AND_TRIG | asinh -ATAN | CATEGORY_MATH_AND_TRIG | atan -ATAN2 | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::ATAN2 -ATANH | CATEGORY_MATH_AND_TRIG | atanh -AVEDEV | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::AVEDEV -AVERAGE | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::AVERAGE -AVERAGEA | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::AVERAGEA -AVERAGEIF | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::AVERAGEIF -AVERAGEIFS | CATEGORY_STATISTICAL | **Not yet Implemented** +Excel Function | Category | PhpSpreadsheet Function +-------------------------|-------------------------------|------------------------- +ABS | CATEGORY_MATH_AND_TRIG | abs +ACCRINT | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::ACCRINT +ACCRINTM | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::ACCRINTM +ACOS | CATEGORY_MATH_AND_TRIG | acos +ACOSH | CATEGORY_MATH_AND_TRIG | acosh +ACOT | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::ACOT +ACOTH | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::ACOTH +ADDRESS | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::cellAddress +AGGREGATE | CATEGORY_MATH_AND_TRIG | **Not yet Implemented** +AMORDEGRC | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::AMORDEGRC +AMORLINC | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::AMORLINC +AND | CATEGORY_LOGICAL | \PhpOffice\PhpSpreadsheet\Calculation\Logical::logicalAnd +ARABIC | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::ARABIC +AREAS | CATEGORY_LOOKUP_AND_REFERENCE | **Not yet Implemented** +ASC | CATEGORY_TEXT_AND_DATA | **Not yet Implemented** +ASIN | CATEGORY_MATH_AND_TRIG | asin +ASINH | CATEGORY_MATH_AND_TRIG | asinh +ATAN | CATEGORY_MATH_AND_TRIG | atan +ATAN2 | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::ATAN2 +ATANH | CATEGORY_MATH_AND_TRIG | atanh +AVEDEV | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::AVEDEV +AVERAGE | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::AVERAGE +AVERAGEA | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::AVERAGEA +AVERAGEIF | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::AVERAGEIF +AVERAGEIFS | CATEGORY_STATISTICAL | **Not yet Implemented** ## B -Excel Function | Category | PhpSpreadsheet Function ---------------------|--------------------------------|------------------------------------------- -BAHTTEXT | CATEGORY_TEXT_AND_DATA | **Not yet Implemented** -BASE | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::BASE -BESSELI | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::BESSELI -BESSELJ | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::BESSELJ -BESSELK | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::BESSELK -BESSELY | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::BESSELY -BETADIST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::BETADIST -BETAINV | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::BETAINV -BIN2DEC | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::BINTODEC -BIN2HEX | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::BINTOHEX -BIN2OCT | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::BINTOOCT -BINOMDIST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::BINOMDIST -BITAND | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::BITAND -BITLSHIFT | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::BITLSHIFT -BITOR | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::BITOR -BITRSHIFT | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::BITRSHIFT -BITXOR | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::BITOR +Excel Function | Category | PhpSpreadsheet Function +-------------------------|-------------------------------|------------------------- +BAHTTEXT | CATEGORY_TEXT_AND_DATA | **Not yet Implemented** +BASE | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::BASE +BESSELI | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::BESSELI +BESSELJ | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::BESSELJ +BESSELK | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::BESSELK +BESSELY | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::BESSELY +BETADIST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::BETADIST +BETA.DIST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::BETADIST +BETAINV | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::BETAINV +BETA.INV | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::BETAINV +BIN2DEC | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::BINTODEC +BIN2HEX | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::BINTOHEX +BIN2OCT | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::BINTOOCT +BINOMDIST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::BINOMDIST +BINOM.DIST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::BINOMDIST +BINOM.DIST.RANGE | CATEGORY_STATISTICAL | **Not yet Implemented** +BINOM.INV | CATEGORY_STATISTICAL | **Not yet Implemented** +BITAND | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::BITAND +BITLSHIFT | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::BITLSHIFT +BITOR | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::BITOR +BITRSHIFT | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::BITRSHIFT +BITXOR | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::BITOR ## C -Excel Function | Category | PhpSpreadsheet Function ---------------------|--------------------------------|------------------------------------------- -CEILING | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::CEILING -CELL | CATEGORY_INFORMATION | **Not yet Implemented** -CHAR | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::CHARACTER -CHIDIST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::CHIDIST -CHIINV | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::CHIINV -CHITEST | CATEGORY_STATISTICAL | **Not yet Implemented** -CHOOSE | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::CHOOSE -CLEAN | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::TRIMNONPRINTABLE -CODE | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::ASCIICODE -COLUMN | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::COLUMN -COLUMNS | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::COLUMNS -COMBIN | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::COMBIN -COMPLEX | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::COMPLEX -CONCAT | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::CONCATENATE -CONCATENATE | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::CONCATENATE -CONFIDENCE | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::CONFIDENCE -CONVERT | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::CONVERTUOM -CORREL | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::CORREL -COS | CATEGORY_MATH_AND_TRIG | cos -COSH | CATEGORY_MATH_AND_TRIG | cosh -COT | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::COT -COTH | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::COTH -COUNT | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::COUNT -COUNTA | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::COUNTA -COUNTBLANK | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::COUNTBLANK -COUNTIF | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::COUNTIF -COUNTIFS | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::COUNTIFS -COUPDAYBS | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::COUPDAYBS -COUPDAYS | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::COUPDAYS -COUPDAYSNC | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::COUPDAYSNC -COUPNCD | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::COUPNCD -COUPNUM | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::COUPNUM -COUPPCD | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::COUPPCD -COVAR | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::COVAR -CRITBINOM | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::CRITBINOM -CSC | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::CSC -CSCH | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::CSCH -CUBEKPIMEMBER | CATEGORY_CUBE | **Not yet Implemented** -CUBEMEMBER | CATEGORY_CUBE | **Not yet Implemented** -CUBEMEMBERPROPERTY | CATEGORY_CUBE | **Not yet Implemented** -CUBERANKEDMEMBER | CATEGORY_CUBE | **Not yet Implemented** -CUBESET | CATEGORY_CUBE | **Not yet Implemented** -CUBESETCOUNT | CATEGORY_CUBE | **Not yet Implemented** -CUBEVALUE | CATEGORY_CUBE | **Not yet Implemented** -CUMIPMT | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::CUMIPMT -CUMPRINC | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::CUMPRINC +Excel Function | Category | PhpSpreadsheet Function +-------------------------|-------------------------------|------------------------- +CEILING | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::CEILING +CEILING.MATH | CATEGORY_MATH_AND_TRIG | **Not yet Implemented** +CEILING.PRECISE | CATEGORY_MATH_AND_TRIG | **Not yet Implemented** +CELL | CATEGORY_INFORMATION | **Not yet Implemented** +CHAR | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::CHARACTER +CHIDIST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::CHIDIST +CHIINV | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::CHIINV +CHISQ.DIST | CATEGORY_STATISTICAL | **Not yet Implemented** +CHISQ.DIST.RT | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::CHIDIST +CHISQ.INV | CATEGORY_STATISTICAL | **Not yet Implemented** +CHISQ.INV.RT | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::CHIINV +CHISQ.TEST | CATEGORY_STATISTICAL | **Not yet Implemented** +CHITEST | CATEGORY_STATISTICAL | **Not yet Implemented** +CHOOSE | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::CHOOSE +CLEAN | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::TRIMNONPRINTABLE +CODE | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::ASCIICODE +COLUMN | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::COLUMN +COLUMNS | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::COLUMNS +COMBIN | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::COMBIN +COMBINA | CATEGORY_MATH_AND_TRIG | **Not yet Implemented** +COMPLEX | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::COMPLEX +CONCAT | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::CONCATENATE +CONCATENATE | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::CONCATENATE +CONFIDENCE | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::CONFIDENCE +CONFIDENCE.NORM | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::CONFIDENCE +CONFIDENCE.T | CATEGORY_STATISTICAL | **Not yet Implemented** +CONVERT | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::CONVERTUOM +CORREL | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::CORREL +COS | CATEGORY_MATH_AND_TRIG | cos +COSH | CATEGORY_MATH_AND_TRIG | cosh +COT | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::COT +COTH | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::COTH +COUNT | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::COUNT +COUNTA | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::COUNTA +COUNTBLANK | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::COUNTBLANK +COUNTIF | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::COUNTIF +COUNTIFS | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::COUNTIFS +COUPDAYBS | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::COUPDAYBS +COUPDAYS | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::COUPDAYS +COUPDAYSNC | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::COUPDAYSNC +COUPNCD | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::COUPNCD +COUPNUM | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::COUPNUM +COUPPCD | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::COUPPCD +COVAR | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::COVAR +COVARIANCE.P | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::COVAR +COVARIANCE.S | CATEGORY_STATISTICAL | **Not yet Implemented** +CRITBINOM | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::CRITBINOM +CSC | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::CSC +CSCH | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::CSCH +CUBEKPIMEMBER | CATEGORY_CUBE | **Not yet Implemented** +CUBEMEMBER | CATEGORY_CUBE | **Not yet Implemented** +CUBEMEMBERPROPERTY | CATEGORY_CUBE | **Not yet Implemented** +CUBERANKEDMEMBER | CATEGORY_CUBE | **Not yet Implemented** +CUBESET | CATEGORY_CUBE | **Not yet Implemented** +CUBESETCOUNT | CATEGORY_CUBE | **Not yet Implemented** +CUBEVALUE | CATEGORY_CUBE | **Not yet Implemented** +CUMIPMT | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::CUMIPMT +CUMPRINC | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::CUMPRINC ## D -Excel Function | Category | PhpSpreadsheet Function ---------------------|--------------------------------|------------------------------------------- -DATE | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::DATE -DATEDIF | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::DATEDIF -DATEVALUE | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::DATEVALUE -DAVERAGE | CATEGORY_DATABASE | \PhpOffice\PhpSpreadsheet\Calculation\Database::DAVERAGE -DAY | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::DAYOFMONTH -DAYS | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::DAYS -DAYS360 | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::DAYS360 -DB | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::DB -DCOUNT | CATEGORY_DATABASE | \PhpOffice\PhpSpreadsheet\Calculation\Database::DCOUNT -DCOUNTA | CATEGORY_DATABASE | \PhpOffice\PhpSpreadsheet\Calculation\Database::DCOUNTA -DDB | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::DDB -DEC2BIN | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::DECTOBIN -DEC2HEX | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::DECTOHEX -DEC2OCT | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::DECTOOCT -DEGREES | CATEGORY_MATH_AND_TRIG | rad2deg -DELTA | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::DELTA -DEVSQ | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::DEVSQ -DGET | CATEGORY_DATABASE | \PhpOffice\PhpSpreadsheet\Calculation\Database::DGET -DISC | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::DISC -DMAX | CATEGORY_DATABASE | \PhpOffice\PhpSpreadsheet\Calculation\Database::DMAX -DMIN | CATEGORY_DATABASE | \PhpOffice\PhpSpreadsheet\Calculation\Database::DMIN -DOLLAR | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::DOLLAR -DOLLARDE | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::DOLLARDE -DOLLARFR | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::DOLLARFR -DPRODUCT | CATEGORY_DATABASE | \PhpOffice\PhpSpreadsheet\Calculation\Database::DPRODUCT -DSTDEV | CATEGORY_DATABASE | \PhpOffice\PhpSpreadsheet\Calculation\Database::DSTDEV -DSTDEVP | CATEGORY_DATABASE | \PhpOffice\PhpSpreadsheet\Calculation\Database::DSTDEVP -DSUM | CATEGORY_DATABASE | \PhpOffice\PhpSpreadsheet\Calculation\Database::DSUM -DURATION | CATEGORY_FINANCIAL | **Not yet Implemented** -DVAR | CATEGORY_DATABASE | \PhpOffice\PhpSpreadsheet\Calculation\Database::DVAR -DVARP | CATEGORY_DATABASE | \PhpOffice\PhpSpreadsheet\Calculation\Database::DVARP +Excel Function | Category | PhpSpreadsheet Function +-------------------------|-------------------------------|------------------------- +DATE | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::DATE +DATEDIF | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::DATEDIF +DATEVALUE | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::DATEVALUE +DAVERAGE | CATEGORY_DATABASE | \PhpOffice\PhpSpreadsheet\Calculation\Database::DAVERAGE +DAY | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::DAYOFMONTH +DAYS | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::DAYS +DAYS360 | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::DAYS360 +DB | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::DB +DBCS | CATEGORY_TEXT_AND_DATA | **Not yet Implemented** +DCOUNT | CATEGORY_DATABASE | \PhpOffice\PhpSpreadsheet\Calculation\Database::DCOUNT +DCOUNTA | CATEGORY_DATABASE | \PhpOffice\PhpSpreadsheet\Calculation\Database::DCOUNTA +DDB | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::DDB +DEC2BIN | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::DECTOBIN +DEC2HEX | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::DECTOHEX +DEC2OCT | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::DECTOOCT +DECIMAL | CATEGORY_MATH_AND_TRIG | **Not yet Implemented** +DEGREES | CATEGORY_MATH_AND_TRIG | rad2deg +DELTA | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::DELTA +DEVSQ | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::DEVSQ +DGET | CATEGORY_DATABASE | \PhpOffice\PhpSpreadsheet\Calculation\Database::DGET +DISC | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::DISC +DMAX | CATEGORY_DATABASE | \PhpOffice\PhpSpreadsheet\Calculation\Database::DMAX +DMIN | CATEGORY_DATABASE | \PhpOffice\PhpSpreadsheet\Calculation\Database::DMIN +DOLLAR | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::DOLLAR +DOLLARDE | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::DOLLARDE +DOLLARFR | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::DOLLARFR +DPRODUCT | CATEGORY_DATABASE | \PhpOffice\PhpSpreadsheet\Calculation\Database::DPRODUCT +DSTDEV | CATEGORY_DATABASE | \PhpOffice\PhpSpreadsheet\Calculation\Database::DSTDEV +DSTDEVP | CATEGORY_DATABASE | \PhpOffice\PhpSpreadsheet\Calculation\Database::DSTDEVP +DSUM | CATEGORY_DATABASE | \PhpOffice\PhpSpreadsheet\Calculation\Database::DSUM +DURATION | CATEGORY_FINANCIAL | **Not yet Implemented** +DVAR | CATEGORY_DATABASE | \PhpOffice\PhpSpreadsheet\Calculation\Database::DVAR +DVARP | CATEGORY_DATABASE | \PhpOffice\PhpSpreadsheet\Calculation\Database::DVARP ## E -Excel Function | Category | PhpSpreadsheet Function ---------------------|--------------------------------|------------------------------------------- -EDATE | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::EDATE -EFFECT | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::EFFECT -EOMONTH | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::EOMONTH -ERF | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::ERF -ERF.PRECISE | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::ERFPRECISE -ERFC | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::ERFC -ERFC.PRECISE | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::ERFC -ERROR.TYPE | CATEGORY_INFORMATION | \PhpOffice\PhpSpreadsheet\Calculation\Functions::errorType -EVEN | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::EVEN -EXACT | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::EXACT -EXP | CATEGORY_MATH_AND_TRIG | exp -EXPONDIST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::EXPONDIST +Excel Function | Category | PhpSpreadsheet Function +-------------------------|-------------------------------|------------------------- +EDATE | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::EDATE +EFFECT | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::EFFECT +ENCODEURL | CATEGORY_TEXT_AND_DATA | **Not yet Implemented** +EOMONTH | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::EOMONTH +ERF | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::ERF +ERFC | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::ERFC +ERFC.PRECISE | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::ERFC +ERF.PRECISE | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::ERFPRECISE +ERROR.TYPE | CATEGORY_INFORMATION | \PhpOffice\PhpSpreadsheet\Calculation\Functions::errorType +EVEN | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::EVEN +EXACT | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::EXACT +EXP | CATEGORY_MATH_AND_TRIG | exp +EXPONDIST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::EXPONDIST +EXPON.DIST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::EXPONDIST ## F -Excel Function | Category | PhpSpreadsheet Function ---------------------|--------------------------------|------------------------------------------- -FACT | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::FACT -FACTDOUBLE | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::FACTDOUBLE -FALSE | CATEGORY_LOGICAL | \PhpOffice\PhpSpreadsheet\Calculation\Logical::FALSE -FDIST | CATEGORY_STATISTICAL | **Not yet Implemented** -FIND | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::SEARCHSENSITIVE -FINDB | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::SEARCHSENSITIVE -FINV | CATEGORY_STATISTICAL | **Not yet Implemented** -FISHER | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::FISHER -FISHERINV | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::FISHERINV -FIXED | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::FIXEDFORMAT -FLOOR | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::FLOOR -FLOOR.MATH | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::FLOORMATH -FLOOR.PRECISE | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::FLOORPRECISE -FORECAST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::FORECAST -FORMULATEXT | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::FORMULATEXT -FREQUENCY | CATEGORY_STATISTICAL | **Not yet Implemented** -FTEST | CATEGORY_STATISTICAL | **Not yet Implemented** -FV | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::FV -FVSCHEDULE | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::FVSCHEDULE +Excel Function | Category | PhpSpreadsheet Function +-------------------------|-------------------------------|------------------------- +FACT | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::FACT +FACTDOUBLE | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::FACTDOUBLE +FALSE | CATEGORY_LOGICAL | \PhpOffice\PhpSpreadsheet\Calculation\Logical::FALSE +FDIST | CATEGORY_STATISTICAL | **Not yet Implemented** +F.DIST | CATEGORY_STATISTICAL | **Not yet Implemented** +F.DIST.RT | CATEGORY_STATISTICAL | **Not yet Implemented** +FILTER | CATEGORY_LOOKUP_AND_REFERENCE | **Not yet Implemented** +FILTERXML | CATEGORY_TEXT_AND_DATA | **Not yet Implemented** +FIND | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::SEARCHSENSITIVE +FINDB | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::SEARCHSENSITIVE +FINV | CATEGORY_STATISTICAL | **Not yet Implemented** +F.INV | CATEGORY_STATISTICAL | **Not yet Implemented** +F.INV.RT | CATEGORY_STATISTICAL | **Not yet Implemented** +FISHER | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::FISHER +FISHERINV | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::FISHERINV +FIXED | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::FIXEDFORMAT +FLOOR | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::FLOOR +FLOOR.MATH | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::FLOORMATH +FLOOR.PRECISE | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::FLOORPRECISE +FORECAST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::FORECAST +FORECAST.ETS | CATEGORY_STATISTICAL | **Not yet Implemented** +FORECAST.ETS.CONFINT | CATEGORY_STATISTICAL | **Not yet Implemented** +FORECAST.ETS.SEASONALITY | CATEGORY_STATISTICAL | **Not yet Implemented** +FORECAST.ETS.STAT | CATEGORY_STATISTICAL | **Not yet Implemented** +FORECAST.LINEAR | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::FORECAST +FORMULATEXT | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::FORMULATEXT +FREQUENCY | CATEGORY_STATISTICAL | **Not yet Implemented** +FTEST | CATEGORY_STATISTICAL | **Not yet Implemented** +F.TEST | CATEGORY_STATISTICAL | **Not yet Implemented** +FV | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::FV +FVSCHEDULE | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::FVSCHEDULE ## G -Excel Function | Category | PhpSpreadsheet Function ---------------------|--------------------------------|------------------------------------------- -GAMMADIST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::GAMMADIST -GAMMAINV | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::GAMMAINV -GAMMALN | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::GAMMALN -GCD | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::GCD -GEOMEAN | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::GEOMEAN -GESTEP | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::GESTEP -GETPIVOTDATA | CATEGORY_LOOKUP_AND_REFERENCE | **Not yet Implemented** -GROWTH | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::GROWTH +Excel Function | Category | PhpSpreadsheet Function +-------------------------|-------------------------------|------------------------- +GAMMA | CATEGORY_STATISTICAL | **Not yet Implemented** +GAMMADIST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::GAMMADIST +GAMMA.DIST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::GAMMADIST +GAMMAINV | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::GAMMAINV +GAMMA.INV | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::GAMMAINV +GAMMALN | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::GAMMALN +GAMMALN.PRECISE | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::GAMMALN +GAUSS | CATEGORY_STATISTICAL | **Not yet Implemented** +GCD | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::GCD +GEOMEAN | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::GEOMEAN +GESTEP | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::GESTEP +GETPIVOTDATA | CATEGORY_LOOKUP_AND_REFERENCE | **Not yet Implemented** +GROWTH | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::GROWTH ## H -Excel Function | Category | PhpSpreadsheet Function ---------------------|--------------------------------|------------------------------------------- -HARMEAN | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::HARMEAN -HEX2BIN | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::HEXTOBIN -HEX2DEC | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::HEXTODEC -HEX2OCT | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::HEXTOOCT -HLOOKUP | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::HLOOKUP -HOUR | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::HOUROFDAY -HYPERLINK | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::HYPERLINK -HYPGEOMDIST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::HYPGEOMDIST +Excel Function | Category | PhpSpreadsheet Function +-------------------------|-------------------------------|------------------------- +HARMEAN | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::HARMEAN +HEX2BIN | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::HEXTOBIN +HEX2DEC | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::HEXTODEC +HEX2OCT | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::HEXTOOCT +HLOOKUP | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::HLOOKUP +HOUR | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::HOUROFDAY +HYPERLINK | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::HYPERLINK +HYPGEOMDIST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::HYPGEOMDIST ## I -Excel Function | Category | PhpSpreadsheet Function ---------------------|--------------------------------|------------------------------------------- -IF | CATEGORY_LOGICAL | \PhpOffice\PhpSpreadsheet\Calculation\Logical::statementIf -IFERROR | CATEGORY_LOGICAL | \PhpOffice\PhpSpreadsheet\Calculation\Logical::IFERROR -IFNA | CATEGORY_LOGICAL | \PhpOffice\PhpSpreadsheet\Calculation\Logical::IFNA -IFS | CATEGORY_LOGICAL | **Not yet Implemented** -IMABS | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMABS -IMAGINARY | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMAGINARY -IMARGUMENT | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMARGUMENT -IMCONJUGATE | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMCONJUGATE -IMCOS | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMCOS -IMCOSH | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMCOSH -IMCOT | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMCOT -IMCSC | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMCSC -IMCSCH | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMCSCH -IMDIV | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMDIV -IMEXP | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMEXP -IMLN | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMLN -IMLOG10 | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMLOG10 -IMLOG2 | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMLOG2 -IMPOWER | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMPOWER -IMPRODUCT | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMPRODUCT -IMREAL | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMREAL -IMSEC | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMSEC -IMSECH | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMSECH -IMSIN | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMSIN -IMSINH | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMSINH -IMSQRT | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMSQRT -IMSUB | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMSUB -IMSUM | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMSUM -IMTAN | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMTAN -INDEX | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::INDEX -INDIRECT | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::INDIRECT -INFO | CATEGORY_INFORMATION | **Not yet Implemented** -INT | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::INT -INTERCEPT | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::INTERCEPT -INTRATE | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::INTRATE -IPMT | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::IPMT -IRR | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::IRR -ISBLANK | CATEGORY_INFORMATION | \PhpOffice\PhpSpreadsheet\Calculation\Functions::isBlank -ISERR | CATEGORY_INFORMATION | \PhpOffice\PhpSpreadsheet\Calculation\Functions::isErr -ISERROR | CATEGORY_INFORMATION | \PhpOffice\PhpSpreadsheet\Calculation\Functions::isError -ISEVEN | CATEGORY_INFORMATION | \PhpOffice\PhpSpreadsheet\Calculation\Functions::isEven -ISFORMULA | CATEGORY_INFORMATION | \PhpOffice\PhpSpreadsheet\Calculation\Functions::isFormula -ISLOGICAL | CATEGORY_INFORMATION | \PhpOffice\PhpSpreadsheet\Calculation\Functions::isLogical -ISNA | CATEGORY_INFORMATION | \PhpOffice\PhpSpreadsheet\Calculation\Functions::isNa -ISNONTEXT | CATEGORY_INFORMATION | \PhpOffice\PhpSpreadsheet\Calculation\Functions::isNonText -ISNUMBER | CATEGORY_INFORMATION | \PhpOffice\PhpSpreadsheet\Calculation\Functions::isNumber -ISODD | CATEGORY_INFORMATION | \PhpOffice\PhpSpreadsheet\Calculation\Functions::isOdd -ISOWEEKNUM | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::ISOWEEKNUM -ISPMT | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::ISPMT -ISREF | CATEGORY_INFORMATION | **Not yet Implemented** -ISTEXT | CATEGORY_INFORMATION | \PhpOffice\PhpSpreadsheet\Calculation\Functions::isText +Excel Function | Category | PhpSpreadsheet Function +-------------------------|-------------------------------|------------------------- +IF | CATEGORY_LOGICAL | \PhpOffice\PhpSpreadsheet\Calculation\Logical::statementIf +IFERROR | CATEGORY_LOGICAL | \PhpOffice\PhpSpreadsheet\Calculation\Logical::IFERROR +IFNA | CATEGORY_LOGICAL | \PhpOffice\PhpSpreadsheet\Calculation\Logical::IFNA +IFS | CATEGORY_LOGICAL | \PhpOffice\PhpSpreadsheet\Calculation\Logical::IFS +IMABS | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMABS +IMAGINARY | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMAGINARY +IMARGUMENT | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMARGUMENT +IMCONJUGATE | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMCONJUGATE +IMCOS | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMCOS +IMCOSH | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMCOSH +IMCOT | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMCOT +IMCSC | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMCSC +IMCSCH | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMCSCH +IMDIV | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMDIV +IMEXP | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMEXP +IMLN | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMLN +IMLOG10 | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMLOG10 +IMLOG2 | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMLOG2 +IMPOWER | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMPOWER +IMPRODUCT | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMPRODUCT +IMREAL | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMREAL +IMSEC | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMSEC +IMSECH | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMSECH +IMSIN | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMSIN +IMSINH | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMSINH +IMSQRT | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMSQRT +IMSUB | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMSUB +IMSUM | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMSUM +IMTAN | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMTAN +INDEX | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::INDEX +INDIRECT | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::INDIRECT +INFO | CATEGORY_INFORMATION | **Not yet Implemented** +INT | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::INT +INTERCEPT | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::INTERCEPT +INTRATE | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::INTRATE +IPMT | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::IPMT +IRR | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::IRR +ISBLANK | CATEGORY_INFORMATION | \PhpOffice\PhpSpreadsheet\Calculation\Functions::isBlank +ISERR | CATEGORY_INFORMATION | \PhpOffice\PhpSpreadsheet\Calculation\Functions::isErr +ISERROR | CATEGORY_INFORMATION | \PhpOffice\PhpSpreadsheet\Calculation\Functions::isError +ISEVEN | CATEGORY_INFORMATION | \PhpOffice\PhpSpreadsheet\Calculation\Functions::isEven +ISFORMULA | CATEGORY_INFORMATION | \PhpOffice\PhpSpreadsheet\Calculation\Functions::isFormula +ISLOGICAL | CATEGORY_INFORMATION | \PhpOffice\PhpSpreadsheet\Calculation\Functions::isLogical +ISNA | CATEGORY_INFORMATION | \PhpOffice\PhpSpreadsheet\Calculation\Functions::isNa +ISNONTEXT | CATEGORY_INFORMATION | \PhpOffice\PhpSpreadsheet\Calculation\Functions::isNonText +ISNUMBER | CATEGORY_INFORMATION | \PhpOffice\PhpSpreadsheet\Calculation\Functions::isNumber +ISO.CEILING | CATEGORY_MATH_AND_TRIG | **Not yet Implemented** +ISODD | CATEGORY_INFORMATION | \PhpOffice\PhpSpreadsheet\Calculation\Functions::isOdd +ISOWEEKNUM | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::ISOWEEKNUM +ISPMT | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::ISPMT +ISREF | CATEGORY_INFORMATION | **Not yet Implemented** +ISTEXT | CATEGORY_INFORMATION | \PhpOffice\PhpSpreadsheet\Calculation\Functions::isText ## J -Excel Function | Category | PhpSpreadsheet Function ---------------------|--------------------------------|------------------------------------------- -JIS | CATEGORY_TEXT_AND_DATA | **Not yet Implemented** +Excel Function | Category | PhpSpreadsheet Function +-------------------------|-------------------------------|------------------------- +JIS | CATEGORY_TEXT_AND_DATA | **Not yet Implemented** ## K -Excel Function | Category | PhpSpreadsheet Function ---------------------|--------------------------------|------------------------------------------- -KURT | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::KURT +Excel Function | Category | PhpSpreadsheet Function +-------------------------|-------------------------------|------------------------- +KURT | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::KURT ## L -Excel Function | Category | PhpSpreadsheet Function ---------------------|--------------------------------|------------------------------------------- -LARGE | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::LARGE -LCM | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::LCM -LEFT | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::LEFT -LEFTB | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::LEFT -LEN | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::STRINGLENGTH -LENB | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::STRINGLENGTH -LINEST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::LINEST -LN | CATEGORY_MATH_AND_TRIG | log -LOG | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::logBase -LOG10 | CATEGORY_MATH_AND_TRIG | log10 -LOGEST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::LOGEST -LOGINV | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::LOGINV -LOGNORMDIST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::LOGNORMDIST -LOOKUP | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::LOOKUP -LOWER | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::LOWERCASE +Excel Function | Category | PhpSpreadsheet Function +-------------------------|-------------------------------|------------------------- +LARGE | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::LARGE +LCM | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::LCM +LEFT | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::LEFT +LEFTB | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::LEFT +LEN | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::STRINGLENGTH +LENB | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::STRINGLENGTH +LINEST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::LINEST +LN | CATEGORY_MATH_AND_TRIG | log +LOG | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::logBase +LOG10 | CATEGORY_MATH_AND_TRIG | log10 +LOGEST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::LOGEST +LOGINV | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::LOGINV +LOGNORMDIST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::LOGNORMDIST +LOGNORM.DIST | CATEGORY_STATISTICAL | **Not yet Implemented** +LOGNORM.INV | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::LOGINV +LOOKUP | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::LOOKUP +LOWER | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::LOWERCASE ## M -Excel Function | Category | PhpSpreadsheet Function ---------------------|--------------------------------|------------------------------------------- -MATCH | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::MATCH -MAX | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MAX -MAXA | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MAXA -MAXIFS | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MAXIFS -MDETERM | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::MDETERM -MDURATION | CATEGORY_FINANCIAL | **Not yet Implemented** -MEDIAN | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MEDIAN -MEDIANIF | CATEGORY_STATISTICAL | **Not yet Implemented** -MID | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::MID -MIDB | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::MID -MIN | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MIN -MINA | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MINA -MINIFS | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MINIFS -MINUTE | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::MINUTE -MINVERSE | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::MINVERSE -MIRR | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::MIRR -MMULT | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::MMULT -MOD | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::MOD -MODE | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MODE -MODE.SNGL | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MODE -MONTH | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::MONTHOFYEAR -MROUND | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::MROUND -MULTINOMIAL | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::MULTINOMIAL +Excel Function | Category | PhpSpreadsheet Function +-------------------------|-------------------------------|------------------------- +MATCH | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::MATCH +MAX | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MAX +MAXA | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MAXA +MAXIFS | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MAXIFS +MDETERM | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::MDETERM +MDURATION | CATEGORY_FINANCIAL | **Not yet Implemented** +MEDIAN | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MEDIAN +MEDIANIF | CATEGORY_STATISTICAL | **Not yet Implemented** +MID | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::MID +MIDB | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::MID +MIN | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MIN +MINA | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MINA +MINIFS | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MINIFS +MINUTE | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::MINUTE +MINVERSE | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::MINVERSE +MIRR | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::MIRR +MMULT | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::MMULT +MOD | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::MOD +MODE | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MODE +MODE.MULT | CATEGORY_STATISTICAL | **Not yet Implemented** +MODE.SNGL | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::MODE +MONTH | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::MONTHOFYEAR +MROUND | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::MROUND +MULTINOMIAL | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::MULTINOMIAL +MUNIT | CATEGORY_MATH_AND_TRIG | **Not yet Implemented** ## N -Excel Function | Category | PhpSpreadsheet Function ---------------------|--------------------------------|------------------------------------------- -N | CATEGORY_INFORMATION | \PhpOffice\PhpSpreadsheet\Calculation\Functions::n -NA | CATEGORY_INFORMATION | \PhpOffice\PhpSpreadsheet\Calculation\Functions::NA -NEGBINOMDIST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::NEGBINOMDIST -NETWORKDAYS | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::NETWORKDAYS -NETWORKDAYS.INTL | CATEGORY_DATE_AND_TIME | **Not yet Implemented** -NOMINAL | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::NOMINAL -NORMDIST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::NORMDIST -NORMINV | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::NORMINV -NORMSDIST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::NORMSDIST -NORMSINV | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::NORMSINV -NOT | CATEGORY_LOGICAL | \PhpOffice\PhpSpreadsheet\Calculation\Logical::NOT -NOW | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::DATETIMENOW -NPER | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::NPER -NPV | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::NPV -NUMBERVALUE | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::NUMBERVALUE +Excel Function | Category | PhpSpreadsheet Function +-------------------------|-------------------------------|------------------------- +N | CATEGORY_INFORMATION | \PhpOffice\PhpSpreadsheet\Calculation\Functions::n +NA | CATEGORY_INFORMATION | \PhpOffice\PhpSpreadsheet\Calculation\Functions::NA +NEGBINOMDIST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::NEGBINOMDIST +NEGBINOM.DIST | CATEGORY_STATISTICAL | **Not yet Implemented** +NETWORKDAYS | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::NETWORKDAYS +NETWORKDAYS.INTL | CATEGORY_DATE_AND_TIME | **Not yet Implemented** +NOMINAL | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::NOMINAL +NORMDIST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::NORMDIST +NORM.DIST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::NORMDIST +NORMINV | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::NORMINV +NORM.INV | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::NORMINV +NORMSDIST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::NORMSDIST +NORM.S.DIST | CATEGORY_STATISTICAL | **Not yet Implemented** +NORMSINV | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::NORMSINV +NORM.S.INV | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::NORMSINV +NOT | CATEGORY_LOGICAL | \PhpOffice\PhpSpreadsheet\Calculation\Logical::NOT +NOW | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::DATETIMENOW +NPER | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::NPER +NPV | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::NPV +NUMBERVALUE | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::NUMBERVALUE ## O -Excel Function | Category | PhpSpreadsheet Function ---------------------|--------------------------------|------------------------------------------- -OCT2BIN | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::OCTTOBIN -OCT2DEC | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::OCTTODEC -OCT2HEX | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::OCTTOHEX -ODD | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::ODD -ODDFPRICE | CATEGORY_FINANCIAL | **Not yet Implemented** -ODDFYIELD | CATEGORY_FINANCIAL | **Not yet Implemented** -ODDLPRICE | CATEGORY_FINANCIAL | **Not yet Implemented** -ODDLYIELD | CATEGORY_FINANCIAL | **Not yet Implemented** -OFFSET | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::OFFSET -OR | CATEGORY_LOGICAL | \PhpOffice\PhpSpreadsheet\Calculation\Logical::logicalOr +Excel Function | Category | PhpSpreadsheet Function +-------------------------|-------------------------------|------------------------- +OCT2BIN | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::OCTTOBIN +OCT2DEC | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::OCTTODEC +OCT2HEX | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::OCTTOHEX +ODD | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::ODD +ODDFPRICE | CATEGORY_FINANCIAL | **Not yet Implemented** +ODDFYIELD | CATEGORY_FINANCIAL | **Not yet Implemented** +ODDLPRICE | CATEGORY_FINANCIAL | **Not yet Implemented** +ODDLYIELD | CATEGORY_FINANCIAL | **Not yet Implemented** +OFFSET | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::OFFSET +OR | CATEGORY_LOGICAL | \PhpOffice\PhpSpreadsheet\Calculation\Logical::logicalOr ## P -Excel Function | Category | PhpSpreadsheet Function ---------------------|--------------------------------|------------------------------------------- -PDURATION | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::PDURATION -PEARSON | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::CORREL -PERCENTILE | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::PERCENTILE -PERCENTRANK | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::PERCENTRANK -PERMUT | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::PERMUT -PHONETIC | CATEGORY_TEXT_AND_DATA | **Not yet Implemented** -PI | CATEGORY_MATH_AND_TRIG | pi -PMT | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::PMT -POISSON | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::POISSON -POWER | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::POWER -PPMT | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::PPMT -PRICE | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::PRICE -PRICEDISC | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::PRICEDISC -PRICEMAT | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::PRICEMAT -PROB | CATEGORY_STATISTICAL | **Not yet Implemented** -PRODUCT | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::PRODUCT -PROPER | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::PROPERCASE -PV | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::PV +Excel Function | Category | PhpSpreadsheet Function +-------------------------|-------------------------------|------------------------- +PDURATION | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::PDURATION +PEARSON | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::CORREL +PERCENTILE | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::PERCENTILE +PERCENTILE.EXC | CATEGORY_STATISTICAL | **Not yet Implemented** +PERCENTILE.INC | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::PERCENTILE +PERCENTRANK | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::PERCENTRANK +PERCENTRANK.EXC | CATEGORY_STATISTICAL | **Not yet Implemented** +PERCENTRANK.INC | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::PERCENTRANK +PERMUT | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::PERMUT +PERMUTATIONA | CATEGORY_STATISTICAL | **Not yet Implemented** +PHI | CATEGORY_STATISTICAL | **Not yet Implemented** +PHONETIC | CATEGORY_TEXT_AND_DATA | **Not yet Implemented** +PI | CATEGORY_MATH_AND_TRIG | pi +PMT | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::PMT +POISSON | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::POISSON +POISSON.DIST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::POISSON +POWER | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::POWER +PPMT | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::PPMT +PRICE | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::PRICE +PRICEDISC | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::PRICEDISC +PRICEMAT | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::PRICEMAT +PROB | CATEGORY_STATISTICAL | **Not yet Implemented** +PRODUCT | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::PRODUCT +PROPER | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::PROPERCASE +PV | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::PV ## Q -Excel Function | Category | PhpSpreadsheet Function ---------------------|--------------------------------|------------------------------------------- -QUARTILE | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::QUARTILE -QUOTIENT | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::QUOTIENT +Excel Function | Category | PhpSpreadsheet Function +-------------------------|-------------------------------|------------------------- +QUARTILE | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::QUARTILE +QUARTILE.EXC | CATEGORY_STATISTICAL | **Not yet Implemented** +QUARTILE.INC | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::QUARTILE +QUOTIENT | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::QUOTIENT ## R -Excel Function | Category | PhpSpreadsheet Function ---------------------|--------------------------------|------------------------------------------- -RADIANS | CATEGORY_MATH_AND_TRIG | deg2rad -RAND | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::RAND -RANDBETWEEN | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::RAND -RANK | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::RANK -RATE | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::RATE -RECEIVED | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::RECEIVED -REPLACE | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::REPLACE -REPLACEB | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::REPLACE -REPT | CATEGORY_TEXT_AND_DATA | str_repeat -RIGHT | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::RIGHT -RIGHTB | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::RIGHT -ROMAN | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::ROMAN -ROUND | CATEGORY_MATH_AND_TRIG | round -ROUNDDOWN | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::ROUNDDOWN -ROUNDUP | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::ROUNDUP -ROW | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::ROW -ROWS | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::ROWS -RRI | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::RRI -RSQ | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::RSQ -RTD | CATEGORY_LOOKUP_AND_REFERENCE | **Not yet Implemented** +Excel Function | Category | PhpSpreadsheet Function +-------------------------|-------------------------------|------------------------- +RADIANS | CATEGORY_MATH_AND_TRIG | deg2rad +RAND | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::RAND +RANDARRAY | CATEGORY_MATH_AND_TRIG | **Not yet Implemented** +RANDBETWEEN | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::RAND +RANK | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::RANK +RANK.AVG | CATEGORY_STATISTICAL | **Not yet Implemented** +RANK.EQ | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::RANK +RATE | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::RATE +RECEIVED | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::RECEIVED +REPLACE | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::REPLACE +REPLACEB | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::REPLACE +REPT | CATEGORY_TEXT_AND_DATA | str_repeat +RIGHT | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::RIGHT +RIGHTB | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::RIGHT +ROMAN | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::ROMAN +ROUND | CATEGORY_MATH_AND_TRIG | round +ROUNDDOWN | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::ROUNDDOWN +ROUNDUP | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::ROUNDUP +ROW | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::ROW +ROWS | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::ROWS +RRI | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::RRI +RSQ | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::RSQ +RTD | CATEGORY_LOOKUP_AND_REFERENCE | **Not yet Implemented** ## S -Excel Function | Category | PhpSpreadsheet Function ---------------------|--------------------------------|------------------------------------------- -SEARCH | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::SEARCHINSENSITIVE -SEARCHB | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::SEARCHINSENSITIVE -SEC | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SEC -SECH | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SECH -SECOND | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::SECOND -SERIESSUM | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SERIESSUM -SHEET | CATEGORY_INFORMATION | **Not yet Implemented** -SHEETS | CATEGORY_INFORMATION | **Not yet Implemented** -SIGN | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SIGN -SIN | CATEGORY_MATH_AND_TRIG | sin -SINH | CATEGORY_MATH_AND_TRIG | sinh -SKEW | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::SKEW -SLN | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::SLN -SLOPE | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::SLOPE -SMALL | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::SMALL -SQRT | CATEGORY_MATH_AND_TRIG | sqrt -SQRTPI | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SQRTPI -STANDARDIZE | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STANDARDIZE -STDEV | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STDEV -STDEV.P | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STDEVP -STDEV.S | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STDEV -STDEVA | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STDEVA -STDEVP | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STDEVP -STDEVPA | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STDEVPA -STEYX | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STEYX -SUBSTITUTE | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::SUBSTITUTE -SUBTOTAL | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SUBTOTAL -SUM | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SUM -SUMIF | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SUMIF -SUMIFS | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SUMIFS -SUMPRODUCT | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SUMPRODUCT -SUMSQ | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SUMSQ -SUMX2MY2 | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SUMX2MY2 -SUMX2PY2 | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SUMX2PY2 -SUMXMY2 | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SUMXMY2 -SWITCH | CATEGORY_LOGICAL | \PhpOffice\PhpSpreadsheet\Calculation\Logical::statementSwitch -SYD | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::SYD +Excel Function | Category | PhpSpreadsheet Function +-------------------------|-------------------------------|------------------------- +SEARCH | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::SEARCHINSENSITIVE +SEARCHB | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::SEARCHINSENSITIVE +SEC | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SEC +SECH | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SECH +SECOND | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::SECOND +SEQUENCE | CATEGORY_MATH_AND_TRIG | **Not yet Implemented** +SERIESSUM | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SERIESSUM +SHEET | CATEGORY_INFORMATION | **Not yet Implemented** +SHEETS | CATEGORY_INFORMATION | **Not yet Implemented** +SIGN | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SIGN +SIN | CATEGORY_MATH_AND_TRIG | sin +SINH | CATEGORY_MATH_AND_TRIG | sinh +SKEW | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::SKEW +SKEW.P | CATEGORY_STATISTICAL | **Not yet Implemented** +SLN | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::SLN +SLOPE | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::SLOPE +SMALL | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::SMALL +SORT | CATEGORY_LOOKUP_AND_REFERENCE | **Not yet Implemented** +SORTBY | CATEGORY_LOOKUP_AND_REFERENCE | **Not yet Implemented** +SQRT | CATEGORY_MATH_AND_TRIG | sqrt +SQRTPI | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SQRTPI +STANDARDIZE | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STANDARDIZE +STDEV | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STDEV +STDEVA | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STDEVA +STDEVP | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STDEVP +STDEV.P | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STDEVP +STDEVPA | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STDEVPA +STDEV.S | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STDEV +STEYX | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::STEYX +SUBSTITUTE | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::SUBSTITUTE +SUBTOTAL | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SUBTOTAL +SUM | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SUM +SUMIF | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SUMIF +SUMIFS | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SUMIFS +SUMPRODUCT | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SUMPRODUCT +SUMSQ | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SUMSQ +SUMX2MY2 | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SUMX2MY2 +SUMX2PY2 | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SUMX2PY2 +SUMXMY2 | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::SUMXMY2 +SWITCH | CATEGORY_LOGICAL | \PhpOffice\PhpSpreadsheet\Calculation\Logical::statementSwitch +SYD | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::SYD ## T -Excel Function | Category | PhpSpreadsheet Function ---------------------|--------------------------------|------------------------------------------- -T | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::RETURNSTRING -TAN | CATEGORY_MATH_AND_TRIG | tan -TANH | CATEGORY_MATH_AND_TRIG | tanh -TBILLEQ | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::TBILLEQ -TBILLPRICE | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::TBILLPRICE -TBILLYIELD | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::TBILLYIELD -TDIST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::TDIST -TEXT | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::TEXTFORMAT -TEXTJOIN | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::TEXTJOIN -TIME | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::TIME -TIMEVALUE | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::TIMEVALUE -TINV | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::TINV -TODAY | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::DATENOW -TRANSPOSE | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::TRANSPOSE -TREND | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::TREND -TRIM | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::TRIMSPACES -TRIMMEAN | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::TRIMMEAN -TRUE | CATEGORY_LOGICAL | \PhpOffice\PhpSpreadsheet\Calculation\Logical::TRUE -TRUNC | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::TRUNC -TTEST | CATEGORY_STATISTICAL | **Not yet Implemented** -TYPE | CATEGORY_INFORMATION | \PhpOffice\PhpSpreadsheet\Calculation\Functions::TYPE +Excel Function | Category | PhpSpreadsheet Function +-------------------------|-------------------------------|------------------------- +T | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::RETURNSTRING +TAN | CATEGORY_MATH_AND_TRIG | tan +TANH | CATEGORY_MATH_AND_TRIG | tanh +TBILLEQ | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::TBILLEQ +TBILLPRICE | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::TBILLPRICE +TBILLYIELD | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::TBILLYIELD +TDIST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::TDIST +T.DIST | CATEGORY_STATISTICAL | **Not yet Implemented** +T.DIST.2T | CATEGORY_STATISTICAL | **Not yet Implemented** +T.DIST.RT | CATEGORY_STATISTICAL | **Not yet Implemented** +TEXT | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::TEXTFORMAT +TEXTJOIN | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::TEXTJOIN +TIME | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::TIME +TIMEVALUE | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::TIMEVALUE +TINV | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::TINV +T.INV | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::TINV +T.INV.2T | CATEGORY_STATISTICAL | **Not yet Implemented** +TODAY | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::DATENOW +TRANSPOSE | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::TRANSPOSE +TREND | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::TREND +TRIM | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::TRIMSPACES +TRIMMEAN | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::TRIMMEAN +TRUE | CATEGORY_LOGICAL | \PhpOffice\PhpSpreadsheet\Calculation\Logical::TRUE +TRUNC | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::TRUNC +TTEST | CATEGORY_STATISTICAL | **Not yet Implemented** +T.TEST | CATEGORY_STATISTICAL | **Not yet Implemented** +TYPE | CATEGORY_INFORMATION | \PhpOffice\PhpSpreadsheet\Calculation\Functions::TYPE ## U -Excel Function | Category | PhpSpreadsheet Function ---------------------|--------------------------------|------------------------------------------- -UNICHAR | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::CHARACTER -UNICODE | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::ASCIICODE -UPPER | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::UPPERCASE -USDOLLAR | CATEGORY_FINANCIAL | **Not yet Implemented** +Excel Function | Category | PhpSpreadsheet Function +-------------------------|-------------------------------|------------------------- +UNICHAR | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::CHARACTER +UNICODE | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::ASCIICODE +UNIQUE | CATEGORY_LOOKUP_AND_REFERENCE | **Not yet Implemented** +UPPER | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::UPPERCASE +USDOLLAR | CATEGORY_FINANCIAL | **Not yet Implemented** ## V -Excel Function | Category | PhpSpreadsheet Function ---------------------|--------------------------------|------------------------------------------- -VALUE | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::VALUE -VAR | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::VARFunc -VAR.P | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::VARP -VAR.S | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::VARFunc -VARA | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::VARA -VARP | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::VARP -VARPA | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::VARPA -VDB | CATEGORY_FINANCIAL | **Not yet Implemented** -VLOOKUP | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::VLOOKUP +Excel Function | Category | PhpSpreadsheet Function +-------------------------|-------------------------------|------------------------- +VALUE | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData::VALUE +VAR | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::VARFunc +VARA | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::VARA +VARP | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::VARP +VAR.P | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::VARP +VARPA | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::VARPA +VAR.S | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::VARFunc +VDB | CATEGORY_FINANCIAL | **Not yet Implemented** +VLOOKUP | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::VLOOKUP ## W -Excel Function | Category | PhpSpreadsheet Function ---------------------|--------------------------------|------------------------------------------- -WEEKDAY | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::WEEKDAY -WEEKNUM | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::WEEKNUM -WEIBULL | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::WEIBULL -WORKDAY | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::WORKDAY -WORKDAY.INTL | CATEGORY_DATE_AND_TIME | **Not yet Implemented** +Excel Function | Category | PhpSpreadsheet Function +-------------------------|-------------------------------|------------------------- +WEBSERVICE | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\Web::WEBSERVICE +WEEKDAY | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::WEEKDAY +WEEKNUM | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::WEEKNUM +WEIBULL | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::WEIBULL +WEIBULL.DIST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::WEIBULL +WORKDAY | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::WORKDAY +WORKDAY.INTL | CATEGORY_DATE_AND_TIME | **Not yet Implemented** ## X -Excel Function | Category | PhpSpreadsheet Function ---------------------|--------------------------------|------------------------------------------- -XIRR | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::XIRR -XNPV | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::XNPV -XOR | CATEGORY_LOGICAL | \PhpOffice\PhpSpreadsheet\Calculation\Logical::logicalXor +Excel Function | Category | PhpSpreadsheet Function +-------------------------|-------------------------------|------------------------- +XIRR | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::XIRR +XLOOKUP | CATEGORY_LOOKUP_AND_REFERENCE | **Not yet Implemented** +XMATCH | CATEGORY_LOOKUP_AND_REFERENCE | **Not yet Implemented** +XNPV | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::XNPV +XOR | CATEGORY_LOGICAL | \PhpOffice\PhpSpreadsheet\Calculation\Logical::logicalXor ## Y -Excel Function | Category | PhpSpreadsheet Function ---------------------|--------------------------------|------------------------------------------- -YEAR | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::YEAR -YEARFRAC | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::YEARFRAC -YIELD | CATEGORY_FINANCIAL | **Not yet Implemented** -YIELDDISC | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::YIELDDISC -YIELDMAT | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::YIELDMAT +Excel Function | Category | PhpSpreadsheet Function +-------------------------|-------------------------------|------------------------- +YEAR | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::YEAR +YEARFRAC | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTime::YEARFRAC +YIELD | CATEGORY_FINANCIAL | **Not yet Implemented** +YIELDDISC | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::YIELDDISC +YIELDMAT | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial::YIELDMAT ## Z -Excel Function | Category | PhpSpreadsheet Function ---------------------|--------------------------------|------------------------------------------- -ZTEST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::ZTEST +Excel Function | Category | PhpSpreadsheet Function +-------------------------|-------------------------------|------------------------- +ZTEST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::ZTEST +Z.TEST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical::ZTEST diff --git a/docs/topics/accessing-cells.md b/docs/topics/accessing-cells.md index 4770d72196..a777afc11b 100644 --- a/docs/topics/accessing-cells.md +++ b/docs/topics/accessing-cells.md @@ -8,7 +8,7 @@ topic lists some of the options to access a cell. Setting a cell value by coordinate can be done using the worksheet's `setCellValue()` method. -``` php +```php // Set cell A1 with a string value $spreadsheet->getActiveSheet()->setCellValue('A1', 'PhpSpreadsheet'); @@ -28,7 +28,7 @@ $spreadsheet->getActiveSheet()->setCellValue( Alternatively, you can retrieve the cell object, and then call the cell’s `setValue()` method: -``` php +```php $spreadsheet->getActiveSheet() ->getCell('B8') ->setValue('Some value'); @@ -56,7 +56,7 @@ the cell object will still retain its data values. What does this mean? Consider the following code: -``` +```php $spreadSheet = new Spreadsheet(); $workSheet = $spreadSheet->getActiveSheet(); @@ -74,7 +74,7 @@ $cellA1 = $workSheet->getCell('A1'); echo 'Value: ', $cellA1->getValue(), '; Address: ', $cellA1->getCoordinate(), PHP_EOL; echo 'Value: ', $cellC1->getValue(), '; Address: ', $cellC1->getCoordinate(), PHP_EOL; -``` +``` The call to `getCell('C1')` returns the cell at `C1` containing its value (`3`), together with its link to the collection (used to identify its @@ -153,7 +153,7 @@ was a formula. To do this, you need to "escape" the value by setting it as "quoted text". -``` +```php // Set cell A4 with a formula $spreadsheet->getActiveSheet()->setCellValue( 'A4', @@ -175,7 +175,7 @@ point value), and a number format mask is used to show how that value should be formatted; so if we want to store a date in a cell, we need to calculate the correct Excel timestamp, and set a number format mask. -``` php +```php // Get the current date/time and convert to an Excel date/time $dateTimeNow = time(); $excelDateValue = \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel( $dateTimeNow ); @@ -210,7 +210,7 @@ behaviour. Firstly, you can set the datatype explicitly as a string so that it is not converted to a number. -``` php +```php // Set cell A8 with a numeric value, but tell PhpSpreadsheet it should be treated as a string $spreadsheet->getActiveSheet()->setCellValueExplicit( 'A8', @@ -222,7 +222,7 @@ $spreadsheet->getActiveSheet()->setCellValueExplicit( Alternatively, you can use a number format mask to display the value with leading zeroes. -``` php +```php // Set cell A9 with a numeric value $spreadsheet->getActiveSheet()->setCellValue('A9', 1513789642); // Set a number format mask to display the value as 11 digits with leading zeroes @@ -236,7 +236,7 @@ $spreadsheet->getActiveSheet()->getStyle('A9') With number format masking, you can even break up the digits into groups to make the value more easily readable. -``` php +```php // Set cell A10 with a numeric value $spreadsheet->getActiveSheet()->setCellValue('A10', 1513789642); // Set a number format mask to display the value as 11 digits with leading zeroes @@ -259,7 +259,7 @@ writers (Xlsx and Xls). It is also possible to set a range of cell values in a single call by passing an array of values to the `fromArray()` method. -``` php +```php $arrayData = [ [NULL, 2010, 2011, 2012], ['Q1', 12, 15, 21], @@ -282,7 +282,7 @@ If you pass a 2-d array, then this will be treated as a series of rows and columns. A 1-d array will be treated as a single row, which is particularly useful if you're fetching an array of data from a database. -``` php +```php $rowArray = ['Value1', 'Value2', 'Value3', 'Value4']; $spreadsheet->getActiveSheet() ->fromArray( @@ -299,7 +299,7 @@ If you have a simple 1-d array, and want to write it as a column, then the following will convert it into an appropriately structured 2-d array that can be fed to the `fromArray()` method: -``` php +```php $rowArray = ['Value1', 'Value2', 'Value3', 'Value4']; $columnArray = array_chunk($rowArray, 1); $spreadsheet->getActiveSheet() @@ -319,7 +319,7 @@ To retrieve the value of a cell, the cell should first be retrieved from the worksheet using the `getCell()` method. A cell's value can be read using the `getValue()` method. -``` php +```php // Get the value from cell A1 $cellValue = $spreadsheet->getActiveSheet()->getCell('A1')->getValue(); ``` @@ -331,7 +331,7 @@ value rather than the formula itself, then use the cell's `getCalculatedValue()` method. This is further explained in [the calculation engine](./calculation-engine.md). -``` php +```php // Get the value from cell A4 $cellValue = $spreadsheet->getActiveSheet()->getCell('A4')->getCalculatedValue(); ``` @@ -340,7 +340,7 @@ Alternatively, if you want to see the value with any cell formatting applied (e.g. for a human-readable date or time value), then you can use the cell's `getFormattedValue()` method. -``` php +```php // Get the value from cell A6 $cellValue = $spreadsheet->getActiveSheet()->getCell('A6')->getFormattedValue(); ``` @@ -350,7 +350,7 @@ $cellValue = $spreadsheet->getActiveSheet()->getCell('A6')->getFormattedValue(); Setting a cell value by coordinate can be done using the worksheet's `setCellValueByColumnAndRow()` method. -``` php +```php // Set cell A5 with a string value $spreadsheet->getActiveSheet()->setCellValueByColumnAndRow(1, 5, 'PhpSpreadsheet'); ``` @@ -363,7 +363,7 @@ To retrieve the value of a cell, the cell should first be retrieved from the worksheet using the `getCellByColumnAndRow()` method. A cell’s value can be read again using the following line of code: -``` php +```php // Get the value from cell B5 $cellValue = $spreadsheet->getActiveSheet()->getCellByColumnAndRow(2, 5)->getValue(); ``` @@ -371,7 +371,7 @@ $cellValue = $spreadsheet->getActiveSheet()->getCellByColumnAndRow(2, 5)->getVal If you need the calculated value of a cell, use the following code. This is further explained in [the calculation engine](./calculation-engine.md). -``` php +```php // Get the value from cell A4 $cellValue = $spreadsheet->getActiveSheet()->getCellByColumnAndRow(1, 4)->getCalculatedValue(); ``` @@ -382,7 +382,7 @@ It is also possible to retrieve a range of cell values to an array in a single call using the `toArray()`, `rangeToArray()` or `namedRangeToArray()` methods. -``` php +```php $dataArray = $spreadsheet->getActiveSheet() ->rangeToArray( 'C3:E5', // The worksheet range that we want to retrieve @@ -409,7 +409,7 @@ cells within a row. Below is an example where we read all the values in a worksheet and display them in a table. -``` php +```php $reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx'); $reader->setReadDataOnly(TRUE); $spreadsheet = $reader->load("test.xlsx"); @@ -422,8 +422,10 @@ foreach ($worksheet->getRowIterator() as $row) { $cellIterator = $row->getCellIterator(); $cellIterator->setIterateOnlyExistingCells(FALSE); // This loops through all cells, // even if a cell value is not set. - // By default, only cells that have a value - // set will be iterated. + // For 'TRUE', we loop through cells + // only when their value is set. + // If this method is not called, + // the default value is 'false'. foreach ($cellIterator as $cell) { echo '' . $cell->getValue() . @@ -456,7 +458,7 @@ loops. Below is an example where we read all the values in a worksheet and display them in a table. -``` php +```php $reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx'); $reader->setReadDataOnly(TRUE); $spreadsheet = $reader->load("test.xlsx"); @@ -482,7 +484,7 @@ echo '' . PHP_EOL; Alternatively, you can take advantage of PHP's "Perl-style" character incrementors to loop through the cells by coordinate: -``` php +```php $reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx'); $reader->setReadDataOnly(TRUE); $spreadsheet = $reader->load("test.xlsx"); @@ -528,7 +530,7 @@ dates entered as strings to the correct format, also setting the cell's style information. The following example demonstrates how to set the value binder in PhpSpreadsheet: -``` php +```php /** PhpSpreadsheet */ require_once 'src/Boostrap.php'; diff --git a/docs/topics/architecture.md b/docs/topics/architecture.md index 0295d672d8..1c544ef746 100644 --- a/docs/topics/architecture.md +++ b/docs/topics/architecture.md @@ -43,7 +43,7 @@ PhpSpreadsheet supports fluent interfaces in most locations. This means that you can easily "chain" calls to specific methods without requiring a new PHP statement. For example, take the following code: -``` php +```php $spreadsheet->getProperties()->setCreator("Maarten Balliauw"); $spreadsheet->getProperties()->setLastModifiedBy("Maarten Balliauw"); $spreadsheet->getProperties()->setTitle("Office 2007 XLSX Test Document"); @@ -55,7 +55,7 @@ $spreadsheet->getProperties()->setCategory("Test result file"); This can be rewritten as: -``` php +```php $spreadsheet->getProperties() ->setCreator("Maarten Balliauw") ->setLastModifiedBy("Maarten Balliauw") diff --git a/docs/topics/autofilters.md b/docs/topics/autofilters.md index 66321ee9dc..d5a07f8bba 100644 --- a/docs/topics/autofilters.md +++ b/docs/topics/autofilters.md @@ -42,7 +42,7 @@ column, such as "Equals a red cell color" or "Larger than 150". To set an autoFilter on a range of cells. -``` php +```php $spreadsheet->getActiveSheet()->setAutoFilter('A1:E20'); ``` @@ -56,7 +56,7 @@ developer to avoid such errors. If you want to set the whole worksheet as an autofilter region -``` php +```php $spreadsheet->getActiveSheet()->setAutoFilter( $spreadsheet->getActiveSheet() ->calculateWorksheetDimension() @@ -74,7 +74,7 @@ will extend this to other formats. To apply a filter expression to an autoFilter range, you first need to identify which column you're going to be applying this filter to. -``` php +```php $autoFilter = $spreadsheet->getActiveSheet()->getAutoFilter(); $columnFilter = $autoFilter->getColumn('C'); ``` @@ -114,7 +114,7 @@ To create a filter expression, we need to start by identifying the filter type. In this case, we're just going to specify that this filter is a standard filter. -``` php +```php $columnFilter->setFilterType( \PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column::AUTOFILTER_FILTERTYPE_FILTER ); @@ -127,7 +127,7 @@ When creating a simple filter in PhpSpreadsheet, you only need to specify the values for "checked" columns: you do this by creating a filter rule for each value. -``` php +```php $columnFilter->createRule() ->setRule( \PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_EQUAL, @@ -152,7 +152,7 @@ standard filters are always treated as being joined by an OR condition. If you want to create a filter to select blank cells, you would use: -``` php +```php $columnFilter->createRule() ->setRule( \PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_EQUAL, @@ -170,7 +170,7 @@ within a year, or individual days within each month. DateGroup filters are still applied as a Standard Filter type. -``` php +```php $columnFilter->setFilterType( \PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column::AUTOFILTER_FILTERTYPE_FILTER ); @@ -181,7 +181,7 @@ for "checked" columns as an associative array of year. month, day, hour minute and second. To select a year and month, you need to create a DateGroup rule identifying the selected year and month: -``` php +```php $columnFilter->createRule() ->setRule( \PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_EQUAL, @@ -229,7 +229,7 @@ either an AND or an OR. We start by specifying a Filter type, this time a CUSTOMFILTER. -``` php +```php $columnFilter->setFilterType( \PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER ); @@ -240,7 +240,7 @@ And then define our rules. The following shows a simple wildcard filter to show all column entries beginning with the letter `U`. -``` php +```php $columnFilter->createRule() ->setRule( \PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_EQUAL, @@ -264,7 +264,7 @@ is the \~ itself. To create a "between" condition, we need to define two rules: -``` php +```php $columnFilter->createRule() ->setRule( \PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL, @@ -289,7 +289,7 @@ This defined two rules, filtering numbers that are `>= -20` OR `<= 20`, so we also need to modify the join condition to reflect AND rather than OR. -``` php +```php $columnFilter->setAndOr( \PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column::AUTOFILTER_COLUMN_ANDOR_AND ); @@ -320,7 +320,7 @@ column at a time. Again, we start by specifying a Filter type, this time a DYNAMICFILTER. -``` php +```php $columnFilter->setFilterType( \PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER ); @@ -330,7 +330,7 @@ When defining the rule for a dynamic filter, we don't define a value (we can simply set that to NULL) but we do specify the dynamic filter category. -``` php +```php $columnFilter->createRule() ->setRule( \PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_EQUAL, @@ -420,7 +420,7 @@ column at a time. We start by specifying a Filter type, this time a DYNAMICFILTER. -``` php +```php $columnFilter->setFilterType( \PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column::AUTOFILTER_FILTERTYPE_TOPTENFILTER ); @@ -428,7 +428,7 @@ $columnFilter->setFilterType( Then we create the rule: -``` php +```php $columnFilter->createRule() ->setRule( \PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT, @@ -444,7 +444,7 @@ This will filter the Top 5 percent of values in the column. To specify the lowest (bottom 2 values), we would specify a rule of: -``` php +```php $columnFilter->createRule() ->setRule( \PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BY_VALUE, @@ -490,7 +490,7 @@ If you wish to execute your filter from within a script, you need to do this manually. You can do this using the autofilters `showHideRows()` method. -``` php +```php $autoFilter = $spreadsheet->getActiveSheet()->getAutoFilter(); $autoFilter->showHideRows(); ``` @@ -505,7 +505,7 @@ ever row, whether it matches the filter criteria or not. To selectively access only the filtered rows, you need to test each row’s visibility settings. -``` php +```php foreach ($spreadsheet->getActiveSheet()->getRowIterator() as $row) { if ($spreadsheet->getActiveSheet() ->getRowDimension($row->getRowIndex())->getVisible()) { diff --git a/docs/topics/calculation-engine.md b/docs/topics/calculation-engine.md index 779d73e1af..4fd300e82e 100644 --- a/docs/topics/calculation-engine.md +++ b/docs/topics/calculation-engine.md @@ -13,7 +13,7 @@ evaluates to the sum of values in A1, A2, ..., A10. To calculate a formula, you can call the cell containing the formula’s method `getCalculatedValue()`, for example: -``` php +```php $spreadsheet->getActiveSheet()->getCell('E11')->getCalculatedValue(); ``` @@ -32,7 +32,7 @@ You see that the formula contained in cell E11 is "SUM(E4:E9)". Now, when I write the following line of code, two new product lines are added: -``` php +```php $spreadsheet->getActiveSheet()->insertNewRowBefore(7, 2); ``` @@ -55,7 +55,7 @@ However, there may be times when you don't want this, perhaps you've changed the underlying data and need to re-evaluate the same formula with that new data. -``` +```php Calculation::getInstance($spreadsheet)->disableCalculationCache(); ``` @@ -63,7 +63,7 @@ Will disable calculation caching, and flush the current calculation cache. If you want only to flush the cache, then you can call -``` +```php Calculation::getInstance($spreadsheet)->clearCalculationCache(); ``` @@ -118,7 +118,7 @@ date values by calling the `\PhpOffice\PhpSpreadsheet\Calculation\Functions::setReturnDateType()` method: -``` php +```php \PhpOffice\PhpSpreadsheet\Calculation\Functions::setReturnDateType($returnDateType); ``` @@ -134,7 +134,7 @@ if an invalid value is passed in for the return date type). The `\PhpOffice\PhpSpreadsheet\Calculation\Functions::getReturnDateType()` method can be used to determine the current value of this setting: -``` php +```php $returnDateType = \PhpOffice\PhpSpreadsheet\Calculation\Functions::getReturnDateType(); ``` @@ -172,7 +172,7 @@ It is possible for scripts to change the calendar used for calculating Excel date values by calling the `\PhpOffice\PhpSpreadsheet\Shared\Date::setExcelCalendar()` method: -``` php +```php \PhpOffice\PhpSpreadsheet\Shared\Date::setExcelCalendar($baseDate); ``` @@ -187,7 +187,7 @@ if an invalid value is passed in). The `\PhpOffice\PhpSpreadsheet\Shared\Date::getExcelCalendar()` method can be used to determine the current value of this setting: -``` php +```php $baseDate = \PhpOffice\PhpSpreadsheet\Shared\Date::getExcelCalendar(); ``` @@ -353,7 +353,7 @@ This is the statistical mean. ##### Examples -``` php +```php $database = [ [ 'Tree', 'Height', 'Age', 'Yield', 'Profit' ], [ 'Apple', 18, 20, 14, 105.00 ], @@ -421,7 +421,7 @@ in which you specify a condition for the column. ##### Examples -``` php +```php $database = [ [ 'Tree', 'Height', 'Age', 'Yield', 'Profit' ], [ 'Apple', 18, 20, 14, 105.00 ], @@ -456,7 +456,7 @@ has not yet been implemented in PhpSpreadsheet. #### DCOUNTA -The DCOUNT function returns the count of cells that aren’t blank in a +The DCOUNTA function returns the count of cells that aren’t blank in a column of a list or database and that match conditions that you specify. ##### Syntax @@ -492,7 +492,7 @@ in which you specify a condition for the column. ##### Examples -``` php +```php $database = [ [ 'Tree', 'Height', 'Age', 'Yield', 'Profit' ], [ 'Apple', 18, 20, 14, 105.00 ], @@ -563,7 +563,7 @@ in which you specify a condition for the column. #### Examples -``` php +```php $database = [ [ 'Tree', 'Height', 'Age', 'Yield', 'Profit' ], [ 'Apple', 18, 20, 14, 105.00 ], @@ -631,7 +631,7 @@ in which you specify a condition for the column. ##### Examples -``` php +```php $database = [ [ 'Tree', 'Height', 'Age', 'Yield', 'Profit' ], [ 'Apple', 18, 20, 14, 105.00 ], @@ -699,7 +699,7 @@ in which you specify a condition for the column. ##### Examples -``` php +```php $database = [ [ 'Tree', 'Height', 'Age', 'Yield', 'Profit' ], [ 'Apple', 18, 20, 14, 105.00 ], @@ -767,7 +767,7 @@ in which you specify a condition for the column. ##### Examples -``` php +```php $database = [ [ 'Tree', 'Height', 'Age', 'Yield', 'Profit' ], [ 'Apple', 18, 20, 14, 105.00 ], @@ -836,7 +836,7 @@ in which you specify a condition for the column. ##### Examples -``` php +```php $database = [ [ 'Tree', 'Height', 'Age', 'Yield', 'Profit' ], [ 'Apple', 18, 20, 14, 105.00 ], @@ -905,7 +905,7 @@ in which you specify a condition for the column. ##### Examples -``` php +```php $database = [ [ 'Tree', 'Height', 'Age', 'Yield', 'Profit' ], [ 'Apple', 18, 20, 14, 105.00 ], @@ -973,7 +973,7 @@ in which you specify a condition for the column. ##### Examples -``` php +```php $database = [ [ 'Tree', 'Height', 'Age', 'Yield', 'Profit' ], [ 'Apple', 18, 20, 14, 105.00 ], @@ -1074,7 +1074,7 @@ or an Excel timestamp value (real), depending on the value of ##### Examples -``` php +```php $worksheet->setCellValue('A1', 'Year') ->setCellValue('A2', 'Month') ->setCellValue('A3', 'Day'); @@ -1089,7 +1089,7 @@ $retVal = $worksheet->getCell('D1')->getCalculatedValue(); // $retVal = 1230681600 ``` -``` php +```php // We're going to be calling the same cell calculation multiple times, // and expecting different return values, so disable calculation cacheing \PhpOffice\PhpSpreadsheet\Calculation\Calculation::getInstance()->setCalculationCacheEnabled(FALSE); @@ -1170,7 +1170,7 @@ the third parameter. ##### Examples -``` php +```php $worksheet->setCellValue('A1', 'Year') ->setCellValue('A2', 'Month') ->setCellValue('A3', 'Day'); @@ -1208,7 +1208,7 @@ $retVal = $worksheet->getCell('D6')->getCalculatedValue(); // $retVal = 30 ``` -``` php +```php $date1 = 1193317015; // PHP timestamp for 25-Oct-2007 $date2 = 1449579415; // PHP timestamp for 8-Dec-2015 @@ -1279,7 +1279,7 @@ or an Excel timestamp value (real), depending on the value of ##### Examples -``` php +```php $worksheet->setCellValue('A1', 'Date String'); ->setCellValue('A2', '31-Dec-2008') ->setCellValue('A3', '31/12/2008') @@ -1301,7 +1301,7 @@ $retVal = $worksheet->getCell('B4')->getCalculatedValue(); // $retVal = 39813.0 for all cases ``` -``` php +```php // We're going to be calling the same cell calculation multiple times, // and expecting different return values, so disable calculation cacheing \PhpOffice\PhpSpreadsheet\Calculation\Calculation::getInstance()->setCalculationCacheEnabled(FALSE); @@ -1371,7 +1371,7 @@ This is an integer ranging from 1 to 31. ##### Examples -``` php +```php $worksheet->setCellValue('A1', 'Date String') ->setCellValue('A2', '31-Dec-2008') ->setCellValue('A3', '14-Feb-2008'); @@ -1386,7 +1386,7 @@ $retVal = $worksheet->getCell('B3')->getCalculatedValue(); // $retVal = 14 ``` -``` php +```php $retVal = call_user_func_array( ['\PhpOffice\PhpSpreadsheet\Calculation\Functions', 'DAYOFMONTH'], ['25-Dec-2008'] @@ -1444,7 +1444,7 @@ day year. ##### Examples -``` php +```php $worksheet->setCellValue('B1', 'Start Date') ->setCellValue('C1', 'End Date') ->setCellValue('A2', 'Year') @@ -1469,7 +1469,7 @@ $retVal = $worksheet->getCell('E4')->getCalculatedValue(); // $retVal = 1557 ``` -``` php +```php $date1 = 37655.0; // Excel timestamp for 25-Oct-2007 $date2 = 39233.0; // Excel timestamp for 8-Dec-2015 @@ -1529,7 +1529,7 @@ or an Excel timestamp value (real), depending on the value of ##### Examples -``` php +```php $worksheet->setCellValue('A1', 'Date String') ->setCellValue('A2', '1-Jan-2008') ->setCellValue('A3', '29-Feb-2008'); @@ -1548,7 +1548,7 @@ $retVal = $worksheet->getCell('B3')->getCalculatedValue(); // $retVal = 39141.0 (28-Feb-2007) ``` -``` php +```php \PhpOffice\PhpSpreadsheet\Calculation\Functions::setReturnDateType( \PhpOffice\PhpSpreadsheet\Calculation\Functions::RETURNDATE_EXCEL ); @@ -1602,7 +1602,7 @@ or an Excel timestamp value (real), depending on the value of ##### Examples -``` php +```php $worksheet->setCellValue('A1', 'Date String') ->setCellValue('A2', '1-Jan-2000') ->setCellValue('A3', '14-Feb-2009'); @@ -1619,7 +1619,7 @@ $retVal = $worksheet->getCell('B3')->getCalculatedValue(); // $retVal = 39507.0 (29-Feb-2008) ``` -``` php +```php \PhpOffice\PhpSpreadsheet\Calculation\Functions::setReturnDateType( \PhpOffice\PhpSpreadsheet\Calculation\Functions::RETURNDATE_EXCEL ); @@ -1661,7 +1661,7 @@ This is an integer ranging from 0 to 23. ##### Examples -``` php +```php $worksheet->setCellValue('A1', 'Time String') ->setCellValue('A2', '31-Dec-2008 17:30') ->setCellValue('A3', '14-Feb-2008 4:20 AM') @@ -1681,7 +1681,7 @@ $retVal = $worksheet->getCell('B4')->getCalculatedValue(); // $retVal = 16 ``` -``` php +```php $retVal = call_user_func_array( ['\PhpOffice\PhpSpreadsheet\Calculation\Functions', 'HOUROFDAY'], ['09:30'] @@ -1719,7 +1719,7 @@ This is an integer ranging from 0 to 59. ##### Examples -``` php +```php $worksheet->setCellValue('A1', 'Time String') ->setCellValue('A2', '31-Dec-2008 17:30') ->setCellValue('A3', '14-Feb-2008 4:20 AM') @@ -1739,7 +1739,7 @@ $retVal = $worksheet->getCell('B4')->getCalculatedValue(); // $retVal = 45 ``` -``` php +```php $retVal = call_user_func_array( ['\PhpOffice\PhpSpreadsheet\Calculation\Functions', 'MINUTE'], ['09:30'] @@ -1777,7 +1777,7 @@ This is an integer ranging from 1 to 12. ##### Examples -``` php +```php $worksheet->setCellValue('A1', 'Date String'); $worksheet->setCellValue('A2', '31-Dec-2008'); $worksheet->setCellValue('A3', '14-Feb-2008'); @@ -1792,7 +1792,7 @@ $retVal = $worksheet->getCell('B3')->getCalculatedValue(); // $retVal = 2 ``` -``` php +```php $retVal = call_user_func_array( ['\PhpOffice\PhpSpreadsheet\Calculation\Functions', 'MONTHOFYEAR'], ['14-July-2008'] @@ -1847,10 +1847,10 @@ The number of working days between startDate and endDate. ##### Examples -``` php +```php ``` -``` php +```php ``` ##### Notes @@ -1880,10 +1880,10 @@ or an Excel timestamp value (real), depending on the value of ##### Examples -``` php +```php ``` -``` php +```php ``` ##### Notes @@ -1917,7 +1917,7 @@ This is an integer ranging from 0 to 59. ##### Examples -``` php +```php $worksheet->setCellValue('A1', 'Time String') ->setCellValue('A2', '31-Dec-2008 17:30:20') ->setCellValue('A3', '14-Feb-2008 4:20 AM') @@ -1937,7 +1937,7 @@ $retVal = $worksheet->getCell('B4')->getCalculatedValue(); // $retVal = 59 ``` -``` php +```php $retVal = call_user_func_array( ['\PhpOffice\PhpSpreadsheet\Calculation\Functions', 'SECOND'], ['09:30:17'] @@ -2002,7 +2002,7 @@ value of method. ##### Examples -``` php +```php $worksheet->setCellValue('A1', 'Date String') ->setCellValue('A2', '31-Dec-2008') ->setCellValue('A3', '14-Feb-2008'); @@ -2021,7 +2021,7 @@ $retVal = $worksheet->getCell('B4')->getCalculatedValue(); // $retVal = 2 ``` -``` php +```php $retVal = call_user_func_array( ['\PhpOffice\PhpSpreadsheet\Calculation\Functions', 'WEEKDAY'], ['14-July-2008'] @@ -2066,7 +2066,7 @@ This is an integer year value. ##### Examples -``` php +```php $worksheet->setCellValue('A1', 'Date String') ->setCellValue('A2', '17-Jul-1982') ->setCellValue('A3', '16-Apr-2009'); @@ -2081,7 +2081,7 @@ $retVal = $worksheet->getCell('B3')->getCalculatedValue(); // $retVal = 2009 ``` -``` php +```php $retVal = call_user_func_array( ['\PhpOffice\PhpSpreadsheet\Calculation\Functions', 'YEAR'], ['14-July-2001'] diff --git a/docs/topics/creating-spreadsheet.md b/docs/topics/creating-spreadsheet.md index dceafe4b7c..3a82623edf 100644 --- a/docs/topics/creating-spreadsheet.md +++ b/docs/topics/creating-spreadsheet.md @@ -20,7 +20,7 @@ Details of the different spreadsheet formats supported, and the options available to read them into a Spreadsheet object are described fully in the [Reading Files](./reading-files.md) document. -``` php +```php $inputFileName = './sampleData/example1.xls'; /** Load $inputFileName to a Spreadsheet object **/ @@ -32,7 +32,7 @@ $spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($inputFileName); If you want to create a new workbook, rather than load one from file, then you simply need to instantiate it as a new Spreadsheet object. -``` php +```php /** Create a new Spreadsheet Object **/ $spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet(); ``` @@ -53,7 +53,7 @@ then you also need to "break" these cyclic references before doing so. PhpSpreadsheet provides the `disconnectWorksheets()` method for this purpose. -``` php +```php $spreadsheet->disconnectWorksheets(); unset($spreadsheet); ``` diff --git a/docs/topics/defined-names.md b/docs/topics/defined-names.md new file mode 100644 index 0000000000..2dd4fe68fb --- /dev/null +++ b/docs/topics/defined-names.md @@ -0,0 +1,593 @@ +# Defined Names + +There are two types of Defined Names in MS Excel and other Spreadsheet formats: Named Ranges and Named Formulae. Between them, they can add a lot of power to your Spreadsheets, but they need to be used correctly. + +Working examples for all the code shown in this document can be found in the `/samples/DefinedNames` folder. + +## Named Ranges + +A Named Range provides a name reference to a cell or a range of cells. You can then reference that cell or cells by that name within a formula. + +As an example, I'll create a simple Calculator that adds Tax to a Price. + +```php +// Set up some basic data +$worksheet + ->setCellValue('A1', 'Tax Rate:') + ->setCellValue('B1', '=19%') + ->setCellValue('A3', 'Net Price:') + ->setCellValue('B3', 12.99) + ->setCellValue('A4', 'Tax:') + ->setCellValue('A5', 'Price including Tax:'); + +// Define named ranges +$spreadsheet->addNamedRange( new \PhpOffice\PhpSpreadsheet\NamedRange('TAX_RATE', $worksheet, '=$B$1') ); +$spreadsheet->addNamedRange( new \PhpOffice\PhpSpreadsheet\NamedRange('PRICE', $worksheet, '=$B$3') ); + +// Reference that defined name in a formula +$worksheet + ->setCellValue('B4', '=PRICE*TAX_RATE') + ->setCellValue('B5', '=PRICE*(1+TAX_RATE)'); + +echo sprintf( + 'With a Tax Rate of %.2f and a net price of %.2f, Tax is %.2f and the gross price is %.2f', + $worksheet->getCell('B1')->getCalculatedValue(), + $worksheet->getCell('B3')->getValue(), + $worksheet->getCell('B4')->getCalculatedValue(), + $worksheet->getCell('B5')->getCalculatedValue() +), PHP_EOL; +``` +`/samples/DefinedNames/SimpleNamedRange.php` + +This makes formulae in the generated spreadsheet easier to understand when viewing it them MS Excel. Using these Named Ranges (providing meaningful human-readable names for cells) makes the purpose of the formula immediately clear. We don't need to look for cell `B2` to see what it is, the name tells us. + +And, if the Tax Rate changes to 16%, then we only need to change the value in cell `B1` to the new Tax rate (`=16%`), or if we want to calculate the Tax Charges for a different net price, that will immediately be reflected in all the calculations that reference those Named Ranges. No matter whereabouts in the worksheet I used that Named Range, it always references the value in cell `B1`. + +In fact, because we were required to specify a worksheet when we defined the name, that name is available from any worksheet within the spreadsheet, and always means cell `B2` in this worksheet (but see the notes on Named Range Scope below). + +### Absolute Named Ranges + +In the above example, when I define the Named Range values (e.g. `'=$B$1'`), I used a `$` before both the row and the column. This made the Named Range an Absolute Reference. + +Another example: +```php +// Set up some basic data for a timesheet +$worksheet + ->setCellValue('A1', 'Charge Rate/hour:') + ->setCellValue('B1', '7.50') + ->setCellValue('A3', 'Date') + ->setCellValue('B3', 'Hours') + ->setCellValue('C3', 'Charge'); + +// Define named range using an absolute cell reference +$spreadsheet->addNamedRange( new NamedRange('CHARGE_RATE', $worksheet, '=$B$1') ); + +$workHours = [ + '2020-0-06' => 7.5, + '2020-0-07' => 7.25, + '2020-0-08' => 6.5, + '2020-0-09' => 7.0, + '2020-0-10' => 5.5, +]; + +// Populate the Timesheet +$startRow = 4; +$row = $startRow; +foreach ($workHours as $date => $hours) { + $worksheet + ->setCellValue("A{$row}", $date) + ->setCellValue("B{$row}", $hours) + ->setCellValue("C{$row}", "=B{$row}*CHARGE_RATE"); + $row++; +} +$endRow = $row - 1; + +++$row; +$worksheet + ->setCellValue("B{$row}", "=SUM(B{$startRow}:B{$endRow})") + ->setCellValue("C{$row}", "=SUM(C{$startRow}:C{$endRow})"); + + +echo sprintf( + 'Worked %.2f hours at a rate of %.2f - Charge to the client is %.2f', + $worksheet->getCell("B{$row}")->getCalculatedValue(), + $worksheet->getCell('B1')->getValue(), + $worksheet->getCell("C{$row}")->getCalculatedValue() +), PHP_EOL; +``` +`/samples/DefinedNames/AbsoluteNamedRange.php` + +Because the Named Range `CHARGE_RATE` is defined as an Absolute cell reference, then it always references cell `B2` no matter where it is referenced in a formula in the spreadsheet. + +### Relative Named Ranges + +The previous example showed a simple timesheet using an Absolute Reference for the Charge Rate, used to calculate our billed charges to client. + +The use of `B{$row}` in our formula (at least it will appear as an actual cell reference in MS Excel if we save the file and open it) requires a bit of mental agility to remember that column `B` is our hours for that day. Why can't we use another Named Range called something like `HOURS_PER_DAY` to make the formula more easily readable and meaningful. + +But if we used an Absolute Named Range for `HOURS_PER_DAY`, then we'd need a different Named Range for each day (`MONDAY_HOURS_PER_DAY`, `TUESDAY_HOURS_PER_DAY`, etc), and a different formula for each day of the week; if we kept a monthly timesheet, we would have to defined a different Named Range for every day of the month... and that's a lot more trouble than it's worth, and quickly becomes unmanageable. + +This is where Relative Named Ranges are very useful. + +```php +// Set up some basic data for a timesheet +$worksheet + ->setCellValue('A1', 'Charge Rate/hour:') + ->setCellValue('B1', '7.50') + ->setCellValue('A3', 'Date') + ->setCellValue('B3', 'Hours') + ->setCellValue('C3', 'Charge'); + +// Define named ranges +// CHARGE_RATE is an absolute cell reference that always points to cell B1 +$spreadsheet->addNamedRange( new NamedRange('CHARGE_RATE', $worksheet, '=$B$1') ); +// HOURS_PER_DAY is a relative cell reference that always points to column B, but to a cell in the row where it is used +$spreadsheet->addNamedRange( new NamedRange('HOURS_PER_DAY', $worksheet, '=$B1') ); + +$workHours = [ + '2020-0-06' => 7.5, + '2020-0-07' => 7.25, + '2020-0-08' => 6.5, + '2020-0-09' => 7.0, + '2020-0-10' => 5.5, +]; + +// Populate the Timesheet +$startRow = 4; +$row = $startRow; +foreach ($workHours as $date => $hours) { + $worksheet + ->setCellValue("A{$row}", $date) + ->setCellValue("B{$row}", $hours) + ->setCellValue("C{$row}", "=HOURS_PER_DAY*CHARGE_RATE"); + $row++; +} +$endRow = $row - 1; + +++$row; +$worksheet + ->setCellValue("B{$row}", "=SUM(B{$startRow}:B{$endRow})") + ->setCellValue("C{$row}", "=SUM(C{$startRow}:C{$endRow})"); + + +echo sprintf( + 'Worked %.2f hours at a rate of %.2f - Charge to the client is %.2f', + $worksheet->getCell("B{$row}")->getCalculatedValue(), + $worksheet->getCell('B1')->getValue(), + $worksheet->getCell("C{$row}")->getCalculatedValue() +), PHP_EOL; +``` +`/samples/DefinedNames/RelativeNamedRange.php` + +The difference in the cell definition for `HOURS_PER_DAY` (`'=$B1'`) is that we have a `$` in front of the column `B`, but not in front of the row number. The `$` makes the column absolute: no matter where in the worksheet we use this name, it always references column `B`. Without a `$`in front of the row number, we make the row number relative, relative to the row where the name appears in a formula, so it effectively replaces the `1` with its own row number when it executes the calculation. + +When it is used in the formula in row 4, then it references cell `B4`, when it appears in row 5, it references cell `B5`, and so on. Using a Relative Named Range, we can use the same Named Range to refer to cells in different rows (and/or different columns), so we can re-use the same Named Range to refer to different cells relative to the row (or column) where we use them. + +--- + +Named Ranges aren't limited to a single cell, but can point to a range of cells. A common use case might be to provide a series of column totals at the bottom of a dataset. Let's take our timesheet, and modify it just slightly to use a Relative column range for that purpose. + +I won't replicate the entire code from the previous example, because I'm only changing a few lines; but we just replace the block: +```php +++$row; +$worksheet + ->setCellValue("B{$row}", "=SUM(B{$startRow}:B{$endRow})") + ->setCellValue("C{$row}", "=SUM(C{$startRow}:C{$endRow})"); +``` +with: +```php +// COLUMN_TOTAL is another relative cell reference that always points to the same range of rows but to cell in the column where it is used +$spreadsheet->addNamedRange( new NamedRange('COLUMN_DATA_VALUES', $worksheet, "=A\${$startRow}:A\${$endRow}") ); + +++$row; +$worksheet + ->setCellValue("B{$row}", "=SUM(COLUMN_DATA_VALUES)") + ->setCellValue("C{$row}", "=SUM(COLUMN_DATA_VALUES)"); +``` +`/samples/DefinedNames/RelativeNamedRange2.php` + +Now that I've specified column as relative in the definition of `COLUMN_DATA_VALUES` with an address of column `A`, and the rows are absolute. When the same Relative Named Range is used in column `B`,it references cells in column `B` rather than `A`; and when it is used in column `C`, it references cells in column `C`. + +While we still have a piece of code (`"=A\${$startRow}:A\${$endRow}"`) that isn't easily human-readable, when we open the generated spreadsheet in MS Excel, the displayed formula in for the cells for the totals is immediately understandable. + +### Named Range Scope + +Whenever we define a Named Range, we are required to specify a worksheet, and that name is then available from any worksheet within the spreadsheet, and always means that cell or cell range in the specified worksheet. + +```php +// Set up some basic data for a timesheet +$worksheet + ->setCellValue('A1', 'Charge Rate/hour:') + ->setCellValue('B1', '7.50'); + +// Define a global named range on the first worksheet for our Charge Rate +// CHARGE_RATE is an absolute cell reference that always points to cell B1 +// Because it is defined globally, it will still be usable from any worksheet in the spreadsheet +$spreadsheet->addNamedRange( new NamedRange('CHARGE_RATE', $worksheet, '=$B$1') ); + +// Create a second worksheet as our client timesheet +$worksheet = $spreadsheet->addSheet(new \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet($spreadsheet, 'Client Timesheet')); + +// Define named ranges +// HOURS_PER_DAY is a relative cell reference that always points to column B, but to a cell in the row where it is used +$spreadsheet->addNamedRange( new NamedRange('HOURS_PER_DAY', $worksheet, '=$B1') ); + +// Set up some basic data for a timesheet +$worksheet + ->setCellValue('A1', 'Date') + ->setCellValue('B1', 'Hours') + ->setCellValue('C1', 'Charge'); + +$workHours = [ + '2020-0-06' => 7.5, + '2020-0-07' => 7.25, + '2020-0-08' => 6.5, + '2020-0-09' => 7.0, + '2020-0-10' => 5.5, +]; + +// Populate the Timesheet +$startRow = 2; +$row = $startRow; +foreach ($workHours as $date => $hours) { + $worksheet + ->setCellValue("A{$row}", $date) + ->setCellValue("B{$row}", $hours) + ->setCellValue("C{$row}", "=HOURS_PER_DAY*CHARGE_RATE"); + $row++; +} +$endRow = $row - 1; + +// COLUMN_TOTAL is another relative cell reference that always points to the same range of rows but to cell in the column where it is used +$spreadsheet->addNamedRange( new NamedRange('COLUMN_DATA_VALUES', $worksheet, "=A\${$startRow}:A\${$endRow}") ); + +++$row; +$worksheet + ->setCellValue("B{$row}", "=SUM(COLUMN_DATA_VALUES)") + ->setCellValue("C{$row}", "=SUM(COLUMN_DATA_VALUES)"); + +echo sprintf( + 'Worked %.2f hours at a rate of %s - Charge to the client is %.2f', + $worksheet->getCell("B{$row}")->getCalculatedValue(), + $chargeRateCellValue = $spreadsheet + ->getSheetByName($spreadsheet->getNamedRange('CHARGE_RATE')->getWorksheet()->getTitle()) + ->getCell($spreadsheet->getNamedRange('CHARGE_RATE')->getCellsInRange()[0])->getValue(), + $worksheet->getCell("C{$row}")->getCalculatedValue() +), PHP_EOL; +``` +`/samples/DefinedNames/ScopedNamedRange.php` + +Even though `CHARGE_RATE` references a cell on a different worksheet, because is set as global (the default) it is accessible from any worksheet in the spreadsheet. so when we reference it in formulae on the second timesheet worksheet, we are able to access the value from that first worksheet and use it in our calculations. + +--- + +However, a Named Range can be locally scoped so that it is only available when referenced from a specific worksheet, or it can be globally scoped. This means that you can use the same Named Range name with different values on different worksheets. + +Building further on our timesheet, perhaps we use a different worksheet for each client, and we use the same hourly rate when billing most of our clients; but for one particular client (perhaps doing work for a a friend) we use a lower rate. + +```php +$clients = [ + 'Client #1 - Full Hourly Rate' => [ + '2020-0-06' => 2.5, + '2020-0-07' => 2.25, + '2020-0-08' => 6.0, + '2020-0-09' => 3.0, + '2020-0-10' => 2.25, + ], + 'Client #2 - Full Hourly Rate' => [ + '2020-0-06' => 1.5, + '2020-0-07' => 2.75, + '2020-0-08' => 0.0, + '2020-0-09' => 4.5, + '2020-0-10' => 3.5, + ], + 'Client #3 - Reduced Hourly Rate' => [ + '2020-0-06' => 3.5, + '2020-0-07' => 2.5, + '2020-0-08' => 1.5, + '2020-0-09' => 0.0, + '2020-0-10' => 1.25, + ], +]; + +foreach ($clients as $clientName => $workHours) { + $worksheet = $spreadsheet->addSheet(new \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet($spreadsheet, $clientName)); + + // Set up some basic data for a timesheet + $worksheet + ->setCellValue('A1', 'Charge Rate/hour:') + ->setCellValue('B1', '7.50') + ->setCellValue('A3', 'Date') + ->setCellValue('B3', 'Hours') + ->setCellValue('C3', 'Charge'); + ; + + // Define named ranges + // CHARGE_RATE is an absolute cell reference that always points to cell B1 + $spreadsheet->addNamedRange( new NamedRange('CHARGE_RATE', $worksheet, '=$B$1', true) ); + // HOURS_PER_DAY is a relative cell reference that always points to column B, but to a cell in the row where it is used + $spreadsheet->addNamedRange( new NamedRange('HOURS_PER_DAY', $worksheet, '=$B1', true) ); + + // Populate the Timesheet + $startRow = 4; + $row = $startRow; + foreach ($workHours as $date => $hours) { + $worksheet + ->setCellValue("A{$row}", $date) + ->setCellValue("B{$row}", $hours) + ->setCellValue("C{$row}", "=HOURS_PER_DAY*CHARGE_RATE"); + $row++; + } + $endRow = $row - 1; + + // COLUMN_TOTAL is another relative cell reference that always points to the same range of rows but to cell in the column where it is used + $spreadsheet->addNamedRange( new NamedRange('COLUMN_TOTAL', $worksheet, "=A\${$startRow}:A\${$endRow}", true) ); + + ++$row; + $worksheet + ->setCellValue("B{$row}", "=SUM(COLUMN_TOTAL)") + ->setCellValue("C{$row}", "=SUM(COLUMN_TOTAL)"); +} +$spreadsheet->removeSheetByIndex(0); + +// Set the reduced charge rate for our special client +$worksheet + ->setCellValue("B1", 4.5); + +foreach ($spreadsheet->getAllSheets() as $worksheet) { + echo sprintf( + 'Worked %.2f hours for "%s" at a rate of %.2f - Charge to the client is %.2f', + $worksheet->getCell("B{$row}")->getCalculatedValue(), + $worksheet->getTitle(), + $worksheet->getCell('B1')->getValue(), + $worksheet->getCell("C{$row}")->getCalculatedValue() + ), PHP_EOL; +} +``` +`/samples/DefinedNames/ScopedNamedRange2.php` + +Now we are creating three worksheets for each of three different clients. Because each Named Range is linked to a worksheet, we need to create three sets of Named Ranges, so that we don't simply reference the cells on only one of the worksheets; but because we are locally scoping them (note the extra boolean argument used when we define the Named Ranges) we can use the same names on each worksheet, and they will reference the correct cells when we use them in our formulae on that worksheet. + +When Named Ranges are being evaluated, the logic looks first to see if there is a locally scoped Named Range defined for the current worksheet. If there is, then that is the Named Range that will be used in the calculation. If no locally scoped Named Range with that name is found, the logic then looks to see if there is a globally scoped Named Range definition, and will use that if it is found. If no Named Range of the required name is found scoped to the current worksheet, or globally scoped, then a `#NAME` error will be returned. + +## Named Formulae + +A Named Formula is a stored formula, or part of a formula, that can be referenced in cells by name, and re-used in many different places within the spreadsheet. + +As an example, I'll modify the simple Tax Calculator that I created as my example for Named Ranges. + +```php +// Add some Named Formulae +// The first to store our tax rate +$spreadsheet->addNamedFormula(new NamedFormula('TAX_RATE', $worksheet, '=19%')); +// The second to calculate the Tax on a Price value (Note that `PRICE` is defined later as a Named Range) +$spreadsheet->addNamedFormula(new NamedFormula('TAX', $worksheet, '=PRICE*TAX_RATE')); + +// Set up some basic data +$worksheet + ->setCellValue('A1', 'Tax Rate:') + ->setCellValue('B1', '=TAX_RATE') + ->setCellValue('A3', 'Net Price:') + ->setCellValue('B3', 19.99) + ->setCellValue('A4', 'Tax:') + ->setCellValue('A5', 'Price including Tax:'); + +// Define a named range that we can use in our formulae +$spreadsheet->addNamedRange(new NamedRange('PRICE', $worksheet, '=$B$3')); + +// Reference the defined formulae in worksheet formulae +$worksheet + ->setCellValue('B4', '=TAX') + ->setCellValue('B5', '=PRICE+TAX'); + +echo sprintf( + 'With a Tax Rate of %.2f and a net price of %.2f, Tax is %.2f and the gross price is %.2f', + $worksheet->getCell('B1')->getCalculatedValue(), + $worksheet->getCell('B3')->getValue(), + $worksheet->getCell('B4')->getCalculatedValue(), + $worksheet->getCell('B5')->getCalculatedValue() +), PHP_EOL; +``` +`/samples/DefinedNames/SimpleNamedFormula.php` + +There are a few points to note here: + +Firstly. we are actually storing the tax rate in a named formula (`TAX_RATE`) rather than as a cell value. When we display the tax rate in cell `B1`, we are really storing an instruction for MS Excel to evaluate the formula and display the result in that cell. + +Then we are using a Named Formula `TAX` that references both another Named Formula (`TAX_RATE`) and a Named Range (`PRICE`) and executes a calculation using them both (`PRICE * TAX_RATE`). + +Finally, we are using the formula `TAX` in two different contexts. Once to display the tax value (in cell `B4`); and a second time as part of another formula (`PRICE + TAX`) in cell `B5`. + +--- + +Named Formulae aren't just restricted tosimple mathematics, but can include MS EXcel functions as well to provide a lot of flexibility; and they can reference values on other worksheets. + +```php +$worksheet = $spreadsheet->setActiveSheetIndex(0); +setYearlyData($worksheet,'2019', $data2019); +$worksheet = $spreadsheet->addSheet(new Worksheet($spreadsheet)); +setYearlyData($worksheet,'2020', $data2020); +$worksheet = $spreadsheet->addSheet(new Worksheet($spreadsheet)); +setYearlyData($worksheet,'2020', [], 'GROWTH'); + +function setYearlyData(Worksheet $worksheet, string $year, $yearlyData, ?string $title = null) { + // Set up some basic data + $worksheetTitle = $title ?: $year; + $worksheet + ->setTitle($worksheetTitle) + ->setCellValue('A1', 'Month') + ->setCellValue('B1', $worksheetTitle === 'GROWTH' ? 'Growth' : 'Sales') + ->setCellValue('C1', $worksheetTitle === 'GROWTH' ? 'Profit Growth' : 'Margin') + ->setCellValue('A2', Date::stringToExcel("{$year}-01-01")); + for ($row = 3; $row <= 13; ++$row) { + $worksheet->setCellValue("A{$row}", "=NEXT_MONTH"); + } + + if (!empty($yearlyData)) { + $worksheet->fromArray($yearlyData, null, 'B2'); + } else { + for ($row = 2; $row <= 13; ++$row) { + $worksheet->setCellValue("B{$row}", "=GROWTH"); + $worksheet->setCellValue("C{$row}", "=PROFIT_GROWTH"); + } + } + + $worksheet->getStyle('A1:C1') + ->getFont()->setBold(true); + $worksheet->getStyle('A2:A13') + ->getNumberFormat() + ->setFormatCode('mmmm'); + $worksheet->getStyle('B2:C13') + ->getNumberFormat() + ->setFormatCode($worksheetTitle === 'GROWTH' ? '0.00%' : '_-€* #,##0_-'); +} + +// Add some Named Formulae +// The first to store our tax rate +$spreadsheet->addNamedFormula(new NamedFormula('NEXT_MONTH', $worksheet, "=EDATE(OFFSET(\$A1,-1,0),1)")); +$spreadsheet->addNamedFormula(new NamedFormula('GROWTH', $worksheet, "=IF('2020'!\$B1=\"\",\"-\",(('2020'!\$B1/'2019'!\$B1)-1))")); +$spreadsheet->addNamedFormula(new NamedFormula('PROFIT_GROWTH', $worksheet, "=IF('2020'!\$C1=\"\",\"-\",(('2020'!\$C1/'2019'!\$C1)-1))")); + +for ($row = 2; $row<=7; ++$row) { + $month = $worksheet->getCell("A{$row}")->getFormattedValue(); + $growth = $worksheet->getCell("B{$row}")->getFormattedValue(); + $profitGrowth = $worksheet->getCell("C{$row}")->getFormattedValue(); + + echo "Growth for {$month} is {$growth}, with a Profit Growth of {$profitGrowth}", PHP_EOL; +} +``` +`/samples/DefinedNames/CrossWorksheetNamedFormula.php` + +Here we're creating two Named Formulae that both use the `IF()` function, and that compare values on two different worksheets, and calculate the percentage difference between the two. We're also creating a Named Formula that uses the `OFFSET()` function to reference the cell immediately above the current Relative cell reference. + +## Combining Named Ranges and Formulae + +For a slightly more complex example combining Named Ranges and Named Formulae, we can build on our client timesheet. + +```php +// Set up some basic data for a timesheet +$worksheet + ->setCellValue('A1', 'Charge Rate/hour:') + ->setCellValue('B1', '7.50') + ->setCellValue('A3', 'Date') + ->setCellValue('B3', 'Hours') + ->setCellValue('C3', 'Charge'); + +// Define named ranges +// CHARGE_RATE is an absolute cell reference that always points to cell B1 +$spreadsheet->addNamedRange(new NamedRange('CHARGE_RATE', $worksheet, '=$B$1')); +// HOURS_PER_DAY is a relative cell reference that always points to column B, but to a cell in the row where it is used +$spreadsheet->addNamedRange(new NamedRange('HOURS_PER_DAY', $worksheet, '=$B1')); +// Set up the formula for calculating the daily charge +$spreadsheet->addNamedFormula(new NamedFormula('DAILY_CHARGE', null, '=HOURS_PER_DAY*CHARGE_RATE')); +// Set up the formula for calculating the column totals +$spreadsheet->addNamedFormula(new NamedFormula('COLUMN_TOTALS', null, '=SUM(COLUMN_DATA_VALUES)')); + + +$workHours = [ + '2020-0-06' => 7.5, + '2020-0-07' => 7.25, + '2020-0-08' => 6.5, + '2020-0-09' => 7.0, + '2020-0-10' => 5.5, +]; + +// Populate the Timesheet +$startRow = 4; +$row = $startRow; +foreach ($workHours as $date => $hours) { + $worksheet + ->setCellValue("A{$row}", $date) + ->setCellValue("B{$row}", $hours) + ->setCellValue("C{$row}", '=DAILY_CHARGE'); + ++$row; +} +$endRow = $row - 1; + +// COLUMN_TOTAL is another relative cell reference that always points to the same range of rows but to cell in the column where it is used +$spreadsheet->addNamedRange(new NamedRange('COLUMN_DATA_VALUES', $worksheet, "=A\${$startRow}:A\${$endRow}")); + +++$row; +$worksheet + ->setCellValue("B{$row}", '=COLUMN_TOTALS') + ->setCellValue("C{$row}", '=COLUMN_TOTALS'); + +echo sprintf( + 'Worked %.2f hours at a rate of %.2f - Charge to the client is %.2f', + $worksheet->getCell("B{$row}")->getCalculatedValue(), + $worksheet->getCell('B1')->getValue(), + $worksheet->getCell("C{$row}")->getCalculatedValue() +), PHP_EOL; +``` +`/samples/DefinedNames/NamedFormulaeAndRanges.php` + +The main point to notice in this example is that you must specify a Worksheet for Named Ranges, but that it isn't required for Named Formulae; in fact, specifying a Worksheet for named Formulae can lead to MS Excel errors when a saved file is opened. Generally, it is far safer to specify a null Worksheet value when creating a Named Formula, unless it references cell values explicitly, or you wish to scope it to that Worksheet. + +It also doesn't matter what order we define our Named Ranges and Formulae, even when some are dependent on others: this only matters when we try to use them in a cell calculation, or when we save the file; and as long as every Defined Name has been defined at that point, then it isn't important. In this case, we couldn't define `COLUMN_DATA_VALUES` until we new the range of rows that it needed to contain; but we could still define the `COLUMN_TOTALS` formula before that. + +## Additional Comments + +### Helper + +In all the examples so far, we have explicitly used the `NamedRange` and `NamedFormula` classes, and the Spreadsheet's `addNamedRange()` and `addNamedFormula()` methods, e.g. +```php +$spreadsheet->addNamedRange(new NamedRange('HOURS_PER_DAY', $worksheet, '=$B1')); +``` +However, this can lead to errors if we accidentally set a formula value for a Named Range, or a range value for a Named Formula. + +As a helper, the DefinedName class provides a static method that can identify whether the value expression is a Range or a Formula, and instantiate the appropriate class. +```php +$this->spreadsheet->addDefinedName( + DefinedName::createInstance('FOO', $this->spreadsheet->getSheetByName('Sheet #2'), '=16%', true) +); +``` + +### Naming Names + +The names that you assign to Defined Name must follow the following set of rules: + - The first character of a name must be one of the following characters: + - letter (including UTF-8 letters) + - underscore (`_`) + - Remaining characters in the name can be + - letters (including UTF-8 letters) + - numbers (including UTF-8 numbers) + - periods (`.`) + - underscore characters (`_`) + - The following are not allowed: + - Space characters are not allowed as part of a name. + - Names can't look like cell addresses, such as A35 or R2C2 + - Names are not case sensitive. For example, `North` and `NORTH` are treated as the same name. + +### Limitations + +PHPSpreadsheet doesn't yet fully validate the names that you use, so it is possible to create a spreadsheet in PHPSpreadsheet that will break when you save and try to open it in MS Excel; or that will break PHPSpreadsheet when they are referenced in a cell. +So please be sensible when creating names, and follow the rules listed above. + +--- + +There is nothing to stop you creating a Defined Name that matches an existing Function name +```php +$spreadsheet->addNamedFormula(new NamedFormula('SUM', $worksheet, '=SUM(A1:E5)')); +``` +And this will work without problems in MS Excel. However, it is not guaranteed to work correctly in PHPSpreadsheet; and will certainly cause confusion for anybody reading it; so it is not recommended. Names exist to give clarity to the person reading the spreadsheet, and a cell containing `=SUM` is even harder to understand (what is it the sum of?) than a cell containing `=SUM(B4:B8)`. Use names that provide meaning, like `SUM_OF_WORKED_HOURS`. + +--- + +You cannot have a Named Range and a Named Formula with the same name, unless they are differently scoped. + +--- + +MS Excel uses some "special tricks" to simulate Relative Named Ranges where the row or column comes before the current row or column, useful if you want to get column totals that don't include the current cell. These "tricks" aren't supported by PHPSpreadsheet, but can be simulated using the `OFFSET()` function in a Named Formula. +In our `RelativeNamedRange2.php` example, we explicitly created the `COLUMN_DATA_VALUES` Named Range using only the rows that we knew should be included, so that we weren't including the current row (where we were displaying the total) and creating a cyclic reference: +```php +// COLUMN_TOTAL is another relative cell reference that always points to the same range of rows but to cell in the column where it is used +$spreadsheet->addNamedRange(new NamedRange('COLUMN_DATA_VALUES', $worksheet, "=A\${$startRow}:A\${$endRow}")); +``` +We could instead have created a Named Function using `OFFSET()` to specify just the start row, and offset the end row by -1 row: +```php +// COLUMN_TOTAL is another relative cell reference that always points to the same range of rows but to cell in the column where it is used +// To avoid including the current row,or having to hard-code the range itself (as we did in the previous example) +// we wrap it in a named formula using the OFFSET() function +$spreadsheet->addNamedFormula(new NamedFormula('COLUMN_DATA_VALUES', $worksheet, "=OFFSET(A\$4:A1, -1, 0)")); +``` +as demonstrated in example `RelativeNamedRangeAsFunction.php`. diff --git a/docs/topics/images/10-databar-of-conditional-formatting.png b/docs/topics/images/10-databar-of-conditional-formatting.png new file mode 100644 index 0000000000000000000000000000000000000000..10c88f9f779317f7eaac4cfe9918b67969dc190e GIT binary patch literal 143616 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

pR!2!?M&CxOQ+SgakSwcQ1RER2(8!s6kL6!k~Z) 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 $reader->load("05featuredemo.xlsx"); @@ -55,7 +55,7 @@ You can create a `\PhpOffice\PhpSpreadsheet\Reader\IReader` instance using `\PhpOffice\PhpSpreadsheet\IOFactory` in explicit mode using the following code sample: -``` php +```php $reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader("Xlsx"); $spreadsheet = $reader->load("05featuredemo.xlsx"); ``` @@ -68,7 +68,7 @@ mode. You can create a `\PhpOffice\PhpSpreadsheet\Writer\IWriter` instance using `\PhpOffice\PhpSpreadsheet\IOFactory`: -``` php +```php $writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, "Xlsx"); $writer->save("05featuredemo.xlsx"); ``` @@ -84,7 +84,7 @@ outputting the in-memory spreadsheet to a .xlsx file. You can read an .xlsx file using the following code: -``` php +```php $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx(); $spreadsheet = $reader->load("05featuredemo.xlsx"); ``` @@ -94,7 +94,7 @@ $spreadsheet = $reader->load("05featuredemo.xlsx"); You can set the option setReadDataOnly on the reader, to instruct the reader to ignore styling, data validation, … and just read cell data: -``` php +```php $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx(); $reader->setReadDataOnly(true); $spreadsheet = $reader->load("05featuredemo.xlsx"); @@ -105,7 +105,7 @@ $spreadsheet = $reader->load("05featuredemo.xlsx"); You can set the option setLoadSheetsOnly on the reader, to instruct the reader to only load the sheets with a given name: -``` php +```php $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx(); $reader->setLoadSheetsOnly(["Sheet 1", "My special sheet"]); $spreadsheet = $reader->load("05featuredemo.xlsx"); @@ -122,7 +122,7 @@ read using the `\PhpOffice\PhpSpreadsheet\Reader\DefaultReadFilter`. The following code will only read row 1 and rows 20 – 30 of any sheet in the Excel file: -``` php +```php class MyReadFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter { public function readCell($column, $row, $worksheetName = '') { @@ -145,7 +145,7 @@ $spreadsheet = $reader->load("06largescale.xlsx"); You can write an .xlsx file using the following code: -``` php +```php $writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet); $writer->save("05featuredemo.xlsx"); ``` @@ -156,7 +156,7 @@ By default, this writer pre-calculates all formulas in the spreadsheet. This can be slow on large spreadsheets, and maybe even unwanted. You can however disable formula pre-calculation: -``` php +```php $writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet); $writer->setPreCalculateFormulas(false); $writer->save("05featuredemo.xlsx"); @@ -201,7 +201,7 @@ PHP. You can read an .xls file using the following code: -``` php +```php $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xls(); $spreadsheet = $reader->load("05featuredemo.xls"); ``` @@ -211,7 +211,7 @@ $spreadsheet = $reader->load("05featuredemo.xls"); You can set the option setReadDataOnly on the reader, to instruct the reader to ignore styling, data validation, … and just read cell data: -``` php +```php $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xls(); $reader->setReadDataOnly(true); $spreadsheet = $reader->load("05featuredemo.xls"); @@ -222,7 +222,7 @@ $spreadsheet = $reader->load("05featuredemo.xls"); You can set the option setLoadSheetsOnly on the reader, to instruct the reader to only load the sheets with a given name: -``` php +```php $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xls(); $reader->setLoadSheetsOnly(["Sheet 1", "My special sheet"]); $spreadsheet = $reader->load("05featuredemo.xls"); @@ -239,7 +239,7 @@ read using the `\PhpOffice\PhpSpreadsheet\Reader\DefaultReadFilter`. The following code will only read row 1 and rows 20 to 30 of any sheet in the Excel file: -``` php +```php class MyReadFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter { public function readCell($column, $row, $worksheetName = '') { @@ -262,7 +262,7 @@ $spreadsheet = $reader->load("06largescale.xls"); You can write an .xls file using the following code: -``` php +```php $writer = new \PhpOffice\PhpSpreadsheet\Writer\Xls($spreadsheet); $writer->save("05featuredemo.xls"); ``` @@ -282,7 +282,7 @@ spreadsheets via PHP. You can read an Excel 2003 .xml file using the following code: -``` php +```php $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xml(); $spreadsheet = $reader->load("05featuredemo.xml"); ``` @@ -298,7 +298,7 @@ read using the `\PhpOffice\PhpSpreadsheet\Reader\DefaultReadFilter`. The following code will only read row 1 and rows 20 to 30 of any sheet in the Excel file: -``` php +```php class MyReadFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter { public function readCell($column, $row, $worksheetName = '') { @@ -333,7 +333,7 @@ regarding to styling cells and handling large spreadsheets via PHP. You can read an .slk file using the following code: -``` php +```php $reader = new \PhpOffice\PhpSpreadsheet\Reader\Slk(); $spreadsheet = $reader->load("05featuredemo.slk"); ``` @@ -349,7 +349,7 @@ read using the `\PhpOffice\PhpSpreadsheet\Reader\DefaultReadFilter`. The following code will only read row 1 and rows 20 to 30 of any sheet in the SYLK file: -``` php +```php class MyReadFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter { public function readCell($column, $row, $worksheetName = '') { @@ -378,7 +378,7 @@ Open Office or Libre Office Calc files. You can read an .ods file using the following code: -``` php +```php $reader = new \PhpOffice\PhpSpreadsheet\Reader\Ods(); $spreadsheet = $reader->load("05featuredemo.ods"); ``` @@ -394,7 +394,7 @@ read using the `\PhpOffice\PhpSpreadsheet\Reader\DefaultReadFilter`. The following code will only read row 1 and rows 20 to 30 of any sheet in the Calc file: -``` php +```php class MyReadFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter { public function readCell($column, $row, $worksheetName = '') { @@ -427,7 +427,7 @@ regarding to styling cells, number formatting, ... You can read a .csv file using the following code: -``` php +```php $reader = new \PhpOffice\PhpSpreadsheet\Reader\Csv(); $spreadsheet = $reader->load("sample.csv"); ``` @@ -449,7 +449,7 @@ were created in Microsoft Office Excel the correct input encoding may rather be Windows-1252 (CP1252). Always make sure that the input encoding is set appropriately. -``` php +```php $reader = new \PhpOffice\PhpSpreadsheet\Reader\Csv(); $reader->setInputEncoding('CP1252'); $reader->setDelimiter(';'); @@ -458,13 +458,31 @@ $reader->setSheetIndex(0); $spreadsheet = $reader->load("sample.csv"); ``` +You may also let PhpSpreadsheet attempt to guess the input encoding. +It will do so based on a test for BOM (UTF-8, UTF-16BE, UTF-16LE, UTF-32BE, +or UTF-32LE), +or by doing heuristic tests for those encodings, falling back to a +specifiable encoding (default is CP1252) if all of those tests fail. + +```php +$reader = new \PhpOffice\PhpSpreadsheet\Reader\Csv(); +$encoding = \PhpOffice\PhpSpreadsheet\Reader\Csv::guessEncoding('sample.csv'); +// or, e.g. $encoding = \PhpOffice\PhpSpreadsheet\Reader\Csv::guessEncoding( +// 'sample.csv', 'ISO-8859-2'); +$reader->setInputEncoding($encoding); +$reader->setDelimiter(';'); +$reader->setEnclosure(''); +$reader->setSheetIndex(0); + +$spreadsheet = $reader->load('sample.csv'); +``` #### Read a specific worksheet CSV files can only contain one worksheet. Therefore, you can specify which sheet to read from CSV: -``` php +```php $reader->setSheetIndex(0); ``` @@ -475,10 +493,10 @@ data into an existing `Spreadsheet` object. The following code loads a CSV file into an existing `$spreadsheet` containing some sheets, and imports onto the 6th sheet: -``` php +```php $reader = new \PhpOffice\PhpSpreadsheet\Reader\Csv(); $reader->setDelimiter(';'); -$reader->setEnclosure(''); +$reader->setEnclosure('"'); $reader->setSheetIndex(5); $reader->loadIntoExisting("05featuredemo.csv", $spreadsheet); @@ -490,7 +508,7 @@ $reader->loadIntoExisting("05featuredemo.csv", $spreadsheet); You can write a .csv file using the following code: -``` php +```php $writer = new \PhpOffice\PhpSpreadsheet\Writer\Csv($spreadsheet); $writer->save("05featuredemo.csv"); ``` @@ -502,22 +520,35 @@ as a separator. You can instruct `\PhpOffice\PhpSpreadsheet\Writer\Csv` some options before writing a CSV file: -``` php +```php $writer = new \PhpOffice\PhpSpreadsheet\Writer\Csv($spreadsheet); $writer->setDelimiter(';'); -$writer->setEnclosure(''); +$writer->setEnclosure('"'); $writer->setLineEnding("\r\n"); $writer->setSheetIndex(0); $writer->save("05featuredemo.csv"); ``` +#### CSV enclosures + +By default, all CSV fields are wrapped in the enclosure character, +which defaults to double-quote. +You can change to use the enclosure character only when required: + +``` php +$writer = new \PhpOffice\PhpSpreadsheet\Writer\Csv($spreadsheet); +$writer->setEnclosureRequired(false); + +$writer->save("05featuredemo.csv"); +``` + #### Write a specific worksheet CSV files can only contain one worksheet. Therefore, you can specify which sheet to write to CSV: -``` php +```php $writer->setSheetIndex(0); ``` @@ -527,7 +558,7 @@ By default, this writer pre-calculates all formulas in the spreadsheet. This can be slow on large spreadsheets, and maybe even unwanted. You can however disable formula pre-calculation: -``` php +```php $writer = new \PhpOffice\PhpSpreadsheet\Writer\Csv($spreadsheet); $writer->setPreCalculateFormulas(false); $writer->save("05featuredemo.csv"); @@ -538,11 +569,12 @@ $writer->save("05featuredemo.csv"); CSV files are written in UTF-8. If they do not contain characters outside the ASCII range, nothing else need be done. However, if such characters are in the file, +or if the file starts with the 2 characters 'ID', it should explicitly include a BOM file header; if it doesn't, Excel will not interpret those characters correctly. This can be enabled by using the following code: -``` php +```php $writer = new \PhpOffice\PhpSpreadsheet\Writer\Csv($spreadsheet); $writer->setUseBOM(true); $writer->save("05featuredemo.csv"); @@ -560,14 +592,14 @@ to set the characters explicitly as shown below. English users will want to use this before doing the export: -``` php +```php \PhpOffice\PhpSpreadsheet\Shared\StringHelper::setDecimalSeparator('.'); \PhpOffice\PhpSpreadsheet\Shared\StringHelper::setThousandsSeparator(','); ``` German users will want to use the opposite values. -``` php +```php \PhpOffice\PhpSpreadsheet\Shared\StringHelper::setDecimalSeparator(','); \PhpOffice\PhpSpreadsheet\Shared\StringHelper::setThousandsSeparator('.'); ``` @@ -592,7 +624,7 @@ regarding to styling cells, number formatting, ... You can read an .html or .htm file using the following code: -``` php +```php $reader = new \PhpOffice\PhpSpreadsheet\Reader\Html(); $spreadsheet = $reader->load("05featuredemo.html"); @@ -610,7 +642,7 @@ first worksheet by default. You can write a .htm file using the following code: -``` php +```php $writer = new \PhpOffice\PhpSpreadsheet\Writer\Html($spreadsheet); $writer->save("05featuredemo.htm"); @@ -621,7 +653,7 @@ $writer->save("05featuredemo.htm"); HTML files can contain one or more worksheets. If you want to write all sheets into a single HTML file, use the following code: -``` php +```php $writer->writeAllSheets(); ``` @@ -630,7 +662,7 @@ $writer->writeAllSheets(); HTML files can contain one or more worksheets. Therefore, you can specify which sheet to write to HTML: -``` php +```php $writer->setSheetIndex(0); ``` @@ -639,19 +671,19 @@ $writer->setSheetIndex(0); There might be situations where you want to explicitly set the included images root. For example, instead of: - ``` html + ```html ``` You might want to see: -``` html +```html ``` You can use the following code to achieve this result: -``` php +```php $writer->setImagesRoot('http://www.example.com'); ``` @@ -661,7 +693,7 @@ By default, this writer pre-calculates all formulas in the spreadsheet. This can be slow on large spreadsheets, and maybe even unwanted. You can however disable formula pre-calculation: -``` php +```php $writer = new \PhpOffice\PhpSpreadsheet\Writer\Html($spreadsheet); $writer->setPreCalculateFormulas(false); @@ -686,14 +718,14 @@ Supported methods: Here's an example which retrieves all parts independently and merges them into a resulting HTML page: -``` php +```php $writer = new \PhpOffice\PhpSpreadsheet\Writer\Html($spreadsheet); $hdr = $writer->generateHTMLHeader(); $sty = $writer->generateStyles(false); // do not write $newstyle = << $sty -html { +body { background-color: yellow; } @@ -703,16 +735,22 @@ echo $writer->generateSheetData(); echo $writer->generateHTMLFooter(); ``` -#### Writing UTF-8 HTML files +#### Editing HTML during save via a callback -A HTML file can be marked as UTF-8 by writing a BOM file header. This -can be enabled by using the following code: +You can also add a callback function to edit the generated html +before saving. For example, you could change the gridlines +from a thin solid black line: ``` php +function changeGridlines(string $html): string +{ + return str_replace('{border: 1px solid black;}', + '{border: 2px dashed red;}', + $html); +} $writer = new \PhpOffice\PhpSpreadsheet\Writer\Html($spreadsheet); -$writer->setUseBOM(true); - -$writer->save("05featuredemo.htm"); +$writer->setEditHtmlCallback('changeGridlines'); +$writer->save($filename); ``` #### Decimal and thousands separators @@ -751,7 +789,7 @@ own circumstances. You can instantiate a writer with its specific name, like so: -``` php +```php $writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Mpdf'); ``` @@ -759,7 +797,7 @@ Or you can register which writer you are using with a more generic name, so you don't need to remember which library you chose, only that you want to write PDF files: -``` php +```php $class = \PhpOffice\PhpSpreadsheet\Writer\Pdf\Mpdf::class; \PhpOffice\PhpSpreadsheet\IOFactory::registerWriter('Pdf', $class); $writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Pdf'); @@ -767,7 +805,7 @@ $writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Pdf') Or you can instantiate directly the writer of your choice like so: -``` php +```php $writer = \PhpOffice\PhpSpreadsheet\Writer\Pdf\Mpdf($spreadsheet); ``` @@ -776,7 +814,7 @@ $writer = \PhpOffice\PhpSpreadsheet\Writer\Pdf\Mpdf($spreadsheet); If you need a custom implementation, or custom configuration, of a supported PDF library. You can extends the PDF library, and the PDF writer like so: -``` php +```php class My_Custom_TCPDF extends TCPDF { // ... @@ -802,7 +840,7 @@ class My_Custom_TCPDF_Writer extends \PhpOffice\PhpSpreadsheet\Writer\Pdf\Tcpdf Once you have identified the Renderer that you wish to use for PDF generation, you can write a .pdf file using the following code: -``` php +```php $writer = new \PhpOffice\PhpSpreadsheet\Writer\Pdf\Mpdf($spreadsheet); $writer->save("05featuredemo.pdf"); ``` @@ -815,7 +853,7 @@ first worksheet by default. PDF files can contain one or more worksheets. If you want to write all sheets into a single PDF file, use the following code: -``` php +```php $writer->writeAllSheets(); ``` @@ -824,7 +862,7 @@ $writer->writeAllSheets(); PDF files can contain one or more worksheets. Therefore, you can specify which sheet to write to PDF: -``` php +```php $writer->setSheetIndex(0); ``` @@ -834,13 +872,19 @@ By default, this writer pre-calculates all formulas in the spreadsheet. This can be slow on large spreadsheets, and maybe even unwanted. You can however disable formula pre-calculation: -``` php +```php $writer = new \PhpOffice\PhpSpreadsheet\Writer\Pdf\Mpdf($spreadsheet); $writer->setPreCalculateFormulas(false); $writer->save("05featuredemo.pdf"); ``` +#### Editing Pdf during save via a callback + +You can also add a callback function to edit the html used to +generate the Pdf before saving. +[See under Html](#editing-html-during-save-via-a-callback). + #### Decimal and thousands separators See section `\PhpOffice\PhpSpreadsheet\Writer\Csv` how to control the @@ -856,7 +900,7 @@ page setup properties, headers etc. Here is an example how to open a template file, fill in a couple of fields and save it again: -``` php +```php $spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load('template.xlsx'); $worksheet = $spreadsheet->getActiveSheet(); diff --git a/docs/topics/reading-files.md b/docs/topics/reading-files.md index 779082dc85..1451f2ab47 100644 --- a/docs/topics/reading-files.md +++ b/docs/topics/reading-files.md @@ -22,7 +22,7 @@ The simplest way to load a workbook file is to let PhpSpreadsheet's IO Factory identify the file type and load it, calling the static `load()` method of the `\PhpOffice\PhpSpreadsheet\IOFactory` class. -``` php +```php $inputFileName = './sampleData/example1.xls'; /** Load $inputFileName to a Spreadsheet Object **/ @@ -59,7 +59,7 @@ supported filetype by name. However, you may get unpredictable results if the file isn't of the right type (e.g. it is a CSV with an extension of .xls), although this type of exception should normally be trapped. -``` php +```php $inputFileName = './sampleData/example1.xls'; /** Create a new Xls Reader **/ @@ -81,7 +81,7 @@ Alternatively, you can use the IO Factory's `createReader()` method to instantiate the reader object for you, simply telling it the file type of the reader that you want instantiating. -``` php +```php $inputFileType = 'Xls'; // $inputFileType = 'Xlsx'; // $inputFileType = 'Xml'; @@ -104,7 +104,7 @@ If you're uncertain of the filetype, you can use the `IOFactory::identify()` method to identify the reader that you need, before using the `createReader()` method to instantiate the reader object. -``` php +```php $inputFileName = './sampleData/example1.xls'; /** Identify the type of $inputFileName **/ @@ -131,7 +131,7 @@ need any of the cell formatting information, then you can set the reader to read only the data values and any formulae from each cell using the `setReadDataOnly()` method. -``` php +```php $inputFileType = 'Xls'; $inputFileName = './sampleData/example1.xls'; @@ -176,7 +176,7 @@ in reading. To read a single sheet, you can pass that sheet name as a parameter to the `setLoadSheetsOnly()` method. -``` php +```php $inputFileType = 'Xls'; $inputFileName = './sampleData/example1.xls'; $sheetname = 'Data Sheet #2'; @@ -195,7 +195,7 @@ for a working example of this code. If you want to read more than just a single sheet, you can pass a list of sheet names as an array parameter to the `setLoadSheetsOnly()` method. -``` php +```php $inputFileType = 'Xls'; $inputFileName = './sampleData/example1.xls'; $sheetnames = ['Data Sheet #1','Data Sheet #3']; @@ -214,7 +214,7 @@ for a working example of this code. To reset this option to the default, you can call the `setLoadAllSheets()` method. -``` php +```php $inputFileType = 'Xls'; $inputFileName = './sampleData/example1.xls'; @@ -248,7 +248,7 @@ should be read by the loader. A read filter must implement the whether a workbook cell identified by those arguments should be read or not. -``` php +```php $inputFileType = 'Xls'; $inputFileName = './sampleData/example1.xls'; $sheetname = 'Data Sheet #3'; @@ -286,7 +286,7 @@ a very specific circumstance (when you only want cells in the range A1:E7 from your worksheet. A generic Read Filter would probably be more useful: -``` php +```php /** Define a Read Filter class implementing \PhpOffice\PhpSpreadsheet\Reader\IReadFilter */ class MyReadFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter { @@ -324,7 +324,7 @@ to read and process a large workbook in "chunks": an example of this usage might be when transferring data from an Excel worksheet to a database. -``` php +```php $inputFileType = 'Xls'; $inputFileName = './sampleData/example2.xls'; @@ -393,7 +393,7 @@ the `setSheetIndex()` method of the `$reader`, then use the `loadIntoExisting()` method rather than the `load()` method to actually read the file into that worksheet. -``` php +```php $inputFileType = 'Csv'; $inputFileNames = [ './sampleData/example1.csv', @@ -452,7 +452,7 @@ Class that we defined in [the above section](#reading-only-specific-columns-and- and the `setSheetIndex()` method of the `$reader`, we can split the CSV file across several individual worksheets. -``` php +```php $inputFileType = 'Csv'; $inputFileName = './sampleData/example2.csv'; @@ -523,7 +523,7 @@ cannot auto-detect, it will default to the comma. If this does not fit your use-case, you can manually specify a separator by using the `setDelimiter()` method. -``` php +```php $inputFileType = 'Csv'; $inputFileName = './sampleData/example1.tsv'; @@ -585,7 +585,7 @@ it encountered a hyperlink, or HTML markup within a CSV file. So using a Value Binder allows a great deal more flexibility in the loader logic when reading unformatted text files. -``` php +```php /** Tell PhpSpreadsheet that we want to use the Advanced Value Binder **/ \PhpOffice\PhpSpreadsheet\Cell\Cell::setValueBinder( new \PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder() ); @@ -619,7 +619,7 @@ manner. The PhpSpreadsheet Readers throw a `\PhpOffice\PhpSpreadsheet\Reader\Exception`. -``` php +```php $inputFileName = './sampleData/example-1.xls'; try { @@ -646,7 +646,7 @@ whole file. The `listWorksheetNames()` method returns a simple array listing each worksheet name within the workbook: -``` php +```php $reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader($inputFileType); $worksheetNames = $reader->listWorksheetNames($inputFileName); @@ -667,7 +667,7 @@ for a working example of this code. The `listWorksheetInfo()` method returns a nested array, with each entry listing the name and dimensions for a worksheet: -``` php +```php $reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader($inputFileType); $worksheetData = $reader->listWorksheetInfo($inputFileName); diff --git a/docs/topics/recipes.md b/docs/topics/recipes.md index b0956b6e42..bbdc29a818 100644 --- a/docs/topics/recipes.md +++ b/docs/topics/recipes.md @@ -20,7 +20,7 @@ metadata to search for a specific document in its document lists. Setting spreadsheet metadata is done as follows: -``` php +```php $spreadsheet->getProperties() ->setCreator("Maarten Balliauw") ->setLastModifiedBy("Maarten Balliauw") @@ -38,13 +38,13 @@ $spreadsheet->getProperties() The following line of code sets the active sheet index to the first sheet: -``` php +```php $spreadsheet->setActiveSheetIndex(0); ``` You can also set the active sheet by its name/title -``` php +```php $spreadsheet->setActiveSheetIndexByName('DataSheet') ``` @@ -68,7 +68,7 @@ UST. Writing a date value in a cell consists of 2 lines of code. Select the method that suits you the best. Here are some examples: -``` php +```php // MySQL-like timestamp '2008-12-31' or date string \PhpOffice\PhpSpreadsheet\Cell\Cell::setValueBinder( new \PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder() ); @@ -136,14 +136,14 @@ The following line of code writes the formula formula must start with `=` to make PhpSpreadsheet recognise this as a formula. -``` php +```php $spreadsheet->getActiveSheet()->setCellValue('B8','=IF(C4>500,"profit","loss")'); ``` If you want to write a string beginning with an `=` character to a cell, then you should use the `setCellValueExplicit()` method. -``` php +```php $spreadsheet->getActiveSheet() ->setCellValueExplicit( 'B8', @@ -154,14 +154,14 @@ $spreadsheet->getActiveSheet() A cell's formula can be read again using the following line of code: -``` php +```php $formula = $spreadsheet->getActiveSheet()->getCell('B8')->getValue(); ``` If you need the calculated value of a cell, use the following code. This is further explained in [the calculation engine](./calculation-engine.md). -``` php +```php $value = $spreadsheet->getActiveSheet()->getCell('B8')->getCalculatedValue(); ``` @@ -171,7 +171,7 @@ Some localisation elements have been included in PhpSpreadsheet. You can set a locale by changing the settings. To set the locale to Russian you would use: -``` php +```php $locale = 'ru'; $validLocale = \PhpOffice\PhpSpreadsheet\Settings::setLocale($locale); if (!$validLocale) { @@ -185,7 +185,7 @@ will return an error, and English settings will be used throughout. Once you have set a locale, you can translate a formula from its internal English coding. -``` php +```php $formula = $spreadsheet->getActiveSheet()->getCell('B8')->getValue(); $translatedFormula = \PhpOffice\PhpSpreadsheet\Calculation\Calculation::getInstance()->_translateFormulaToLocale($formula); ``` @@ -194,7 +194,7 @@ You can also create a formula using the function names and argument separators appropriate to the defined locale; then translate it to English before setting the cell value: -``` php +```php $formula = '=ДНЕЙ360(ДАТА(2010;2;5);ДАТА(2010;12;31);ИСТИНА)'; $internalFormula = \PhpOffice\PhpSpreadsheet\Calculation\Calculation::getInstance()->translateFormulaToEnglish($formula); $spreadsheet->getActiveSheet()->setCellValue('B8',$internalFormula); @@ -232,7 +232,7 @@ the cell. Here is how to achieve this in PhpSpreadsheet: -``` php +```php $spreadsheet->getActiveSheet()->getCell('A1')->setValue("hello\nworld"); $spreadsheet->getActiveSheet()->getStyle('A1')->getAlignment()->setWrapText(true); ``` @@ -247,7 +247,7 @@ AdvancedValuebinder.php automatically turns on "wrap text" for the cell when it sees a newline character in a string that you are inserting in a cell. Just like Microsoft Office Excel. Try this: -``` php +```php \PhpOffice\PhpSpreadsheet\Cell\Cell::setValueBinder( new \PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder() ); $spreadsheet->getActiveSheet()->getCell('A1')->setValue("hello\nworld"); @@ -261,7 +261,7 @@ You can set a cell's datatype explicitly by using the cell's setValueExplicit method, or the setCellValueExplicit method of a worksheet. Here's an example: -``` php +```php $spreadsheet->getActiveSheet()->getCell('A1') ->setValueExplicit( '25', @@ -273,7 +273,7 @@ $spreadsheet->getActiveSheet()->getCell('A1') You can make a cell a clickable URL by setting its hyperlink property: -``` php +```php $spreadsheet->getActiveSheet()->setCellValue('E26', 'www.phpexcel.net'); $spreadsheet->getActiveSheet()->getCell('E26')->getHyperlink()->setUrl('https://www.example.com'); ``` @@ -281,7 +281,7 @@ $spreadsheet->getActiveSheet()->getCell('E26')->getHyperlink()->setUrl('https:// If you want to make a hyperlink to another worksheet/cell, use the following code: -``` php +```php $spreadsheet->getActiveSheet()->setCellValue('E26', 'www.phpexcel.net'); $spreadsheet->getActiveSheet()->getCell('E26')->getHyperlink()->setUrl("sheet://'Sheetname'!A1"); ``` @@ -293,7 +293,7 @@ $spreadsheet->getActiveSheet()->getCell('E26')->getHyperlink()->setUrl("sheet:// Setting a worksheet's page orientation and size can be done using the following lines of code: -``` php +```php $spreadsheet->getActiveSheet()->getPageSetup() ->setOrientation(\PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_LANDSCAPE); $spreadsheet->getActiveSheet()->getPageSetup() @@ -324,7 +324,7 @@ setFitToHeight(...) | 1 | setFitToPage(TRUE) | value 0 mean Here is how to fit to 1 page wide by infinite pages tall: -``` php +```php $spreadsheet->getActiveSheet()->getPageSetup()->setFitToWidth(1); $spreadsheet->getActiveSheet()->getPageSetup()->setFitToHeight(0); ``` @@ -340,7 +340,7 @@ the initial values. To set page margins for a worksheet, use this code: -``` php +```php $spreadsheet->getActiveSheet()->getPageMargins()->setTop(1); $spreadsheet->getActiveSheet()->getPageMargins()->setRight(0.75); $spreadsheet->getActiveSheet()->getPageMargins()->setLeft(0.75); @@ -356,7 +356,7 @@ Note that the margin values are specified in inches. To center a page horizontally/vertically, you can use the following code: -``` php +```php $spreadsheet->getActiveSheet()->getPageSetup()->setHorizontalCentered(true); $spreadsheet->getActiveSheet()->getPageSetup()->setVerticalCentered(false); ``` @@ -366,7 +366,7 @@ $spreadsheet->getActiveSheet()->getPageSetup()->setVerticalCentered(false); Setting a worksheet's print header and footer can be done using the following lines of code: -``` php +```php $spreadsheet->getActiveSheet()->getHeaderFooter() ->setOddHeader('&C&HPlease treat this document as confidential!'); $spreadsheet->getActiveSheet()->getHeaderFooter() @@ -460,13 +460,13 @@ $spreadsheet->getActiveSheet()->getHeaderFooter()->addImage($drawing, \PhpOffice To set a print break, use the following code, which sets a row break on row 10. -``` php +```php $spreadsheet->getActiveSheet()->setBreak('A10', \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_ROW); ``` The following line of code sets a print break on column D: -``` php +```php $spreadsheet->getActiveSheet()->setBreak('D10', \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_COLUMN); ``` @@ -484,7 +484,7 @@ PhpSpreadsheet can repeat specific rows/cells at top/left of a page. The following code is an example of how to repeat row 1 to 5 on each printed page of a specific worksheet: -``` php +```php $spreadsheet->getActiveSheet()->getPageSetup()->setRowsToRepeatAtTopByStartAndEnd(1, 5); ``` @@ -492,13 +492,13 @@ $spreadsheet->getActiveSheet()->getPageSetup()->setRowsToRepeatAtTopByStartAndEn To specify a worksheet's printing area, use the following code: -``` php +```php $spreadsheet->getActiveSheet()->getPageSetup()->setPrintArea('A1:E5'); ``` There can also be multiple printing areas in a single worksheet: -``` php +```php $spreadsheet->getActiveSheet()->getPageSetup()->setPrintArea('A1:E5,G4:M20'); ``` @@ -511,7 +511,7 @@ For example, one can set the foreground colour of a cell to red, aligned to the right, and the border to black and thick border style. Let's do that on cell B2: -``` php +```php $spreadsheet->getActiveSheet()->getStyle('B2') ->getFont()->getColor()->setARGB(\PhpOffice\PhpSpreadsheet\Style\Color::COLOR_RED); $spreadsheet->getActiveSheet()->getStyle('B2') @@ -533,7 +533,7 @@ $spreadsheet->getActiveSheet()->getStyle('B2') `getStyle()` also accepts a cell range as a parameter. For example, you can set a red background color on a range of cells: -``` php +```php $spreadsheet->getActiveSheet()->getStyle('B3:B7')->getFill() ->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID) ->getStartColor()->setARGB('FFFF0000'); @@ -548,7 +548,7 @@ There is also an alternative manner to set styles. The following code sets a cell's style to font bold, alignment right, top border thin and a gradient fill: -``` php +```php $styleArray = [ 'font' => [ 'bold' => true, @@ -578,7 +578,7 @@ $spreadsheet->getActiveSheet()->getStyle('A3')->applyFromArray($styleArray); Or with a range of cells: -``` php +```php $spreadsheet->getActiveSheet()->getStyle('B3:B7')->applyFromArray($styleArray); ``` @@ -587,6 +587,13 @@ execution whenever you are setting more than one style property. But the difference may barely be measurable unless you have many different styles in your workbook. +You can perform the opposite function, exporting a Style as an array, +as follows: + +``` php +$styleArray = $spreadsheet->getActiveSheet()->getStyle('A3')->exportArray(); +``` + ### Number formats You often want to format numbers in Excel. For example you may want a @@ -602,7 +609,7 @@ number format code unless you need a custom number format. In PhpSpreadsheet, you can also apply various predefined number formats. Example: -``` php +```php $spreadsheet->getActiveSheet()->getStyle('A1')->getNumberFormat() ->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_NUMBER_COMMA_SEPARATED1); ``` @@ -614,7 +621,7 @@ up as 1.587,20) You can achieve exactly the same as the above by using this: -``` php +```php $spreadsheet->getActiveSheet()->getStyle('A1')->getNumberFormat() ->setFormatCode('#,##0.00'); ``` @@ -623,7 +630,7 @@ In Microsoft Office Excel, as well as in PhpSpreadsheet, you will have to interact with raw number format codes whenever you need some special custom number format. Example: -``` php +```php $spreadsheet->getActiveSheet()->getStyle('A1')->getNumberFormat() ->setFormatCode('[Blue][>=3000]$#,##0;[Red][<0]$#,##0;$#,##0'); ``` @@ -631,7 +638,7 @@ $spreadsheet->getActiveSheet()->getStyle('A1')->getNumberFormat() Another example is when you want numbers zero-padded with leading zeros to a fixed length: -``` php +```php $spreadsheet->getActiveSheet()->getCell('A1')->setValue(19); $spreadsheet->getActiveSheet()->getStyle('A1')->getNumberFormat() ->setFormatCode('0000'); // will show as 0019 in Excel @@ -646,7 +653,7 @@ The readers shipped with PhpSpreadsheet come to the rescue. Load your template workbook using e.g. Xlsx reader to reveal the number format code. Example how read a number format code for cell A1: -``` php +```php $reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx'); $spreadsheet = $reader->load('template.xlsx'); var_dump($spreadsheet->getActiveSheet()->getStyle('A1')->getNumberFormat()->getFormatCode()); @@ -661,14 +668,14 @@ code in *xl/styles.xml*. Let's set vertical alignment to the top for cells A1:D4 -``` php +```php $spreadsheet->getActiveSheet()->getStyle('A1:D4') ->getAlignment()->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_TOP); ``` Here is how to achieve wrap text: -``` php +```php $spreadsheet->getActiveSheet()->getStyle('A1:D4') ->getAlignment()->setWrapText(true); ``` @@ -678,7 +685,7 @@ $spreadsheet->getActiveSheet()->getStyle('A1:D4') It is possible to set the default style of a workbook. Let's set the default font to Arial size 8: -``` php +```php $spreadsheet->getDefaultStyle()->getFont()->setName('Arial'); $spreadsheet->getDefaultStyle()->getFont()->setSize(8); ``` @@ -689,7 +696,7 @@ In PhpSpreadsheet it is easy to apply various borders on a rectangular selection. Here is how to apply a thick red border outline around cells B2:G8. -``` php +```php $styleArray = [ 'borders' => [ 'outline' => [ @@ -753,69 +760,74 @@ another style array. Array key | Maps to property -------------|------------------- -fill | getFill() -font | getFont() -borders | getBorders() -alignment | getAlignment() -numberFormat | getNumberFormat() -protection | getProtection() +alignment | setAlignment() +borders | setBorders() +fill | setFill() +font | setFont() +numberFormat | setNumberFormat() +protection | setProtection() +quotePrefix | setQuotePrefix() -**\PhpOffice\PhpSpreadsheet\Style\Fill** +**\PhpOffice\PhpSpreadsheet\Style\Alignment** -Array key | Maps to property ------------|------------------- -fillType | setFillType() -rotation | setRotation() -startColor | getStartColor() -endColor | getEndColor() -color | getStartColor() +Array key | Maps to property +------------|------------------- +horizontal | setHorizontal() +indent | setIndent() +readOrder | setReadOrder() +shrinkToFit | setShrinkToFit() +textRotation| setTextRotation() +vertical | setVertical() +wrapText | setWrapText() -**\PhpOffice\PhpSpreadsheet\Style\Font** +**\PhpOffice\PhpSpreadsheet\Style\Border** Array key | Maps to property ------------|------------------- -name | setName() -bold | setBold() -italic | setItalic() -underline | setUnderline() -strikethrough | setStrikethrough() -color | getColor() -size | setSize() -superscript | setSuperscript() -subscript | setSubscript() +borderStyle | setBorderStyle() +color | setColor() **\PhpOffice\PhpSpreadsheet\Style\Borders** Array key | Maps to property ------------------|------------------- -allBorders | getLeft(); getRight(); getTop(); getBottom() -left | getLeft() -right | getRight() -top | getTop() -bottom | getBottom() -diagonal | getDiagonal() -vertical | getVertical() -horizontal | getHorizontal() +allBorders | setLeft(); setRight(); setTop(); setBottom() +bottom | setBottom() +diagonal | setDiagonal() diagonalDirection | setDiagonalDirection() -outline | setOutline() +left | setLeft() +right | setRight() +top | setTop() -**\PhpOffice\PhpSpreadsheet\Style\Border** +**\PhpOffice\PhpSpreadsheet\Style\Color** Array key | Maps to property ------------|------------------- -borderStyle | setBorderStyle() -color | getColor() +argb | setARGB() -**\PhpOffice\PhpSpreadsheet\Style\Alignment** +**\PhpOffice\PhpSpreadsheet\Style\Fill** + +Array key | Maps to property +-----------|------------------- +color | getStartColor() +endColor | getEndColor() +fillType | setFillType() +rotation | setRotation() +startColor | getStartColor() + +**\PhpOffice\PhpSpreadsheet\Style\Font** Array key | Maps to property ------------|------------------- -horizontal | setHorizontal() -vertical | setVertical() -textRotation| setTextRotation() -wrapText | setWrapText() -shrinkToFit | setShrinkToFit() -indent | setIndent() +bold | setBold() +color | getColor() +italic | setItalic() +name | setName() +size | setSize() +strikethrough | setStrikethrough() +subscript | setSubscript() +superscript | setSuperscript() +underline | setUnderline() **\PhpOffice\PhpSpreadsheet\Style\NumberFormat** @@ -839,7 +851,7 @@ is below zero, and to green if its value is zero or more. One can set a conditional style ruleset to a cell using the following code: -``` php +```php $conditional1 = new \PhpOffice\PhpSpreadsheet\Style\Conditional(); $conditional1->setConditionType(\PhpOffice\PhpSpreadsheet\Style\Conditional::CONDITION_CELLIS); $conditional1->setOperatorType(\PhpOffice\PhpSpreadsheet\Style\Conditional::OPERATOR_LESSTHAN); @@ -864,7 +876,7 @@ $spreadsheet->getActiveSheet()->getStyle('B2')->setConditionalStyles($conditiona If you want to copy the ruleset to other cells, you can duplicate the style object: -``` php +```php $spreadsheet->getActiveSheet() ->duplicateStyle( $spreadsheet->getActiveSheet()->getStyle('B2'), @@ -872,12 +884,50 @@ $spreadsheet->getActiveSheet() ); ``` +### DataBar of Conditional formatting +The basics are the same as conditional formatting. +Additional DataBar object to conditional formatting. + +For example, the following code will result in the conditional formatting shown in the image. +```php +$conditional = new Conditional(); +$conditional->setConditionType(Conditional::CONDITION_DATABAR); +$conditional->setDataBar(new ConditionalDataBar()); +$conditional->getDataBar() + ->setMinimumConditionalFormatValueObject(new ConditionalFormatValueObject('num', '2')) + ->setMaximumConditionalFormatValueObject(new ConditionalFormatValueObject('max')) + ->setColor('FFFF555A'); +$ext = $conditional + ->getDataBar() + ->setConditionalFormattingRuleExt(new ConditionalFormattingRuleExtension()) + ->getConditionalFormattingRuleExt(); + +$ext->setCfRule('dataBar'); +$ext->setSqref('A1:A5'); // target CellCoordinates +$ext->setDataBarExt(new ConditionalDataBarExtension()); +$ext->getDataBarExt() + ->setMinimumConditionalFormatValueObject(new ConditionalFormatValueObject('num', '2')) + ->setMaximumConditionalFormatValueObject(new ConditionalFormatValueObject('autoMax')) + ->setMinLength(0) + ->setMaxLength(100) + ->setBorder(true) + ->setDirection('rightToLeft') + ->setNegativeBarBorderColorSameAsPositive(false) + ->setBorderColor('FFFF555A') + ->setNegativeFillColor('FFFF0000') + ->setNegativeBorderColor('FFFF0000') + ->setAxisColor('FF000000'); + +``` + +![10-databar-of-conditional-formatting.png](./images/10-databar-of-conditional-formatting.png) + ## Add a comment to a cell To add a comment to a cell, use the following code. The example below adds a comment to cell E11: -``` php +```php $spreadsheet->getActiveSheet() ->getComment('E11') ->setAuthor('Mark Baker'); @@ -899,7 +949,7 @@ $spreadsheet->getActiveSheet() To apply an autofilter to a range of cells, use the following code: -``` php +```php $spreadsheet->getActiveSheet()->setAutoFilter('A1:C9'); ``` @@ -919,45 +969,85 @@ disallow inserting rows on a specific sheet, disallow sorting, ... - Cell: offers the option to lock/unlock a cell as well as show/hide the internal formula. +**Make sure you enable worksheet protection if you need any of the +worksheet or cell protection features!** This can be done using the following +code: + +```php +$spreadsheet->getActiveSheet()->getProtection()->setSheet(true); +``` + +### Document + An example on setting document security: -``` php -$spreadsheet->getSecurity()->setLockWindows(true); -$spreadsheet->getSecurity()->setLockStructure(true); -$spreadsheet->getSecurity()->setWorkbookPassword("PhpSpreadsheet"); +```php +$security = $spreadsheet->getSecurity(); +$security->setLockWindows(true); +$security->setLockStructure(true); +$security->setWorkbookPassword("PhpSpreadsheet"); ``` +### Worksheet + An example on setting worksheet security: -``` php -$spreadsheet->getActiveSheet() - ->getProtection()->setPassword('PhpSpreadsheet'); -$spreadsheet->getActiveSheet() - ->getProtection()->setSheet(true); -$spreadsheet->getActiveSheet() - ->getProtection()->setSort(true); -$spreadsheet->getActiveSheet() - ->getProtection()->setInsertRows(true); -$spreadsheet->getActiveSheet() - ->getProtection()->setFormatCells(true); +```php +$protection = $spreadsheet->getActiveSheet()->getProtection(); +$protection->setPassword('PhpSpreadsheet'); +$protection->setSheet(true); +$protection->setSort(true); +$protection->setInsertRows(true); +$protection->setFormatCells(true); ``` +If writing Xlsx files you can specify the algorithm used to hash the password +before calling `setPassword()` like so: + +```php +$protection = $spreadsheet->getActiveSheet()->getProtection(); +$protection->setAlgorithm(Protection::ALGORITHM_SHA_512); +$protection->setSpinCount(20000); +$protection->setPassword('PhpSpreadsheet'); +``` + +The salt should **not** be set manually and will be automatically generated +when setting a new password. + +### Cell + An example on setting cell security: -``` php +```php $spreadsheet->getActiveSheet()->getStyle('B1') ->getProtection() ->setLocked(\PhpOffice\PhpSpreadsheet\Style\Protection::PROTECTION_UNPROTECTED); ``` -**Make sure you enable worksheet protection if you need any of the -worksheet protection features!** This can be done using the following -code: +## Reading protected spreadsheet -``` php -$spreadsheet->getActiveSheet()->getProtection()->setSheet(true); +Spreadsheets that are protected as described above can always be read by +PhpSpreadsheet. There is no need to know the password or do anything special in +order to read a protected file. + +However if you need to implement a password verification mechanism, you can use the +following helper method: + + +```php +$protection = $spreadsheet->getActiveSheet()->getProtection(); +$allowed = $protection->verify('my password'); + +if ($allowed) { + doSomething(); +} else { + throw new Exception('Incorrect password'); +} ``` +If you need to completely prevent reading a file by any tool, including PhpSpreadsheet, +then you are looking for "encryption", not "protection". + ## Setting data validation on a cell Data validation is a powerful feature of Xlsx. It allows to specify an @@ -968,7 +1058,7 @@ filter can be a range (i.e. value must be between 0 and 10), a list The following piece of code only allows numbers between 10 and 20 to be entered in cell B3: -``` php +```php $validation = $spreadsheet->getActiveSheet()->getCell('B3') ->getDataValidation(); $validation->setType( \PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_WHOLE ); @@ -987,7 +1077,7 @@ $validation->setFormula2(20); The following piece of code only allows an item picked from a list of data to be entered in cell B5: -``` php +```php $validation = $spreadsheet->getActiveSheet()->getCell('B5') ->getDataValidation(); $validation->setType( \PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_LIST ); @@ -1017,7 +1107,7 @@ the item values themselves can contain the comma `,` character itself. If you need data validation on multiple cells, one can clone the ruleset: -``` php +```php $spreadsheet->getActiveSheet()->getCell('B8')->setDataValidation(clone $validation); ``` @@ -1025,7 +1115,7 @@ $spreadsheet->getActiveSheet()->getCell('B8')->setDataValidation(clone $validati A column's width can be set using the following code: -``` php +```php $spreadsheet->getActiveSheet()->getColumnDimension('D')->setWidth(12); ``` @@ -1033,7 +1123,7 @@ If you want PhpSpreadsheet to perform an automatic width calculation, use the following code. PhpSpreadsheet will approximate the column with to the width of the widest column value. -``` php +```php $spreadsheet->getActiveSheet()->getColumnDimension('B')->setAutoSize(true); ``` @@ -1070,7 +1160,7 @@ To set a worksheet's column visibility, you can use the following code. The first line explicitly shows the column C, the second line hides column D. -``` php +```php $spreadsheet->getActiveSheet()->getColumnDimension('C')->setVisible(true); $spreadsheet->getActiveSheet()->getColumnDimension('D')->setVisible(false); ``` @@ -1079,7 +1169,7 @@ $spreadsheet->getActiveSheet()->getColumnDimension('D')->setVisible(false); To group/outline a column, you can use the following code: -``` php +```php $spreadsheet->getActiveSheet()->getColumnDimension('E')->setOutlineLevel(1); ``` @@ -1087,7 +1177,7 @@ You can also collapse the column. Note that you should also set the column invisible, otherwise the collapse will not be visible in Excel 2007. -``` php +```php $spreadsheet->getActiveSheet()->getColumnDimension('E')->setCollapsed(true); $spreadsheet->getActiveSheet()->getColumnDimension('E')->setVisible(false); ``` @@ -1098,7 +1188,7 @@ on collapsing. You can instruct PhpSpreadsheet to add a summary to the right (default), or to the left. The following code adds the summary to the left: -``` php +```php $spreadsheet->getActiveSheet()->setShowSummaryRight(false); ``` @@ -1106,7 +1196,7 @@ $spreadsheet->getActiveSheet()->setShowSummaryRight(false); A row's height can be set using the following code: -``` php +```php $spreadsheet->getActiveSheet()->getRowDimension('10')->setRowHeight(100); ``` @@ -1119,7 +1209,7 @@ of values is between 0 and 409 pts, where 0 pts is a hidden row. To set a worksheet''s row visibility, you can use the following code. The following example hides row number 10. -``` php +```php $spreadsheet->getActiveSheet()->getRowDimension('10')->setVisible(false); ``` @@ -1131,21 +1221,21 @@ AutoFilter range if you save the file. To group/outline a row, you can use the following code: -``` php +```php $spreadsheet->getActiveSheet()->getRowDimension('5')->setOutlineLevel(1); ``` You can also collapse the row. Note that you should also set the row invisible, otherwise the collapse will not be visible in Excel 2007. -``` php +```php $spreadsheet->getActiveSheet()->getRowDimension('5')->setCollapsed(true); $spreadsheet->getActiveSheet()->getRowDimension('5')->setVisible(false); ``` Here's an example which collapses rows 50 to 80: -``` php +```php for ($i = 51; $i <= 80; $i++) { $spreadsheet->getActiveSheet()->setCellValue('A' . $i, "FName $i"); $spreadsheet->getActiveSheet()->setCellValue('B' . $i, "LName $i"); @@ -1162,7 +1252,7 @@ $spreadsheet->getActiveSheet()->getRowDimension(81)->setCollapsed(true); You can instruct PhpSpreadsheet to add a summary below the collapsible rows (default), or above. The following code adds the summary above: -``` php +```php $spreadsheet->getActiveSheet()->setShowSummaryBelow(false); ``` @@ -1172,13 +1262,13 @@ If you have a big piece of data you want to display in a worksheet, you can merge two or more cells together, to become one cell. This can be done using the following code: -``` php +```php $spreadsheet->getActiveSheet()->mergeCells('A18:E22'); ``` Removing a merge can be done using the unmergeCells method: -``` php +```php $spreadsheet->getActiveSheet()->unmergeCells('A18:E22'); ``` @@ -1187,7 +1277,7 @@ $spreadsheet->getActiveSheet()->unmergeCells('A18:E22'); You can insert/remove rows/columns at a specific position. The following code inserts 2 new rows, right before row 7: -``` php +```php $spreadsheet->getActiveSheet()->insertNewRowBefore(7, 2); ``` @@ -1198,7 +1288,7 @@ to a worksheet. Therefore, you must first instantiate a new `\PhpOffice\PhpSpreadsheet\Worksheet\Drawing`, and assign its properties a meaningful value: -``` php +```php $drawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing(); $drawing->setName('Logo'); $drawing->setDescription('Logo'); @@ -1210,13 +1300,13 @@ To add the above drawing to the worksheet, use the following snippet of code. PhpSpreadsheet creates the link between the drawing and the worksheet: -``` php +```php $drawing->setWorksheet($spreadsheet->getActiveSheet()); ``` You can set numerous properties on a drawing, here are some examples: -``` php +```php $drawing->setName('Paid'); $drawing->setDescription('Paid'); $drawing->setPath('./images/paid.png'); @@ -1230,7 +1320,7 @@ $drawing->getShadow()->setDirection(45); You can also add images created using GD functions without needing to save them to disk first as In-Memory drawings. -``` php +```php // Use GD to create an in-memory image $gdImage = @imagecreatetruecolor(120, 20) or die('Cannot Initialize new GD image stream'); $textColor = imagecolorallocate($gdImage, 255, 255, 255); @@ -1258,7 +1348,7 @@ that has been loaded, and save them as individual image files to disk. The following code extracts images from the current active worksheet, and writes each as a separate file. -``` php +```php $i = 0; foreach ($spreadsheet->getActiveSheet()->getDrawingCollection() as $drawing) { if ($drawing instanceof \PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing) { @@ -1303,7 +1393,7 @@ creates the following rich text string: > This invoice is ***payable within thirty days after the end of the > month*** unless specified otherwise on the invoice. -``` php +```php $richText = new \PhpOffice\PhpSpreadsheet\RichText\RichText(); $richText->createText('This invoice is '); $payable = $richText->createTextRun('payable within thirty days after the end of the month'); @@ -1319,7 +1409,7 @@ $spreadsheet->getActiveSheet()->getCell('A18')->setValue($richText); PhpSpreadsheet supports the definition of named ranges. These can be defined using the following code: -``` php +```php // Add some data $spreadsheet->setActiveSheetIndex(0); $spreadsheet->getActiveSheet()->setCellValue('A1', 'Firstname:'); @@ -1328,14 +1418,73 @@ $spreadsheet->getActiveSheet()->setCellValue('B1', 'Maarten'); $spreadsheet->getActiveSheet()->setCellValue('B2', 'Balliauw'); // Define named ranges -$spreadsheet->addNamedRange( new \PhpOffice\PhpSpreadsheet\NamedRange('PersonFN', $spreadsheet->getActiveSheet(), 'B1') ); -$spreadsheet->addNamedRange( new \PhpOffice\PhpSpreadsheet\NamedRange('PersonLN', $spreadsheet->getActiveSheet(), 'B2') ); +$spreadsheet->addNamedRange( new \PhpOffice\PhpSpreadsheet\NamedRange('PersonFN', $spreadsheet->getActiveSheet(), '$B$1')); +$spreadsheet->addNamedRange( new \PhpOffice\PhpSpreadsheet\NamedRange('PersonLN', $spreadsheet->getActiveSheet(), '$B$2')); ``` Optionally, a fourth parameter can be passed defining the named range local (i.e. only usable on the current worksheet). Named ranges are global by default. +## Define a named formula + +In addition to named ranges, PhpSpreadsheet also supports the definition of named formulae. These can be +defined using the following code: + +```php +// Add some data +$spreadsheet->setActiveSheetIndex(0); +$worksheet = $spreadsheet->getActiveSheet(); +$worksheet + ->setCellValue('A1', 'Product') + ->setCellValue('B1', 'Quantity') + ->setCellValue('C1', 'Unit Price') + ->setCellValue('D1', 'Price') + ->setCellValue('E1', 'VAT') + ->setCellValue('F1', 'Total'); + +// Define named formula +$spreadsheet->addNamedFormula( new \PhpOffice\PhpSpreadsheet\NamedFormula('GERMAN_VAT_RATE', $worksheet, '=16.0%')); +$spreadsheet->addNamedFormula( new \PhpOffice\PhpSpreadsheet\NamedFormula('CALCULATED_PRICE', $worksheet, '=$B1*$C1')); +$spreadsheet->addNamedFormula( new \PhpOffice\PhpSpreadsheet\NamedFormula('GERMAN_VAT', $worksheet, '=$D1*GERMAN_VAT_RATE')); +$spreadsheet->addNamedFormula( new \PhpOffice\PhpSpreadsheet\NamedFormula('TOTAL_INCLUDING_VAT', $worksheet, '=$D1+$E1')); + +$worksheet + ->setCellValue('A2', 'Advanced Web Application Architecture') + ->setCellValue('B2', 2) + ->setCellValue('C2', 23.0) + ->setCellValue('D2', '=CALCULATED_PRICE') + ->setCellValue('E2', '=GERMAN_VAT') + ->setCellValue('F2', '=TOTAL_INCLUDING_VAT'); +$spreadsheet->getActiveSheet() + ->setCellValue('A3', 'Object Design Style Guide') + ->setCellValue('B3', 5) + ->setCellValue('C3', 12.0) + ->setCellValue('D3', '=CALCULATED_PRICE') + ->setCellValue('E3', '=GERMAN_VAT') + ->setCellValue('F3', '=TOTAL_INCLUDING_VAT'); +$spreadsheet->getActiveSheet() + ->setCellValue('A4', 'PHP For the Web') + ->setCellValue('B4', 3) + ->setCellValue('C4', 10.0) + ->setCellValue('D4', '=CALCULATED_PRICE') + ->setCellValue('E4', '=GERMAN_VAT') + ->setCellValue('F4', '=TOTAL_INCLUDING_VAT'); + +// Use a relative named range to provide the totals for rows 2-4 +$spreadsheet->addNamedRange( new \PhpOffice\PhpSpreadsheet\NamedRange('COLUMN_TOTAL', $worksheet, '=A$2:A$4') ); + +$spreadsheet->getActiveSheet() + ->setCellValue('B6', '=SUBTOTAL(109,COLUMN_TOTAL)') + ->setCellValue('D6', '=SUBTOTAL(109,COLUMN_TOTAL)') + ->setCellValue('E6', '=SUBTOTAL(109,COLUMN_TOTAL)') + ->setCellValue('F6', '=SUBTOTAL(109,COLUMN_TOTAL)'); +``` + +As with named ranges, an optional fourth parameter can be passed defining the named formula +scope as local (i.e. only usable on the specified worksheet). Otherwise, named formulae are +global by default. + ## Redirect output to a client's web browser Sometimes, one really wants to output a file to a client''s browser, @@ -1362,7 +1511,7 @@ your document is needed, it is recommended not to use `php://output`. Example of a script redirecting an Excel 2007 file to the client's browser: -``` php +```php /* Here there will be some code where you create $spreadsheet */ // redirect output to client browser @@ -1376,7 +1525,7 @@ $writer->save('php://output'); Example of a script redirecting an Xls file to the client's browser: -``` php +```php /* Here there will be some code where you create $spreadsheet */ // redirect output to client browser @@ -1404,7 +1553,7 @@ at the client browser, and/or that headers cannot be set by PHP Default column width can be set using the following code: -``` php +```php $spreadsheet->getActiveSheet()->getDefaultColumnDimension()->setWidth(12); ``` @@ -1412,7 +1561,7 @@ $spreadsheet->getActiveSheet()->getDefaultColumnDimension()->setWidth(12); Default row height can be set using the following code: -``` php +```php $spreadsheet->getActiveSheet()->getDefaultRowDimension()->setRowHeight(15); ``` @@ -1425,7 +1574,7 @@ file to a temporary location. Here''s an example which generates an image in memory and adds it to the active worksheet: -``` php +```php // Generate an image $gdImage = @imagecreatetruecolor(120, 20) or die('Cannot Initialize new GD image stream'); $textColor = imagecolorallocate($gdImage, 255, 255, 255); @@ -1446,7 +1595,7 @@ $drawing->setWorksheet($spreadsheet->getActiveSheet()); To set a worksheet's zoom level, the following code can be used: -``` php +```php $spreadsheet->getActiveSheet()->getSheetView()->setZoomScale(75); ``` @@ -1457,7 +1606,7 @@ Note that zoom level should be in range 10 - 400. Sometimes you want to set a color for sheet tab. For example you can have a red sheet tab: -``` php +```php $worksheet->getTabColor()->setRGB('FF0000'); ``` @@ -1465,7 +1614,7 @@ $worksheet->getTabColor()->setRGB('FF0000'); If you need to create more worksheets in the workbook, here is how: -``` php +```php $worksheet1 = $spreadsheet->createSheet(); $worksheet1->setTitle('Another sheet'); ``` @@ -1478,7 +1627,7 @@ worksheets in the workbook. Set a worksheet to be **hidden** using this code: -``` php +```php $spreadsheet->getActiveSheet() ->setSheetState(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::SHEETSTATE_HIDDEN); ``` @@ -1500,7 +1649,7 @@ Worksheets can be set individually whether column `A` should start at left or right side. Default is left. Here is how to set columns from right-to-left. -``` php +```php // right-to-left worksheet $spreadsheet->getActiveSheet()->setRightToLeft(true); ``` diff --git a/docs/topics/settings.md b/docs/topics/settings.md index a9aae9f923..d28a9996dd 100644 --- a/docs/topics/settings.md +++ b/docs/topics/settings.md @@ -13,7 +13,7 @@ Read more about [memory saving](./memory_saving.md). To enable cell caching, you must provide your own implementation of cache like so: -``` php +```php $cache = new MyCustomPsr16Implementation(); \PhpOffice\PhpSpreadsheet\Settings::setCache($cache); @@ -25,7 +25,7 @@ Some localisation elements have been included in PhpSpreadsheet. You can set a locale by changing the settings. To set the locale to Brazilian Portuguese you would use: -``` php +```php $locale = 'pt_br'; $validLocale = \PhpOffice\PhpSpreadsheet\Settings::setLocale($locale); if (!$validLocale) { @@ -43,3 +43,20 @@ More details of the features available once a locale has been set, including a list of the languages and locales currently supported, can be found in [Locale Settings for Formulae](./recipes.md#locale-settings-for-formulae). + +## HTTP client + +In order to use the `WEBSERVICE` function in formulae, you must configure an +HTTP client. Assuming you chose Guzzle 7, this can be done like: + + +```php +use GuzzleHttp\Client; +use Http\Factory\Guzzle\RequestFactory; +use PhpOffice\PhpSpreadsheet\Settings; + +$client = new Client(); +$requestFactory = new RequestFactory(); + +Settings::setHttpClient($client, $requestFactory); +``` diff --git a/docs/topics/worksheets.md b/docs/topics/worksheets.md index f97a00665d..0199f13c6a 100644 --- a/docs/topics/worksheets.md +++ b/docs/topics/worksheets.md @@ -25,7 +25,7 @@ each worksheet "tab" is shown when the workbook is opened in MS Excel (or other appropriate Spreadsheet program). To access a sheet by its index, use the `getSheet()` method. -``` php +```php // Get the second sheet in the workbook // Note that sheets are indexed from 0 $spreadsheet->getSheet(1); @@ -38,7 +38,7 @@ workbook. To access a sheet by name, use the `getSheetByName()` method, specifying the name of the worksheet that you want to access. -``` php +```php // Retrieve the worksheet called 'Worksheet 1' $spreadsheet->getSheetByName('Worksheet 1'); ``` @@ -48,7 +48,7 @@ and you can access that directly. The currently active worksheet is the one that will be active when the workbook is opened in MS Excel (or other appropriate Spreadsheet program). -``` php +```php // Retrieve the current active worksheet $spreadsheet->getActiveSheet(); ``` @@ -64,7 +64,7 @@ a new "last" sheet; but you can also specify an index position as an argument, and the worksheet will be inserted at that position, shuffling all subsequent worksheets in the collection down a place. -``` php +```php $spreadsheet->createSheet(); ``` @@ -76,7 +76,7 @@ Alternatively, you can instantiate a new worksheet (setting the title to whatever you choose) and then insert it into your workbook using the `addSheet()` method. -``` php +```php // Create a new worksheet called "My Data" $myWorkSheet = new \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet($spreadsheet, 'My Data'); @@ -93,7 +93,7 @@ Sheets within the same workbook can be copied by creating a clone of the worksheet you wish to copy, and then using the `addSheet()` method to insert the clone into the workbook. -``` php +```php $clonedWorksheet = clone $spreadsheet->getSheetByName('Worksheet 1'); $clonedWorksheet->setTitle('Copy of Worksheet 1'); $spreadsheet->addSheet($clonedWorksheet); @@ -117,7 +117,7 @@ duplicate name. You can delete a worksheet from a workbook, identified by its index position, using the `removeSheetByIndex()` method -``` php +```php $sheetIndex = $spreadsheet->getIndex( $spreadsheet->getSheetByName('Worksheet 1') ); diff --git a/mkdocs.yml b/mkdocs.yml index cf87a142a3..f79acb69cd 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -5,3 +5,5 @@ edit_uri: edit/master/docs/ theme: readthedocs extra_css: - extra/extra.css +extra_javascript: + - extra/extrajs.js diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 0000000000..53bbb0e6e3 --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,16 @@ +parameters: + level: 2 + paths: + - src/ + - tests/ + ignoreErrors: + - '~^Class GdImage not found\.$~' + - '~^Return typehint of method .* has invalid type GdImage\.$~' + - '~^Property .* has unknown class GdImage as its type\.$~' + - '~^Parameter .* of method .* has invalid typehint type GdImage\.$~' + + # Ignore all JpGraph issues + - '~^Constant (MARK_CIRCLE|MARK_CROSS|MARK_DIAMOND|MARK_DTRIANGLE|MARK_FILLEDCIRCLE|MARK_SQUARE|MARK_STAR|MARK_UTRIANGLE|MARK_X|SIDE_RIGHT) not found\.$~' + - '~^Instantiated class (AccBarPlot|AccLinePlot|BarPlot|ContourPlot|Graph|GroupBarPlot|GroupBarPlot|LinePlot|LinePlot|PieGraph|PiePlot|PiePlot3D|PiePlotC|RadarGraph|RadarPlot|ScatterPlot|Spline|StockPlot) not found\.$~' + - '~^Call to method .*\(\) on an unknown class (AccBarPlot|AccLinePlot|BarPlot|ContourPlot|Graph|GroupBarPlot|GroupBarPlot|LinePlot|LinePlot|PieGraph|PiePlot|PiePlot3D|PiePlotC|RadarGraph|RadarPlot|ScatterPlot|Spline|StockPlot)\.$~' + - '~^Access to property .* on an unknown class (AccBarPlot|AccLinePlot|BarPlot|ContourPlot|Graph|GroupBarPlot|GroupBarPlot|LinePlot|LinePlot|PieGraph|PiePlot|PiePlot3D|PiePlotC|RadarGraph|RadarPlot|ScatterPlot|Spline|StockPlot)\.$~' diff --git a/samples/Basic/07_Reader.php b/samples/Basic/07_Reader.php index 4d9bd79e2f..67b3ae561d 100644 --- a/samples/Basic/07_Reader.php +++ b/samples/Basic/07_Reader.php @@ -17,3 +17,4 @@ // Save $helper->write($spreadsheet, __FILE__); +unlink($filename); diff --git a/samples/Basic/13_Calculation.php b/samples/Basic/13_Calculation.php index 087b443fe5..ed2e1dcc28 100644 --- a/samples/Basic/13_Calculation.php +++ b/samples/Basic/13_Calculation.php @@ -157,8 +157,10 @@ $helper->log('Calculated data'); for ($col = 'B'; $col != 'G'; ++$col) { for ($row = 14; $row <= 41; ++$row) { - if ((($formula = $spreadsheet->getActiveSheet()->getCell($col . $row)->getValue()) !== null) && - ($formula[0] == '=')) { + if ( + (($formula = $spreadsheet->getActiveSheet()->getCell($col . $row)->getValue()) !== null) && + ($formula[0] == '=') + ) { $helper->log('Value of ' . $col . $row . ' [' . $formula . ']: ' . $spreadsheet->getActiveSheet()->getCell($col . $row)->getCalculatedValue()); } } diff --git a/samples/Basic/13_CalculationCyclicFormulae.php b/samples/Basic/13_CalculationCyclicFormulae.php index a446e56e08..b7c6a511bf 100644 --- a/samples/Basic/13_CalculationCyclicFormulae.php +++ b/samples/Basic/13_CalculationCyclicFormulae.php @@ -22,8 +22,10 @@ $helper->log('Calculated data'); for ($row = 1; $row <= 2; ++$row) { for ($col = 'A'; $col != 'C'; ++$col) { - if ((($formula = $spreadsheet->getActiveSheet()->getCell($col . $row)->getValue()) !== null) && - ($formula[0] == '=')) { + if ( + (($formula = $spreadsheet->getActiveSheet()->getCell($col . $row)->getValue()) !== null) && + ($formula[0] == '=') + ) { $helper->log('Value of ' . $col . $row . ' [' . $formula . ']: ' . $spreadsheet->getActiveSheet()->getCell($col . $row)->getCalculatedValue()); } } diff --git a/samples/Basic/16_Csv.php b/samples/Basic/16_Csv.php index de753d565a..15bbf0d456 100644 --- a/samples/Basic/16_Csv.php +++ b/samples/Basic/16_Csv.php @@ -1,13 +1,15 @@ log('Write to CSV format'); /** @var \PhpOffice\PhpSpreadsheet\Writer\Csv $writer */ -$writer = IOFactory::createWriter($spreadsheet, 'Csv')->setDelimiter(',') +$writer = new CsvWriter($spreadsheet); +$writer->setDelimiter(',') ->setEnclosure('"') ->setSheetIndex(0); @@ -19,13 +21,15 @@ $helper->log('Read from CSV format'); /** @var \PhpOffice\PhpSpreadsheet\Reader\Csv $reader */ -$reader = IOFactory::createReader('Csv')->setDelimiter(',') +$reader = new CsvReader(); +$reader->setDelimiter(',') ->setEnclosure('"') ->setSheetIndex(0); $callStartTime = microtime(true); $spreadsheetFromCSV = $reader->load($filename); $helper->logRead('Csv', $filename, $callStartTime); +unlink($filename); // Write Xlsx $helper->write($spreadsheetFromCSV, __FILE__, ['Xlsx']); @@ -33,7 +37,7 @@ // Write CSV $filenameCSV = $helper->getFilename(__FILE__, 'csv'); /** @var \PhpOffice\PhpSpreadsheet\Writer\Csv $writerCSV */ -$writerCSV = IOFactory::createWriter($spreadsheetFromCSV, 'Csv'); +$writerCSV = new CsvWriter($spreadsheetFromCSV); $writerCSV->setExcelCompatibility(true); $callStartTime = microtime(true); diff --git a/samples/Basic/17b_Html.php b/samples/Basic/17b_Html.php new file mode 100644 index 0000000000..97bb29a337 --- /dev/null +++ b/samples/Basic/17b_Html.php @@ -0,0 +1,20 @@ +getFilename(__FILE__, 'html'); +$writer = new Html($spreadsheet); + +function changeGridlines(string $html): string +{ + return str_replace('{border: 1px solid black;}', '{border: 2px dashed red;}', $html); +} + +$callStartTime = microtime(true); +$writer->setEmbedImages(true); +$writer->setEditHtmlCallback('changeGridlines'); +$writer->save($filename); +$helper->logWrite($writer, $filename, $callStartTime); diff --git a/samples/Basic/20_Read_Excel2003XML.php b/samples/Basic/20_Read_Excel2003XML.php index 44425e20a5..48ac3373c5 100644 --- a/samples/Basic/20_Read_Excel2003XML.php +++ b/samples/Basic/20_Read_Excel2003XML.php @@ -4,7 +4,7 @@ require __DIR__ . '/../Header.php'; -$filename = __DIR__ . '/../templates/Excel2003XMLTest.xml'; +$filename = __DIR__ . '/../templates/excel2003.xml'; $callStartTime = microtime(true); $spreadsheet = IOFactory::load($filename); $helper->logRead('Xml', $filename, $callStartTime); diff --git a/samples/Basic/20_Read_Xls.php b/samples/Basic/20_Read_Xls.php index 9e5fa014ad..daeaf66437 100644 --- a/samples/Basic/20_Read_Xls.php +++ b/samples/Basic/20_Read_Xls.php @@ -17,6 +17,7 @@ $callStartTime = microtime(true); $spreadsheet = IOFactory::load($filename); $helper->logRead('Xls', $filename, $callStartTime); +unlink($filename); // Save $helper->write($spreadsheet, __FILE__); diff --git a/samples/Basic/24_Readfilter.php b/samples/Basic/24_Readfilter.php index 844996f249..ab1c2e411c 100644 --- a/samples/Basic/24_Readfilter.php +++ b/samples/Basic/24_Readfilter.php @@ -3,6 +3,7 @@ namespace PhpOffice\PhpSpreadsheet; use PhpOffice\PhpSpreadsheet\Reader\IReadFilter; +use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; require __DIR__ . '/../Header.php'; @@ -29,10 +30,11 @@ public function readCell($column, $row, $worksheetName = '') } $helper->log('Load from Xlsx file'); -$reader = IOFactory::createReader('Xlsx'); +$reader = new XlsxReader(); $reader->setReadFilter(new MyReadFilter()); $callStartTime = microtime(true); $spreadsheet = $reader->load($filename); +unlink($filename); $helper->logRead('Xlsx', $filename, $callStartTime); $helper->log('Remove unnecessary rows'); $spreadsheet->getActiveSheet()->removeRow(2, 18); diff --git a/samples/Basic/26_Utf8.php b/samples/Basic/26_Utf8.php index 52a64509b3..52953251c8 100644 --- a/samples/Basic/26_Utf8.php +++ b/samples/Basic/26_Utf8.php @@ -12,10 +12,12 @@ // at this point, we could do some manipulations with the template, but we skip this step $helper->write($spreadsheet, __FILE__, ['Xlsx', 'Xls', 'Html']); -// Export to PDF (.pdf) -$helper->log('Write to PDF format'); -IOFactory::registerWriter('Pdf', \PhpOffice\PhpSpreadsheet\Writer\Pdf\Dompdf::class); -$helper->write($spreadsheet, __FILE__, ['Pdf']); +if (\PHP_VERSION_ID < 80000) { + // Export to PDF (.pdf) + $helper->log('Write to PDF format'); + IOFactory::registerWriter('Pdf', \PhpOffice\PhpSpreadsheet\Writer\Pdf\Dompdf::class); + $helper->write($spreadsheet, __FILE__, ['Pdf']); +} // Remove first two rows with field headers before exporting to CSV $helper->log('Removing first two heading rows for CSV export'); diff --git a/samples/Basic/28_Iterator.php b/samples/Basic/28_Iterator.php index 4aec7a9203..104dc47f36 100644 --- a/samples/Basic/28_Iterator.php +++ b/samples/Basic/28_Iterator.php @@ -1,21 +1,22 @@ getTemporaryFilename(); -$writer = new Xlsx($sampleSpreadsheet); +$writer = new XlsxWriter($sampleSpreadsheet); $callStartTime = microtime(true); $writer->save($filename); $helper->logWrite($writer, $filename, $callStartTime); $callStartTime = microtime(true); -$reader = IOFactory::createReader('Xlsx'); +$reader = new XlsxReader(); $spreadsheet = $reader->load($filename); $helper->logRead('Xlsx', $filename, $callStartTime); +unlink($filename); $helper->log('Iterate worksheets'); foreach ($spreadsheet->getWorksheetIterator() as $worksheet) { $helper->log('Worksheet - ' . $worksheet->getTitle()); diff --git a/samples/Basic/30_Templatebiff5.php b/samples/Basic/30_Templatebiff5.php new file mode 100644 index 0000000000..53c4c2a833 --- /dev/null +++ b/samples/Basic/30_Templatebiff5.php @@ -0,0 +1,43 @@ +log('Load from Xls template'); +$reader = IOFactory::createReader('Xls'); +$spreadsheet = $reader->load(__DIR__ . '/../templates/30templatebiff5.xls'); + +$helper->log('Add new data to the template'); +$data = [['title' => 'Excel for dummies', + 'price' => 17.99, + 'quantity' => 2, +], + ['title' => 'PHP for dummies', + 'price' => 15.99, + 'quantity' => 1, + ], + ['title' => 'Inside OOP', + 'price' => 12.95, + 'quantity' => 1, + ], +]; + +$spreadsheet->getActiveSheet()->setCellValue('D1', Date::PHPToExcel(time())); + +$baseRow = 5; +foreach ($data as $r => $dataRow) { + $row = $baseRow + $r; + $spreadsheet->getActiveSheet()->insertNewRowBefore($row, 1); + + $spreadsheet->getActiveSheet()->setCellValue('A' . $row, $r + 1) + ->setCellValue('B' . $row, $dataRow['title']) + ->setCellValue('C' . $row, $dataRow['price']) + ->setCellValue('D' . $row, $dataRow['quantity']) + ->setCellValue('E' . $row, '=C' . $row . '*D' . $row); +} +$spreadsheet->getActiveSheet()->removeRow($baseRow - 1, 1); + +// Save +$helper->write($spreadsheet, __FILE__); diff --git a/samples/Basic/40_Duplicate_style.php b/samples/Basic/40_Duplicate_style.php index 0366703d90..38f7fb495e 100644 --- a/samples/Basic/40_Duplicate_style.php +++ b/samples/Basic/40_Duplicate_style.php @@ -30,7 +30,7 @@ } } $d = microtime(true) - $t; -$helper->log('Add data (end) . time: ' . round((string) ($d . 2)) . ' s'); +$helper->log('Add data (end) . time: ' . (string) round($d, 2) . ' s'); // Save $helper->write($spreadsheet, __FILE__); diff --git a/samples/Basic/42_RichText.php b/samples/Basic/42_RichText.php index 43b35a62c1..d5fa85b4a7 100644 --- a/samples/Basic/42_RichText.php +++ b/samples/Basic/42_RichText.php @@ -30,7 +30,7 @@ while this block uses an underline.

-

+

I want to eat healthy food pizza. '; diff --git a/samples/Basic/43_Merge_workbooks.php b/samples/Basic/43_Merge_workbooks.php index 86314b3ba3..28353cc60b 100644 --- a/samples/Basic/43_Merge_workbooks.php +++ b/samples/Basic/43_Merge_workbooks.php @@ -18,6 +18,10 @@ foreach ($spreadsheet2->getSheetNames() as $sheetName) { $sheet = $spreadsheet2->getSheetByName($sheetName); + if ($sheet === null) { + continue; + } + $sheet->setTitle($sheet->getTitle() . ' copied'); $spreadsheet1->addExternalSheet($sheet); } diff --git a/samples/Basic/44_Worksheet_info.php b/samples/Basic/44_Worksheet_info.php index 33c0cd0577..578223699c 100644 --- a/samples/Basic/44_Worksheet_info.php +++ b/samples/Basic/44_Worksheet_info.php @@ -24,3 +24,5 @@ $helper->log('Worksheet Names:'); var_dump($sheetInfo); + +unlink($filename); diff --git a/samples/Basic/45_Quadratic_equation_solver.php b/samples/Basic/45_Quadratic_equation_solver.php index a59a0cebf9..84c126aae3 100644 --- a/samples/Basic/45_Quadratic_equation_solver.php +++ b/samples/Basic/45_Quadratic_equation_solver.php @@ -1,4 +1,5 @@ log('Returns a text reference to a single cell in a worksheet.'); + +// Create new PhpSpreadsheet object +$spreadsheet = new Spreadsheet(); +$worksheet = $spreadsheet->getActiveSheet(); + +$worksheet->getCell('A1')->setValue('=ADDRESS(2,3)'); +$worksheet->getCell('A2')->setValue('=ADDRESS(2,3,2)'); +$worksheet->getCell('A3')->setValue('=ADDRESS(2,3,2,FALSE)'); +$worksheet->getCell('A4')->setValue('=ADDRESS(2,3,1,FALSE,"[Book1]Sheet1")'); +$worksheet->getCell('A5')->setValue('=ADDRESS(2,3,1,FALSE,"EXCEL SHEET")'); + +for ($row = 1; $row <= 5; ++$row) { + $cell = $worksheet->getCell("A{$row}"); + $helper->log("A{$row}: {$cell->getValue()} => {$cell->getCalculatedValue()}"); +} diff --git a/samples/Calculations/LookupRef/COLUMN.php b/samples/Calculations/LookupRef/COLUMN.php new file mode 100644 index 0000000000..e9e5846608 --- /dev/null +++ b/samples/Calculations/LookupRef/COLUMN.php @@ -0,0 +1,23 @@ +log('Returns the column index of a cell.'); + +// Create new PhpSpreadsheet object +$spreadsheet = new Spreadsheet(); +$worksheet = $spreadsheet->getActiveSheet(); + +$worksheet->getCell('A1')->setValue('=COLUMN(C13)'); +$worksheet->getCell('A2')->setValue('=COLUMN(E13:G15)'); +$worksheet->getCell('F1')->setValue('=COLUMN()'); + +for ($row = 1; $row <= 2; ++$row) { + $cell = $worksheet->getCell("A{$row}"); + $helper->log("A{$row}: {$cell->getValue()} => {$cell->getCalculatedValue()}"); +} + +$cell = $worksheet->getCell('F1'); +$helper->log("F1: {$cell->getValue()} => {$cell->getCalculatedValue()}"); diff --git a/samples/Calculations/LookupRef/COLUMNS.php b/samples/Calculations/LookupRef/COLUMNS.php new file mode 100644 index 0000000000..4d7f8d10e6 --- /dev/null +++ b/samples/Calculations/LookupRef/COLUMNS.php @@ -0,0 +1,21 @@ +log('Returns the number of columns in an array or reference.'); + +// Create new PhpSpreadsheet object +$spreadsheet = new Spreadsheet(); +$worksheet = $spreadsheet->getActiveSheet(); + +$worksheet->getCell('A1')->setValue('=COLUMNS(C1:G4)'); +$worksheet->getCell('A2')->setValue('=COLUMNS({1,2,3;4,5,6})'); +$worksheet->getCell('A3')->setValue('=ROWS(C1:E4 D3:G5)'); +$worksheet->getCell('A4')->setValue('=COLUMNS(1:1)'); + +for ($row = 1; $row <= 4; ++$row) { + $cell = $worksheet->getCell("A{$row}"); + $helper->log("A{$row}: {$cell->getValue()} => {$cell->getCalculatedValue()}"); +} diff --git a/samples/Calculations/LookupRef/INDEX.php b/samples/Calculations/LookupRef/INDEX.php new file mode 100644 index 0000000000..9ef0b94562 --- /dev/null +++ b/samples/Calculations/LookupRef/INDEX.php @@ -0,0 +1,39 @@ +log('Returns the row index of a cell.'); + +// Create new PhpSpreadsheet object +$spreadsheet = new Spreadsheet(); +$worksheet = $spreadsheet->getActiveSheet(); + +$data1 = [ + ['Apples', 'Lemons'], + ['Bananas', 'Pears'], +]; + +$data2 = [ + [4, 6], + [5, 3], + [6, 9], + [7, 5], + [8, 3], +]; + +$worksheet->fromArray($data1, null, 'A1'); +$worksheet->fromArray($data2, null, 'C1'); + +$worksheet->getCell('A11')->setValue('=INDEX(A1:B2, 2, 2)'); +$worksheet->getCell('A12')->setValue('=INDEX(A1:B2, 2, 1)'); +$worksheet->getCell('A13')->setValue('=INDEX({1,2;3,4}, 0, 2)'); +$worksheet->getCell('A14')->setValue('=INDEX(C1:C5, 5)'); +$worksheet->getCell('A15')->setValue('=INDEX(C1:D5, 5, 2)'); +$worksheet->getCell('A16')->setValue('=SUM(INDEX(C1:D5, 5, 0))'); + +for ($row = 11; $row <= 16; ++$row) { + $cell = $worksheet->getCell("A{$row}"); + $helper->log("A{$row}: {$cell->getValue()} => {$cell->getCalculatedValue()}"); +} diff --git a/samples/Calculations/LookupRef/INDIRECT.php b/samples/Calculations/LookupRef/INDIRECT.php new file mode 100644 index 0000000000..ffbada9ad7 --- /dev/null +++ b/samples/Calculations/LookupRef/INDIRECT.php @@ -0,0 +1,33 @@ +log('Returns the cell specified by a text string.'); + +// Create new PhpSpreadsheet object +$spreadsheet = new Spreadsheet(); +$worksheet = $spreadsheet->getActiveSheet(); + +$data = [ + [8, 9, 0], + [3, 4, 5], + [9, 1, 3], + [4, 6, 2], +]; +$worksheet->fromArray($data, null, 'C1'); + +$spreadsheet->addNamedRange(new NamedRange('NAMED_RANGE_FOR_CELL_D4', $worksheet, '="$D$4"')); + +$worksheet->getCell('A1')->setValue('=INDIRECT("C1")'); +$worksheet->getCell('A2')->setValue('=INDIRECT("D"&4)'); +$worksheet->getCell('A3')->setValue('=INDIRECT("E"&ROW())'); +$worksheet->getCell('A4')->setValue('=SUM(INDIRECT("$C$4:$E$4"))'); +$worksheet->getCell('A5')->setValue('=INDIRECT(NAMED_RANGE_FOR_CELL_D4)'); + +for ($row = 1; $row <= 5; ++$row) { + $cell = $worksheet->getCell("A{$row}"); + $helper->log("A{$row}: {$cell->getValue()} => {$cell->getCalculatedValue()}"); +} diff --git a/samples/Calculations/LookupRef/OFFSET.php b/samples/Calculations/LookupRef/OFFSET.php new file mode 100644 index 0000000000..ae613ec525 --- /dev/null +++ b/samples/Calculations/LookupRef/OFFSET.php @@ -0,0 +1,33 @@ +log('Returns a cell range that is a specified number of rows and columns from a cell or range of cells.'); + +// Create new PhpSpreadsheet object +$spreadsheet = new Spreadsheet(); +$worksheet = $spreadsheet->getActiveSheet(); + +$data = [ + [null, 'Week 1', 'Week 2', 'Week 3', 'Week 4'], + ['Sunday', 4500, 2200, 3800, 1500], + ['Monday', 5500, 6100, 5200, 4800], + ['Tuesday', 7000, 6200, 5000, 7100], + ['Wednesday', 8000, 4000, 3900, 7600], + ['Thursday', 5900, 5500, 6900, 7100], + ['Friday', 4900, 6300, 6900, 5200], + ['Saturday', 3500, 3900, 5100, 4100], +]; +$worksheet->fromArray($data, null, 'A3'); + +$worksheet->getCell('H1')->setValue('=OFFSET(A3, 3, 1)'); +$worksheet->getCell('H2')->setValue('=SUM(OFFSET(A3, 3, 1, 1, 4))'); +$worksheet->getCell('H3')->setValue('=SUM(OFFSET(B3:E3, 3, 0))'); +$worksheet->getCell('H4')->setValue('=SUM(OFFSET(E3, 1, -3, 7))'); + +for ($row = 1; $row <= 4; ++$row) { + $cell = $worksheet->getCell("H{$row}"); + $helper->log("H{$row}: {$cell->getValue()} => {$cell->getCalculatedValue()}"); +} diff --git a/samples/Calculations/LookupRef/ROW.php b/samples/Calculations/LookupRef/ROW.php new file mode 100644 index 0000000000..560639a516 --- /dev/null +++ b/samples/Calculations/LookupRef/ROW.php @@ -0,0 +1,20 @@ +log('Returns the row index of a cell.'); + +// Create new PhpSpreadsheet object +$spreadsheet = new Spreadsheet(); +$worksheet = $spreadsheet->getActiveSheet(); + +$worksheet->getCell('A1')->setValue('=ROW(C13)'); +$worksheet->getCell('A2')->setValue('=ROW(E19:G21)'); +$worksheet->getCell('A3')->setValue('=ROW()'); + +for ($row = 1; $row <= 3; ++$row) { + $cell = $worksheet->getCell("A{$row}"); + $helper->log("A{$row}: {$cell->getValue()} => {$cell->getCalculatedValue()}"); +} diff --git a/samples/Calculations/LookupRef/ROWS.php b/samples/Calculations/LookupRef/ROWS.php new file mode 100644 index 0000000000..3cdf085bd9 --- /dev/null +++ b/samples/Calculations/LookupRef/ROWS.php @@ -0,0 +1,20 @@ +log('Returns the row index of a cell.'); + +// Create new PhpSpreadsheet object +$spreadsheet = new Spreadsheet(); +$worksheet = $spreadsheet->getActiveSheet(); + +$worksheet->getCell('A1')->setValue('=ROWS(C1:E4)'); +$worksheet->getCell('A2')->setValue('=ROWS({1,2,3;4,5,6})'); +$worksheet->getCell('A3')->setValue('=ROWS(C1:E4 D3:G5)'); + +for ($row = 1; $row <= 3; ++$row) { + $cell = $worksheet->getCell("A{$row}"); + $helper->log("A{$row}: {$cell->getValue()} => {$cell->getCalculatedValue()}"); +} diff --git a/samples/Chart/34_Chart_update.php b/samples/Chart/34_Chart_update.php index a428792776..5d725c4947 100644 --- a/samples/Chart/34_Chart_update.php +++ b/samples/Chart/34_Chart_update.php @@ -1,20 +1,22 @@ getTemporaryFilename(); -$writer = new Xlsx($sampleSpreadsheet); +$writer = new XlsxWriter($sampleSpreadsheet); +$writer->setIncludeCharts(true); $writer->save($filename); $helper->log('Load from Xlsx file'); -$reader = IOFactory::createReader('Xlsx'); +$reader = new XlsxReader(); $reader->setIncludeCharts(true); $spreadsheet = $reader->load($filename); +unlink($filename); $helper->log('Update cell data values that are displayed in the chart'); $worksheet = $spreadsheet->getActiveSheet(); @@ -31,7 +33,7 @@ // Save Excel 2007 file $filename = $helper->getFilename(__FILE__); -$writer = IOFactory::createWriter($spreadsheet, 'Xlsx'); +$writer = new XlsxWriter($spreadsheet); $writer->setIncludeCharts(true); $callStartTime = microtime(true); $writer->save($filename); diff --git a/samples/Chart/35_Chart_render.php b/samples/Chart/35_Chart_render.php index 9638c679d7..ebab16a78c 100644 --- a/samples/Chart/35_Chart_render.php +++ b/samples/Chart/35_Chart_render.php @@ -5,6 +5,11 @@ require __DIR__ . '/../Header.php'; +if (PHP_VERSION_ID >= 80000) { + $helper->log('Jpgraph no longer runs against PHP8'); + exit; +} + // Change these values to select the Rendering library that you wish to use Settings::setChartRenderer(\PhpOffice\PhpSpreadsheet\Chart\Renderer\JpGraph::class); diff --git a/samples/DefinedNames/AbsoluteNamedRange.php b/samples/DefinedNames/AbsoluteNamedRange.php new file mode 100644 index 0000000000..30afc00d65 --- /dev/null +++ b/samples/DefinedNames/AbsoluteNamedRange.php @@ -0,0 +1,54 @@ +setActiveSheetIndex(0); + +// Set up some basic data for a timesheet +$worksheet + ->setCellValue('A1', 'Charge Rate/hour:') + ->setCellValue('B1', '7.50') + ->setCellValue('A3', 'Date') + ->setCellValue('B3', 'Hours') + ->setCellValue('C3', 'Charge'); + +// Define named range using an absolute cell reference +$spreadsheet->addNamedRange(new NamedRange('CHARGE_RATE', $worksheet, '=$B$1')); + +$workHours = [ + '2020-0-06' => 7.5, + '2020-0-07' => 7.25, + '2020-0-08' => 6.5, + '2020-0-09' => 7.0, + '2020-0-10' => 5.5, +]; + +// Populate the Timesheet +$startRow = 4; +$row = $startRow; +foreach ($workHours as $date => $hours) { + $worksheet + ->setCellValue("A{$row}", $date) + ->setCellValue("B{$row}", $hours) + ->setCellValue("C{$row}", "=B{$row}*CHARGE_RATE"); + ++$row; +} +$endRow = $row - 1; + +++$row; +$worksheet + ->setCellValue("B{$row}", "=SUM(B{$startRow}:B{$endRow})") + ->setCellValue("C{$row}", "=SUM(C{$startRow}:C{$endRow})"); + +$helper->log(sprintf( + 'Worked %.2f hours at a rate of %.2f - Charge to the client is %.2f', + $worksheet->getCell("B{$row}")->getCalculatedValue(), + $worksheet->getCell('B1')->getValue(), + $worksheet->getCell("C{$row}")->getCalculatedValue() +)); + +$helper->write($spreadsheet, __FILE__, ['Xlsx']); diff --git a/samples/DefinedNames/CrossWorksheetNamedFormula.php b/samples/DefinedNames/CrossWorksheetNamedFormula.php new file mode 100644 index 0000000000..5ce7651628 --- /dev/null +++ b/samples/DefinedNames/CrossWorksheetNamedFormula.php @@ -0,0 +1,90 @@ +setActiveSheetIndex(0); +setYearlyData($worksheet, '2019', $data2019); +$worksheet = $spreadsheet->addSheet(new Worksheet($spreadsheet)); +setYearlyData($worksheet, '2020', $data2020); +$worksheet = $spreadsheet->addSheet(new Worksheet($spreadsheet)); +setYearlyData($worksheet, '2020', [], 'GROWTH'); + +function setYearlyData(Worksheet $worksheet, string $year, $yearlyData, ?string $title = null): void +{ + // Set up some basic data + $worksheetTitle = $title ?: $year; + $worksheet + ->setTitle($worksheetTitle) + ->setCellValue('A1', 'Month') + ->setCellValue('B1', $worksheetTitle === 'GROWTH' ? 'Growth' : 'Sales') + ->setCellValue('C1', $worksheetTitle === 'GROWTH' ? 'Profit Growth' : 'Margin') + ->setCellValue('A2', Date::stringToExcel("{$year}-01-01")); + for ($row = 3; $row <= 13; ++$row) { + $worksheet->setCellValue("A{$row}", '=NEXT_MONTH'); + } + + if (!empty($yearlyData)) { + $worksheet->fromArray($yearlyData, null, 'B2'); + } else { + for ($row = 2; $row <= 13; ++$row) { + $worksheet->setCellValue("B{$row}", '=GROWTH'); + $worksheet->setCellValue("C{$row}", '=PROFIT_GROWTH'); + } + } + + $worksheet->getStyle('A1:C1') + ->getFont()->setBold(true); + $worksheet->getStyle('A2:A13') + ->getNumberFormat() + ->setFormatCode('mmmm'); + $worksheet->getStyle('B2:C13') + ->getNumberFormat() + ->setFormatCode($worksheetTitle === 'GROWTH' ? '0.00%' : '_-€* #,##0_-'); +} + +// Add some Named Formulae +// The first to store our tax rate +$spreadsheet->addNamedFormula(new NamedFormula('NEXT_MONTH', $worksheet, '=EDATE(OFFSET($A1,-1,0),1)')); +$spreadsheet->addNamedFormula(new NamedFormula('GROWTH', $worksheet, "=IF('2020'!\$B1=\"\",\"-\",(('2020'!\$B1/'2019'!\$B1)-1))")); +$spreadsheet->addNamedFormula(new NamedFormula('PROFIT_GROWTH', $worksheet, "=IF('2020'!\$C1=\"\",\"-\",(('2020'!\$C1/'2019'!\$C1)-1))")); + +for ($row = 2; $row <= 7; ++$row) { + $month = $worksheet->getCell("A{$row}")->getFormattedValue(); + $growth = $worksheet->getCell("B{$row}")->getFormattedValue(); + $profitGrowth = $worksheet->getCell("C{$row}")->getFormattedValue(); + + $helper->log("Growth for {$month} is {$growth}, with a Profit Growth of {$profitGrowth}"); +} + +$helper->write($spreadsheet, __FILE__, ['Xlsx']); diff --git a/samples/DefinedNames/NamedFormulaeAndRanges.php b/samples/DefinedNames/NamedFormulaeAndRanges.php new file mode 100644 index 0000000000..a5ca80e706 --- /dev/null +++ b/samples/DefinedNames/NamedFormulaeAndRanges.php @@ -0,0 +1,65 @@ +setActiveSheetIndex(0); + +// Set up some basic data for a timesheet +$worksheet + ->setCellValue('A1', 'Charge Rate/hour:') + ->setCellValue('B1', '7.50') + ->setCellValue('A3', 'Date') + ->setCellValue('B3', 'Hours') + ->setCellValue('C3', 'Charge'); + +// Define named ranges +// CHARGE_RATE is an absolute cell reference that always points to cell B1 +$spreadsheet->addNamedRange(new NamedRange('CHARGE_RATE', $worksheet, '=$B$1')); +// HOURS_PER_DAY is a relative cell reference that always points to column B, but to a cell in the row where it is used +$spreadsheet->addNamedRange(new NamedRange('HOURS_PER_DAY', $worksheet, '=$B1')); +// Set up the formula for calculating the daily charge +$spreadsheet->addNamedFormula(new NamedFormula('DAILY_CHARGE', null, '=HOURS_PER_DAY*CHARGE_RATE')); +// Set up the formula for calculating the column totals +$spreadsheet->addNamedFormula(new NamedFormula('COLUMN_TOTALS', null, '=SUM(COLUMN_DATA_VALUES)')); + +$workHours = [ + '2020-0-06' => 7.5, + '2020-0-07' => 7.25, + '2020-0-08' => 6.5, + '2020-0-09' => 7.0, + '2020-0-10' => 5.5, +]; + +// Populate the Timesheet +$startRow = 4; +$row = $startRow; +foreach ($workHours as $date => $hours) { + $worksheet + ->setCellValue("A{$row}", $date) + ->setCellValue("B{$row}", $hours) + ->setCellValue("C{$row}", '=DAILY_CHARGE'); + ++$row; +} +$endRow = $row - 1; + +// COLUMN_TOTAL is another relative cell reference that always points to the same range of rows but to cell in the column where it is used +$spreadsheet->addNamedRange(new NamedRange('COLUMN_DATA_VALUES', $worksheet, "=A\${$startRow}:A\${$endRow}")); + +++$row; +$worksheet + ->setCellValue("B{$row}", '=COLUMN_TOTALS') + ->setCellValue("C{$row}", '=COLUMN_TOTALS'); + +$helper->log(sprintf( + 'Worked %.2f hours at a rate of %.2f - Charge to the client is %.2f', + $worksheet->getCell("B{$row}")->getCalculatedValue(), + $worksheet->getCell('B1')->getValue(), + $worksheet->getCell("C{$row}")->getCalculatedValue() +)); + +$helper->write($spreadsheet, __FILE__, ['Xlsx']); diff --git a/samples/DefinedNames/RelativeNamedRange.php b/samples/DefinedNames/RelativeNamedRange.php new file mode 100644 index 0000000000..fac75a4715 --- /dev/null +++ b/samples/DefinedNames/RelativeNamedRange.php @@ -0,0 +1,57 @@ +setActiveSheetIndex(0); + +// Set up some basic data for a timesheet +$worksheet + ->setCellValue('A1', 'Charge Rate/hour:') + ->setCellValue('B1', '7.50') + ->setCellValue('A3', 'Date') + ->setCellValue('B3', 'Hours') + ->setCellValue('C3', 'Charge'); + +// Define named ranges +// CHARGE_RATE is an absolute cell reference that always points to cell B1 +$spreadsheet->addNamedRange(new NamedRange('CHARGE_RATE', $worksheet, '=$B$1')); +// HOURS_PER_DAY is a relative cell reference that always points to column B, but to a cell in the row where it is used +$spreadsheet->addNamedRange(new NamedRange('HOURS_PER_DAY', $worksheet, '=$B1')); + +$workHours = [ + '2020-0-06' => 7.5, + '2020-0-07' => 7.25, + '2020-0-08' => 6.5, + '2020-0-09' => 7.0, + '2020-0-10' => 5.5, +]; + +// Populate the Timesheet +$startRow = 4; +$row = $startRow; +foreach ($workHours as $date => $hours) { + $worksheet + ->setCellValue("A{$row}", $date) + ->setCellValue("B{$row}", $hours) + ->setCellValue("C{$row}", '=HOURS_PER_DAY*CHARGE_RATE'); + ++$row; +} +$endRow = $row - 1; + +++$row; +$worksheet + ->setCellValue("B{$row}", "=SUM(B{$startRow}:B{$endRow})") + ->setCellValue("C{$row}", "=SUM(C{$startRow}:C{$endRow})"); + +$helper->log(sprintf( + 'Worked %.2f hours at a rate of %.2f - Charge to the client is %.2f', + $worksheet->getCell("B{$row}")->getCalculatedValue(), + $worksheet->getCell('B1')->getValue(), + $worksheet->getCell("C{$row}")->getCalculatedValue() +)); + +$helper->write($spreadsheet, __FILE__, ['Xlsx']); diff --git a/samples/DefinedNames/RelativeNamedRange2.php b/samples/DefinedNames/RelativeNamedRange2.php new file mode 100644 index 0000000000..b3e957fd2e --- /dev/null +++ b/samples/DefinedNames/RelativeNamedRange2.php @@ -0,0 +1,60 @@ +setActiveSheetIndex(0); + +// Set up some basic data for a timesheet +$worksheet + ->setCellValue('A1', 'Charge Rate/hour:') + ->setCellValue('B1', '7.50') + ->setCellValue('A3', 'Date') + ->setCellValue('B3', 'Hours') + ->setCellValue('C3', 'Charge'); + +// Define named ranges +// CHARGE_RATE is an absolute cell reference that always points to cell B1 +$spreadsheet->addNamedRange(new NamedRange('CHARGE_RATE', $worksheet, '=$B$1')); +// HOURS_PER_DAY is a relative cell reference that always points to column B, but to a cell in the row where it is used +$spreadsheet->addNamedRange(new NamedRange('HOURS_PER_DAY', $worksheet, '=$B1')); + +$workHours = [ + '2020-0-06' => 7.5, + '2020-0-07' => 7.25, + '2020-0-08' => 6.5, + '2020-0-09' => 7.0, + '2020-0-10' => 5.5, +]; + +// Populate the Timesheet +$startRow = 4; +$row = $startRow; +foreach ($workHours as $date => $hours) { + $worksheet + ->setCellValue("A{$row}", $date) + ->setCellValue("B{$row}", $hours) + ->setCellValue("C{$row}", '=HOURS_PER_DAY*CHARGE_RATE'); + ++$row; +} +$endRow = $row - 1; + +// COLUMN_TOTAL is another relative cell reference that always points to the same range of rows but to cell in the column where it is used +$spreadsheet->addNamedRange(new NamedRange('COLUMN_DATA_VALUES', $worksheet, "=A\${$startRow}:A\${$endRow}")); + +++$row; +$worksheet + ->setCellValue("B{$row}", '=SUM(COLUMN_DATA_VALUES)') + ->setCellValue("C{$row}", '=SUM(COLUMN_DATA_VALUES)'); + +$helper->log(sprintf( + 'Worked %.2f hours at a rate of %.2f - Charge to the client is %.2f', + $worksheet->getCell("B{$row}")->getCalculatedValue(), + $worksheet->getCell('B1')->getValue(), + $worksheet->getCell("C{$row}")->getCalculatedValue() +)); + +$helper->write($spreadsheet, __FILE__, ['Xlsx']); diff --git a/samples/DefinedNames/RelativeNamedRangeAsFunction.php b/samples/DefinedNames/RelativeNamedRangeAsFunction.php new file mode 100644 index 0000000000..333d01ab0b --- /dev/null +++ b/samples/DefinedNames/RelativeNamedRangeAsFunction.php @@ -0,0 +1,63 @@ +setActiveSheetIndex(0); + +// Set up some basic data for a timesheet +$worksheet + ->setCellValue('A1', 'Charge Rate/hour:') + ->setCellValue('B1', '7.50') + ->setCellValue('A3', 'Date') + ->setCellValue('B3', 'Hours') + ->setCellValue('C3', 'Charge'); + +// Define named ranges +// CHARGE_RATE is an absolute cell reference that always points to cell B1 +$spreadsheet->addNamedRange(new NamedRange('CHARGE_RATE', $worksheet, '=$B$1')); +// HOURS_PER_DAY is a relative cell reference that always points to column B, but to a cell in the row where it is used +$spreadsheet->addNamedRange(new NamedRange('HOURS_PER_DAY', $worksheet, '=$B1')); + +$workHours = [ + '2020-0-06' => 7.5, + '2020-0-07' => 7.25, + '2020-0-08' => 6.5, + '2020-0-09' => 7.0, + '2020-0-10' => 5.5, +]; + +// Populate the Timesheet +$startRow = 4; +$row = $startRow; +foreach ($workHours as $date => $hours) { + $worksheet + ->setCellValue("A{$row}", $date) + ->setCellValue("B{$row}", $hours) + ->setCellValue("C{$row}", '=HOURS_PER_DAY*CHARGE_RATE'); + ++$row; +} +$endRow = $row - 1; + +// COLUMN_TOTAL is another relative cell reference that always points to the same range of rows but to cell in the column where it is used +// To avoid including the current row,or having to hard-code the range itself (as we did in the previous example) +// we wrap it in a named formula using the OFFSET() function +$spreadsheet->addNamedFormula(new NamedFormula('COLUMN_DATA_VALUES', $worksheet, '=OFFSET(A$4:A1, -1, 0)')); + +++$row; +$worksheet + ->setCellValue("B{$row}", '=SUM(COLUMN_DATA_VALUES)') + ->setCellValue("C{$row}", '=SUM(COLUMN_DATA_VALUES)'); + +$helper->log(sprintf( + 'Worked %.2f hours at a rate of %.2f - Charge to the client is %.2f', + $worksheet->getCell("B{$row}")->getCalculatedValue(), + $worksheet->getCell('B1')->getValue(), + $worksheet->getCell("C{$row}")->getCalculatedValue() +)); + +$helper->write($spreadsheet, __FILE__, ['Xlsx']); diff --git a/samples/DefinedNames/ScopedNamedRange.php b/samples/DefinedNames/ScopedNamedRange.php new file mode 100644 index 0000000000..aa71454df0 --- /dev/null +++ b/samples/DefinedNames/ScopedNamedRange.php @@ -0,0 +1,72 @@ +setActiveSheetIndex(0); +$worksheet->setTitle('Base Data'); + +// Set up some basic data for a timesheet +$worksheet + ->setCellValue('A1', 'Charge Rate/hour:') + ->setCellValue('B1', '7.50'); + +// Define a global named range on the first worksheet for our Charge Rate +// CHARGE_RATE is an absolute cell reference that always points to cell B1 +// Because it is defined globally, it will still be usable from any worksheet in the spreadsheet +$spreadsheet->addNamedRange(new NamedRange('CHARGE_RATE', $worksheet, '=$B$1')); + +// Create a second worksheet as our client timesheet +$worksheet = $spreadsheet->addSheet(new \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet($spreadsheet, 'Client Timesheet')); + +// Define named ranges +// HOURS_PER_DAY is a relative cell reference that always points to column B, but to a cell in the row where it is used +$spreadsheet->addNamedRange(new NamedRange('HOURS_PER_DAY', $worksheet, '=$B1')); + +// Set up some basic data for a timesheet +$worksheet + ->setCellValue('A1', 'Date') + ->setCellValue('B1', 'Hours') + ->setCellValue('C1', 'Charge'); + +$workHours = [ + '2020-0-06' => 7.5, + '2020-0-07' => 7.25, + '2020-0-08' => 6.5, + '2020-0-09' => 7.0, + '2020-0-10' => 5.5, +]; + +// Populate the Timesheet +$startRow = 2; +$row = $startRow; +foreach ($workHours as $date => $hours) { + $worksheet + ->setCellValue("A{$row}", $date) + ->setCellValue("B{$row}", $hours) + ->setCellValue("C{$row}", '=HOURS_PER_DAY*CHARGE_RATE'); + ++$row; +} +$endRow = $row - 1; + +// COLUMN_TOTAL is another relative cell reference that always points to the same range of rows but to cell in the column where it is used +$spreadsheet->addNamedRange(new NamedRange('COLUMN_DATA_VALUES', $worksheet, "=A\${$startRow}:A\${$endRow}")); + +++$row; +$worksheet + ->setCellValue("B{$row}", '=SUM(COLUMN_DATA_VALUES)') + ->setCellValue("C{$row}", '=SUM(COLUMN_DATA_VALUES)'); + +$helper->log(sprintf( + 'Worked %.2f hours at a rate of %s - Charge to the client is %.2f', + $worksheet->getCell("B{$row}")->getCalculatedValue(), + $chargeRateCellValue = $spreadsheet + ->getSheetByName($spreadsheet->getNamedRange('CHARGE_RATE')->getWorksheet()->getTitle()) + ->getCell($spreadsheet->getNamedRange('CHARGE_RATE')->getCellsInRange()[0])->getValue(), + $worksheet->getCell("C{$row}")->getCalculatedValue() +)); + +$helper->write($spreadsheet, __FILE__, ['Xlsx']); diff --git a/samples/DefinedNames/ScopedNamedRange2.php b/samples/DefinedNames/ScopedNamedRange2.php new file mode 100644 index 0000000000..5f0898c91e --- /dev/null +++ b/samples/DefinedNames/ScopedNamedRange2.php @@ -0,0 +1,89 @@ +setActiveSheetIndex(0); + +$clients = [ + 'Client #1 - Full Hourly Rate' => [ + '2020-0-06' => 2.5, + '2020-0-07' => 2.25, + '2020-0-08' => 6.0, + '2020-0-09' => 3.0, + '2020-0-10' => 2.25, + ], + 'Client #2 - Full Hourly Rate' => [ + '2020-0-06' => 1.5, + '2020-0-07' => 2.75, + '2020-0-08' => 0.0, + '2020-0-09' => 4.5, + '2020-0-10' => 3.5, + ], + 'Client #3 - Reduced Hourly Rate' => [ + '2020-0-06' => 3.5, + '2020-0-07' => 2.5, + '2020-0-08' => 1.5, + '2020-0-09' => 0.0, + '2020-0-10' => 1.25, + ], +]; + +foreach ($clients as $clientName => $workHours) { + $worksheet = $spreadsheet->addSheet(new \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet($spreadsheet, $clientName)); + + // Set up some basic data for a timesheet + $worksheet + ->setCellValue('A1', 'Charge Rate/hour:') + ->setCellValue('B1', '7.50') + ->setCellValue('A3', 'Date') + ->setCellValue('B3', 'Hours') + ->setCellValue('C3', 'Charge'); + + // Define named ranges + // CHARGE_RATE is an absolute cell reference that always points to cell B1 + $spreadsheet->addNamedRange(new NamedRange('CHARGE_RATE', $worksheet, '=$B$1', true)); + // HOURS_PER_DAY is a relative cell reference that always points to column B, but to a cell in the row where it is used + $spreadsheet->addNamedRange(new NamedRange('HOURS_PER_DAY', $worksheet, '=$B1', true)); + + // Populate the Timesheet + $startRow = 4; + $row = $startRow; + foreach ($workHours as $date => $hours) { + $worksheet + ->setCellValue("A{$row}", $date) + ->setCellValue("B{$row}", $hours) + ->setCellValue("C{$row}", '=HOURS_PER_DAY*CHARGE_RATE'); + ++$row; + } + $endRow = $row - 1; + + // COLUMN_TOTAL is another relative cell reference that always points to the same range of rows but to cell in the column where it is used + $spreadsheet->addNamedRange(new NamedRange('COLUMN_TOTAL', $worksheet, "=A\${$startRow}:A\${$endRow}", true)); + + ++$row; + $worksheet + ->setCellValue("B{$row}", '=SUM(COLUMN_TOTAL)') + ->setCellValue("C{$row}", '=SUM(COLUMN_TOTAL)'); +} +$spreadsheet->removeSheetByIndex(0); + +// Set the reduced charge rate for our special client +$worksheet + ->setCellValue('B1', 4.5); + +foreach ($spreadsheet->getAllSheets() as $worksheet) { + $helper->log(sprintf( + 'Worked %.2f hours for "%s" at a rate of %.2f - Charge to the client is %.2f', + $worksheet->getCell("B{$row}")->getCalculatedValue(), + $worksheet->getTitle(), + $worksheet->getCell('B1')->getValue(), + $worksheet->getCell("C{$row}")->getCalculatedValue() + )); +} +$worksheet = $spreadsheet->setActiveSheetIndex(0); + +$helper->write($spreadsheet, __FILE__, ['Xlsx']); diff --git a/samples/DefinedNames/SimpleNamedFormula.php b/samples/DefinedNames/SimpleNamedFormula.php new file mode 100644 index 0000000000..ea1f802da1 --- /dev/null +++ b/samples/DefinedNames/SimpleNamedFormula.php @@ -0,0 +1,43 @@ +setActiveSheetIndex(0); + +// Add some Named Formulae +// The first to store our tax rate +$spreadsheet->addNamedFormula(new NamedFormula('TAX_RATE', $worksheet, '=19%')); +// The second to calculate the Tax on a Price value (Note that `PRICE` is defined later as a Named Range) +$spreadsheet->addNamedFormula(new NamedFormula('TAX', $worksheet, '=PRICE*TAX_RATE')); + +// Set up some basic data +$worksheet + ->setCellValue('A1', 'Tax Rate:') + ->setCellValue('B1', '=TAX_RATE') + ->setCellValue('A3', 'Net Price:') + ->setCellValue('B3', 19.99) + ->setCellValue('A4', 'Tax:') + ->setCellValue('A5', 'Price including Tax:'); + +// Define a named range that we can use in our formulae +$spreadsheet->addNamedRange(new NamedRange('PRICE', $worksheet, '=$B$3')); + +// Reference the defined formulae in worksheet formulae +$worksheet + ->setCellValue('B4', '=TAX') + ->setCellValue('B5', '=PRICE+TAX'); + +$helper->log(sprintf( + 'With a Tax Rate of %.2f and a net price of %.2f, Tax is %.2f and the gross price is %.2f', + $worksheet->getCell('B1')->getCalculatedValue(), + $worksheet->getCell('B3')->getValue(), + $worksheet->getCell('B4')->getCalculatedValue(), + $worksheet->getCell('B5')->getCalculatedValue() +)); + +$helper->write($spreadsheet, __FILE__, ['Xlsx']); diff --git a/samples/DefinedNames/SimpleNamedRange.php b/samples/DefinedNames/SimpleNamedRange.php new file mode 100644 index 0000000000..7a7cdc94a1 --- /dev/null +++ b/samples/DefinedNames/SimpleNamedRange.php @@ -0,0 +1,37 @@ +setActiveSheetIndex(0); + +// Set up some basic data +$worksheet + ->setCellValue('A1', 'Tax Rate:') + ->setCellValue('B1', '=19%') + ->setCellValue('A3', 'Net Price:') + ->setCellValue('B3', 12.99) + ->setCellValue('A4', 'Tax:') + ->setCellValue('A5', 'Price including Tax:'); + +// Define named ranges +$spreadsheet->addNamedRange(new NamedRange('TAX_RATE', $worksheet, '=$B$1')); +$spreadsheet->addNamedRange(new NamedRange('PRICE', $worksheet, '=$B$3')); + +// Reference that defined name in a formula +$worksheet + ->setCellValue('B4', '=PRICE*TAX_RATE') + ->setCellValue('B5', '=PRICE*(1+TAX_RATE)'); + +$helper->log(sprintf( + 'With a Tax Rate of %.2f and a net price of %.2f, Tax is %.2f and the gross price is %.2f', + $worksheet->getCell('B1')->getCalculatedValue(), + $worksheet->getCell('B3')->getValue(), + $worksheet->getCell('B4')->getCalculatedValue(), + $worksheet->getCell('B5')->getCalculatedValue() +)); + +$helper->write($spreadsheet, __FILE__, ['Xlsx']); diff --git a/samples/Pdf/21a_Pdf.php b/samples/Pdf/21a_Pdf.php new file mode 100644 index 0000000000..b5572afe41 --- /dev/null +++ b/samples/Pdf/21a_Pdf.php @@ -0,0 +1,25 @@ +log('Hide grid lines'); +$spreadsheet->getActiveSheet()->setShowGridLines(false); + +$helper->log('Set orientation to landscape'); +$spreadsheet->getActiveSheet()->getPageSetup()->setOrientation(PageSetup::ORIENTATION_LANDSCAPE); +$spreadsheet->setActiveSheetIndex(0)->setPrintGridlines(true); + +function changeGridlines(string $html): string +{ + return str_replace('{border: 1px solid black;}', '{border: 2px dashed red;}', $html); +} + +$helper->log('Write to Mpdf'); +$writer = new Mpdf($spreadsheet); +$filename = $helper->getFileName('21a_Pdf_mpdf.xlsx', 'pdf'); +$writer->setEditHtmlCallback('changeGridlines'); +$writer->save($filename); diff --git a/samples/Pdf/21b_Pdf.php b/samples/Pdf/21b_Pdf.php new file mode 100644 index 0000000000..ad2f609b7c --- /dev/null +++ b/samples/Pdf/21b_Pdf.php @@ -0,0 +1,55 @@ +.*~ms'; + $bodyrepl = << +

Serif

+

$lorem

+

Sans-Serif

+

$lorem

+

Monospace

+

$lorem

+ +EOF; + + return preg_replace($bodystring, $bodyrepl, $html); +} + +require __DIR__ . '/../Header.php'; +$spreadsheet = require __DIR__ . '/../templates/sampleSpreadsheet.php'; + +$helper->log('Hide grid lines'); +$spreadsheet->getActiveSheet()->setShowGridLines(false); + +$helper->log('Set orientation to landscape'); +$spreadsheet->getActiveSheet()->getPageSetup()->setOrientation(PageSetup::ORIENTATION_LANDSCAPE); + +if (\PHP_VERSION_ID < 80000) { + $helper->log('Write to Dompdf'); + $writer = new Dompdf($spreadsheet); + $filename = $helper->getFileName('21b_Pdf_dompdf.xlsx', 'pdf'); + $writer->setEditHtmlCallback('replaceBody'); + $writer->save($filename); +} + +$helper->log('Write to Mpdf'); +$writer = new Mpdf($spreadsheet); +$filename = $helper->getFileName('21b_Pdf_mpdf.xlsx', 'pdf'); +$writer->setEditHtmlCallback('replaceBody'); +$writer->save($filename); + +if (\PHP_VERSION_ID < 80000) { + $helper->log('Write to Tcpdf'); + $writer = new Tcpdf($spreadsheet); + $filename = $helper->getFileName('21b_Pdf_tcpdf.xlsx', 'pdf'); + $writer->setEditHtmlCallback('replaceBody'); + $writer->save($filename); +} diff --git a/samples/Reader/sampleData/example1xls b/samples/Reader/sampleData/example1xls new file mode 100644 index 0000000000000000000000000000000000000000..bd9bb110b9a41b0aab816ba9e20682ee5bb43aea GIT binary patch literal 22528 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 literal 0 HcmV?d00001 diff --git a/samples/images/bmp.bmp b/samples/images/bmp.bmp new file mode 100644 index 0000000000000000000000000000000000000000..01fee85ebcd7ca3dda1783f0d0b2c59494e34ac6 GIT binary patch literal 30186 zcmeHQEpHu35cILYKtsboqr=gmkdu5Y(9kgC&;djLguuW+lamY$1N}219WgY_mECcv z)HBmNA3GcSX`hsr*_rO@>e`;!*?BqLAAkP$_tDemcYMFYpMUV@FZ}uK=osHeNB{mD zf4cbj^<)2Rs{nN0HsBuZ$FBMIMX+nWYree#+eh5%eAj$?1-6g4*ZHpb_6lqtaj)}T z^X(PbKH}lk`PJ1GlDfFK*j~)ah=-^7n>TNe)Jx9mC3CAR8)x>U_pZ59W$xwiu^piv zU0+|%jns>Ncy<2i(&lRp$B~p61`be>crvY-%r-*hr~S^M{9r5u8cB2dO!i z^p77uHtDv_KJy?o|NQwgOT9_+ZT6W5sX27+)4b2C>9!iTR+m>{-=|)mSF0e1((3Xm z?EBQ~^J*1j*IX6bHCHuS+kNWwd3A7Wb$J!`ed^I`>airEyb29lNm9KgM|w?_Wh!`6 zzUvxOy(U>8`}JTOh6PjfvS8|RR+vYtS5Gq}9GB!6wJ zzJvYx^{dK0SZ1u))?eu8xTs7?(#8H7RIR_zX{lGnYBj6T z9GG9ee9;Qu$`)h?6X#NbRWXB72O6uwA&*~<>^%^@+g(d8%>uA# zXH!o}bAV9Hz(&oQ;#L}yL#4el>vpH3o8wlBXJ=<4+lD|-#tm$cBh6gCt6UC(ce~Tk z&2jgV()WA^cu5?_4Qz0`X;#7+=^q5|cBdm6%3qfqvvCtajZ1BZsb;0PYQyIgwwKz! zR+}990Qod*Rfdir;Fb~hO5X9}HljJma3_z~s6JU@FXrAVn!&Op+!7H7#i-VE_U&Wj z#jRU&2o5qH!?gX?f6gtT^Ys1l!`&#{RAe0sj z(gu`gX9pkpTBC%Z3p*?q7a7=;pfsYh(M;XY>X)REMdk`%&vCbrqi>LA7S=k|9qnN0 z(1m?5R$!njl_Q3K4gp7e=IvM*(E{O{J=);HqR;WH^6FLDRs}#H-kcz4OqD@Pxxj!W zg1 z5f*uNx`96p0P^kIw^oL?x3`RLjTnLRBAyX|3UC;aHn@XK1O?Cjc?~Aw-Us!Wf_a@K zfVbDyO@+f@^cv0eVH%WrljgwL;N8<@t%+#;aOukyIYCP9G>98|*J7;+WO>c1tKc+#pPVm|9o+(>0;ZWMc zA=jWK8g8iOQlD!zk1&>c>0BzM>v_ZC<$iGBz5F-;K3F%xH%gV7D{Ur=#^QS} z6CZ3asNt}d;s>Z1<4A<=Xw<9^DO<>{An}dURXvp*I4M4R7)3$YX^l8Kp#~caYB-#C ziw{t>3!Y3skGa<@ESk<>TQ@M^Q`(kjl&tKPnrF=v z5C&5V9{Bb_My(5riZIyL<)ySOaUoPCTal26v(d}vL?8dwezx6$Gf#FhL1=B0$u^Dj z)Qe;)8^%9it2lv~-bz#gh~ac7u zQ%&VyZ8AxRDtnqVN4|@j6G-6}J*+mBWCAm0Y7LvDf&IK8K1OTyEm;~(npf^@xtBA2 zD!sDwtW($UwJ?Kl@es31_s_8-M=LeQqJgD@Ue-X(^M_HB!hL7G97xS2*Kotfv!ljq zl8qjx^o58u=R9&^)?BVQ{olQN{C|qsXGPdP#o&eA%lr55ySE2OBna(ZHlOoe{+rL} z0K9&PZvpGV))QFQGS(cim~U3-wN{UHlX~H3i<-Z1{;!eVuKD(NZP$F)e0v49kBB<| EA4~Nmpa1{> literal 0 HcmV?d00001 diff --git a/samples/images/gif.gif b/samples/images/gif.gif new file mode 100644 index 0000000000000000000000000000000000000000..4cf06035b8a0e830eafd83733c77eee7bcb06d0b GIT binary patch literal 1578 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 delta 4123 zcmai12|QI>7vJZO?#0D5≪vB$ z$75VBcaknzRftizqlj6-AycR}Eh|R)PsL}Y) z&M0J%tJO%YkHP?j0zfw)fQzI`BmsDae(OLWK(4LbgZ!Wg!3o+DnD7|Eo*IW)5WKCc z;6H@Lm?cyqNx>u{?LUwfS&GgFu8psX8%qcffynm+KATEJr~bi{(u6-367Cs!fss?_ z3=bh!@Ft0W+>R~+lJRFu;JFcFsACw=A75pXBK=5dk;j*ifIj32Coqvbd5pqIL^qmt zjGtxEk4`uL=v0w{J2l}+C6XB2K@mjby%S@_iBpQf4jw;1QPQ@gVrIY{_yZr{0fLZJ zAV~o-5CuT_tg?!_ii)P;x;c|jfg}$ou#1m{Q6WZC!uX&HZ6>A$Eou7LEEr3hi>X5n zZ80_*4$<_mERH%|g@gvS3`wNMr8-ZlAt}zuXT)Gw7St3@Lb5_w63I5~XG;ElLBC=Bkj?*x095%@EHYg$o(LOon z z1^pLzpbU2g%d?%I20zvp-hK1Ta8!B<>Td9_6kh(!cTQ2c`e?@OLCstuiSdx!D>@g`iHCX!YB4Bd#p7QFmB2z zJV?mY-6Rrt=$`w<3^o0QBOj7V0xyW3u#5Si6Dm-}x@WLxYcbm)p(=g-{*zui17>Za z%gps`-#4Q@v-5BX8#~r;`y+K|M_5pE6 z5*_7LGUod>sFq>I0(V!=*xEl-&fueFzpvHY{KV$2!LBDUJs!-~@IJ>r>o+;aoU9TY zsSnQ`kaRhF%4f%5pAq|amPJ*(TT0Bj{0~kU%Y*OwSL!#*^4l!FKR8^dFUpjV6H#k1 z&2ec?yJSmOP5mVTrhxH&4ll>?4833FtO8SQXX4MU$5re zJ*5z8-ek-#{;s1nY<}&V_2pkp2}Padiy3|c9-1vmUJ?*=b&v3leyuG2`iel3{yE#S zo;cIhvf5YJ33SyvA2o*!slBF#kvTcN4i zHGS&Y`y#SRdY!XglnFlhoTAa&F+~= zM^*dX7p*+s-zOe#WoD(5`$yStj)&(j`Fuo6sP#_D>XgC*2cqVgYJ2k+Ms0^0YxeDW zq+4^~&rcW2zL}cG=q-(084zKizPn5C@1|Lw-6E)dfrTJTZRehRuBkO^GOV$S>->M@WAk`2!oIU82P*!WS_=;Y~2C0wtBiSm$4IWL|uF=N~{d_Vj=D!YYJ>0#q zDI_58xj=aS{;}48V(-#?UGZ-6$g4cLEiOnv%!Os8oHuq+^@8F#sEs=gB z8P`Kvjf*%kzFqqHo~19>$CPh6*)$Z_EOa|)-Ch?H-5L{}CWV1}Z&o>-?rO~p4G$_J z8OATq;JO}aq+DjZQ`tLSHdJ<3Z``C$EhBo+QhfRaMS8aZ8I& zTXacT5;d<@*VqZ<9IEWtQt0XIFL7FXsakY`!~*SYt-%k9EY$pJ6uLd$==R<+GU|+V zu6bN>ew5}XWMS8`Zbwp?cK9NgeJN(gTGK5%j;b`}?uXvJ+ojk7VbtqUdy<7>RgF`g zwd&Sgm~A!?Rkt%!(f8%;vCbNQrxA^`SZz(VNXVe={qFVrZ3DzIhc9vt&n&At)hwgD zYg0aO)8j81uLcG8*%kY%RMg|Rjr`U3Z05LX_?*wHFE}9ey4~}hWsS4_f)$xAm%99R z2_D;RpDSfkEp}SHjeXEfW00I+@}%>hwBVu_x$!R?8KFk4v2sM>pi#7kqUQCp{#qK0 zCvI1|+VbdI`NCArOMI-`=pTNs@Zh%4Zf@V@n48jhj9U|GItqybdMEb#wa=v8 zc&&H)qW{I*4GS0+4eR?UlDDEe(r3>+d)vh2qFbzUefjC(3{St$jJ8@%eDBvoZUMWl z1pMu?--fn5`eJ;s>=XWZUZ37FYcf1Xtb9c`vK{S4pU1C>s9JKSCrvqZUh8cM(Xj$E zg7MOZ*9H|e@6;dv`5PBqa9l1C*sdUwU}d-r=i#m*?E97Q}B?l)4K6K7ugs zswD&U@x*?DL-`!a)qdpgT>NK_{0k1~pK>@c zSMV2{2+H$+wxcq~NvM4|k3<0^F6=&Rh7qCa5nU361xBIk5gj%ekj4aoDj6Vy3Q~nM zDk??+9pHPmLp=aD00WIMU_bFkLX0p^Gs)PaWpEDP|IZx0|DQR0|C1bM4~0`*Fa_wJ z>PoWJN+QC>R3pJb`c2f)*~i7*{HeRBpLgLp{d4Q=jUVTu7sDr`M7 zgWQT9z!JzvSHvh#BYg#uVd)*xZWuq1K~sM|5e4mVCH@vr(@hWB1aB7A>Vlt95C<#HFsfbT9#Un;J^Heje~CZagPOIsG-_(?0! zeALYG1lZbVV^BbFfL91>>CTOw{w!@xO&yj~K#*VXW>3!$^hyCR_3`r3M^Oj>&qTu> z*J$X2p#_(=aiVuCVL})%0|8(Qs&;Kq5$203Xf0tT`rz?JCmUW?lbOhgAB^6;LST}{ Ndqjl%AEG-_^M5t_oK*k- diff --git a/samples/templates/30templatebiff5.xls b/samples/templates/30templatebiff5.xls new file mode 100644 index 0000000000000000000000000000000000000000..0523e83d451785ba550c2ded8cfaa60d9535906c GIT binary patch literal 338944 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 z zd!gty{c!;OaS*xa?rcHu^Q0SXH{O48!Xn@GVENTAdW*7&gfWQO7RVITGo-Ima5x-T zRROJ?S)(N;Z+%J=G+hp7}MQWFdJEh}?vZRhVN~>J0d7jSnk>+5& z!mdc-Qo?sD-F7S4b}11%mMI*cs+}qnPAGLIJ5(t@R4sR{Q5>#Qx-}?|G%AlaDULNO zkGCq1L-nCu?$#mu5j&!P_fV(8zDsG}riAPTWZRCnYh7F9_Dv#)*-rPX&-Uv+Y1ACf z5*U58`sQ1#H~x9j(Xl@`Y_@NFV;k0XqgB!(q@oAy;Gio5oz2js2%XJR-RzDo4Wbh4W*bUAE3VS!wi8Z18Z$B#CdC zBDPMz%p)ZGf%Bf<-v80|r?+<=w+Wb=u%r%9Y-%mG;Ed_5@CA;$mYwr6yvb>h1zC-}*c3D!-LdFILHQY6%oCu1)5;kEFTx z$6xIYztkUgZaC`W*~C+{%wr=dr<*gqNP|^c$trkRaPhzu5M2OJpe1W)e{ktYwz)gA z)ob0+D;-hX&e)Bfgq{B6?f#_o&bUM2*GJPX`iKXz3+nkKiPO;GRS~y{Szh$>7lYwKAt_^|1=o`(=tV zCCZZ}YS%&qlxA!niR~ZB93QD2a@4jNa_ba{RjSY?U1XDnSv-{OeSrT5q#wWDvA(c% zC48f-XM1rIltv&krXS}A3QGW19S-s@ND>DvxQBcCGJk1=H(9aLm&xr&TcY}Mh27pwzR;ZFm^BK$&W80qRE{DSYr_`L1825xU6bTP0x5;)z-Ykg_F z-c(LU;!-OVCt^9R@nFl%v7DyZm8KX@WAsu(zpH{e`NKg^D23 z(>wF!0dr-y7Yc6EassJYcSw1mGr2({5B~tad5pO!=x_MT@e8x&N`{ojVlhL1i5|a_ z0|A&p@GgG&`|m;DpVTTG`Cf$Is9pR@4#w*73+@;BUcfIhz^`qh#3n&xn<}$}tbV3; ze}>8+Q)Zfn+ZI8tSL|FSb|@9wmEyK#P()VRJ=GVHD^(6vD#se-ZUy;JJpfSU+N3@V z)~t4ERv&8C{G_>cXpeP6Xs2?kR~@a_Kq%yhG zQ~dfh{`>wr53)C#Mz>e^0+2^V5CSg`T5xL6I|2PRkO4uGQ=p!wlyXI!F%XsJ4d$%% zByaU3Z}%pzcP6Z~L@hUlavE=Q>V4=?GS0tFDe_>HdC-fmk{*3MlX`CQ;n~U9&t~F2 zB_w`COgcK3>^zZucCtQkl{%u6Z|}ksTG*hI0xbf9jtV5ufOIIOf_45v+v>CQ#qMNA zXELihmD7{K=}BjIq_El&m)a7S+mn`Bli1AA~Z^6D77k- zR&x&uY~+d%;*-GHvfG4G{}EF%?F~}DE>*Z|HGa58>Q*at ztCt;V&|9PIXp{VCv*JhWi2mJ^?dmffP#V^ruGO5W)1I!^9&6MB1wP!VKGCmxzejhZ zPHmaPfAfpE-@e*;;MI;x=VuFwHb^v;7)~Dwm3D&{^e>8I;C!kTxI!_@S7%V<=ljL> z&6riA(g{)^4Z71cx(};$A3~pIxyrUgcAyA5kdIm9;WpV)his{HHtv!wbxMc4BECNY z|Lq;@f8G=y2o&E;mz1=LM`^-s9HK<A#;# z_-Hx?G7Trk8{L&1e1IJchm?cf zzJ)JhzEaDF(p~skCm{#ykbt`;%8sV1-5>{=rL#y?n{ z7N`%E$qzl1g1J1Exm3tptMJ3sQnwn}5hxwk%a1n5cI|kx;&|&%8|rUp(w(f;o~qWq zU#mS+uRGPGJJO8WB#%PTxLNC5CNoW5+ka`s(zf@rBO@`9?CxHiy8&Wkdcb_U0qnw^ zRbP05<-@ZgO*&Qa#WT#V88>fKSvTvP8g++jb;s&-$7^+mtJF3XDwC%w<5HDbp~~u! z+CES1n5%NmQQN1fpk?wv6#m|Q=^LT4lPQYKcGYvTYHs#UozUFH^}`mF_2yynl6zBB-~yAy!s_< zAkDMe&!!Tu@rkud9g)JM6k(PUBr} z^L=j9-R1gVR#gDAB7j+Whfxzs2U-sJEe@P4_Ma}gHUHR;lI2g%3?SwPPi6-W#a)LM zjKbrl*)FDkE`ER5nDgSRzY|GxMbDH+q9pr$C1!r#;rBqO@W5Tc-ao*v9>qW)%%db0 z(YQW%lY$4feuzW-2KcpvoKdpeHdAGrqdAzRF-(=emx%32!VJ@Z39Bq2K+nr$0oIp$g#_&aFlzR;eci+Ej|-T%W|cQF5SOZqlGOuTxvqsO@Uib~P&NDurdW%A#6h z_Ec+HtToHmS>)*~vvro~x`T;oxHnnHi5(Jgmvp6Hy|$a8;|kD71lXOPcOp$N@bj=^ z2-iU51vE||%0_~5a=vVXv_o!R8!P4wWbyjbczr3W-OynWz0w%Ut-ZBc?a8h8U8=tY zH8)DZm&}6m+>#5t(u=Ie=Vwwr9*q0oS<)BN#Xd`&@qALFbd#=Dg5w2V0%;VGFNIYb zNJSV3K42Lo>EO@B)&=u*fL%)cU1D_zz?WQqk5YdR(#%xI-6HG)h(jn9LDZ@sMqLQA zE`(JVyi^yoTzhA^CUB`bkX?O;Sp}ux5Ym$nLUGV^aR6YKnCH8Yaf_7hKbIZ!i(pqM z))n?E-6FAlJ`&Sgn29eC2y9;xMjV} zu36>OsDO{|e8VtI)DB%ki zjBE#I^>2*jv->j{-D!(G=}Y}t%l%nPy&20r87n;*+}=!J!9anTP(*HwpddFK#HbEn z*95NA-C3y%;?&$(tO%q$xkD-qA{K|t76(oh`Vw-zN$I}xX?}#PI}=#}gR$<7?tfFi zi?G|Lbc?|Dd1I!&xQP#Da0}lTAT>nYzkM)o%_MW>K-#Kl=C;jasdJIWrAT|IK;u-X zbu7|473-}?>s+jLF3}z;)wq;tT%PEyOmq0D`e?oCc&px8RLB3AozTB{s7-0xE_Y~` z9_f^w>{XrVQ=jaDzBPqwlk7;7>`b%tvu5mzCSmk|c#5UOFz`YMMxZ1r*TG0M0j3nl z6rf~^O2f$i0-%&?p;AK>>Pi^OTf>r%I>c^mm`l6#P^-kHMRd4T4Bf&mt@!aK$(d@= z$qKP6w9l2OYzyF)qO(m?J11iwr0=*FZ%4L@3nrB#TEWvtRB;$t`3nC>l*noIC zv^F3iQIxtuJ`lDtiF9*un?AywZe$OaGM*Js`yNveFX+yrwq??rlUa?iOZ8#Q`e1Uc z-+Tph{oWuJxD)cOPG(;oPQTQbarIeI;7n`QDydH;T+%Ba0f~qI;4g!z5U8dHdxutZ zsREK6u##B+t{PK`UVsArf>!{)Pj6G8pR6cwz9@uH6gXMnGm~?3KFx=idTTZ_@IT@A z^FNFC{&C(Q5a|kfm50Nzy*DuvAKcg*+wY6-y)88i5+eKtNezN!=FqEpUt)Y8Glunt z0OT-mznB^L-e?@LhZra&1BAf?%S;|h_s5Hj;y3J4xbNrlzOGuoTD$snIs04@>wGEi zYAxp7s`YKt`Lt-gTQy)lZ7qv}OonXqkJ}yn>%QX( zuSvDfq$*%i6+EpDol*x)sBb@4`VA=qhLz!Cs+1YcBa*gFrMOtqzGthxAID>gyKmSM|EDE7k5#rNOo0j2>|lK{mOnUV*Y55*mQn)zA|I`#~zs zpr;29{dgEibiiU>eeehysNRY;B&!QMjG?vpj+NOK_H^^&L=$_oer2S3eW-k6plG=- zo!;}1(UZ8;ox*NQrZ>bBt0JeKMoiVk6Z;C6=DS533=N1MAuo>%T^o=YgDxPHQ9+XM zFc1#lic=RDgH&wjE>df0p6po#nfhM zdfQUrh_ITVt0C%Y=5!4U+9t~PjH+#-YMW`AW`_EO(bdgNRqK+nZAI}T)~9jIThj_#mbQVZsiSEc>DnH)ZgNdW7wR_9T4@ldfT&xcgwbDc`alOCnN+Tk z!YkmN&{|#;(;#_bkO85HwB1~FB}-Gv(v+|@MND}S4F`u@vZyI$YRf6=%6a9}d1WbC zT}aX9k##vlUEZ{|cv9U!P(5E%EpBW0Ah?UJ2&q~Fy8bdL4B!EIJ5ZxV+Vy}WKris% z7dmBtF{Ir9Hv%zFzD>h;bApwbt>xJb_S`m$AY@KU>0>hL5TDYyMs3-qcS#t766&CU z*t;>)wmRLyBXw^tPhbL;N(s{Ih(tpT2L53EVqAS;0{MIB;sES|>;b|qq7+Eo4?Ya9 z!*zqCSKu~*vJ!9{(2!<*S{i5$P-Z@dzc{io(*#SED9wrFntKqzu_|w~D{ix%_|r>J zer*2n?WxDU3ey)cvigxz;{ zu^WOzy92`Rz3*WcI#2+>W*07Y30ygPi*WJ5SW*$OdvKXB&z+v74vfwY zPJ#I6*5a0$y`x>)(XNVg>k{4C_YFfs3fFPXOH;4o>UEiVU9R~N+t9zeD%Entx)q^z zRj67ODpo~m`1u@>jxEr!`8u8m#t|UMLf8U*hah4P6Bghk!PF9T`rz!r0C+VBp2OT8 z{Twd6fIpl{xU)lq1(QE1LM>UOqe^rXp_05Uqwc6_d>xgqqiyNvn>ygLRI!eNX-N_d z%n&5;wbX6h5?{9sQ(UlJ&U z-U$PMFr9KsBU@A8Yck1}RI(!%?EY*&s7Lmn0QepSy78OwZbi}U_;I4LW`qH z!qF(8Bmo09AqE6ZI-N+V!(e^}N{a!SQH=udi%NTN;{nO37x;zy0_f1dCqXe3(je%g z;E~~OfNoUqSHO282M(1vi9)a>Sf1j}G|{_rNwpEACjpdFA7+{F(i8v1vH(iK?YUgv ziCnL-JWoA-Z~l__73lI|1>*y3kLOF@>y5qVhZ_V64T6O4-jVLVBR31dO+zuGyO_}n z2m=AeO<@7oZsew?4uD@v=$#A|*xg{dd^2+CPH#@(*z^Q_jm6?JiIn*h=Z}NNb_Vjd#6Xe7UY0o&!}*XTs0!jkR*oR8wQ`DnQ`F5 zVVV&7Q;}>RT%f=eAeRgZpKuUVfKE`C5ussuyO$9pcR~FZCs`v{}S50eg!+xHwostc{*6@t-U3qLp~F%KTVmfz*6|LYDV< zwkN`FrY9l!CL!5pI{o%&y5F;C@V&?Lj~Zt>8~=@X0|vfV=6aX^ru)u5FR_s)X6T8( z=Z)|26YjY!-0Lqj^p~3iV#YxdqhQH_5M;)on8`id>^>|$lvspI4o2XX(83x6q(OEd zK)C*Ie!d&73wZxBBe0$-M|6Hc; zMApsG9IwfAuh~TJnTI~(Nq)mgzI~BbcCnk`bfDzYX9I=Ve=X%G+^VfWz)z@{cvY!jqNQ(=k$HdWisTu!Z4=DlskxgZu#`z#txkwe7IX5|nX3 z+({~xfy^(6DS^7__r;un3!sou{{W2soTCoM5A@t1?9+hf=TX;&Yblj6gu>gzB41MJ zEg~d$^Zkgq{=`iG$xPqpnKviWyk`@9r(%3Y;(VXQdG~~U({S}f{!x>3N28*zKYdn^ zh4A~=)Y*1ayZGh(_A>7c55B>5iLtwE&vofrUcz^M1V-L66L0x}TYCHkK&?~s62B7D z5Q%xHL=R!?-~-$m8rZ|7?|5Uce70%u{sQ2)vA%O{X@tKv&zL8TkH7$sIWCU_XIWpC z{FylVIfwL@_zXB7$jnNh@eAEIku!o{7)~&_S|EjROGZ%`_-RN=0A4Wx#@`fdtZuHa zZfwFtuZ^AU_3h2It&P=*k1y>-}Ak1>alwOu@?hG(FZ^}_e^*GNnf2?oi4wmiZv47)%_>SrCSpK&~Yp^pHy zgPEE7StH=y5H~~tIJj9T>Owb-p3EYLsKgbb&7BPnm%~_Ggh3GtFmDDlYUpe#odwe) z$jDgmoC+#oG!~u4UZk>EFcFFj#%42kt6U*pfL1Movq#4S44!;v5b>i?1H0n_cP}8| zHEQU46YXrQu5n>p7K6j0bJ+A{7LCmygTWXr-U@5?Ef7{>vssJ!2Pon4cs!Vk24bS% zZ4q?8r3Ei}!jB(K_~pL!yD}byH&jEeh@LI*pDXks7W)EriH~oA-_>K+XC%W*?{|GB zW4uQIyT63r-B#!ddL%AEyjL&t-nzbH=q@(CCf$1-f7?s&t~cNCrqsk+W_(M0z+Y$t z)B(Zw1$6-SfT%+#22~7LGHVr%gZRdO5A1?df|)#!_=FK7`c#^}c?Lo}NF|{x7hndBg25MX_y^Ao}XpXU~&|QkK-uH3Is)kFwYOA>yXby zZujSbclY-JdIPrvEie;3eixob&*b|N3cQKMK7;}v!Xw|g9KX2?zln68;SA65RIizb z-V@O`hhx0^V{anfaP0+eDE|6W;Jv6XauL+j{?fkQAK=%0$MBli=9SN8ozqrL+{NO#z3dCa`VE--f3u_^@kWqu_@eqRRV;h)- zroyD4Oah!%pG-oDQOG4Iaq;#h4}`yo4DuA@R6sn6OdN$L$cSi5BTXbSzBQYFbFRpHHs5u+`3qxLt}EykyWClQ_0sB_*R~C> zicGHJ`>tVcdx8q>j-eNB;w3fq5gFYQ8TyHh{3S*&;S1zfAGzV?AeDF*vwVO-8^k*| ze;#kp-?aYAZs4S%tQ)$!0-nI(X`sGS|%0cPle0>b1gJDeD+gnSkT-p+AevvW9 zq!SjIb1Wtiw(y)upJg%T*o%M|kjEXS(?;k3aTvWgI6l$ye6oL(JVW4d*iuZ03dg_~ zz_aRS2!Bf)c6}|lTX=idfEGg*VQotYgMsE2Xkf$z`aGEdTf#gQ2B6K6&?|&_+7y8_ z1*$3pm}5oQU2FqeDwPVZ7s5AK?(}`^{^Yp;Zwr?+Ts$xQ?kZ(>XyM3q45=L-&M&wH+J6h+J@AbsV8pYEj02K8r%{aLf7O=_yU3W zF7Xfwf*ZK`J@D554X}#>67Z=gV$x@%P?bu9cmWLBgE3YR>jR9wb&cSQx7YbgD;sq7 zDuuPOz~U|}!5}gog}q8%JOIUi6dix?qCUn$3y6T= zK+#3EO6Cq!5uZd%=J`xN@|?-{nttRpo$EcD+QuDjgw>|$`;1?hQtpIAMRzR*7lEeU6IBN*CkRFyu6ns9my(3=R;==$< zkimhGtmF*rRwI5RmqH$6+9h1q{gY?x|C2N4_6 z*!cYD*y8-c4tHGzy$1UF1GJ_Cc6WjM60&-(33Un>Vjyjf_rC&(6WrDwvZ=B!SKd3>t*-t>A2FG?-%r(srcXF(%()7u+vA0KR_! z(?9yi&OZ%pNQ&1Cw_r234ecnzdpf1czq8S6C`@rz-Xk827$ zgs!(&Uc0dJ)|E}eZv@7dC3~-k-?_fM=f>6n4~f|giSbQ=p|=Q{e!>0jdSD|!@NJ24 zfW#QV7c&h8>L4`>{n^+>uIs<@14V5*l~|(^AowESBEKjW=~ztJ^O5zoc4lKUrLLJ$ zRx_JhGMrJ+pPt{Bk>8(HIFM631O`viANQr?b|>X@CFOSKl@8XoO%KD!O)`r9nRFNk zIZ2rrU!sr&ye+8U0d0WV8Az5x))%1`oa~DPGlXwYyWWzlGuG*o?74o{Oc!IaeWJgy zx3#*rt){1?vZ<=5x-_@;$)mQqGI+7Sy|%Bd`dLTa*t1r`;THp^yX$wQtK^kum2;(GQNxVK0<2f3BV<6JAH~L1`{Y!OMP>l`h6vY=&o#L;J*LE;dz}N2ufq?K0;Jb@m^H9(U z|34VJh!_0uYC@+9&6rk8pjHO;Q|M+{UDr)c%FD~R;bAj3Jo_(S?Y!*Xd*NdL?|LU+4ooLP)sYw`k z8q=PCuP!UFHZ!0q)vq|tBQN~&qlj-x;yj)v`&6dhsz~vzO7&^T4(O@Lo){ZmpdjD7 zi(i;w!sT*zc`qRM&&4m?e!j&or@vyZT2WPNjH{B;V*y zxi*~WJ|2BTk6*7}7Qg=i`27aq*W{9T-}mwBAvW>i8+r@&d5iTBMjjaAH{ierZezv) z7{YH54%ERU1b^$s&*KgM<$bR{*9+BG@Q7Lv?Paa1yLu%_Nz0cmOgTIB7#Xzf-Pd7Y z*k`nNc>kNv_q;az?r)#H^V?_dy*jY>KZd~I%eP+3bI( zujuLA@C*!7B`}so8H^D&ov^vWk%=(WV}^RTAhQ6TaQ8ZbO9s>8JlWd(#zGH$uxz?1 zY52*#{({@Rd0ySAUvL*+qE<#(K(2051_b1Dn4eG*_>o8r;i z(Ks8e*ktN;|uu)M0Y`~LhpNjc-{WkSG#c|jM|oR$$(vg9=l^%*Cuk^{}8*$ z*M<}RBz8+Keg?f|4C3_SUz022W9lNk@BJQjCA-+&JV!Hd|4*q?Ktqs3DHW)t zP>lt~%h}QP3=5*;X&-$wVz8(4-+$HfPyg`zSO1Ij%CDKfdxieHeRMA=z;@$zk%L%}|6{-#x?bko{dD`&<29ulM>*b;SFP)g^sHljD<< z)B3q*Fja7EeQkH{8A_|@Iq%Co{`9|p47)b~yW_d;W8i(k?`GVbO1?RgVHejFwF{5&Ldx`en6z}&I@Ar`yfcYZVi^LC!W1x+Vju*Mqe=?|IUo}rq#~7cD(oO={n*5g4!kd<) z*AJ3jGaLTpQ)Z;T^1gj4G7QEEf!wL#oEug&VOxKBP~mwdWZ z`q}c6-@_B?qxXp!M`jY8CSt7zgWv18`PR`8D{u3CjG8u?M*_94CFO*ZpkSlwDN_TwG! zC`DGr;Bi6j7_-Sikrh-m~`F17XEua@ln$vjh+G#;X$Ym(2UDy3=L_AZQa|gz>(J_DP!oKnz94g; zXJBD^25KV2nYp>y1qO``B9$NnK0|l9^xk>gSS_41xm+3xjBkW=)d{*B%yZqjHuEF_L!}II=zS`J#k!O5)!~Bxy zz$NjXEBt-ewvF6(j1YVU`#nW_^tSh=7(B4)ZLxKT_)vtzH3~QKQ@?f|Gx~D=YG_A! zbuUb)M%ZN#CMGBJ*xf{HuK&wp7o8x0l1#_q=&}bCAC!npQ~9PT!h^XYvm(A_$=1=5 zt&=4?P9=Q%0=`SW$hA~@xKeqzLg@-(#6=S4La`IbY-ivHLq&$a=e}z;|K{(e_q{vi z;LzuFtE0Ykl+7Wp@rVrS1c@-sVJ~4~=$h0c30*TvImVH2`nP8bmiy!8n;uNphRrv| zv%6AwJ*iv}(rry%>d4{r=5qS7V8JA%Gi9M8mE4&|@6BSv%1KzUSsO;kyFM6xs@>PR zF7&g;!iY=J60QwCbRSCgdY*K1G{$2j%zfzo^#Q={y&FA2SGsO} z-thI2f|KT{hs_@AvCIBjU{`Y7hr5ei!}C1D^Ba4=+Sq%Mci{4d#U&A>ANE|?`95|H zJVkq7V%HF`>o2kj7CVPyu2E8>TYz1O(fRqSA)QaFdl#wGKg90FFNWPemlwP^4?p$~ zI3ZYILEhA*PisDDQd*ZPtV`8))mob-jYEt2bgSk=y$UNxww|n0ooa>&8M>2gx|7Y? z6OEv@sy)%5J6f)DPEuKU3XG4l_889ZdvDU-uIHv-M}6BEi?hJvK~nOFi+6UGdDGR9H>CHk`LJlDj>awLP5AA1fD(m+p){ULVfk4rOzOa#lw2 z*2eNT$MQD@vp~{@TwE zeu?mFbZOoEqVT{a5yJ1)tpo1cM%VcU;CpWf_uUZg1@jUa_=rsWgb2S8*x@MIfm<3q zeu@9v@%v*y{PW*Kjp#dS&CHHAWkLIKkH)r6Yg?~#=+N2q>KyxZr~7pu_Uqhwb%#5& z$J^A$yS2waioI6{Pfm1dPj=}}b?c5d>OhF(U;y9nL*_fCguQzv91wOp{{(iSIsp&p zfRyZTKUMyM1`7VeBa-WYql4yc_h z;?I-_r;B$+a#mpsVWH{dtdz3*7$49 zNjJI^ZwyDfk36_Kc+dUWU60 zx_fmpMPdBG1ua7SSAG4qA1lV8F^0d5*Be zq;0IQHddE-+-2?x=ckRgvbw&+W0RI9yXPBn$8thO5AUefzW}dn4bMAZc`OP&KSAqoc&B|}Dz4uxLcfu=f9d5W5{Fk#kF41O} z&iNeSui3(0yMu?@A;9evp&;}^S?~QKlYl8gz_dZ&IQpO%e@Kjn#XAss!;rXZ0C6{b z3Ug%2@WhnG*;(f+^B&g~Mv)6h+}-v4xT{<00VCWWy&=BfM<4sA9|yJ$w$MV_by6iG zO=#}+s<&0kZ1d%|`6`EEwRO3|rd$f$HoXdEcJE|%#q+KeQkNQ~L#@WXM(t1~cd3$j z)+wP_nvP%CASL`6n#dAc*BZ2blix+(3;QOfxfJGyCybJtWA!OAO15I~-Ar#D@H^3& zIo^>e?s_inc|Fnbe4^#yXybihg<@3~@ zruXdLPVQjuP(O9B7p9P585)J$L8A7M8B{8}zYjLOK$i$;((^6#_6>q%hUq#cv?Xlm z{rIjuH@V^N>Bji5j`$FA;^FS3h^EAd%G(jew~tiZ46lz1ZoClm@qF<6bD=foLaU+z zE06DZw{Ow+{;R&>cVE?I!0)q?IqwMGn$0W&;MZS(+aW^j5~2192mvCKeN%*e(*_5| z(1D|nejpqg1M7ebn+EG(5C+y^5)(0Lczn__YS!(_yw`Q5@hR5)1k8XiN?F~-siG`{7G`|cvtpB zSJpO*Sqd?{{wHqo3h+L%7vkj`(oKimvK`0;4-Ls4Ba!tVWpwdY;lg%Wbl_!eZ| zX{xKFlRG%HJ{E0|HZVBQN9*sUQ^_>wtWED7fc0p-&@reJhWzAjsJnyQ1H{v)y+dRQ zwV~^MUel|D4=IuLH$&QQ9-<^3rX)qQCq&f6hnHUuueuug@yelw3qc>w9jg25*o7MD zPfKRK!ntcU525_Icz-_LUx?Z%MDO7f_X$n+P7?M`8SEFM4~!5FjuOH7hEC$crXl>I zLZ?unkOLk!JT_q!HRB#9^|_%k0e1Jy;r@cy``2LlyZ|4%bHr^=CtDth!QtxJZOYKI3vriIaFOybxc;(d*-_IMr@WrrjV&q3BXu=0>Ag%Q zZIHp}r^6->cK;xUPUA9R^9Q*f*0GaFeI)82tgQT+dMGqlaoNvd&;@;iv}RKMo95Sv zA5u@%-wbZMevlL&OuiM?aTAs@gj8J&uD%rX@p4f8g@f;7f@)(zYGQsT?$&3eaVVW^ zU7q*5xw7AVotBNwYM&M(?5^EBgx<~}Z08ZS52JSsV|Mcl_YRxwnIP_+MCsWLMA#*U zOcFw;umEnzq(R6yCTz^;=(u&%v`3uG_omwP^pCQuo4(hLQS^tuAYbsszy3!~VTG-9 zUL_kCS3l@g?XHqKy^%V;QMwkX9mqeco!FQWd5VqXuq$KchPker>)nX8YtDl=IVEGOOV^yDDM4DOJ*)HPw?d z()M(u?Frc0>F#IZj%-0o_GojKo?k(I8uw!wuO4v^BTWy0UvA+gDC665&9^$z>UpGF zYW&exuQS?OD+dQV=?v-s^n;+&`{;}TI+U&9uo!F_b+Dh(H!#Q;WU{}Z0S0m}gUcA= zlIeZ*l#h9BuaoNUodJHEuO8^ScCh<;aOYJdM^<+(_@%y{4;j)Pt?X%)pZ{T;+5PIA1@N2kt68$TFL#D7HQ>fqxgWxfki!(a1 z$nW}i7+(KTet-Ab{kL6&g^d8|x|>>na;-tJ*)*c7J@|Sy$a&Q`!Ep zuA}K=N8@+Y*;vzEU(#Lkx})%6%d;B|5PNThHeCzsK-dlKz829DcckUg(Z-8mA1?;h zo!kG{vI}*evWsSX4s+ISWTLjR@!PnB?E;KH5_^ez1g5*kiF+nc0i&4xA_7>4ATc3$ zf)Fx=3zF@qvWQ{FUUFe-y zNTsU%YZSI`l{RlQc7+R;rAo_EnR~I^qd*B4oeGuf%hZ05AFI@QltZU^rB|`S7kopN z))%V!-d9BIGD(1Vrz?n3XQ(3Tq;tAX8M zlUqU7X=gUQxf52Ddiq^*^y|cPuWrY_jE~K^8Jin_@!g#(?`~brkH7Hx*2UaAmvV1^ zLl@!R*S9a{CtNALd!;t*VtranW6H_KJBOR&L)vbJbj64F#2@dz8QFFHbW7Zkj~5Tt z#st)!4g4H;kNLmd=bGt-%u~R4?f*vHozi9(P6N9J8=2^>Y-k-u*v`lJ3ov`YHwesj zPZ;i*MDH8L1PCGhKnxNahD;Dcr(o0uGZzMj#OUA=qa$NBQ8V6G<(uLc%ui|mxm$wU z3HgSKmw7c`%u^t~KtIp+*UeS@E{1>jB2=bROv@F$qYL*anw`}uM+m%c795MT)@5qz zGNpI1$_LsAG?#{lQBp()ZgPm?EP?$Q=cY)tV7d%7_)H;FZO|NxW1wKly%-ASV2p88D{-1(mnU6 zd*&gx`}^I}_Iaf4_DI_9ma^3?ZM*yZZEp9rx+HIPN!o@e@k?!WPW&vVge}fDHoM1f z-;fcqyWm{thg&Dw?w;zndmI|BHA4i3N#W}eO>xH>E*<*_YZ@*<>B|QtLy~EgRHZW0}*@Vqp!WIE$ zn*g(mYq*1>Kb;&Ly+14dE0lL+C$@6migu{z-Y+N>HZU#!NF0w0CNdy9NU(1XB>I6YU%2Zb-jM-*L zxwBn4lJ=~*mWTH!-my?zL{frsBHL~j=9at~bNts}&3BdA>h)V^Wdepq=XhQ(*N1C!YO zQ|JKj6JwYIBZgt4R*@5)ang+m8uN?lpI|C(e{XQ~$5*fFSp%IDA_|Mq1-n_vq&|^o z1U8jJCJJuwmilj{^V>fho}EG>S5hJF_9(ZP%d87jwnbnI7JSRp-W4j(a;00T8hUkl zma4tVpzEf>v0Q}=|xXg*h2ZD;*123an?uArd_b)!>UL0gy z9AHrpV3E7i?Ad0+tc}FS>j@8i3HLp*>2BzBS5&%-L8{A|MAub!Tvy(4Tb1CpD$#vS zlKa|ZPgIH*I@KF{&j)|cmyqg(yYFZ8e4ks<@m-bCfgdl2w%k10apz?F?PD$R5%t$X z>n;b^TsTx29b9=f=zZ*==F7n!&IVK-+g*8lU(Kn5HIe%(BeuWY<^0&wB-!08BWOou z=Dq%&_MssrjRt$8kjn%r^mq0K1_@+ z54U~Y}~3m3PAZMbO|zj+w)zrZeb|0sH&7`0~{y=M}$X9|w!J!6=F5n}L&`N?s_ z7r4jGZ%9&`UsEnWI!ZVQ{5HRO*~sb#eo1UbM^}40h13rfTvQsUNdvvl{@wY7oolf3 zk~+3IEb81+5Q;n;r=2)A=9n@!#JQ zen=awzdr#8H$NJ0cp#`whDK>!&!RuxKUxvDxA>@UQHWbnm`72Vdtsta}&cq;E%ex!m z_B7wv*Lpjk<<|bz`2DT74z%4m*mf(Z{dQ2t9ay^%+Ic6e<91lrox?pz$H{jiNy#U_ zWVbQyaQ)@b4;Mq;$A;9MNAP`rKIr}V1GQ)N)to%={#5Y$)4|op_mu^2dcD>Dk(+Uf zyLm?N&ho4@N@p`noD9G|rGWu`T@)}4ORxgF--*3b(AVkUzU2aV>w<<|clL7<_>rzEvLa){5=V;HOY1!v!*)e(z zK8v9}i-99(;AwRK)3b1$^Wr=|HoLJ8D^X%+%?CerU^ntRdJymA{-k%Nr7=5_*Oi20By(K{#^R_xX z26kP|9|rBL%uJ(nH23$z27LrvaJ!R}ld#bPkOjw}Hx0lpoP0IzPJ-txoOV0N_{qKR zXKy;nhlBMKqSgyho7lKbETfGA!lq#Ze=Z8~1$zY8eIm@B5%g{`X7@OD_at`r1ZKAw zE*b`lEKiPk#m#R@Qd|Db@1ODwur?i~cME%}DgwijbCDct&KtT|28l(xt( z%m_E!7l3uBy~_h&tCRHzkJP1()TWNq+!a-)2rH8WWp}<5zx1|Vypmhol6VgEIxfD+ zE4nEvx-nXGeWdW3sNkyb?bYEoas1a;xUVj8UR+}3TpG&0IQ0Ay<7sSfx{lx5N84{7 zZHqtBa^nd2h9!RMVuQgn)WigrMeQ#-y{9~Ce{FO~ZB$74$%DlayNiQ1z1i-R>1LAb zY?g7rzv59ErM;!Ej|zMAVO#$A`1qFk?^a}2v{0=EkY9>why8XXc^oe+Cp zk!(&>TgClI?9~$g*;=#4 z+h2^d=7?LL!b&o6(>-C+z2TPo!e*FJ%Mdj_fba{RR|w9xE)`MjU5LFR9k{^mNKMKp z_>cEMGh9y*L42-G5>zJgD-*cTBev`|r}P%P^cJV&CbVbey}QDFdxevKnf?0G(5s7# z7qNrcG5t@XsgI*6kD|zr&X6)rcc-32VlRMu>qzU(h-M@?7T$0rto~96_=eh8$TS?P zjy_a=_CQh8zS5`z6=#AID;}oxb+*xIG!~l! zrKq43D>z)eS6Bk~%fS0v3YJa59PztP?0Wu!UHlTe+kjmzW{&{93#N}pFgwK9ofFty zV~gwt39XJJ>~8s+-9Is2)3XZ|(Pu^nxSemIv(zHHZ3w%gElo8oID5_2xsYw!6B@S06KxboK8A2+%==xCM zRVLC%_zDe_f0>?l0lERwa?cOGJlFp`hWad;l69v0QDkSv$@cV&+FL@s)z|Ji3uz|bD;28Kz`_+{IESm5qrwQ{PXP>k6*g54uD`kUYX8~KJ$ zMRTAT1&!Zb!26g!)|d{lcdRW()bf0!IZNF1V6-uHxG|O2m=3dQf{&0aONZDyQhy(Y zYNGdP5PKonz=zOUp1^|!!iYt5V&q+rB_9$c~JYZTC;Mra|54lx9_=uJp z;Z4`V^`_zdMSwS`CMKvF_D!5USQ&G$G$ybxG9W)}cW%(mw_$tBjvXiq-}!Q@bBY~4 z-oYd*>_Br~u8_@!SqUhEy;Ot>$c1xAIV?E_IMHVvmattqSvC!G#QBqM`)NP9^saOt z#CWps-U2k>yI#kxAHuG|7Pi3-4tf_4wS$k^E<|r1!E6^}fZ!b>*!u?TS|1nt{2hD$ zT(0aByK>1mL(o&muSeLOXw4G0JqLD0O_{KQSI6#MZetp^K8*+S!tYbT^A77wgP`Uv zuPTXKnaHb1;FUv+MZ_Bz&9SuH*!~yiso61wv$@b)9I(EZ<#ctij z;F@!=4CfQO(ScwZ3Xbl1b7*H?(C*?R2dg3vm4x{}-C&nsgG=(X$v+oKt*e=xm;_k$ z-WG_2695;6ZC|nr;KD^XE+M;gqGxx)Jz(%BXUYoq0gT5G-jk2^=A-e<05* zg+>93#mZnq#n3RRh!3+54e1jtnc~*x!scu}yCV&E1r49sh0bi;+7xazl5YTN+2waY zU}k|`et9BT2SM2_0C%|X@^Il5LBZuE@!wtIy}h_3F0|Q1d_ycf_dM<8xq;^~eb1tM zpG1)}Pxm}J)tzyw^L}K<{mAyT6MA;r;=@~RhBse7+<5gc7+x?9br%i+yVcPLk)RtD zSP^xg^yI#R!#m#w?JNuls5}u`c``Wvz@`ju%M@pm?0uV>9wZC z>eCDeyNEB)@%!7%!?LNVh3q%u9{UECJCRqq^`qUHI8Pqhi-+=M;Qi>vJ{+PS7qxlF zU^@%#&p~bDp|%RpTZNddBiL=DIDZj-_pr&qVcTP3zsr(s2`cNq`TaxrhF|bI#S)T> z1q}~i1;eC{-{Gcb!p6rV4e6r#6n_0(E-We2O;P~A?3!dYkXwYxU1+2x8M>J$bAt#m~(a@JF5Rl6gBG%wvcCpyzkbbw>Hdlagewj~~E1JeNT-ZPx$ykWxM#q?&(2Hi@-bdKG_dPS`^@e(7Gxa^bnK$G3ejJ(8#HWpOuYWG zWcwW*yAh*=K$v6upPcK3IX1}kx|L~se=*0V`Sl!IhQ{L5B-&^2AH?==&#`^Uu4D>g zZvjl9jX}NvVK-;EDO=c>IRYcJ-?Ceq!l_E)R3?FG&nO#5=!Y@#(w*^b2!WI3+r7KG(%Vwq!c9-HV#+^y<bRX;b=&;ETB?}U$d9yIBYwWGBgZx!Of5q|= zx%!i3)6#{fZzf#*Y0GVT*E;tT+y{y7ECV<0TF)VjH-q5C!uzmL8;8)FS(wcn%q9+Y z6BoZlK-eZCY#qgI5n;9p2z&VEhetgw&cQr|)lKD!NU>oUHTZnfi+$^!)P2XXl~6^`h^EUGOubQ_NOz*W0nyXJajo#m%taG95;1 zqm9X;hD2_CBC9@`^Wp9=4A>ySfH0dftmhX%u1&&97L#2qArE!cB zfcNrX;l)9K_g!q?+w;9|VkmjhGZ0pC1`c&wZ-oGN3#gl#!iMLl*_8LjUV{yF|jb zAz3!Hpnd#i%3&j8xmh2^q0iW@m*_gU+KIi=m2KcLg!KS|nW*(l>;?{gJs0oCBCKZ_ zZ{nJ66&i0D!EY29tcO_)zG3i)FNFZo;rY$#XzLEtS_I+kgi zi?wb=8Yf`4LgrE__b69+muS38HBiODqkO?BN4f6GBzh2}y&6}8&$e{(YuB#PN2VE+ai}|z6_Hj~?xb2C!@$Oi|o$sM8`eE?Y2Pq z-Q=TCs-fv7jMETm>#qcVxD@pM!oj-Oz#3TpcrKtiW?xnGUcG?b+LIv_M?&5O?|&Y! z`|+M#PeVhBuEdcG3a6PYEflHLb?t;Ep-UA8{{RQernK5eZ>H?mGnO0oVIBHR+$coX zfz^%(yK8}6cP7@0f!4F@hpHQOpQ(X;C(Fxbi`91_`Iob|q^u}o2~i5)jN z(I0Z@OLi%g4oY7)O~ezbXdIOnzsdZ3qG+68z^8c z8L(Tbaf0n@U$P5J)tA_y@-kRUH8&|0(WVAJjCJRZ zHp5IrlDPiPXhVXyF=ezlZKOGUv_4f_mn5!Dg5yYS;z&(`u=>t$)$QTR+k%Q){EB#x zpdy}EcAZmliBo)mQ+$zKbOFRFj0Fu9oM*l}$9NY*e;dts8%=)`&3F~f$cYBqOUphx z@bpYy7PP}YNqTUiJN;N^>QU%W1!{$YPO#`4syRVX5!fGmKJa}E%m4?}MD49Uv#09x z?#ffUDo^jKI=!pnWI$1P;EMyhGj?owuxmS#FUW}qd!LlZsHmD{agegd(1vb73o9TY zlznyK*blt6dVVME*VC7qe2%?qoYuG4_*um!Y>=|$HDz>?A^vE9uhfRob^dn zTPCa5oF6wij_{lFqJEIn_9^!MXY&iO*Xk3$>&sNWr3zOazrT*XWlFCSwbxSY1%Bfu z(Yx6{*+3D=AL19@+EAlc1&uSO`Lq1)@%}1N$18rrLtfooer=NQ{atbW{qe>JV;^A! z!=2HZ+oQF2L^ZdCRkwsy@xzrjL4t}K{POF1@k-;kMX}t%bKJu7oPu*8_PZF?+h|sP z^w68LAZA_^^UWFN>od$3XPDV%7}=-k&rT0MIn|$el8Q8`f_@W6y6#4Fri6DShqosl zZo3iIdMT{=LP%3=aKpKvkI{j3X98+Z@2ig7Q+0B8<%wNYClM8d?|Ks8f7gFg%8o5h z!$Ur#B=SGhDg=C_aWnV?=x2;{>qfd={2BAO4BD1Hem&{nNB`NFinXU2yHE%&18W>v zt6W$pHzwANf%T-L{QzGU&KH(G4B`ETj5cyiwhS9>7U4Ds*ZT1dw(<~mFU)%1RfBH; zc1?fDE{8@Uw?j9DLE(R9?A=r-vC30gzSY_nXg!NzGlkLzS$mH72A2|Kp}I4i>wN>T zTdYR#tz7Ud*Sfq?SzMjQ?&1Dq+y7m50a~yQ&~6w$N}fU@RhqCS4Ou-_sf)3dw|Eut-13{8vYYJE>pG)weW>`_q6)9D-koRV#|(kq zAY$f4GhUws(Q~6fv{z?nxo2oEPY*sjHIQ|(KkH;4Pz$XAq07(xBi;8RI`4+JCmn7} z2y00QZMh9wlMlCCI?{Y0yfHSc{#?k1=tH$<0&7kORzZT{<)YxcmJc=jqi|eDwwm(Q<}fk+Pz!wC`JtLuh@0@ zI=hzFrg3|D%k6%YT_ortt+HVZ1Ff*9X2*G>LnL-j-C#q0fA!PBij1L(`|QdzR(TS= zwWL8E$Z!+f!7xXa?kgFQs1j{)R!@!-WP}{Ink6C(Uj~c z63nGNI05Q>aIz!)MBBY%Eq9MJCr30UgxB8={}_Mx1L#Ir&84ua=&;JD(DF0EWv7Em zA_EJL?=L`n!Lfk1NA~6)*^?i!CqI1GoA6yv!}eqx4S99tLi6({+?IN&kfTLBBfLR% zwh{ahNLQWm&uqyBeit%xCmelg%Z++5cD*LfWJ9O^HTFZRoQ4cs8CX|3)`N!ep<{f9 zFg`4d4-@ChG~B>8-YhWOB*d;CUgOJKyM;>#657X3d!?u>lT>SCC(KR^hFogSdAaBt zKJ&{RR?1+UiTncafuYZY{2LziA7_Z~6>D#{%Qn87wRo*C&tI^8t8s@N@1;<4MCJ
+

`ctLN9bLgrqo_+{)>TV0>Q@8kb$KLEbh{QF}z7|S4oH+^#}HB_IL&P|VtM!18# zzBYDOHLJCN+x(i>^n(52DWmo=z2?!9Xw?q~t1|Q&sC=N;U`0B;G?h`3LjNRMaq?hM z(vk)W69ea!bbL=KF}6(k^{SIA3!; zy6W25%BxWoac9adpDMi+S$yF{QS9+WojY0xx*AiJk=j*KAR>1u#)gp&;Lzz#18r%Q zFeY7$w6J{-x_o|0Hv9n$mQAZPk6(>D_zW&Lq+o0*#!e)nW8Ydk<|-$qfeRh$Lc_tL z2X7kMhl%zcLVGi?J`5PP8ExVdHx6U`1gm|Z=ne;eaMy7Hsm>ZiPyp61i8g z)TLPNP@)Fk;8C*RQik{jJ-ePoDo>qn@T$Mq@NZv z(MC4W!ix;kO)%R(Cz+j|7?~0=CU~StR?8H#VUk`yKG-le*f@q98^#9e#b0Q&|D#@G zeeWlGYo{nRlb>1$f?t3goF%K@F>WU%lB9GSJHLQ>Z>m!+B-gH2UX%o>WM zLW^7(G{k^S5>RCvcBsKS6gnvWX?Dl#eFl~jDOj7|uXEhxh{4JZDJ4MvjVMwtAGK9%ZXW*eVfADbxv0 zJ_#;VTG>3(Fh-?-xin~G53TKCzn&C|&#P4mNDjgdH9+{!1+Zl^N_FN-v4eO2 z&jd2gnr!0OW9UFNuw|@vWS{_Er$M~y0LE(&qAtpdiSnXjyy*nLA;XP4!UkAA$zSEo zS-pvkJ;1k#nea%G1HY&XQ|o?!-^CA)f8+mwZ_vkHzx-*l7xN~$Dj2!Bypwwu$-Ihm zu@@GTeLeOoER(3N#^d~i@ngc5b1D*G--o2f*NSoriRw*NLx5$ zAO}dpDP))y=Ahr97Qy_JlyWWTS6?n(ltCH)&r(XYztB8XN<*%wK>CO2{walA3*AJd z@&(u;Dv@d=GWDEXGbht3WJvvZjZ~}tLdX@6{Iih$i;FiPRH=p30wNv0YG4;;)sRo$ zKX+R;qflkN5IcJHufUV=RwPr0ZX<_Y16%rP2Rhnm5a&36cj-ra4FI(Uo`5eM?M27? zGKm|w`1O304{xOxd(}qPZ`lp|mT&m{i~YGL|JQ!^m+YG6NQ_@8EMF^}-^vhn3%|>* z<`cV8Od;&PR9NK7o%5vtu4m!AbCK+uo>#|i-h$mNsmZ^{F4A5FfPt{9RLhhakR0g& z4>Ul22?RIf|G)jb{q5fd{>*xMzI{Y|fetYU`g$R8b5Xi?3ew4YNlSO>9gXhz`SG9b zh27_B7Q-##6MD9_j^ek3;S1$NeX+825>~G0TgT!l`hkg}%#n56~U%!EktZ#rh zV#F9A;~3gwJ^Xz#yE(;MSwT{q($nGe?g~3Ucor)8pVQ*{!(!9 z*8+E{mqM%lB(z01Yqxcqm&otxzMe*Q0L*j)bx3F|zF<+E7rIw6!)g zl*0hlVF2gUhjZ)4dC+klOpFH|3$fRSN$_LieUYWVt3BCJaR;-HYjqabm02dL(HG_{ zPcy@=w!C-^jZ51mhDm~<9#U6VX9o$I;!aMDs#K8jgO}>x{RY0FbyRz`Mdp(?ZJaG3 z=E%)o%AMaxy$a^Nism8T0J$>P5}3JIK;|sUWlm)>_Y#E{9|NRGNK0q0{;0+K{>(SY6l-+(ELhp5xGmNm4kq9 zsQm`)!d3W9;Q8x7g#?*Yqny~k*~{^x#UW{-_3g$O5KW~R?dQJ zq1Nl2#;r){Tq1XY99oIWuVldsu9vG_DH{DX)u_+*Q9Kj3$b@9$pJ1E;%d z={vu8e(;lj`zK#|I*ZXCUKS+W!sX95_yU{{G#LVW{EIt2KjeqbmQAX)&t6P;xb?3v z=w4~kv1VNx#;glxM8%r*p{%KEtozW`y+m7znf<^zC#H!j6YoYxyA7aRXc(6vyfX*y z!p3+p@jKX7r^ekA)b`0*)5}WxbIj2A4_UdD{oM^S{LV3M7n$5r*VJ0m+|$*|;Bt7= z;!*kRv{Evsn1f3CpQVyWRP&2c>rNE&64@sy=I0kBomWbwpCywjLGtfQE}xglB;S=% zCQ(Rdgp;!sbjgK!$n{E1pDBn>ROZj*PB~JK*K_V~B#v)owgpP(e2sU$2E2lEvBCvv zN0w;Tmui7u=TfNmuW~EaI6jaX9q0WMN~PNXzYURRYby|bd%0XPncCh>>fvw%(m7bx zyY!L&XI{-8dddICJm@pKs|>nVn{*hMw_$-@SnIG3VRx-{AI63X>>Alp4Q=}gjss}t zegl_21D8RBT^EF1tk)0$VlTq3$|+TAd0p;wffjP3F6(7gUq|yai!7#-s2yF+4dj+) zCb^qG(8FW*vPOnj<6QO_hds{aENOzro#gZMn&R^&_`FdbPsru#CE^K2^x_YX3WQ4% z4~xcxBV$51en(=VXmnWkZH>UcHY}JB4AGd3cU`=5)nl99N-STfOtKfuvsHH4Qm5y$ zjxT1cU(H#(mO11pz4Fyxvg?O935||jwOi4G<9(UYG5$}U{l8+D$AvBri|o$-&F&w1 zKY?%1KFb;RaO+#SwrjOXyMcKt*0d9A+>14%qOE!jtf&}kD#3|X-kt_(cN4R> zjYEcM;|5Y+SILTI(O(r@1rvMsWhm=MusM0OQKRpdiC{^LH&ABDgB^c zQr~w)A@xzn-w~PA-%sgfQ=ykg`|Ad3bSZDkn`xUDN|S7@S@wc;w!%JJVwW>#`BDNC zZw`4X&wQ18fx@Xsj>KJ9{ZOp+ELw0b(KwZ@FZe?*|9|{9 zEt^tmpFJPMwriDfyTLjgzeZp3+Y9^}T9JuXy?9$H+OZem*J%Lj#Kb$XaZW7o zz_{%!ixZ=+x8zP~T8ldhs|$m>uD-wbyq?7&<8h-!dDg0x<0OT9A+1X7#y(?7ZY?u zOcr@bLo6~PHU-4`Ld+pDo!;|ZG3iivmHa)C_)M~xMP&>Q6tyufRq*_C$E=@7h>ui8 zj~C3JYHf2C9C8=z@)m6JG)}o{_t#37e7VCrnd3Vs{I7=6{vL%3PT+PS8}Ux-kS5o~ z-XHZ1x&!V~hJu)(G0Oz&ptXCcwRxqndZluF zsdR((*>B|bZ)J}8a*uavp8~B%fsWl0J-Z5oU5`J-F5(M*=e7I$(!#>BDfvS7GqI;r z@2XWDYly9AlV-e8JB~ocno!UdWCL>w)`DbYO)|CVHMJcuw(rL|P}ka1*4Xu;?FR97 z47@!9=T0a3vu#g~dnc$|9%>EmEugMX;LeaOk5z6u{pR%T!bh*_s@`|DcapnE-5oux zZ9NTLq$a45P3!3z?CBin?)bXe`+8cbq-MQ(dz!wc{+?D^Psevf@97xo>12IV98w3f zyQ{N>Qu=}Rppt#MK)ChAgiWTzC_`bCp@^VQz(wC?YqlZD2ySmT1kUWMV2;h)?GmRpmpzQ{LxZOry}?EUd~<+3T6Cj05A zmm_8M%Jwyc7L;)l&ae%SCt;1rXmb*39T{g%GP3M3u^<~;Qi(RbXj}4Xo1Qf`WVG#o zo?W~C|q!WksEBmxThpW2!C=P{6TgX zQ~7_)1pY7kuCKFeL`JXs#I8lRky$sSa+|rCSh#)7vrug-EiBG-3hVJ z9ffO#+A|Zb{q2YW3O%C!sZ4LTfgJ*Z4$ymUnpdlDrOAEy+8y+UHP>Pf!hV{JMODs@ET= z-0*Gfui9|1eEs2~^=Ha_uC#b2F`OTa**%t8WvZ#%z9A3?7AL9^DaZvE`n({&Y}lx-D6 z1>?6317@Lor??r16p2;FoXbn4*Bh0mC89+<(#8*%9h)`kXpZ*x6PHgK>F;B)-_M-@NU5#Vy~Xx z!y`Yr(R8u5ercZKYrf&{*!$xcFPoOAvNJ`V_T<$o+SYtx7l-S?8ImyTdQj^~IP)Gu zi!LLxE}~fv-inN~BV+8zuwxT%Hwf&at@>Bm^sMmcM(-Oij~;ePnR0(Baet+7c&S8>Gx(?u;4T5}MrL8OL!57x}o@5%jrngP7^H(KBnKr`MjH0UD%%qz9Cgb3>*2$71$+NdFt71 zhuS$XN4z*s!4}NV!dh3DPt{Me{XNI_qx3GDk|=>)FMINu6>V$r&1i_b#8!k|A_-&K zgEA%I%zBL0bs4YgHe83KdI@&DID1e(!H$N9pld;0VbQhRv2)dC5;2tNd_%k;Yu@9D z-133K@V*BBKx_C2T=0VRW0g&Y%qmT4epg|dqBcohFa;$cN0THCqC~Y(g31_?8U*Kt zi7MmUD)TEU>vL+5)p@n$Ih93>5>d3mA_k;1k5-t+DB!s6l-%@)%rruV9KWWcGSl#R z(~w!yLo=qqpJfs}^QDXrPMe0!SjNoRLFzAAWuLCGd8o0@P+C2hw|Ow<_)y~VSn85B z@9<>SCTrR*OXB!cZkHpoekrkiC3VSFdE{wb^z0TcIKR=_-&GOA5nnL!lRJI6HRTh( ztz0IB&mr|L&9lwI95F0=(J#^Yo8KSicUr1`{$$kKk+NoG`&xVp+OP?aZ^NTWI2;*c zNJ0~P@P^$+#+^o{Jw!7KVO=l5q8D#P#aZ{`tp*6zgIJ4xlqIYKrl6dtP@~WC5YOe( znDe!1`)d;Wc$HI<)-6@*o~m_A(Yhq5ofFk?#U(}S3QAgVO;EcgYFrXDE=iyT=VZN* z2Y{UKA`kOSQu!pveG)+my?m3DAirc4Xnl%m{arY!{Sq`jH?)XuYJEZRi2QDA*Wb}@ zNYE)!yDi$mlZCwGaHxx`7Gug*Ki%{yI_+22;$CNJ3D z)7Yk~ZPHaX=?a@PsqKBKLx$Apq0H%#%;AyL?vVtdv3;i8FX36AB{J zUi>;L&b$vqup9spt!M;mI>C}ou%ct#Y4}}qlOV25#Hh=uSO5$*8*6!rA zZRCtY)V#}ixm&EvEoR0wdeSXs+7(XD&$!0Uxm*NETrbVLU6#6Ck-1$_xW~ynuFrVh zobZYVO?uy&^0__jduL{S;_QZ`xsAz^O)2xB&3C06ljQ5~s6ZPMR2vgjo03$UQ&d~- zs4D0HU1fzZRzSwsld8YCxvf{!aGIo1-dKqygTo4SK^*J=aD++nkunP zky^mkrWBb~ip=`1+$L3SdrxkcuCRZgaCo3_%uqT$L}Z&Gw|yjcdZuuF4hBN%0C6~5 z4HL?Cc?zdIg~tn}bBfgD#OMk?&MJF`txxl*n2OSp_sqdg78BSdQK(ejFmGNqzOVo_ zkfeXJ`=i+E9=B}zyX;~Sc8NyaIAgLNUz{0&FCvTn&+J;!@m4g*|Ni9Gz0!|@+r}{7 z%`@LGTz5cZ8~`=>c(~nM{2m@LVA%NJsM(KsN5$)ojjcO24o8ar)`H&FL}QGvjtq6ZU5(9ipckW2T+X%{ZT*agLpJ zik)+el{m)EIbED{z9ezEEOEUeal0yUzb^5(G4FXp>UmS@6)*L=E%i>2dnL-f;Fuuy zNRYcF%3KqruF3PR$r6{OIp-A6tYh+=b@IGLlFTAmYMCru#P^=uHcf%xdtd4BK&i(U z*tLP79QXpqXL7es{Mu%#fM46!a;H4G#|xEHvds86!tZK(hP_|Qnb^wmvX3l!7iWmv z*GKN{>*E8z(s7OEZ+`zJc994F0KdA}tLK+sLIJ__G2U#EUp>MEt3jeQ4R1|bW8M1? z+pd2&_N;X8Tk8!4&xTMNSOy!0R{Jqm`ZD0S2EN<`MN_#3TZYzdWv<%?G+g8MUI#N5iVc^e_#~9f0S@QObi?|JUDI?G+}&b(j;WcG<4cD zWZEoj#w2{&_{g;Ju_=>dQ>MqKO;602Mb4}{HEn)o+9GPkGHTZ9?5tJvtWE5!1K5J= zQm1S4j@PC3pd0h{HzjuQpgG(4Ih$LcS?k+#mI*q3ll1&bKZoD6-|+iT2G-X#TkiB! z>hMHv_e^b*qq2pa7q8_WIS9K($3|E9a@II79M-pz3_MtCJy`}`Y?K!V?E?)2c&mL`tNfU&)(@>(&sx2KwR$6G^=8iM zE!@>xd8@baS8o@9*7y$_?1WCpV)X7Y5N6LfcJBmk-y}X@im-p0cwojbaMtL+tkJe8I$ zWeK7)k|ON-an?98oHn*bU#h9BY-BULc^vW}jWR%^ z2}MGwbV{v;vKD%F|AiOtfB8dyklhYLViyeD@Zfxndho_1ya^d^O2Mt`#ex5|=)+rr zf#@e#4PdPYQFb%~I~vNKj&WpSorW+@EUXI) zZ!K(zgU)Zz^mvQVV5`Vr+layTQ4q?16uk>t$BqNFn7tF&eUrF=Dg6Fv!hsoL;4A{} z!8v14kOUtrL5E7vVG>N31bcWM7e0?aGEX=VU;eodLVbmQaV3XIAqD}G8HyYG*;Ov>z8teS8}&(r9-0B=!h8M*NNt| zsX6*mReAZxA$kXwL!vXNj3JhId|WPvjb9S&0*u*^{RKZhN&3J1kw3~Wk=ThNcHs@G2OFSP3xV*v&q|2V5@L*maDI*uDolFx6_)qpR`=x&nM&s;3WrQRyK1X! zfiU|=um?tQfnxl@G5o=CLeMzz(1cO&q+#$RHe`AYD0F&l*tEgn8H4Z{RKyJW=nVGw z4DQ4%{^T5@ND2Ow1b143KLeV>MM-ec68yP&!ufgP1u5~8lz2r(jFS_u$_=l{jc)4j zy(Ke&O~7}gW(m@DNr11^B1L9-7r|FAi&VK~y4?P;!YNB;|5ysS2kT6gKp+_iu)wf*r_aQ(&&0XU$dPL3&`SV0 zQVGueP8@S7&Ld!%s6l-WO$+CG%Stc>N8Py9Yn+TW7m zLq~t|Vy$wZI&AtFb*Z$ZteQU9%;!;th1`j$X(-fymz;(moN3ID|q4l-zl)>LDMUSnsfkyEc;hR#$&mp((c0Yi^LLrK%?BQ*drV9lpBR~)lR||jq&kgCi4;`)lbdS)myO|mlwBgO z9fxVhVLNcR4&0{*j6)(Yl7!IBU+AVV%*i+_65fV{w5zf=(3u@C3mk8>Ho zyABdS9yGi+4eJL4BSDz3o`K)Mgx1IS%`Ds&HVD6!1Hx_Np|=YS{6`ITAVTdF!w?R7 z&*Sz?;`dJB_e~K3riq~aGq|7`bl5EV|7Y(j;M!W#^gAp6-Pzq)o!uR|J2Us*7EK6H zD8(TOPD|bGwv-y$;_lJ{6{vfuP&cS65S%0g*SgVS!Kv_nP7b7LjL=eM`EgXSn-~!M-=Ldl~!sO%n**{SI@N&a;}66O(+0l~>sOs;0XWDzdho9wd@M z7&)?j_9M{~kw{Z%VpI4HB<60~!6rM4GSa74T{}l$V=E2dT|}p}wF= zW8q86;%4d+sLNWkerlmDZPNxV6U<$x99DK|tnAWU)kRs|MP1XS<qJn;UskQW#_OfQ$vS!M%R<)%aYD+uSmvw45cWN&0 zq^#(ouEf0QqOF0%Pmm7Q?wvZ`Z92X!I=-zWY5TWo2efJjwbDXbK-!_Lw6GSf@K)_8 z%-t?>`htNVSO)@r2@M)CeK!p<5EC{N*lS8)Z=WdkPCV3an%2kI_r^4V;2Zv6Q}<^J zp3TC-EqmFu4UO;KHKR5TQqKT*2^|9xdG+?aMZp{;_Ksxoj(OGd_Qc7iIaA6hjJ&HQ8F4fMpIMFSN9{RIO5w z7Oj-3Q%2FNq!?6DC)Uu6YH7yxG?NA`(|W3DJ;khnYTgLaGQUryH&U4O6ze*Qb-iE) zH&E;vDfag$_V+0c4=9r!XkrFWdPH%0M4kDFIu{y<$JDt`wB|k4n*WTp;2CYG`+m2e#`6wd)7BPYB^p2<7XC^K}KPGo9!z-I(t2vE6!c-ForF^ab-v zpfC8}tDqW4YawPx29H3n49 z*gur0eve66q^+K(J+4IO`zqSlIt}&mahgSHnnhF%j+O=oYl1X|#HDHqNP}2wY4f!8 zt7sFewI){446A7ZhaIYMEr?=LM>Pf2Q%#8IO;ip*035}p4yn)py&5QAXxE@=->Bhm zPs8z^#w3{H?rToIuQmOl_Uy;nv!BprKhd7^ls5MnZQe7j`Omc$yda6TpovEC1q+*~ zi(hE|^h|TTHZu)c}ZQ_OkLGNUENAqLlD)qjpos=PX};_v?in9U!r&C;h7 zj8`eu9$TTQTBfeSRtKX?Ws4Jqttli;qD_Gg*szRZT&`(Etciw6xrQl{pH--vSAsOm zt3b#CtY%ZKW(TTKMHXO?Lmfem^(vDZR3T@1y%z30f=aJgn zM{4sPtAUMJ@Kg;t;DygrNm}$=b?Gxzm!~Q&PgPeuRbBZUq_XOT%9jbxJhqhCqJ2YYi6xX2{ z&!;31L|xxO-O!MN~nFAer~SnPXa zx#cz0v+2Jk@#xN1(@$l*c*ySUfe#L`2v8UEmdK%q%F(E$f;IUsqN>*$ zHnuuC)QgG-}uw5W24+(4JDWyq$QaI}m$ zG!rgJOQ3k5n)4_YWr&~GqE~>Rc+jw|R!8PvBKdDEk-i#^JV?XtSg$szL3MJ2ic_P? zWH|LSs?LDA?19>xhpM2tk5uP@1s14&iCOzpW#Q9tSTA~}y6n07iWh32l`qs+y#%SP zex>Hxtmf9D>fQ=c^Jr7^Y*+PY`_aAm2lrP$dNhyqY#z6^S;f0W)we~>uSMOzRU@EH zGq8;U3T~%H@~N>MS}`43u^qHHzIHrcJAtpWz5}GYfj>Tpubb44H8FQL_tH~)t#))W zwznE>Ytl)5LEYM{y}f(F)?Sl@Hhs4mjj6Yn`rc&amG^YQe{67q3hSMtK`xlc3O+OC zX5)zhpS zv}_u*kcrZ^k!E*K%lBeIe+XXU? z>oSS&GEL|*N$4`%&||c*$9zkt)z)UK?M?JuFO7D!>h9>!+}uHpZ!(Iw=ipzu$Tx4- zp^7I@T47Hk2Od)uNDD>*3mW)|ML8=+Is#+*2l`uj-xXF4ByVrEcPjtxPq%;bZ`c3u zU5+NTNKe0LyiTqT1yi_0Ta8OoD;<`!poGvZr05lD=@ro?lz_A*0*yH|VS8VAN|c>ouA68t_81Zcw*wRJXaOZhK!1WOq;1?w&g2x5T+_KY_DIY?{93+hZz6LnS-1vL95F?|H*Q#_6QsxdUSk2y}EuqdVyUNLOLddbWRBE zGzjaQ7~V0FbVrNoG>Yytj_x#x=`@b%GEC^3nA~ra+-C?~PvGk8q@TYmq$zGofp zuIKdyL_RUGVE^uS+5h#Qtna=nP*pF~pyW|B3Mi_@>T1O*>ci7GO2LoXxj*XUj@8K< zJHBA7Ug6jYMdJ*LRSemx##}@mtD2OmnebH1%Enq&{=lgIky-tNfWEh?`QEzr2b;R@ zZR@|ctN)9DzIOosdtYP9Lyf7A)TcgDoAyY3`eTA-JW-noda6F_8Axr;bG5n8)fPNg zU-$x~v8YLN@k`AmuQZl6YbkAzM@@ab;mf@P8GKfRrgMinrEk) zSEu^gPK|Y4AWiR1P46y>Z^F2jw$n4b1m$X5;Y#W(Hz&2BIM$3{lwfBLTIxBs2@`~S=P z(+~MSeqS{9`(ibfA`MCbNL^DNsZsJ&v~pEwxvI20Rh+^@e2^`C&Gc9=G;tM z-lFZ&N&~HEp{{6CTivd@hOg$@uI|PMX?XB8Jo%&>kHC|MPlZ1ZY;nPzI+2~)QSdo~ z*{w@Ax>Gl%b3EZT*{K)bsh`lLzrI61p<_Z^zkb}n`1J#n_3wU+ef>jBmsVn%)vgxj zBdS0}>?Sv5A z^=6>|e$(sIm%6?Eo2+f?)To?4k1PD{`@%o|vG}{+7pwfGkfxSTQ_t5@lSi7W`Rb|# z>S_fVYK0maMH-qVgc1TOVH#IU2eBAsRK0R7&;)p)RcM))YnfM2K^73?s*nhniZWV^ z8Y&7IS`nmaT?;E5#R(?3dmswdQ|?oz-lt4^K$-qPbNWM#8IM4kGapl-P@DTqd){*p zZT<_|f*0Bgo3t0b)LGK3^HYn?vR1GWAgyI>>dX0RE`0SBgQT&FuK{QF)r2#+Gk6uK-cFbGV zn0J4Qc>Cw@PJ^W8B}ZFQS)F(9_dewJb-wO-JJ5ro4JdAjS=-r(EZ04lrozce;w||T z{aC~wk@B~U5g7CKh z;oEK))~*-XsTtX;5z{v@scZhBjt#fF?=-%ChT?fdp@!}^10AmiP=e&m>wZE;!(_#? zz+?sDg6G8|WF5$U{7bngG6G`;P#3uS_1ibC18-jp47BvT?Qd)vxSIDiHLYuHMAJOy z`!;sfhK4*XeNTqUN`zae zS=541HA|COMU-u^sUpa_ieg($v8$!n*J=t%#u962&?eQ7n_BoJX&%XbcIrqPx^Wgir4@vrQ{-bdVpQwLDE_QLU5KH`#A!vPy2AvXk$2x@T+dn>&r~p0pEn)dV3vC^6Ip z){J-{gjMamUI7!C6x)EeiaajmHew?%1~p(Xgs=PC-@bkY_#GJN>wY`%pk*Mh=G}=~ zy(x#=A`_l@`aPKE-Z%;5(KuN?5t}tQx;Hp@G&p)Tf*ic=Ijp_!u+4uewzk5^su_nkV;DJ-X!zn?JoPr+@Qpn@UA&)184pG=+$M7eP5lpE^W6 zb%=iI82!v4=DCm@qn{5_Y?C7>?xkb=t4ZsdCv9k+l-M#UsgX7A(A{mwj3y47;JyqEp#-HWb)KBCGK+-<<#_Vl8(6OkL|{j9|^z#qt&LgZrN@H~%W_2FV?mTr@Kv|uqvO7=abe+uYI+53PGQaC&Ue}5I zZj#bjT}SV99=X$XUy=d#s@MSW)lM!oH*V{YUda zuaD*vbgY1+{$qvx$BO!o759;Jv;bT7l7xNJ3wzUxdXE?P9xv%V!R|fD={?2mJ<07o z!s$E6={dsXXVkQpJnMMe(bEaiEwH{6d`)tG<5b{W5N#;@Ov5uQ)kP>z6d1e7D790BDBC`aHI zHv)tP>=$>_${SXWfN}(sBcL1s~5s|Dfbw?nv|x|-_dS13;Me)-blr99uLkHFXG3r78xln4A-j6g>R|9)fRy#`__ zE90??i%Qw-QZBcY%OM)P}jC`Uj! z0;4kmtu4*ZpFVA@ujjIh3F5HJc%>C(W#v3xIj?kRjRx69=SYd%m*5M?*2vdorR5;v z3dD)WVRK4~*(Jp##YODm;@ax!_Etn#C>P}jC`Uj!0-rhp{I<5*stO*bxPr&6uHe;F zmeo~P)Ynwj*HqP2Rn%6NlT=q(QD0Sw9(hzQBh%GaY0dWK{30qh*Ma^Eh;JJl-861g{vy7 zs>;iF<)xg8GG0|hMMW96jK?W~4xo(3@8CltsrX2-i1yP+L3-_>oL$_8FXap(X`aGeFPm6tjV@;eQ1BG zY)Uz_PX_JZd~hcy<E2X<^cur(!ZPujUtH?wYWIYl)UWndyJ%gV|)oU&3V19+9? zyfPk_&1P3sRwAj?mw47nzTiuYvhp0t5%_{5P*7W%bRliUuITxxzVmkk&fgg{e`nzQ z?Ewo?{pP3o%}XKH^Edm=P4pos$!Aug_pF4qbK|_`#$hdk;=Sg^c+3r3GdE=Q+|Unb zM(FCvA**DOQ}BwZfy-r)lmAbX{1;CatR4LpI{Gi35x&wrF>w3Ntv4=bRFrY*s-Y1m z<&{D)P+n7BR>|X4l$OF|1H(37DP(`aNu@m1FKGmRj(h=x&4-<>m+QFRXH#vrdl_$A zX|!{>@lF?GtamLpO}-;}jG)A2CUHL*M=b#vMJ_goSZElwz$9$GN$7l|;JHQt zb3lgva}4}vi<9?^iJsF9J*F9YPD6tv1COctZccixliritWW6C8zuG}}h3$JT)aX6*eF(XX^<|9j3T0aF6#raOw~=167sf&^=a{l~8`Zb!+)3CYcIB@I$P0J*)8^4b#VWIe0E_C)R5pB zIoxVqS#^0?Il(@%mDu<(6J=oF=g1d)+(;<*`els3n|E&;_%Bm=nbXdOoA38D+waLZ z=FK?fW0|&&ambr_z~6FDh{c{ji~VaY_OCPF?Pa#Z%Y3`1xnOOQ>SmncYO=-6Y?H?z zCA%7LSYsG3jS|qg8e|l^$~a<$fL0oZuP_OB5t31e^TfcVLNW;W$-sY!K>$G${1)i@ z&YJ-8nFG>aJ6nI<>`6A8ZHsK5Ei z5X)0xj8nmkQz7&-;f&J}mdE1F53e^rnqYn`&f-{{`O!G@BXQ=(63mY$3TT7*@eStb zpv3odY=hac4d#M18XwS+c(b%vbI`#d+8=4TCsarlJA%zq1I)Ju5bM-HlP$hR8@-G+ zt|chh%P`r?DACZ*#GG9F}WKx8GC^YVofA3ti=D0fnhz%O6~Q1AUgUu)Wf{Q1|n8mEU_ zoQkHOji#TArk{yqo(Z?U5Mh%UZFMou^h~tLnF#v%ND%XEH1kX}^K11+(0pji6pX!=PUP0){ngHDFikB3>NhglvACFp2~<&i*oS^)iU0R503 z{h%Lxzpv#UZ_C}@7CYCO?_9?K?eH?&=4qDVPEe|cS&Eza=GEpK*O(=(G)ZtV212f1 zWfHf-GRoO3XtBZCIeP9>2|0VEy^f2mj84i)lBYHO+nu@MHEQmzske*qI* z!CO@73ltovU)W~7{cn!duoq?THOLG#x)5xW8O6L5LC*-IUkqbrgji<;+h&B=WCqeR z0xU8E7?%SXnE_T80|~kiXmvh-c|O4EoImrNAM>0q#dohw`wx9loV&C{%XgS8-mYX%YbuuePzX<^;*e0{=%lZ@^+OYp!f*zU-w>n zQtonluTe(Gglj>Tw}P#123p+;Wn2%YUk$>Vc{PNY5y-gYYjxG1c`b;3If$MaKu|^i z{bB(9f`HEYGR^`UNqF>OobzFv_GX@1$2hr`e!`29zK(uu9sTIqK|11Pc}Ro^Y!uKw zcZ*$a?`emt02=|11ev9-Hrc$wWD`NA8&{YmtujplzvyC`;50br0pD9D&gv zf%iF31f^O(@dTnrLmL^WK02ZSMO@(V+uGXN)YSCs*)y0Y$>rtCm+kHCsPgiOmnwC4 zoV2T}i_ho3di84PgoA%yym-;l(t_%s$o(~1nYDz&_0b3Hefv7|N!gs-6!Y8u=C^$< zvqBl!;moWMtJ`59=B-fX%}}dbVT_w0Br$J8WM%}>3HCAA z`a&?{Vj$WCo%3Uy@nfF$Cy8;=mvO>}pyNJ6D7lt?gdhSd4+`jjr{zA6LE7VPvD3|B zhnpxw5tifHG;NnJ7diIGe>fo5eVrM}w9bg)h?fo2~6JLwA)EWf>Ei z$e>*tatrbsDk}xNBe#lI+WfLf>Z4S4{%l47ZwGSjeU3%)y~8UB5CGtCIGpV4Y}U|% zHo_3lkINX*z~X=?KviE~k0a#d5~nkAAF_7E2db~1LUN%T*ENUZgO=Z%No(CR7LE;hhNx8%rcckVP)5%^eM z%C0EoylVPLd_*m^2M-<~1yEySW7F4WK%1MJaT&N6yhxwv+uzmIDRRFbIcU$HKO?X6 z;ll@?={iN<+V=Lg)2B~6J1?L7c@{4(uY!UCQIvf6@Zs9EYptxTjEs!*_4OwVEoh?$ z{rE4!T178XP8&zwyLa!588a*_EJTlnqvEH5fdP}rT)uqy_3PK=9EAKA5CcQu>gsB1 zYpboT{pUab`M>}BzyJB4|M~C#{_p?cAO7M036_8T*MI%PAO4`Ou5M;#wsh&z=;&ww z%dkh5wE@5lptODab|A2o6;nq?`^O)D{J;PEzkmPte<#m~HQD&}uYdj9-~RUd@4wg3 z(9qM3rz#xg>DF=yp)}Mab7es(0$x4#&2sPvpANpZZ8M&4LO(dAe{Q^F6ue;@5 z4~t#yU?ShcV=6evL3lLZw2B}oAlE|yIRuY!%gmye0UNP43||b7A7G=-3S0R4EROd- zekB9p*Kq5BLl5E#U%nWzT#adV^ypDHH@Afg7tWhE4i)Zj^I0d^<4?n6iM>zxf-YvZsOzFbjYl78VAa8WH1# zX$d|zDk{p!$!YA^u?n6}Y_mW9@sF04mMd1Qz}{&DbM6!pIPyvil}kV!_A*{ujhPAroZW1k)7kP~XdihNHwkv4!xQ4%tjcO$It zL|6%!OE8qo8==f=1XePjoV)}hP9QXsj0*%%5{4TAH%UL|4}#3iIOWGYK}g$lKqN`W zz)G$IE6IS#2BJ0rk%Mx!1p$$ev!RL{;viF3o2RTMP?8k22O)A$4`~+fY#vLB*Un~9 z%O(cQ*K?gp;^V9c_kHJ2R`7VpCWNSK=pbK=JYMGBy?ZnoO?1r3HokVl4*TeCfEgPX z7x!=fMij3VUFdXrU0vO1ovW&)bIU#3m6?y{ssc%%9Shs@gM(D&KW5bN=8P8=%q?+LoXN*7$Es@#f}VS>%xT# zQjdU6aO%J)sgFrY{!Y#@75^6o)4{>v z@ZrNkVM9`1Fn9p~P)tfNp>mGyO<=hZa*>d>_X^B5P(?z>21*k4>^0_KB!Q9wRV3DfrW&Xs*DnV|5=zKe zXN%b7M!}2qJf`cew1aFtJ#f{YQ^$~=qQ1JatfG|7DSrM8)!aw60DAuNm%m6p0`LSQ zJJ2U@jqhbZKmfQ0v5PS@G%PGE{5Z{xnB5APbT`!QZYI znd87l@Qw(W`DkpE`g!_V*!Y{@{6=g(fG2|-JBrwdS3~R+`1$GB2rfz-8)fnZl2cfL z9^m6=6|BiY(bd%z#YbEhv>M^z;lKOc?<7Y}*aNkpd=?Ur7abEV`Q;NHL3sA7SFcJP z4DfN_WQRS1*a7AIgrJ<4mnZdHWM^@4F%&0IEy_8_NB@fiXgLYCfR8(N?2s?Y1G^Q) zM^*+sGurf4fKyhOQ+Avc3j}Af9i$fg4(l2A_G2!rj0uyYGsCI^8M z`bl^Loey9LtTykBHQ*p2XA2<`LN=`0kh4iWBsj=D0wJ4(NQl}3e;;Bk;2=#0E!nWt zh(P3eXVWNWL!WsFF9h#6FLvFb%(IBJM!YqVjDpMl2-zCE;}1XlAo-M`ev~tFKF7bn zJHqk^b-LKa7#kapF7F5>kk~2kbN%}Dir>__b?YRLK&4W}@lo+34!0$+5&C(_la6kW zBsSu@V0nRhL(Yl7&jGmF+FE7Z#Ely_z?I550{#V)3KVJZ!jU`(MimBV$%!HVfb#zf zc?m`$$#aoCun9tu{7?V%Px24)(f`G_CN?%!RE;5aC8LXtiMg4K%xII_L5{5G$+?M4 zR=gD}(vB5vpBqb(9V-loMr}9SI`jaI8r}8wd%lB%zoLhwTO`NygO> z#ub7ufLbyben13DlK2RntDAB$SYYbZDLB0a6t?WUblbZn<0F z^+U+ngIdpibuVEj@M81k&9L7{0v6mON2+uM7Q+OBMRy&w33LZ+2CRl?QG;XUw;i!p-q|}=bd)xEK1GPm6ahg zE($8XP~xNHbpNmib{VN`(9ZxLA>)E20?y#o8|m^t|MNc(f8oW9xAMaQ8~k-B?a+Vz z=YLA#gRlpVhbK;)K#+*&?m*jR%a#eZk=g*R!Y~XLQ*=kN4buspi-I;|pb#WxXsY2A z4wWH%w&-*^df?3?$+^nHM##HMmMnqyqSQ+jcEbM&sJLLkf<1fpXIRmr24v0}O=b(mE@gc{dbmn;clc z!~|>Onu?VZ7H*e{ZnqZeMHgj=NTw?OJ28-EGi>ErN~kg(SSilUAF= zIUD%T)mc4t{F-U=<9!cZK2ui4twA_IDXPqVOrFONct*f6vv;RrrPrkwVx22EpGOT07w=0WW}>SurMXr4h5R$im0m zTo!z}<(!E87dS<*m^cMoL}iT_%+fID!_I|Ef|VNgi1ER92u=Xw#*KsLrX0ihDC1+f zI6gYC*4qOmWsn^!0kCM38x8ZebxxFBZj5bSyj5-tJuAXeK=iCIW_Ae3@@|0T9e>Na zpa5ch$KQe=UyEDbgLHG9#Z7OEt6t{k+|17olG#}|kojqMv-CBlM^>92B?;{{=7-$r zhY+{zVMRcsm*M`kCi^^XcDP&aay1t-(}=i>e+hgPnG;HE6uzO7jc*eh5kqnR{(W$7 zSn%GBXpzM-3hy0nI{bU2n(&0P1`I1+dDz#;y}%p6sb9W)i+!h0pDvYg#6cizgYy#} zUupxKC0wn~o;?c>P2tswHURk0Xo8IsHEjLqPk%zs&}Jh31>pPOG%kjuk|&-$dzOOn zDnsWW{=&KjD;Es>h<=1k8dod}Sh4Xa;$wS%cTD94W>!1{Y+&97@Q?PnU?D{*(IJ;4 z{A&<2?Q-LxiiB0h7VA84j`22-u(P74W+lwbO`gF@nwk|qEjxZ%PW&{MV2y^5vg4*@ z#|}~4w4AtUS+P@ZMNPgI?szS1@}>u-D9&pgdX z|Dcyqnj1ac-5L;i$iw`A2V*S#1*UY~c4H|5(l?VrIb1bCh;#(a9*i z{WF%W;S#8blZW(iQJ@zl68I#_H<3W1g+zjb0izKG4JJtvTSR-X!TtmLrJfu(i#rnC zSZW*ogO@K~mU;xyPT)JZJqY?F^uY=1{Q2{u8=wspD>R65M0uzIOwCMh-?=TelfD!%Zr z_~QMdjD`Y|G8+p?I@gf5t^87A{u$3Z2bmXQCmdc2KGHhf&H6BryTalC5qU_5#}8@3 z-sxse#?*)^Ah)bG-GFrC^L1ROy_cUvBc?>b#f_gGp|f<#Uuy|52-k0SF}DO<}K$!}A({sPztc^yIK z@(nZ)@uBd$cI_HcHHb_oDNQhrh$xmi&R`s&Jss9|49f^OTB%2n)rng`E&)kt9?T^) zwQ~F%p)Ul-iTcU1jy#I}qkxT~d;w%@QGB$`Pjt*n20l6h8S^%YQ1TE^3g9wt17LJg zPKw>#WagDHtJ49lnJMSlx2r2E9+g+tak)U8y6UR_4^45SG73+6_*<&)PP&|AeAt@+ zN4WVMTx+%8+xUQ|@d0;BHiqz`?K)|y5-FM#&9aYL-9AOkAyI`?~pSB#s6Qa2w zn#;Oisdw?wouhz{L+2J^BQs|M_(ccsiv^n<^EZp2q7Wg0mV-2@FlkERMu(g&mNyg3 zF9lg$2ynl#^&c_CJ{@74hLjavRtMKv z@Ao0FalboVG@3><+Au}QJAUdT_*r11n16u?ZhQg$k$eIvDJfEYqsVK40UNjmcRy(A zM6OP}i5z5*Wo6kWpq)cdyL>$-0>P2d>{BPj(Ef<7MlKym4U^!DjoyEsh0SCKYS}M9M%(0xl;PCQmC&a=e>tem&0cdZ6W%0QXy4&$DkK zHm|b0lvm2-u{m`$)nLX);cg`k(f97{)_RuJ<-`fcycowkYz})`A6#dV=4FoD6+^kt zEQFynL(zt2NiIs+T4F3m(DAdtN8$VWLF0>IBQ$o%ocO`HNB9GvhY)U9lx00RSa^T> z=_ffR5}Xh=piz``5nMfW>Qp&+4ov`{7$Uf=YaYe^&x4Igj)hz7iU+Y#2#+L4imfLX zBu~xX=yW%gej~}?W|;YvAn=aoN^X_$N|8K~SIQ|ZDXy)q`b^l^^`?J+W1+*fjpL8G zTc*2OAM;=wT1!8)j^GOzh^rxE4)+ZOWD66YZF4nCmcYkyZjHcht<*g#GC`I2sK_V{ zyUm$1XXLPt^3f6**^qw;Zh&&m0*xXZ73CiX$r$7ytek^O{TIBJknKZGwm7&|B2@;Q z9AS6{E`0O?>>CzjKLb7{Gg-jL6uZI{l6Ra`kTOIBD=9{f`5PzaZJC_2-RAaY)0>gz z*8<$`q-2!bsVL>u@X9L)*}Ax{rs^}v)}3$q_cY|&58)&8IO4Irh|=3c^=(1c3Si@I zH-dd6@~oVp3}#>&zPSe@Ch&H)+w=3>U5XZsl-NUqcY+?GF!v2{)|$= z4FW2HnSm`IoK@70K>h=F`1rbyz{eY#^-r#4q{pB~jgr z$T3dTsu4|mMw+pqhupl%C}Qz=uNit?GZ(D)L(B}^9qY?0kdZ>kJ1X)LDDhE|Q5tp| zgtx#ade~SPA&!YQ5Nv`17V=L4Y=kqT=w6cT5qa~-KOO#y{JZiq>_X80kN@}&I4yl# z#HKL96CpE*);D%wh7qf~fwDon)& z1d8S*GqXd@v;5uiwhOS4$P8A_Lt?O!&nH_WZ|@*Bo>&W1WSsD3o?M4|u!D7K0FT0? zXYleN<7jrdBZUuvjh^qZu}tXm`0e`iNb&_Trm8ZRnT&xUx)sS~_QiCMYV2pExbr{upB;T`ww+c)fS6yIQ2-cjsEKK&;|d0Dw8M;#v>^HS{cQ-F_? zi+4^b+%ctSnO$gRRiU-24 z=dc$SZI z3i$@{5zhH;%Q|FE@?+Dpiez7ryZf}GeK?f=IO&N7;w9Zel zDoAxI-Zi;+*OcO&Q$aw;LLu!C!AU|H=~T2G^7rI|R0!o(+2Iy<14-Voyp&T_#)IV= z()DM@#;Y4Aob+Ox_9C$HlsDtJA3fcl4h|C7NEW6cILM(g*?T?AQ&t&AFVXXxHDTTC zg#x{!5F1A!SC7b(MX)~dFCkSO0hA_uXOWFo<3P6dAnt4(f-b#|0xRuGAg;2Kf51$F<* zxSY@D6F#VWyuQicj1Tjy4-=_#&-yS<`Z13CGl-`U1I`f(E5zY+=pu$>m!K6VVJiHVL>xYsVIqZ-H1ukqzY$tAv6h3SP8h>T@ zDDBn3SVPlt+QDA9DOb_Xd^=Zi6s@BCt&n!>c0L2zDQMCCoOcU_+6!OMQ9( z6Fhxs!yvB@;{ZgJWseY^2pdeBHcj4v1Z9Cf`r?Hr5;ibIMnF+JmU31 z0$n*i;mEUVZf*|0aU@^ETKIX(w<% zYL0{v*`cK7qWCzVz+T7=1Cbk9zvOVuKHAp0#jj0*b~3=DuXMOIh&Z@!!h zLOyfwhKSpi^Za1tf&vGAG;)rE(hj2-GXrghySuxbctSiXl%>hf&qpmSh z2dGkzK3&!=K-z#iq|`web=eyD2>x*@m`C>hsU`bol+Ynx4Wv}IPHTkiHN#diUYPaZr#Ui4>ApSCnNV#x!g&3toI+ zpe$AlwH}bV8?h&{W(d+mqVT=^;?CgA5Vj-h2qW!BB~{D~Iov98nZpxYKFce5Qf#?% z=T7V;IoZe_xIFl(boI^9%2d0-Cn8rQ;c$_v!d#9BU zGz68qp__Cn+~ttJ-I5iDf(TxDyKj~iJbLipGx3YUdq;!c+qEjjp8EpvOgUk;WLca9)l+I{uONi zl%cJaA3%)Vk?laLUhebZBjR3QDv}esAv$rDLnaQuh0+fMr6@tDj<_~fP6n*f1 z5IeZ==bN{0Zr!?NXe7@xgDXc$?@zP{3eSiJSa>rQ@bQ)tQ3f=Fy#{=S*p18jDGM8AO|8-G|8#7Gr62qd22FIt(8i!ay#&4l z61k0y4FD5pFYDs*-9e#L$sziD*obc&9JJ-spclOu@I%=-+4$H^Mf}7@1$Pv=ysC$XmugcN zxgh#^6oLH^9|hI1Eun-Y_(#}f=1lOOzhFaPTILx7AImFHpOD8M%{;i|S6M9Vo-AI1 zk0L}sFRA5gsA%cE+m*6{6@u4jMQ@i~};T zqQVH&QF7czCQh6PWuO?({aNvGfQ>L$*Onu5#wcTBOiYZ_Cob6upGUAec%y|gRURb2C(>VI8_^vm6HC2d(N1LbL7gt7KO@H+ zp*xQsKNj5pZ5V?Pz#>N)hDHG;7!(n&0q$@zmy^y2PkHyA-LkZ=qmGYugEzy zyrb#0Acw3_=8aJLm0(6@5c5JH1NaC!>xqw7$kOAH^8bPe@eQQ>-Y zdB>5~P{72)ER_mS_*BdKMv6|(kHAN%r~A0hPsc_ol?rx2^p51Wku7o6s#Rn;D6wmX z@)F|-22^;2&xehW=8>LDcp^CsSefCjC_nQzqy;3>7MhYVAi#M!x&#Cr+_)*z3GDHc z#~329!4dlj-c){l!k4dJf5FgL@F-{3wAPF}g!i zBL)GY9x9wl+Bl%m#YaW7K$If*2%7@lI=G%FzCcL`0Fv)sLq04#9VntPfMW0@_7kTD z&m{VM_wL;bXH2opgggZ3I=zv6Tpq1AU=bZEHAi;j0YcCO#n^Uv9BM zMjb-feZzHr(X9$B)14hg4x4g778irh1|0Y$FM#YB1|J32 zq&POlR%O^0q&Tqm&E}q%TYP*;$?^H@6Eis{XK_!?E| z4>3I_F+(L5R3?<~GK~ihbG4hho0zH@;0r-kY+8u^gk}Z>SRsr`oRjE)Z5zIga*&Bsc;e2ML`V zf=Gy*!8tmGlV(@E+m^Lub#eOjXEn&(E$eDWz5hKvW(x3;6@h#h^y@){c%2bQ&k%^$ zaPxte_z?TZNMDDLnQs;!F+>omq)OO2x;iOy{SlRFd!MVb*AD{{eUcS2pB;k8lVBvhQ3Ly1#z$}=@MDlvx%~Mr zf8M-hGxXk4X9L+O3m;+pljZs#=V-G2h5GJNC8w{2kGM{78ywN&@hIk_?>iI8f+;QO5UAa$oe|6Ox>qES2jQ z9$};nCXoXAnVj*|0v>P-R@t^`=(>>{;QK+2PFWaGSf4*4M)smq_hn0Q~|C*aB=k?au%%aPax$SarS4?a@8qi`G$<^Uc-bTXg~wgU;QWT|S%ePv-IRQJ$wOLb5ao>cM1 z=Zm!-sa-~R&4@d*x3>oviVYe*3b$|FCO7+v*ofy66cj99P{frZM-h~K!kZFpydn64 zH*ZkAN)8JNYz2TK*PG~`qHV~m?(S~GGO%KUDCeh@wN+NOyqGRrjE|3xRL252haL7` z(8Gf{9afn!iueeH;nVKc3s1SxWf^M<(^qhgxt1RFEIsN`dTb3ZU4)LW;Rz{yRoU^t z>J04F`vpa6;S+D~?b(=rjd3Q{^h%&TAy`LP=S13N#n{}6WL^tlUJ4?_YXTqr8E5^O z!{H;5mXbS~KH(pYkKlwPeS9G9Kp7)H0xAkwwKQIEA&7#qd)1;lMac$FIJ0QO40ch4^tA7Qlu(=BP8g)svC4&||Hm@{{SY#BmWKp8fZe8~E@KN-hh&D`qP&-Rb5^zFt1SjnHi^WD5l@N9$RfL1S zjU3;vf{if30`Z3R$cBfCpPwJ@g1l5&Yskh(nCRoT&#?@fPr9j5T00zND65z-@6MzadW?NPoMhft8L(rj2 zfzKoIQJ|J0hm9#X(z_mY-6KE{P8AXpWDFA213Kkg-Y*s(@gl&pPbxkNOI3X>d_=MY zs6fRW21Oq(E*mdC3T?=FvciAC(H$u$WI5=7Lq)bC;l^?r&_yGC(}INyP}fI}ye@j3 z`3)dUTthzuPgfAoSq4)sF_zfo=2(|D(g8QV{(cq$p%F!co zPXsHDcTKZQn=S59VCv4 zGr}ANsR%;z&^iBvpNO}Fsus5ceKOq3h2cYro_g3*>L9 zHzlVNyNuSMa26a5Vm*rG!Qp?;-aX(B$ptD~Xgb03!CL~SlXE>I{TI#%CwkaHkun4Z zXe1jLI_F0GM6QvVni_oXu-QjDTYPzJ#I^SJ5qv>kFTP+yOMhPFvesjdb8f zv_10RZYjLQJih_>2;HL{E7~eMj0xE~CmKpf;3Faonb(OZnnC|?A_&`$e%#x9>uSTO z#b!~la1LN17ibspUVqjr1Vq4%7msG>oE`Y7h`DFjtNQZz*xb@m zSyKb5s;vb9;_Irb>Z_|zC;?Ohs;#cCs|8h6R5ZPKG3+hK+5q2-VHen~6JWPK8*6es z%po@xL_lJ8w6zc$Zwasw25hj8WcUoQF@2rI_BF z`h!Dp1;I=#_l(${zgTPp3j)Sk(wi7b65$|4j?$-NqaykOu_vPZ`87x23*%$+i9Ey zZ)~`K@1B4JZK8*0+-qof+;FeFx+?PaDXYx&X1622I|3h}egr;RWfL}Ro194NyWz}R zp>&AX&_I&-2m{RtAB&x?-z+}D;~G4oB;$yLP6%|yOD=2B(ErQCM@W7c8A*?8GDy9I zJ(xvU5qyL`Pkuy_oWWQA>zf*ZFN}@%8t)Ys6qa*Jt9WGhaMJX2v6Dv^In#Y5t83q5EUm;3=WUfE8b+x&9EKW%=hYey2sDz6)dIX)NY<4x9 z!^&rc-#Bi2Ey?U|gl%r511s7d*vN`u=ERWxJ=WQT>XCjum~nY1y6|Ekio06ubTf%v zN{?RdvOD_7tqX+Sk>Cpivh`QzA78zC2?f4XA6nhfRdsDeWo>0;T}63ad3i05KuK6R%F0V&2d&0;;a4XsD`sTv-e5H15uM`)kSOw<8=`F?OstCM(7|FUAJ`k8lnL?+Df7 zoiOIjQ0CPTW@Zqq*YNMLI^jp(>292`j2=tiQcF%Y5BDW>E9&-@i|)!|x~N z>iHSR6T)f$7d%N|L=TjXqUjeE;T=)W2|g|uE-bLsD!w4w2Q01luEB5yosigk{)T`4 ztn~$-9UtGl>jysq_V4v;XlU;@G?amHD=Vww@lb&fr3b(#R+pC5msSuxW!&2Gvc|Hq zN-&h*Co4G>r6mn40uQR0h6Nl4jAHcIq+?k3?sxCBGnAs>%hzjcTof%$-xKH`4J2Dg-;9|Rz}6F zAiQj4VT3olo&uJCd)^KKovb$Pp`h3W^*mo>TaA9E5e2qYr={f#6~cV0^X6tMu^vlodNr3burL- z5F3}9B(8AY8*}1TCR7F0aOoLsY(!3FMII|Y9PnU6xY@XIGHX;K#UI>OhCP}XHWGY+ z5Ng2<{Z-_n0KPzxIP9%r1B6^6H4tL(WcgadcMl1?M59m?$4B9~jkJNBGhiVV4<=FM zPLZ*FtF zy4mgO7T1i8t1^;TUfQ@KGtng@Va25+m(2CcGvZt_;@?wd+{*Lu&ZmF;rs|C5E*#n6dA(nsjI7t*IabmWVOZcQTS^AwO}Ke zj0=M%HeC1#3m_DG$m5H6IaE}EH?*99VWe9?(m=7i4qpHp!LZ^>Hf(hOS&vWIKRP2I z)E9gveC&PQms!i1cWR6AW^a13J9Cp8V}q+j*fR6rMP>mDO#J5y$=GkcvG06y|3#L; zODFowv-VpYxg#+PLcwJQ+aW<7t?>98uTK`(uvXxQMH)hDUhW zD9hIHG(lJ(qF*5Yi7v=90d*PlTF53V>AxccHL`&?8F=j}XiWWU$45lgp>hcZO?0@( zHqzAKUl?aO_{4k#-$WQUsG2{*mHo}QD+VC`h&Y_!RZbI6Z#$c?un^p3IC zgS?{v9&;jKy+(Elq^2N}Kcf1~=>Us^o<_S|5OED`Ouvx<8$7TPxhOcJT3_Z9-#e(j zP`hN;?p=!WB7g1JsKAJQpf8ZVJ)~Rb<<9Vpl60)k_}W2eP~xNH;D7m^ z(Z$EN1MeR6wH&Q0a=CKU?93*^j6_CeBJ*6b*~yLO$JSe>MOz+Xsvr-aKXxz|vO2pZABL3LM@TDbPSa(U4a zhn$@jc`0^9sZNDkk)oVYxD{lVyVZuZjghxQDp};EnzOdkb9Y-8?XW9IwaH1aVMROS z#XGXr10R_J>os~n5U)w+N2|NxjGH0!t3iZw_!)nTG*6?Q0({&TbNpr|VV8kb3AP`0 z@r#ejgA2RKD7{{ThXFVu)gzSrBYM&PitrH{h0z@`91T`yBwd9{QmW+`5gFh0hxuir5HXyx-e$27Uati(Y7RLF_jPsiY z>D(rUfNajE*j?OieKOf1#oze1TV)Bt*C({+;x5!#Z^EY}l{? zI<%j9{@_Q*?v9igkBriIX|X<2j(V#d=W8Ves3!!d z9`n~o@K=xbQ=^M|9A7PVv%F};`f4SFXde$#+v}s~Z8^cwcrwq}obMEqc%-0ETvduF zAClbxvkm+D)}6Ahth(yug$I;{q0@7B>f-j<;ys3Wkve&ix_P_xb9U?I?9vwR*2;_0 z&JMp@L_4&y!g|VfO;MP7R)|KruV#jaw$Otk^wQ4s(8}~s&+=3w9yYY48|i)Qu^oGL zqe_C4%Ar+CQTD&ljYUOeMTNw-mgHZ*_QZM2kj4(T2L6LTD|{WPN^s8(IA%!e@;lP0 z;c|j*T()f4{Yjz_$0rmDA81kij*AdU3+~T0i}pWJ3=@)QA!Fbl6dIa=K`Y!u1|b57 z;O_reaQK-1>gMJK3lj0;c;Lo+PwRo&AY^npBA)Gu<=i`t(EkB2pFXJso}zwi!acYu z)729M6JkAPCi!p@eKivURO16x69P38{WaqGn#cV#6Zq<}zG_D{tH=7_S>>RQ@*zLf z_^oQO!PA2s$2yxA2}y4+$e9So@BeOc#CdsNO#E&))fhKvjTL5{_?`f^3@i3>k7T0 z8PzkrH8Z`TAE6mlvJm+QBa-MxtzP{oPmY_q9=0PHy*})u6lFJM3wL_Z67)UpOF~=^ z3E_l(ym0YCOTRa`df*8{3i@^{V>Z#NWg*As{8 zii32;!8+m~QgQ-yvIBIo{dGjX@}iyP#mV;6&+*X_ZC20RtdRll8oCklWyr8KQH(-2 zOCRSvIGiLzJNx_Eks<-%Q_yVDXdE`weG#GC(ay#%#`8>;;G2?;V;aK|5BXs}o1a@~+bQDB#pWyn4sm~&kp(5)aVnh++ zQ&V#h5@VFn{ZeJt^j@V1n!`WTetZ=CH2P2QBCo2kvn}vUs#cosj3h5kS}-R)Nas`l zCtbif8Kj*m&`b(IftJ)b#)loL5$6l@QT>RQ>R}(%qXP9qfzv`(eYa-*_qx<&LJthe^cIawH+jD!VtSDL5jlL2 z85fZ~DhLig`m3DUtdkk4lNq9u z5y(mVjedl;P09)4YC}b$s2)XmuzoBnE-t|xQ41uc+bZL=yE#8G+j%76h!MZN~z+?I;n8OUlb1q8p>< z6zrWXf%&+bHXr{A-8eI8je6WFrM>phjT+nS9QX1MpNv0KQQpYJ@IL<4|BtW>9>2NF zmj7)Dl$4Z2M@OUcW?i3$v$He0=yC6#$DH{o*N<8vfpV6ge(n}s^#0`q8)kg8>r`yNAdbI|>Gf8QftCu?($iuCT)k6J=^GPjV# zgp+AAWNaow-uH;1>$NkFIZfSfKR#@!QP|3TqJ)OV$GTPSzyB&=j*yW6BY_7=phrJK zH}dt!AP5GG1$w!`hB;eM^m9UW`l!e5VVVR#()*ZzxN(7UwA(a4TLq6Li^IK67G=Ym z$B4rN2OS=y8~5}K&2;NVq8{_19>WaC(99OS+)%w7sK+PJjffB@F+IKNk=BiB^eq)I zv2e;k-n1RdR{k1XeWm6>!eG|@=On=N7yRc=`4b&5tshf#gaI=%`T9^sxpMs&0?lZU z6N+aPSdcoWg6|Gv_vlB}grFHQ-cy5DPWG~18nIbeR@fQIOGVx%r6TaQ6r5M>1}mvR3KXD#d+{^A30( zC>A!hw?2`Sn2i|;FcSE$NuXCZstWlA=y%K`el=`IgWOP*Zrv!Ny=!`yMl%`X*Zvrw z(M&|lc+iZ!K9fUMP4cu_8qLo-U)K5XyApQm#)23f31WKo!LObz+4tw_Mq(Gxc^}nJ z(Q!Q~YZQ(yH$3RFO%!{zu^uxi|7$0kIbKErj0B!Y0>9CZbZ?(rKceeVKQ9E{EmWjl zcBqapgpARo26Yc$CkJRG2~-n;l@9n$+qQ1H_cE*9ey1*$b;urNJO0V~kwg2|NZKRs zdL%g?GhAk#T&I|{a>nts>vCc));&6{>=Ox)*_e^QKTHBlf5Fo`A>H~hBXH&^J{gzE z3(*xrEpE|;OO251yilx!&y7An9bqtHA@9ccBnN6F2G5KOm=f-+7_ef^(eSbxmynI; zVNH|Uk3Sd0=-#y-dxvK3`qoDII}GW*M-0ti*L2_GJ-#&w3+K>|^*z3|1|lDkog#CS zX2vFsbj-RYd3%tqM=5c&;)&I^1yNb`=N{HPW<4VTMgoik`bvP-jj5O*Gc%p9mnR^D zGUBazIbpgvTji5h4Dv$ZYLl_!>>!;C#EA>EQpn&np0!d1%18Vrg*)r-@!nM|?0UFj z#2&FD#Do7Li7|j~>>jg5r(<{6Bi-+asPKqc`8$osteI{4d0X{~X2!V8UESC-Uj|cV z_}aqF@=iy2zauhIsAjsU5ZlpBH{C<~q`O+iCZ!B##iUglCpK8*>`bq#?0ewP=rUs$ zJfkO$Y4$TI0l9wE$`UAv_#APdR<1wvQSYEN9Bb&uZu2oa1hZ*4X+iQRNcCiYr9|K9 z`@ClF4-6_uu5PL8|M+|R9$`D8*O4yhev}7?levY4`B8>>QAT;tk~Hj14o=*M67cFg4Z5MjVxU^FqhN z$Bg=KIrAwa0Y(CUvjqMwy8!wT`6#rr0%nMOHN`=ix&C^&fpc<0e}H=p*OruEy&Sqg zKMF&k9}yz1krJSo_olJM# z4CFG~F!Pl2v^1y5Cmj{i*38QZiMg2D($&#_^O#Q=2{001B+yp^jdUI}?aZJ_ncmZL z{FHP2RCD~avjerW1!~y>brey6yl7w{(@!OZubjxAa@=R~QBT980$1UY)Ag78R`{@G zVISEUSBg*2BbC!IA;d9FIWKlOS= zZA*i+U%&rhZD!X0B@+0%?1I1K;Fz5}(-LUy>O6G0cv1d7bzzXEC{QOSm_xc5gY~mR z^hI>R0$mKUgAK9*Iq89FC;gO?{pKcz1?C+ux_Z8&>#-8TAP=svuG+EakY@IFHStb$ zakyIEPPIHdhpFa;(q#*tRq}$Aas!oe1C+D*%GtguqVA$3^is<5Bn1oI#a&5wwU5fG{NNb{)L7678LAVAlbX1 zAjYI{kGw1>jQ)j`C{hX{7D&QP@*{c+-rlpO_aiJU$Pc%YM6NE{<606MT$Oq9T4h~3 zGoAWxySA7eGZOe0NB~+3-Ey}(S{fd&)VDX?>S)HDw7RH=M({jPEad)MogHVI>r!u4 zC0#2|x>lBSwG7Y6J%y~fTS}9!l_p>9DLvctZrpbP-#T%%q`2WyU0Y*)d-I)+wsvL| z=7HycS^3vU;5T-`U$aYQFaH(^v`af%yV~0xP&(SWeosNiCMx{5h=(~2Mgoik7zsR! z5@76tXYs5tZDu6ENPv+5BLPMNe~ttcSi=VXIW;r2KC2QK$x>lG&l<#<#?sf&S6yMs zwVd+eEE}GQCELWx#=)A$wy|dO7IWFA4t922ytS})w6QSdj$w0Gm|8lRSy(S(&sLf> zb|h;mYcQ%A|Ilj8$#xH^4PRGbz3_WlVPW0371LO6u)GIw2Ab)d{NE%xyZiu_@xbW= zUu4A(?nyTPKI=8sbFZ=n;QavBtN0r*U?1xv7Mt~`=&cv=jiEz^uvmkKv0j`de&MBo z!@BEyj`dQ*AXdQbL9AC;15xYCcs9ds$nxuWX0e8`hOoL~ZWrk+)K~n4UjHS%el-2; zLa$kmZF(M`4jhC<_5g3lWB~r0u|?V-a`)=DfGhp24fOM8_?9{Ss8#&D&+@3=+O8^o z(Pudv$2t_PBefD47!Nca_#OQ?t=RKAfb|3F9>Q9KBcUJqugPB_NEYikK#plh_wf&8 z4I*zL&e!O5z}o@>hE@*KE!Y!fpJTMK(ra`|yB zEqG(uvn?D=tQ_pw+(le`!J5Y%%QoU-Hx738SZnQIZOXRiTG`^OJR3Wdv1}zvi)9Wb zR%}ZLQyw<4b+98F;&PW)sODgA%4IupE$R2zyOouVnGM_C#MXjq%{H@GWNl$@Z-Hj^ zERALBb1g0L8{yh7bKtTUn=G{8p&@K58@q)TY@VHkJr6tNq8ZjW4AN8{+ZsnhFIbsa za_t=k4U}I%hWJSu@k~sYmf16laDapJJi=LEfQ`2MM3Ijx}<~P}HJ;&B@swiN67?cUdhIh0;b*9h9t->XK4j zG79lKy+ukPHBXnUt6h5MW@~FhC)G|#Td9`Y6taZ1c2Rd^)E$btEv0U>bTza}+oj~2 zs1PZ{&_&omNl~YaLh_2<3b8C}Yi(_5LZp$bwN2WN9b+b9D|Mk(T3b)m+@flmWf+KV zLba$L+eoQ)+1)xjDU8OTlHNLT-@h*0xT&e7rKznA`>kuIDsHq0tFOjnlzPWx1{@NH z9M0X9l%HODp|tkam1gN3Db<1s@!Zo+93(WdJZ%w3F@d021S%8xphD6Xz)XvdaKG9pPFIF43E2DExB#0o6S5U`HT zF0^(YEg%o2Mj6CN z3Ozn{Pn{%f683nLqS7v0TX!IFZfJ~d*kO(6B*nd_#_u}yv1iODOTE4{TK0+3{7)5) zHBB7(k#WTrZ{n2UfDz6hLo-RFXm2}7BLNzDNlNW6J#Tj)*)gNUwdVTbvRcLD@^P_+ z9|(?pH07cEKy7~D*$!j4eo_E?Y%qnn%iTgVoo41q}jM1}L<`I^D`fNQ!*@n}Ps|w9U zQ|UsQj*C>*(Mi>JP?c>|MAdbNtP;wx4Asc;1XZ$Y>o)(flQ4 zv$1pRHtaZDUfa-wrjRp4Dxs5=(h@+k+q-1VUF}WMrVi?cjQaKb6>eOXY4%x8#@SK7 z=Drq`z*-m0vfs)w@nLB=u%<71Roi;fB4@#ojM^qCxzsyl7$$6ybv04aCS2h}!jOgx z*RHfphAnUgPn@k?6q7tRE{~I3G9gwnA}o3EroBU5_C9aBjWx@8fTrCWhP=rZt^o(r zYHoL+hDMp}woF<_Nw3o7rnIxUv#YBEb<&5>PK2FWb=~n$#?q6;3&a<-GS7`Uko#uH zv9|&ehHpCf;*#LOv)8?0=AGp~1Kfr2N;3 zGlpxaS$d$T#wn#>W$9J(%6ir8^IybE-rbe<_O_(Ay<%Un-8Oix%c}ldC| zU5g6a+Nl=&R4J;Sz7%hfmQ$@AvQGTETf1(zw4W@y;1Zu{l`3(lX`EYC|8-i)h`8M0 z`!iqJmN<0dzCo5j&&_pyb(X`2b8Kz=BgN;hHlrQwxSKnE{#0!nb*8cN^vy=`kgD*Smcxj>M>vLDJ z=h!aw-I-l|8J9GEDs6YDste7>#MNQRxgN2}Yxcz2ZQafF*)nINzs@GXI46(MUR(5q zr!AYi=H2O8juaUL$B$Ya{Lbot5BX8!qT?nXO4dFuT9kiwO-<9r8=XGQo$GIR zEWOcrPf6=Z(ne*FDadxzQYK%FnnUOw7D#j>x*eM86sE9rqrTPJ;9UpL-)Ly-q}sZO zB7%JJb0r!O|A!IAfwfR|_^)~8#;r|L^N0|2Hw$3lSgfods9xc9)&; zJ6*XZx6JHB_D_e?7aYl&yH}_inXMU~XB3gWWUp}J(d>lci?xmIO{lQ7nIfZ=(D!H= z)KoK66KOB8Vj8K=4(t!=zOai*?WDHX-ddAgz9y^OyYTXglydXe#F%Ika_<$Im6+>%QxC zEKKce>}||>Y}zkp%^)9^c(7hukUM~0j%!`EJF#>CkvH>TB)~|3kpLqBMgoik7zr>E zU?jjufRO+r0Y(CkD}ny}|A8gql5Jy03=0T`|39Yv0P+8a!$*GsuXv$-Q{t~{05Vu; z21wt)20(mVZ$SL}KtTNcBGA{rC;m9?Q`=z}%#z18x3J`5Rj)sO*B4f?v9q;-TaBSZ zd@djOy8HS+?2P!sq>n*UK2%K)nLcRji*8W&cs9qvnrqK`8Q(^S1HD&L^BfzyrS^-t zT%LXJs3+-+AP1t0K}j>Vp^5b(e2?srNP&4U5@002NPv+5BLPMNj06}7FcM%Sz(|0R z03(5az66K|OMG(T%@Qw|jN6kjd*b60f0_i8k|+e?8519#xWTUi;_nk5|8+pd^vU== z@#jgX^4s7YK*rSn54;CP01^`XJ|OP&2Y?Me1RsHq!6)ET@EQ0Vi~?VPFTrRq27Cp` zK>F8U9QX!&3%gYjSjm}Tlz|GE2~>d^PzR)6LlbBL zZNLFKKo{r%eP93#ff1MmegLz<9AFIQf*-+8;Ab!o%m=@K1;7L>1g5|YkPZ-Yum~&$ z7GMcj3M_#YAblV2;0=7hX5b6>zz_I?01yZSAP5A55D*HsfURH~2m{-}4zLr1g9s1_qChm* z1$KiyAO`FO`@nwiD>wiSf!7-2kj)M~*5hQ_RkOES{NstE8!6}dd zGJz0e0TIXsIUpD00Wru25>NmNK@m6&ia`k|1!bTdRDeoQ1cK700B(ajpb<2IX3zp!K^tfX9iS6*0V$9H3Xtxr0bn3_ z4h#a%0}^Nc0vG~@f)~L{;AQX%con<`hJn|?8{kbq`Uc+uZ-aNhyWs!8dtd|@3El_) z10Mi3_z-*qJ_etFPr+y4b1({g0loyI!5Hur7z@4zffmpP9H0YqfgaEY2EY&)fmz@OFdNJPB+h9r z_!0aBeg^ZveDDic08GF_U<%9t7np-ZU@@=&OTbcK39NuMumL1~Y8kKt_J9W*fFoEA zR)CdY6>tJ1&T0)<3)X@4z!|szSFizW1e<^xa0ed16L{HXfeerdgdhuuKsLw$xgZaSK|YXx0#FEwz-dqn zNp0u}%hun?F6Gr$GrU=dgh zEWi@56j%Z)U=3`5Em#KZfIZ*=2jB>ngB4&USOuKGYOn^7sMB?LT@Rdr3vdM+z(znm zcf+eY@Bp5`3wQ$`Kt3na10KYqB3kh^=J5}bfFUeN8(5BPSiaVHwWBRYVjMi)<84?B zJ%@u!gEedjd6wH&cu}L@bU(2-a#xVpRdS8=tXHP@zqIG=6Tc#Mxp)7h9gkZfk=iznO!1^a` T;L>N{$=WmNcO3twKKuWFt;$r$ literal 0 HcmV?d00001 diff --git a/samples/templates/GnumericTest.gnumeric b/samples/templates/GnumericTest.gnumeric index ea2fac379e51e707060c8c8d5314899c60bad287..0493e762360924353d0d60a1709528637db0224b 100644 GIT binary patch literal 8064 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 False + + + + @@ -28,7 +41,7 @@ - + Test String 1 @@ -38,7 +51,16 @@ - 1 + 1 + + + 12 + + + 11 + + + 1960-12-19T00:00:00.000
diff --git a/samples/templates/excel2003.xml b/samples/templates/excel2003.xml new file mode 100644 index 0000000000..cd1188a72b --- /dev/null +++ b/samples/templates/excel2003.xml @@ -0,0 +1,944 @@ + + + + + Xml2003 Workbook + Test Gnumeric Workbook Subject + Mark Baker + PHPExcel Xml Reader Test Keywords + Some comments about the PHPExcel Gnumeric Reader + Owen Leibman + 2010-09-02T20:48:39Z + 2010-09-02T20:48:39Z + PHPExcel Xml Reader Test Category + Maarten Balliauw + PHPExcel + https://github.com/PHPOffice/PhpSpreadsheet + 16.00 + + + AbCd1234 + 2019-01-31T07:00:00Z + 1 + 3 + 2 + 3.14159 + + + + + + 8964 + 23040 + 32767 + 32767 + False + False + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Test String 1Test for a simple colour-formatted string + 1 + 5 + A + E + 6 + AE + + + Test - String 2 + 2 + 6 + B + F + 8 + BF + Dot + + + Test #3 + 3 + 7 + C + G + 10 + CG + Red + Red + Dash + + + Test with (") in string + 4 + 8 + D + H + 12 + DH + Orange + Orange + Dash/Dot/Dot + + + 10 + 26 + 36 + Yellow + Yellow + Dash/Dot + + + Test #3 + 1.23 + 1 + 1 + 22 + Green + Green + Thin Line + + + Test #3 + 2.34 + 0 + 0 + 36 + Blue + Blue + Thick Dash/Dot/Dot + + + Test #3 + 3.45 + Purple + Purple + Variant Thick Dash/Dot/Dot + + + Pink + Pink + Thick Dash/Dot + + + 1960-12-19T00:00:00.000 + TOP + 0 + Brown + Brown + Thick Dash + + + 1.5 + #DIV/0! + Thick Line + + + BOTTOM + Extra Thick Line + + + 1899-12-31T02:30:00.000 + Мойва сушенаяTests for UTF-8 content + Double Line + + + LEFT + Ärendetext + + + 1960-12-19T01:30:00.000 + Højde + + + RIGHT + + + + BOX + + Test Column 1 + + + + + + + Test Column 2 + + Patterned + Patterned 2 + + + + + Test Column 3 + + + PhpSpreadsheet + + + + + + + + + + + + + + + + Underline None + + Rotate 90 + Rotate 45 + Rotate -90 + Rotate -45 + + + + + + + Underline 1 + Subscript + + + + + + + Underline 2 + Superscript + + + + + + + Underline 3 + + + + + + + + Underline 4 + + + + + + + + + + + + + + + + + + + + + + + + I don't know if Gnumeric supports Rich Text in the same way as Excel, And this row should be autofit height with text wrap + + + + + + + + + + + + + + Blue with underline + + + + + + + + + + + + + + 5 + 5 + #NAME? + + + + + + + + + + + + + + Hidden row above + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +